'The grammar here is based on the description in the
`Specification of Physical Units within OGIP FITS files
<http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/ogip_93_001/>`__,
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/>`_.'

@classmethod
def _make_parser(cls):

    from ...extern.ply import yacc
    tokens = cls._tokens
    def p_main(p):
        u'\n                                                main    :    UNKNOWN\n                                                                    |    complete_expression\n                                                                    |    scale_factor    complete_expression\n                                                                    |    scale_factor    WHITESPACE    complete_expression\n                                                '
        if (len(p) == 4):
            p[0] = (p[1] * p[3])
        elif (len(p) == 3):
            p[0] = (p[1] * p[2])
        else:
            p[0] = p[1]
    def p_complete_expression(p):
        u'\n                                                complete_expression    :    product_of_units\n                                                '
        p[0] = p[1]
    def p_product_of_units(p):
        u'\n                                                product_of_units    :    unit_expression\n                                                                                                                    |    division    unit_expression\n                                                                                                                    |    product_of_units    product    unit_expression\n                                                                                                                    |    product_of_units    division    unit_expression\n                                                '
        if (len(p) == 4):
            if (p[2] == u'DIVISION'):
                p[0] = (p[1] / p[3])
            else:
                p[0] = (p[1] * p[3])
        elif (len(p) == 3):
            p[0] = (p[2] ** (-1))
        else:
            p[0] = p[1]
    def p_unit_expression(p):
        u'\n                                                unit_expression    :    unit\n                                                                                                                |    UNIT    OPEN_PAREN    complete_expression    CLOSE_PAREN\n                                                                                                                |    OPEN_PAREN    complete_expression    CLOSE_PAREN\n                                                                                                                |    UNIT    OPEN_PAREN    complete_expression    CLOSE_PAREN    power    numeric_power\n                                                                                                                |    OPEN_PAREN    complete_expression    CLOSE_PAREN    power    numeric_power\n                                                '
        p1_str = str(p[1])
        if ((p1_str in cls._functions) and (p1_str != u'sqrt')):
            raise ValueError(u"The    function    '{0}'    is    valid    in    OGIP,    but    not    understood    by    astropy.units.".format(p[1]))
        if (len(p) == 7):
            if (p1_str == u'sqrt'):
                p[0] = (p[1] * (p[3] ** (0.5 * p[6])))
            else:
                p[0] = (p[1] * (p[3] ** p[6]))
        elif (len(p) == 6):
            p[0] = (p[2] ** p[5])
        elif (len(p) == 5):
            if (p1_str == u'sqrt'):
                p[0] = (p[3] ** 0.5)
            else:
                p[0] = (p[1] * p[3])
        elif (len(p) == 4):
            p[0] = p[2]
        else:
            p[0] = p[1]
    def p_scale_factor(p):
        u'\n                                                scale_factor    :    LIT10    power    numeric_power\n                                                                                                    |    LIT10\n                                                                                                    |    signed_float\n                                                                                                    |    signed_float    power    numeric_power\n                                                                                                    |    signed_int    power    numeric_power\n                                                '
        if (len(p) == 4):
            p[0] = (10 ** p[3])
        else:
            p[0] = p[1]
        if ((math.log10(p[0]) % 1.0) != 0.0):
            from ..core import UnitsWarning
            warnings.warn(u"'{0}'    scale    should    be    a    power    of    10    in    OGIP    format".format(p[0]), UnitsWarning)
    def p_division(p):
        u'\n                                                division    :    DIVISION\n                                                                                    |    WHITESPACE    DIVISION\n                                                                                    |    WHITESPACE    DIVISION    WHITESPACE\n                                                                                    |    DIVISION    WHITESPACE\n                                                '
        p[0] = u'DIVISION'
    def p_product(p):
        u'\n                                                product    :    WHITESPACE\n                                                                                |    STAR\n                                                                                |    WHITESPACE    STAR\n                                                                                |    WHITESPACE    STAR    WHITESPACE\n                                                                                |    STAR    WHITESPACE\n                                                '
        p[0] = u'PRODUCT'
    def p_power(p):
        u'\n                                                power    :    STARSTAR\n                                                '
        p[0] = u'POWER'
    def p_unit(p):
        u'\n                                                unit    :    UNIT\n                                                                    |    UNIT    power    numeric_power\n                                                '
        if (len(p) == 4):
            p[0] = (p[1] ** p[3])
        else:
            p[0] = p[1]
    def p_numeric_power(p):
        u'\n                                                numeric_power    :    UINT\n                                                                                                        |    signed_float\n                                                                                                        |    OPEN_PAREN    signed_int    CLOSE_PAREN\n                                                                                                        |    OPEN_PAREN    signed_float    CLOSE_PAREN\n                                                                                                        |    OPEN_PAREN    signed_float    division    UINT    CLOSE_PAREN\n                                                '
        if (len(p) == 6):
            p[0] = Fraction(int(p[2]), int(p[4]))
        elif (len(p) == 4):
            p[0] = p[2]
        else:
            p[0] = p[1]
    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_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_error(p):
        raise ValueError()
    parser = yacc.yacc(debug=False, tabmodule=str(u'ogip_parsetab'), outputdir=os.path.dirname(__file__), write_tables=True)
    return parser
