#!/usr/bin/env python
# -*- coding: utf-8 -*-

# CAVEAT UTILITOR
#
# This file was automatically generated by Grako.
#
#    https://pypi.python.org/pypi/grako/
#
# Any changes you make to it will be overwritten the next time
# the file is generated.


from __future__ import print_function, division, absolute_import, unicode_literals
from grako.parsing import graken, Parser


__version__ = (2014, 9, 19, 16, 34, 11, 4)

__all__ = [
    'ExpressionParser',
    'ExpressionSemantics',
    'main'
]


class ExpressionParser(Parser):
    def __init__(self, whitespace=None, nameguard=True, **kwargs):
        super(ExpressionParser, self).__init__(
            whitespace=whitespace,
            nameguard=nameguard,
            **kwargs
        )

    @graken()
    def _arithmetic_expression_(self):
        self._test_()
        self._check_eof()

    @graken()
    def _test_(self):
        self._or_test_()

    @graken('binary')
    def _or_test_(self):
        self._and_test_()

        def block0():
            self._token('or')
            self._and_test_()
        self._closure(block0)

    @graken('binary')
    def _and_test_(self):
        self._not_test_()

        def block0():
            self._token('and')
            self._not_test_()
        self._closure(block0)

    @graken('unary')
    def _not_test_(self):
        with self._choice():
            with self._option():
                self._token('not')
                self._not_test_()
            with self._option():
                self._comparison_()
            self._error('no available options')

    @graken('binary')
    def _comparison_(self):
        self._or_expr_()

        def block0():
            self._comparison_operator_()
            self._or_expr_()
        self._closure(block0)

    @graken('binary')
    def _or_expr_(self):
        self._and_expr_()

        def block0():
            self._token('|')
            self._and_expr_()
        self._closure(block0)

    @graken('binary')
    def _and_expr_(self):
        self._shift_expr_()

        def block0():
            self._token('&')
            self._shift_expr_()
        self._closure(block0)

    @graken('binary')
    def _shift_expr_(self):
        self._arith_expr_()

        def block0():
            with self._group():
                with self._choice():
                    with self._option():
                        self._token('<<')
                    with self._option():
                        self._token('>>')
                    self._error('expecting one of: << >>')
            self._arith_expr_()
        self._closure(block0)

    @graken('binary')
    def _arith_expr_(self):
        self._term_()

        def block0():
            with self._group():
                with self._choice():
                    with self._option():
                        self._token('+')
                    with self._option():
                        self._token('-')
                    self._error('expecting one of: + -')
            self._term_()
        self._closure(block0)

    @graken('binary')
    def _term_(self):
        self._factor_()

        def block0():
            with self._group():
                with self._choice():
                    with self._option():
                        self._token('*')
                    with self._option():
                        self._token('/')
                    with self._option():
                        self._token('%')
                    with self._option():
                        self._token('//')
                    self._error('expecting one of: % * / //')
            self._factor_()
        self._closure(block0)

    @graken('unary')
    def _factor_(self):
        with self._choice():
            with self._option():
                with self._group():
                    with self._choice():
                        with self._option():
                            self._token('+')
                        with self._option():
                            self._token('-')
                        with self._option():
                            self._token('~')
                        self._error('expecting one of: + - ~')
                self._factor_()
            with self._option():
                self._power_()
            self._error('no available options')

    @graken()
    def _power_(self):
        self._atom_()
        with self._optional():
            self._token('^')
            self._factor_()

    @graken()
    def _atom_(self):
        with self._choice():
            with self._option():
                self._NUMBER_()
            with self._option():
                self._STRING_()
            with self._option():
                self._function_()
            with self._option():
                self._variable_()
            with self._option():
                with self._group():
                    self._token('(')
                    self._test_()
                    self.ast['@'] = self.last_node
                    self._token(')')
            self._error('no available options')

    @graken()
    def _variable_(self):
        self._reference_()

    @graken()
    def _function_(self):
        self._reference_()
        self.ast['ref'] = self.last_node
        self._token('(')
        with self._optional():
            self._arglist_()
            self.ast['args'] = self.last_node
        self._token(')')

        self.ast._define(
            ['ref', 'args'],
            []
        )

    @graken()
    def _arglist_(self):

        def block0():
            self._argument_()
            self.ast.setlist('@', self.last_node)
            self._token(',')
        self._closure(block0)
        self._argument_()
        self.ast.setlist('@', self.last_node)

    @graken()
    def _argument_(self):
        self._test_()

    @graken()
    def _reference_(self):

        def block0():
            self._NAME_()
            self.ast.setlist('@', self.last_node)
            self._token('.')
        self._closure(block0)
        self._NAME_()
        self.ast.setlist('@', self.last_node)

    @graken()
    def _NUMBER_(self):
        self._pattern(r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?')

    @graken()
    def _STRING_(self):
        self._pattern(r"'[^'\\\r\n]*(?:\\.[^'\\\r\n]*)*'")

    @graken()
    def _comparison_operator_(self):
        with self._group():
            with self._choice():
                with self._option():
                    self._token('==')
                with self._option():
                    self._token('!=')
                with self._option():
                    self._token('<')
                with self._option():
                    self._token('<=')
                with self._option():
                    self._token('>')
                with self._option():
                    self._token('>=')
                with self._option():
                    self._token('in')
                with self._option():
                    self._token('is')
                self._error('expecting one of: != < <= == > >= in is')

    @graken()
    def _NAME_(self):
        self._pattern(r'\w+')


class ExpressionSemantics(object):
    def arithmetic_expression(self, ast):
        return ast

    def test(self, ast):
        return ast

    def or_test(self, ast):
        return ast

    def and_test(self, ast):
        return ast

    def not_test(self, ast):
        return ast

    def comparison(self, ast):
        return ast

    def or_expr(self, ast):
        return ast

    def and_expr(self, ast):
        return ast

    def shift_expr(self, ast):
        return ast

    def arith_expr(self, ast):
        return ast

    def term(self, ast):
        return ast

    def factor(self, ast):
        return ast

    def power(self, ast):
        return ast

    def atom(self, ast):
        return ast

    def variable(self, ast):
        return ast

    def function(self, ast):
        return ast

    def arglist(self, ast):
        return ast

    def argument(self, ast):
        return ast

    def reference(self, ast):
        return ast

    def NUMBER(self, ast):
        return ast

    def STRING(self, ast):
        return ast

    def comparison_operator(self, ast):
        return ast

    def NAME(self, ast):
        return ast


def main(filename, startrule, trace=False, whitespace=None):
    import json
    with open(filename) as f:
        text = f.read()
    parser = ExpressionParser(parseinfo=False)
    ast = parser.parse(
        text,
        startrule,
        filename=filename,
        trace=trace,
        whitespace=whitespace)
    print('AST:')
    print(ast)
    print()
    print('JSON:')
    print(json.dumps(ast, indent=2))
    print()

if __name__ == '__main__':
    import argparse
    import string
    import sys

    class ListRules(argparse.Action):
        def __call__(self, parser, namespace, values, option_string):
            print('Rules:')
            for r in ExpressionParser.rule_list():
                print(r)
            print()
            sys.exit(0)

    parser = argparse.ArgumentParser(description="Simple parser for Expression.")
    parser.add_argument('-l', '--list', action=ListRules, nargs=0,
                        help="list all rules and exit")
    parser.add_argument('-t', '--trace', action='store_true',
                        help="output trace information")
    parser.add_argument('-w', '--whitespace', type=str, default=string.whitespace,
                        help="whitespace specification")
    parser.add_argument('file', metavar="FILE", help="the input file to parse")
    parser.add_argument('startrule', metavar="STARTRULE",
                        help="the start rule for parsing")
    args = parser.parse_args()

    main(
        args.file,
        args.startrule,
        trace=args.trace,
        whitespace=args.whitespace
    )
