#!/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
from grako.util import re, RE_FLAGS


__version__ = (2016, 3, 31, 14, 28, 44, 3)

__all__ = [
    'GnomicParser',
    'GnomicSemantics',
    'main'
]


class GnomicParser(Parser):
    def __init__(self,
                 whitespace=re.compile('[\\t ]+', RE_FLAGS | re.DOTALL),
                 nameguard=None,
                 comments_re=None,
                 eol_comments_re=None,
                 ignorecase=None,
                 left_recursion=True,
                 **kwargs):
        super(GnomicParser, self).__init__(
            whitespace=whitespace,
            nameguard=nameguard,
            comments_re=comments_re,
            eol_comments_re=eol_comments_re,
            ignorecase=ignorecase,
            left_recursion=left_recursion,
            **kwargs
        )

    @graken()
    def _start_(self):
        with self._optional():
            self._change_()
            self.ast.setlist('@', self.last_node)

            def block1():
                with self._group():
                    with self._choice():
                        with self._option():
                            self._sep_()
                        with self._option():
                            self._list_separator_()
                            with self._optional():
                                self._sep_()
                        self._error('no available options')
                self._change_()
                self.ast.setlist('@', self.last_node)
            self._closure(block1)
        with self._optional():
            self._sep_()
        self._check_eof()

    @graken()
    def _change_(self):
        with self._choice():
            with self._option():
                self._insertion_()
            with self._option():
                self._replacement_()
            with self._option():
                self._deletion_()
            with self._option():
                self._PLASMID_()
            with self._option():
                self._PHENE_()
            self._error('no available options')

    @graken()
    def _insertion_(self):
        self._token('+')
        self._INSERTABLE_()
        self.ast['new'] = self.last_node
        with self._optional():
            self._MARKER_()
            self.ast['marker'] = self.last_node

        self.ast._define(
            ['new', 'marker'],
            []
        )

    @graken()
    def _replacement_(self):
        with self._choice():
            with self._option():
                self._FEATURE_()
                self.ast['old'] = self.last_node
                self._token('>')
                self.ast['op'] = self.last_node
                self._INSERTABLE_()
                self.ast['new'] = self.last_node
                with self._optional():
                    self._MARKER_()
                    self.ast['marker'] = self.last_node
            with self._option():
                self._FEATURE_()
                self.ast['old'] = self.last_node
                self._token('>>')
                self.ast['op'] = self.last_node
                self._INSERTABLE_()
                self.ast['new'] = self.last_node
                with self._optional():
                    self._MARKER_()
                    self.ast['marker'] = self.last_node
            self._error('no available options')

        self.ast._define(
            ['old', 'op', 'new', 'marker'],
            []
        )

    @graken()
    def _deletion_(self):
        self._token('-')
        self._INSERTABLE_()
        self.ast['old'] = self.last_node
        with self._optional():
            self._MARKER_()
            self.ast['marker'] = self.last_node

        self.ast._define(
            ['old', 'marker'],
            []
        )

    @graken()
    def _INSERTABLE_(self):
        with self._choice():
            with self._option():
                self._PLASMID_()
            with self._option():
                self._FEATURE_SET_()
                self.ast['@'] = self.last_node
            with self._option():
                self._FUSION_()
            with self._option():
                self._FEATURE_()
            self._error('no available options')

    @graken()
    def _PLASMID_(self):
        with self._choice():
            with self._option():
                self._IDENTIFIER_()
                self.ast['name'] = self.last_node
                self._FEATURE_SET_()
                self.ast['contents'] = self.last_node
                with self._optional():
                    self._MARKER_()
                    self.ast['marker'] = self.last_node
            with self._option():
                self._IDENTIFIER_()
                self.ast['name'] = self.last_node
                self._token('{}')
                with self._optional():
                    self._MARKER_()
                    self.ast['marker'] = self.last_node
            self._error('no available options')

        self.ast._define(
            ['name', 'contents', 'marker'],
            []
        )

    @graken()
    def _MARKER_(self):
        self._token('::')
        self._PHENE_()
        self.ast['@'] = self.last_node

    @graken()
    def _PHENE_(self):
        with self._choice():
            with self._option():
                with self._optional():
                    self._FEATURE_ORGANISM_()
                    self.ast['organism'] = self.last_node
                self._IDENTIFIER_()
                self.ast['name'] = self.last_node
                self._ACCESSION_()
                self.ast['accession'] = self.last_node
                self._BINARY_VARIANT_()
                self.ast['variant'] = self.last_node
            with self._option():
                self._ACCESSION_()
                self.ast['accession'] = self.last_node
                self._BINARY_VARIANT_()
                self.ast['variant'] = self.last_node
            with self._option():
                with self._optional():
                    self._FEATURE_ORGANISM_()
                    self.ast['organism'] = self.last_node
                self._IDENTIFIER_()
                self.ast['name'] = self.last_node
                self._VARIANT_()
                self.ast['variant'] = self.last_node
            self._error('no available options')

        self.ast._define(
            ['organism', 'name', 'accession', 'variant'],
            []
        )

    @graken()
    def _FEATURE_(self):
        with self._choice():
            with self._option():
                with self._optional():
                    self._FEATURE_ORGANISM_()
                    self.ast['organism'] = self.last_node
                with self._optional():
                    self._IDENTIFIER_()
                    self.ast['type'] = self.last_node
                    self._token('.')
                self._IDENTIFIER_()
                self.ast['name'] = self.last_node
                with self._optional():
                    self._VARIANT_()
                    self.ast['variant'] = self.last_node
                with self._optional():
                    self._ACCESSION_()
                    self.ast['accession'] = self.last_node
                with self._optional():
                    self._RANGE_()
                    self.ast['range'] = self.last_node
            with self._option():
                self._ACCESSION_()
                self.ast['accession'] = self.last_node
                with self._optional():
                    self._RANGE_()
                    self.ast['range'] = self.last_node
            self._error('no available options')

        self.ast._define(
            ['organism', 'type', 'name', 'variant', 'accession', 'range'],
            []
        )

    @graken()
    def _FEATURE_ORGANISM_(self):
        self._ORGANISM_()
        self.ast['@'] = self.last_node
        self._token('/')

    @graken()
    def _FEATURE_SET_(self):
        with self._choice():
            with self._option():
                self._token('{')
                with self._optional():
                    self._sep_()
                with self._group():
                    with self._choice():
                        with self._option():
                            self._FUSION_()
                        with self._option():
                            self._FEATURE_()
                        self._error('no available options')
                self.ast.setlist('@', self.last_node)

                def block2():
                    with self._group():
                        with self._choice():
                            with self._option():
                                self._sep_()
                            with self._option():
                                self._list_separator_()
                                with self._optional():
                                    self._sep_()
                            self._error('no available options')
                    with self._group():
                        with self._choice():
                            with self._option():
                                self._FUSION_()
                            with self._option():
                                self._FEATURE_()
                            self._error('no available options')
                    self.ast.setlist('@', self.last_node)
                self._closure(block2)
                with self._optional():
                    self._sep_()
                self._token('}')
            with self._option():
                self._token('{')
                with self._optional():
                    self._sep_()
                with self._group():
                    with self._choice():
                        with self._option():
                            self._FUSION_()
                        with self._option():
                            self._FEATURE_()
                        self._error('no available options')
                self.ast.setlist('@', self.last_node)
                with self._optional():
                    self._sep_()
                self._token('}')
            self._error('no available options')

    @graken()
    def _FUSION_(self):
        self._FEATURE_()
        self.ast.setlist('@', self.last_node)

        def block1():
            self._token(':')
            self._FEATURE_()
            self.ast.setlist('@', self.last_node)
        self._positive_closure(block1)

    @graken()
    def _VARIANT_(self):
        with self._choice():
            with self._option():
                self._token('(')
                self._VARIANT_DEFINITION_()
                self.ast['@'] = self.last_node
                self._token(')')
            with self._option():
                self._BINARY_VARIANT_()
                self.ast['@'] = self.last_node
            self._error('no available options')

    @graken()
    def _VARIANT_DEFINITION_(self):
        self._IDENTIFIER_()
        self.ast['@'] = self.last_node

        def block1():
            with self._group():
                with self._choice():
                    with self._option():
                        self._token(',')
                    with self._option():
                        self._token(';')
                    self._error('expecting one of: , ;')
            with self._optional():
                self._sep_()
            self._IDENTIFIER_()
            self.ast['@'] = self.last_node
        self._closure(block1)

    @graken()
    def _BINARY_VARIANT_(self):
        with self._choice():
            with self._option():
                self._token('+')
            with self._option():
                self._token('-')
            self._error('expecting one of: + -')

    @graken()
    def _RANGE_(self):
        with self._choice():
            with self._option():
                self._token('[')
                with self._optional():
                    self._RANGE_SEQUENCE_LEVEL_()
                    self.ast['level'] = self.last_node
                self._INTEGER_()
                self.ast['start'] = self.last_node
                self._token('_')
                self._INTEGER_()
                self.ast['end'] = self.last_node
                self._token(']')
            with self._option():
                self._token('[')
                with self._optional():
                    self._RANGE_SEQUENCE_LEVEL_()
                    self.ast['level'] = self.last_node
                self._INTEGER_()
                self.ast['pos'] = self.last_node
                self._token(']')
            self._error('no available options')

        self.ast._define(
            ['level', 'start', 'end', 'pos'],
            []
        )

    @graken()
    def _RANGE_SEQUENCE_LEVEL_(self):
        with self._group():
            with self._choice():
                with self._option():
                    self._token('c')
                with self._option():
                    self._token('p')
                self._error('expecting one of: c p')
        self.ast['@'] = self.last_node
        self._token('.')

    @graken()
    def _ACCESSION_(self):
        with self._choice():
            with self._option():
                self._token('#')
                self._DATABASE_()
                self.ast['db'] = self.last_node
                self._token(':')
                with self._group():
                    with self._choice():
                        with self._option():
                            self._INTEGER_()
                        with self._option():
                            self._IDENTIFIER_()
                        self._error('no available options')
                self.ast['id'] = self.last_node
            with self._option():
                self._token('#')
                with self._group():
                    with self._choice():
                        with self._option():
                            self._INTEGER_()
                        with self._option():
                            self._IDENTIFIER_()
                        self._error('no available options')
                self.ast['id'] = self.last_node
            self._error('no available options')

        self.ast._define(
            ['db', 'id'],
            []
        )

    @graken()
    def _DATABASE_(self):
        self._pattern(r'[A-Za-z0-9-][A-Za-z0-9]+')

    @graken()
    def _INTEGER_(self):
        self._pattern(r'[0-9]+')
        self.ast['@'] = self.last_node

    @graken()
    def _IDENTIFIER_(self):
        self._pattern(r'[A-Za-z0-9]+([A-Za-z0-9_-]+[A-Za-z0-9])?')

    @graken()
    def _ORGANISM_(self):
        self._pattern(r'[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)?')

    @graken()
    def _list_separator_(self):
        self._token(',')

    @graken()
    def _sep_(self):
        self._pattern(r'[\t ]+')


class GnomicSemantics(object):
    def start(self, ast):
        return ast

    def change(self, ast):
        return ast

    def insertion(self, ast):
        return ast

    def replacement(self, ast):
        return ast

    def deletion(self, ast):
        return ast

    def INSERTABLE(self, ast):
        return ast

    def PLASMID(self, ast):
        return ast

    def MARKER(self, ast):
        return ast

    def PHENE(self, ast):
        return ast

    def FEATURE(self, ast):
        return ast

    def FEATURE_ORGANISM(self, ast):
        return ast

    def FEATURE_SET(self, ast):
        return ast

    def FUSION(self, ast):
        return ast

    def VARIANT(self, ast):
        return ast

    def VARIANT_DEFINITION(self, ast):
        return ast

    def BINARY_VARIANT(self, ast):
        return ast

    def RANGE(self, ast):
        return ast

    def RANGE_SEQUENCE_LEVEL(self, ast):
        return ast

    def ACCESSION(self, ast):
        return ast

    def DATABASE(self, ast):
        return ast

    def INTEGER(self, ast):
        return ast

    def IDENTIFIER(self, ast):
        return ast

    def ORGANISM(self, ast):
        return ast

    def list_separator(self, ast):
        return ast

    def sep(self, ast):
        return ast


def main(filename, startrule, trace=False, whitespace=None, nameguard=None):
    import json
    with open(filename) as f:
        text = f.read()
    parser = GnomicParser(parseinfo=False)
    ast = parser.parse(
        text,
        startrule,
        filename=filename,
        trace=trace,
        whitespace=whitespace,
        nameguard=nameguard)
    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 GnomicParser.rule_list():
                print(r)
            print()
            sys.exit(0)

    parser = argparse.ArgumentParser(description="Simple parser for Gnomic.")
    parser.add_argument('-l', '--list', action=ListRules, nargs=0,
                        help="list all rules and exit")
    parser.add_argument('-n', '--no-nameguard', action='store_true',
                        dest='no_nameguard',
                        help="disable the 'nameguard' feature")
    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,
        nameguard=not args.no_nameguard
    )
