'The grammar here is based on the description in the `FITS
standard
<http://fits.gsfc.nasa.gov/standard30/fits_standard30aa.pdf>`_,
Section 4.3, which is not terribly precise.  The exact grammar
is here is based on the YACC grammar in the `unity library
<https://bitbucket.org/nxg/unity/>`_.
This same grammar is used by the `"fits"` and `"vounit"`
formats, the only difference being the set of available unit
strings.'

@classmethod
def _make_parser(cls):

    from ...extern.ply import yacc
    tokens = cls._tokens
    def p_main(p):
        u'\n                                                main    :    product_of_units\n                                                                    |    factor    product_of_units\n                                                                    |    factor    product    product_of_units\n                                                                    |    division_product_of_units\n                                                                    |    factor    division_product_of_units\n                                                                    |    factor    product    division_product_of_units\n                                                                    |    inverse_unit\n                                                                    |    factor    inverse_unit\n                                                                    |    factor    product    inverse_unit\n                                                                    |    factor\n                                                '
        from ..core import Unit
        if (len(p) == 2):
            p[0] = Unit(p[1])
        elif (len(p) == 3):
            p[0] = Unit((p[1] * p[2]))
        elif (len(p) == 4):
            p[0] = Unit((p[1] * p[3]))
    def p_division_product_of_units(p):
        u'\n                                                division_product_of_units    :    division_product_of_units    division    product_of_units\n                                                                                                                                                        |    product_of_units\n                                                '
        from ..core import Unit
        if (len(p) == 4):
            p[0] = Unit((p[1] / p[3]))
        else:
            p[0] = p[1]
    def p_inverse_unit(p):
        u'\n                                                inverse_unit    :    division    unit_expression\n                                                '
        p[0] = (p[2] ** (-1))
    def p_factor(p):
        u'\n                                                factor    :    factor_fits\n                                                                            |    factor_float\n                                                                            |    factor_int\n                                                '
        p[0] = p[1]
    def p_factor_float(p):
        u'\n                                                factor_float    :    signed_float\n                                                                                                    |    signed_float    UINT    signed_int\n                                                                                                    |    signed_float    UINT    power    numeric_power\n                                                '
        if (cls.name == u'fits'):
            raise ValueError(u'Numeric    factor    not    supported    by    FITS')
        if (len(p) == 4):
            p[0] = (p[1] * (p[2] ** float(p[3])))
        elif (len(p) == 5):
            p[0] = (p[1] * (p[2] ** float(p[4])))
        elif (len(p) == 2):
            p[0] = p[1]
    def p_factor_int(p):
        u'\n                                                factor_int    :    UINT\n                                                                                            |    UINT    signed_int\n                                                                                            |    UINT    power    numeric_power\n                                                                                            |    UINT    UINT    signed_int\n                                                                                            |    UINT    UINT    power    numeric_power\n                                                '
        if (cls.name == u'fits'):
            raise ValueError(u'Numeric    factor    not    supported    by    FITS')
        if (len(p) == 2):
            p[0] = p[1]
        elif (len(p) == 3):
            p[0] = (p[1] ** float(p[2]))
        elif (len(p) == 4):
            if isinstance(p[2], int):
                p[0] = (p[1] * (p[2] ** float(p[3])))
            else:
                p[0] = (p[1] ** float(p[3]))
        elif (len(p) == 5):
            p[0] = (p[1] * (p[2] ** p[4]))
    def p_factor_fits(p):
        u'\n                                                factor_fits    :    UINT    power    OPEN_PAREN    signed_int    CLOSE_PAREN\n                                                                                                |    UINT    power    signed_int\n                                                                                                |    UINT    SIGN    UINT\n                                                                                                |    UINT    OPEN_PAREN    signed_int    CLOSE_PAREN\n                                                '
        if (p[1] != 10):
            if (cls.name == u'fits'):
                raise ValueError(u'Base    must    be    10')
            else:
                return
        if (len(p) == 4):
            if (p[2] in (u'**', u'^')):
                p[0] = (10 ** p[3])
            else:
                p[0] = (10 ** (p[2] * p[3]))
        elif (len(p) == 5):
            p[0] = (10 ** p[3])
        elif (len(p) == 6):
            p[0] = (10 ** p[4])
    def p_product_of_units(p):
        u'\n                                                product_of_units    :    unit_expression    product    product_of_units\n                                                                                                                    |    unit_expression    product_of_units\n                                                                                                                    |    unit_expression\n                                                '
        if (len(p) == 2):
            p[0] = p[1]
        elif (len(p) == 3):
            p[0] = (p[1] * p[2])
        else:
            p[0] = (p[1] * p[3])
    def p_unit_expression(p):
        u'\n                                                unit_expression    :    function\n                                                                                                                |    unit_with_power\n                                                                                                                |    OPEN_PAREN    product_of_units    CLOSE_PAREN\n                                                '
        if (len(p) == 2):
            p[0] = p[1]
        else:
            p[0] = p[2]
    def p_unit_with_power(p):
        u'\n                                                unit_with_power    :    UNIT    power    numeric_power\n                                                                                                                |    UNIT    numeric_power\n                                                                                                                |    UNIT\n                                                '
        if (len(p) == 2):
            p[0] = p[1]
        elif (len(p) == 3):
            p[0] = (p[1] ** p[2])
        else:
            p[0] = (p[1] ** p[3])
    def p_numeric_power(p):
        u'\n                                                numeric_power    :    sign    UINT\n                                                                                                        |    OPEN_PAREN    paren_expr    CLOSE_PAREN\n                                                '
        if (len(p) == 3):
            p[0] = (p[1] * p[2])
        elif (len(p) == 4):
            p[0] = p[2]
    def p_paren_expr(p):
        u'\n                                                paren_expr    :    sign    UINT\n                                                                                            |    signed_float\n                                                                                            |    frac\n                                                '
        if (len(p) == 3):
            p[0] = (p[1] * p[2])
        else:
            p[0] = p[1]
    def p_frac(p):
        u'\n                                                frac    :    sign    UINT    division    sign    UINT\n                                                '
        p[0] = ((p[1] * p[2]) / (p[4] * p[5]))
    def p_sign(p):
        u'\n                                                sign    :    SIGN\n                                                                    |\n                                                '
        if (len(p) == 2):
            p[0] = p[1]
        else:
            p[0] = 1.0
    def p_product(p):
        u'\n                                                product    :    STAR\n                                                                                |    PERIOD\n                                                '
        pass
    def p_division(p):
        u'\n                                                division    :    SOLIDUS\n                                                '
        pass
    def p_power(p):
        u'\n                                                power    :    DOUBLE_STAR\n                                                                        |    CARET\n                                                '
        p[0] = p[1]
    def p_signed_int(p):
        u'\n                                                signed_int    :    SIGN    UINT\n                                                '
        p[0] = (p[1] * p[2])
    def p_signed_float(p):
        u'\n                                                signed_float    :    sign    UINT\n                                                                                                    |    sign    UFLOAT\n                                                '
        p[0] = (p[1] * p[2])
    def p_function_name(p):
        u'\n                                                function_name    :    FUNCNAME\n                                                '
        p[0] = p[1]
    def p_function(p):
        u'\n                                                function    :    function_name    OPEN_PAREN    main    CLOSE_PAREN\n                                                '
        if (p[1] == u'sqrt'):
            p[0] = (p[3] ** 0.5)
            return
        elif (p[1] in (u'mag', u'dB', u'dex')):
            function_unit = cls._parse_unit(p[1])
            if callable(function_unit):
                p[0] = function_unit(p[3])
                return
        raise ValueError(u"'{0}'    is    not    a    recognized    function".format(p[1]))
    def p_error(p):
        raise ValueError()
    parser = yacc.yacc(debug=False, tabmodule=str(u'generic_parsetab'), outputdir=os.path.dirname(__file__))
    return parser
