Major Refactoring on plyxproto

- Reorganized code and removed copy of ply
- Reformatted code to be closer to PEP8
- Updated to Python3 compatibility
- Added tox test runner and created test cases
diff --git a/.gitignore b/.gitignore
index db4561e..3cf9b8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,7 +38,8 @@
 .coverage
 .cache
 nosetests.xml
-coverage.xml
+*results.xml
+*coverage.xml
 
 # Translations
 *.mo
@@ -52,3 +53,6 @@
 
 # PyBuilder
 target/
+
+# Generated files
+parsetab.py
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..fe8974a
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.opencord.org
+port=29418
+project=plyxproto.git
+defaultremote=origin
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..ceeea23
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include VERSION
diff --git a/README.md b/README.md
deleted file mode 100644
index a26869b..0000000
--- a/README.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# PLY Protobuf
-
-[Protocol Buffers] [0] lexer & parser written in Python for [PLY] [1].
-
-With this library you can create and process parse trees of a Protocol Buffers files with Python. 
-For example usage see `demo.py`.
-
-My use case: automated refactoring of `.proto` files.
-
-* Benefit of this project is support for easy refactoring of the protocol buffers files. From the parse
-result one can simply determine position of a particular lexical unit in the source text and replace it.
-* The visitor pattern is used for processing a parse tree.
-
-## Dependency
-* This project has only one dependency, [PLY] [1].
-* `ply/` subdirectory is present in this repo for demonstration purposes and completeness only. If you intend to use this project, prefer better original
- [PLY] [1] repository which is up-to-date.
- 
-## Contributions
-* There may be bugs although it works for me for quite complicated protocol buffers files. 
-If you find a bug, please feel free to submit a pull request or file an issue.
-
-## Bugs
-* `Oneof` is not implemented yet. 
-
-## Demo 1
-* `demo.py`
-* The first demo file shows simple parsing of the example protocol buffer message. It produces a parse tree of this simple
-examples.
-
-## Demo 2 - Protocol Buffers file refactoring.
-* `prefixize.py`
-* Main use-case is rename refactoring of entities in Protocol Buffers.
-* [Protocol Buffers Objetive C compiler] [5] does not prefix entities and this may conflict with some objects in the project.
-This script renames all entties in protobuf file by prefixing them wth specified string. Also identifiers with conflicting
-names can be renamed if specified by parameter (e.g., hash, description). Result of this refactoring can be then used with
-objeciveC protoc without conflicts.
-
-## Acknowledgement
-This work was inspired by:
-* [plyj] [2], Java lexer & parser for PLY.
-* [pyparsing] [3], Protocol Buffers parsing example. 
-* [PLYTalk] [4], nice tutorial for PLY I used.
-
-## Disclaimer
-* This project was created because I needed it for myself and I didn't find Protocol Buffers parser for PLY. 
-It is my first PLY / parser generator project and the first version was created in couple hours so it is not polished code. 
-* There already exist Protocol Buffer parsing variant as [pyparsing] [3] example, but my previous scripts used 
-PLY for parsing Java so I chosen to stay with PLY and to create Protocol Buffer variant for PLY. I like the output I can get from PLY
-(e.g., line, character position in the input text) so I can automatically process input files - e.g., refactoring.
-* API for this project is not guaranteed to remain stable. In particular I mean model generated from `.proto` files.
-Initial model may be considered suboptimal and changed at some point. This project is intended to serve as an 
-inspiration or a starting point. You will probably adapt it for your own needs. 
- 
- [0]: https://developers.google.com/protocol-buffers/
- [1]: https://github.com/dabeaz/ply
- [2]: https://github.com/musiKk/plyj
- [3]: http://pyparsing.wikispaces.com/
- [4]: http://www.dabeaz.com/ply/PLYTalk.pdf
- [5]: https://github.com/alexeyxo/protobuf-objc
diff --git a/README.rst b/README.rst
index 0b5190a..ee9cb9a 100644
--- a/README.rst
+++ b/README.rst
@@ -1,34 +1,9 @@
 plyxproto
 =========
 
-plyxproto is a Python parser and processor for the xproto model definition language.
-xproto is a syntax defined and supported by XOS (https://github.com/opencord/xos). 
+plyxproto is a Python parser and processor for the xproto model definition
+language.
 
-Here is an example of an xproto file:
+xproto is a syntax defined and supported by XOS_.
 
-```protobuf
-message Slice (PlCoreBase){
-     required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
-     required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
-     required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
-     required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
-     required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
-     required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
-     required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
-     optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
-     optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
-     optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
-     optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
-     optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
-     optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
-     optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
-     optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
-     optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
-     required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
-     required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
-}
-```
-
-More details on xproto and XOS can be found via the link below, and at the aforementioned xos homepage.
-
-https://github.com/opencord/xos/blob/master/docs/dev/xproto.md
+.. _XOS: https://guide.xosproject.org/dev/xproto.html
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..3cdeb6b
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+4.0.0rc1
diff --git a/demo.py b/demo.py
deleted file mode 100644
index b709611..0000000
--- a/demo.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import plyxproto.parser as plyproto
-
-test1 = """package tutorial;"""
-
-test2 = """package tutorial;
-
-message Person {
-  required string name = 1;
-  required int32 id = 2;
-  optional string email = 3;
-}
-
-"""
-
-test3 = """package tutorial;
-option java_outer_classname = "PushNotifications";
-option optimize_for = SPEED;
-
-  policy slice_user <foobar>
-
-  message Person(core.Actor) {
-      required string name = 1;
-      required int32 id = 2;
-      optional string email = 3;
-
-      required manytoone work_location->Location/types.Company:employees = 4;
-
-      enum PhoneType {
-        MOBILE = 0;
-        HOME = 1;
-        WORK = 2;
-      }
-
-      message PhoneNumber {
-        required string number = 1;
-        optional PhoneType type = 2 [default = HOME];
-      }
-
-      repeated PhoneNumber phone = 4;
-      extensions 500 to 990;
-}
-
-message AddressBook {
-  repeated Person person = 1;
-
-  // Possible extension numbers.
-  extensions 500 to max;
-}"""
-
-test4 = '''policy foo <exists foo: foo.x=foo.y>'''
-parser = plyproto.ProtobufAnalyzer()
-
-tests = globals()
-
-for t in tests:
-    if t.startswith('test'):
-        print 'parsin %s' % t
-        parser.parse_string(globals()[t])
diff --git a/foldemo.py b/foldemo.py
deleted file mode 100644
index eb350d7..0000000
--- a/foldemo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from plyxproto.logicparser import *
-import ply.lex as lex
-import ply.yacc as yacc
-
-test_1 = "<true>"
-test_2 = "<a | b>"
-test_3 = "<exists a: x=y>"
-test_4 = "<forall a: exists b: x.b=y.b>"
-test_5 = "<forall a: {{ a.endswith('good') }}>"
-test_6 = "< *doit(foo) >"
-
-DEBUG = 0
-
-lexer = lex.lex(module=FOLLexer(), debug=DEBUG)
-parser = yacc.yacc(module=FOLParser(), start='goal', debug=DEBUG)
-
-for x, v in globals().items():
-    if x.startswith('test_'):
-        print parser.parse(v, lexer=lexer, debug=DEBUG)
diff --git a/ply/__init__.py b/ply/__init__.py
deleted file mode 100644
index 853a985..0000000
--- a/ply/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# PLY package
-# Author: David Beazley (dave@dabeaz.com)
-
-__all__ = ['lex','yacc']
diff --git a/ply/cpp.py b/ply/cpp.py
deleted file mode 100644
index 2f6a030..0000000
--- a/ply/cpp.py
+++ /dev/null
@@ -1,908 +0,0 @@
-# -----------------------------------------------------------------------------
-# cpp.py
-#
-# Author:  David Beazley (http://www.dabeaz.com)
-# Copyright (C) 2007
-# All rights reserved
-#
-# This module implements an ANSI-C style lexical preprocessor for PLY. 
-# -----------------------------------------------------------------------------
-from __future__ import generators
-
-# -----------------------------------------------------------------------------
-# Default preprocessor lexer definitions.   These tokens are enough to get
-# a basic preprocessor working.   Other modules may import these if they want
-# -----------------------------------------------------------------------------
-
-tokens = (
-   'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND'
-)
-
-literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\""
-
-# Whitespace
-def t_CPP_WS(t):
-    r'\s+'
-    t.lexer.lineno += t.value.count("\n")
-    return t
-
-t_CPP_POUND = r'\#'
-t_CPP_DPOUND = r'\#\#'
-
-# Identifier
-t_CPP_ID = r'[A-Za-z_][\w_]*'
-
-# Integer literal
-def CPP_INTEGER(t):
-    r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)'
-    return t
-
-t_CPP_INTEGER = CPP_INTEGER
-
-# Floating literal
-t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'
-
-# String literal
-def t_CPP_STRING(t):
-    r'\"([^\\\n]|(\\(.|\n)))*?\"'
-    t.lexer.lineno += t.value.count("\n")
-    return t
-
-# Character constant 'c' or L'c'
-def t_CPP_CHAR(t):
-    r'(L)?\'([^\\\n]|(\\(.|\n)))*?\''
-    t.lexer.lineno += t.value.count("\n")
-    return t
-
-# Comment
-def t_CPP_COMMENT1(t):
-    r'(/\*(.|\n)*?\*/)'
-    ncr = t.value.count("\n")
-    t.lexer.lineno += ncr
-    # replace with one space or a number of '\n'
-    t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' '
-    return t
-
-# Line comment
-def t_CPP_COMMENT2(t):
-    r'(//.*?(\n|$))'
-    # replace with '/n'
-    t.type = 'CPP_WS'; t.value = '\n'
-    
-def t_error(t):
-    t.type = t.value[0]
-    t.value = t.value[0]
-    t.lexer.skip(1)
-    return t
-
-import re
-import copy
-import time
-import os.path
-
-# -----------------------------------------------------------------------------
-# trigraph()
-# 
-# Given an input string, this function replaces all trigraph sequences. 
-# The following mapping is used:
-#
-#     ??=    #
-#     ??/    \
-#     ??'    ^
-#     ??(    [
-#     ??)    ]
-#     ??!    |
-#     ??<    {
-#     ??>    }
-#     ??-    ~
-# -----------------------------------------------------------------------------
-
-_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''')
-_trigraph_rep = {
-    '=':'#',
-    '/':'\\',
-    "'":'^',
-    '(':'[',
-    ')':']',
-    '!':'|',
-    '<':'{',
-    '>':'}',
-    '-':'~'
-}
-
-def trigraph(input):
-    return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input)
-
-# ------------------------------------------------------------------
-# Macro object
-#
-# This object holds information about preprocessor macros
-#
-#    .name      - Macro name (string)
-#    .value     - Macro value (a list of tokens)
-#    .arglist   - List of argument names
-#    .variadic  - Boolean indicating whether or not variadic macro
-#    .vararg    - Name of the variadic parameter
-#
-# When a macro is created, the macro replacement token sequence is
-# pre-scanned and used to create patch lists that are later used
-# during macro expansion
-# ------------------------------------------------------------------
-
-class Macro(object):
-    def __init__(self,name,value,arglist=None,variadic=False):
-        self.name = name
-        self.value = value
-        self.arglist = arglist
-        self.variadic = variadic
-        if variadic:
-            self.vararg = arglist[-1]
-        self.source = None
-
-# ------------------------------------------------------------------
-# Preprocessor object
-#
-# Object representing a preprocessor.  Contains macro definitions,
-# include directories, and other information
-# ------------------------------------------------------------------
-
-class Preprocessor(object):
-    def __init__(self,lexer=None):
-        if lexer is None:
-            lexer = lex.lexer
-        self.lexer = lexer
-        self.macros = { }
-        self.path = []
-        self.temp_path = []
-
-        # Probe the lexer for selected tokens
-        self.lexprobe()
-
-        tm = time.localtime()
-        self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm))
-        self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm))
-        self.parser = None
-
-    # -----------------------------------------------------------------------------
-    # tokenize()
-    #
-    # Utility function. Given a string of text, tokenize into a list of tokens
-    # -----------------------------------------------------------------------------
-
-    def tokenize(self,text):
-        tokens = []
-        self.lexer.input(text)
-        while True:
-            tok = self.lexer.token()
-            if not tok: break
-            tokens.append(tok)
-        return tokens
-
-    # ---------------------------------------------------------------------
-    # error()
-    #
-    # Report a preprocessor error/warning of some kind
-    # ----------------------------------------------------------------------
-
-    def error(self,file,line,msg):
-        print("%s:%d %s" % (file,line,msg))
-
-    # ----------------------------------------------------------------------
-    # lexprobe()
-    #
-    # This method probes the preprocessor lexer object to discover
-    # the token types of symbols that are important to the preprocessor.
-    # If this works right, the preprocessor will simply "work"
-    # with any suitable lexer regardless of how tokens have been named.
-    # ----------------------------------------------------------------------
-
-    def lexprobe(self):
-
-        # Determine the token type for identifiers
-        self.lexer.input("identifier")
-        tok = self.lexer.token()
-        if not tok or tok.value != "identifier":
-            print("Couldn't determine identifier type")
-        else:
-            self.t_ID = tok.type
-
-        # Determine the token type for integers
-        self.lexer.input("12345")
-        tok = self.lexer.token()
-        if not tok or int(tok.value) != 12345:
-            print("Couldn't determine integer type")
-        else:
-            self.t_INTEGER = tok.type
-            self.t_INTEGER_TYPE = type(tok.value)
-
-        # Determine the token type for strings enclosed in double quotes
-        self.lexer.input("\"filename\"")
-        tok = self.lexer.token()
-        if not tok or tok.value != "\"filename\"":
-            print("Couldn't determine string type")
-        else:
-            self.t_STRING = tok.type
-
-        # Determine the token type for whitespace--if any
-        self.lexer.input("  ")
-        tok = self.lexer.token()
-        if not tok or tok.value != "  ":
-            self.t_SPACE = None
-        else:
-            self.t_SPACE = tok.type
-
-        # Determine the token type for newlines
-        self.lexer.input("\n")
-        tok = self.lexer.token()
-        if not tok or tok.value != "\n":
-            self.t_NEWLINE = None
-            print("Couldn't determine token for newlines")
-        else:
-            self.t_NEWLINE = tok.type
-
-        self.t_WS = (self.t_SPACE, self.t_NEWLINE)
-
-        # Check for other characters used by the preprocessor
-        chars = [ '<','>','#','##','\\','(',')',',','.']
-        for c in chars:
-            self.lexer.input(c)
-            tok = self.lexer.token()
-            if not tok or tok.value != c:
-                print("Unable to lex '%s' required for preprocessor" % c)
-
-    # ----------------------------------------------------------------------
-    # add_path()
-    #
-    # Adds a search path to the preprocessor.  
-    # ----------------------------------------------------------------------
-
-    def add_path(self,path):
-        self.path.append(path)
-
-    # ----------------------------------------------------------------------
-    # group_lines()
-    #
-    # Given an input string, this function splits it into lines.  Trailing whitespace
-    # is removed.   Any line ending with \ is grouped with the next line.  This
-    # function forms the lowest level of the preprocessor---grouping into text into
-    # a line-by-line format.
-    # ----------------------------------------------------------------------
-
-    def group_lines(self,input):
-        lex = self.lexer.clone()
-        lines = [x.rstrip() for x in input.splitlines()]
-        for i in xrange(len(lines)):
-            j = i+1
-            while lines[i].endswith('\\') and (j < len(lines)):
-                lines[i] = lines[i][:-1]+lines[j]
-                lines[j] = ""
-                j += 1
-
-        input = "\n".join(lines)
-        lex.input(input)
-        lex.lineno = 1
-
-        current_line = []
-        while True:
-            tok = lex.token()
-            if not tok:
-                break
-            current_line.append(tok)
-            if tok.type in self.t_WS and '\n' in tok.value:
-                yield current_line
-                current_line = []
-
-        if current_line:
-            yield current_line
-
-    # ----------------------------------------------------------------------
-    # tokenstrip()
-    # 
-    # Remove leading/trailing whitespace tokens from a token list
-    # ----------------------------------------------------------------------
-
-    def tokenstrip(self,tokens):
-        i = 0
-        while i < len(tokens) and tokens[i].type in self.t_WS:
-            i += 1
-        del tokens[:i]
-        i = len(tokens)-1
-        while i >= 0 and tokens[i].type in self.t_WS:
-            i -= 1
-        del tokens[i+1:]
-        return tokens
-
-
-    # ----------------------------------------------------------------------
-    # collect_args()
-    #
-    # Collects comma separated arguments from a list of tokens.   The arguments
-    # must be enclosed in parenthesis.  Returns a tuple (tokencount,args,positions)
-    # where tokencount is the number of tokens consumed, args is a list of arguments,
-    # and positions is a list of integers containing the starting index of each
-    # argument.  Each argument is represented by a list of tokens.
-    #
-    # When collecting arguments, leading and trailing whitespace is removed
-    # from each argument.  
-    #
-    # This function properly handles nested parenthesis and commas---these do not
-    # define new arguments.
-    # ----------------------------------------------------------------------
-
-    def collect_args(self,tokenlist):
-        args = []
-        positions = []
-        current_arg = []
-        nesting = 1
-        tokenlen = len(tokenlist)
-    
-        # Search for the opening '('.
-        i = 0
-        while (i < tokenlen) and (tokenlist[i].type in self.t_WS):
-            i += 1
-
-        if (i < tokenlen) and (tokenlist[i].value == '('):
-            positions.append(i+1)
-        else:
-            self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments")
-            return 0, [], []
-
-        i += 1
-
-        while i < tokenlen:
-            t = tokenlist[i]
-            if t.value == '(':
-                current_arg.append(t)
-                nesting += 1
-            elif t.value == ')':
-                nesting -= 1
-                if nesting == 0:
-                    if current_arg:
-                        args.append(self.tokenstrip(current_arg))
-                        positions.append(i)
-                    return i+1,args,positions
-                current_arg.append(t)
-            elif t.value == ',' and nesting == 1:
-                args.append(self.tokenstrip(current_arg))
-                positions.append(i+1)
-                current_arg = []
-            else:
-                current_arg.append(t)
-            i += 1
-    
-        # Missing end argument
-        self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments")
-        return 0, [],[]
-
-    # ----------------------------------------------------------------------
-    # macro_prescan()
-    #
-    # Examine the macro value (token sequence) and identify patch points
-    # This is used to speed up macro expansion later on---we'll know
-    # right away where to apply patches to the value to form the expansion
-    # ----------------------------------------------------------------------
-    
-    def macro_prescan(self,macro):
-        macro.patch     = []             # Standard macro arguments 
-        macro.str_patch = []             # String conversion expansion
-        macro.var_comma_patch = []       # Variadic macro comma patch
-        i = 0
-        while i < len(macro.value):
-            if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist:
-                argnum = macro.arglist.index(macro.value[i].value)
-                # Conversion of argument to a string
-                if i > 0 and macro.value[i-1].value == '#':
-                    macro.value[i] = copy.copy(macro.value[i])
-                    macro.value[i].type = self.t_STRING
-                    del macro.value[i-1]
-                    macro.str_patch.append((argnum,i-1))
-                    continue
-                # Concatenation
-                elif (i > 0 and macro.value[i-1].value == '##'):
-                    macro.patch.append(('c',argnum,i-1))
-                    del macro.value[i-1]
-                    continue
-                elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'):
-                    macro.patch.append(('c',argnum,i))
-                    i += 1
-                    continue
-                # Standard expansion
-                else:
-                    macro.patch.append(('e',argnum,i))
-            elif macro.value[i].value == '##':
-                if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \
-                        ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \
-                        (macro.value[i+1].value == macro.vararg):
-                    macro.var_comma_patch.append(i-1)
-            i += 1
-        macro.patch.sort(key=lambda x: x[2],reverse=True)
-
-    # ----------------------------------------------------------------------
-    # macro_expand_args()
-    #
-    # Given a Macro and list of arguments (each a token list), this method
-    # returns an expanded version of a macro.  The return value is a token sequence
-    # representing the replacement macro tokens
-    # ----------------------------------------------------------------------
-
-    def macro_expand_args(self,macro,args):
-        # Make a copy of the macro token sequence
-        rep = [copy.copy(_x) for _x in macro.value]
-
-        # Make string expansion patches.  These do not alter the length of the replacement sequence
-        
-        str_expansion = {}
-        for argnum, i in macro.str_patch:
-            if argnum not in str_expansion:
-                str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\")
-            rep[i] = copy.copy(rep[i])
-            rep[i].value = str_expansion[argnum]
-
-        # Make the variadic macro comma patch.  If the variadic macro argument is empty, we get rid
-        comma_patch = False
-        if macro.variadic and not args[-1]:
-            for i in macro.var_comma_patch:
-                rep[i] = None
-                comma_patch = True
-
-        # Make all other patches.   The order of these matters.  It is assumed that the patch list
-        # has been sorted in reverse order of patch location since replacements will cause the
-        # size of the replacement sequence to expand from the patch point.
-        
-        expanded = { }
-        for ptype, argnum, i in macro.patch:
-            # Concatenation.   Argument is left unexpanded
-            if ptype == 'c':
-                rep[i:i+1] = args[argnum]
-            # Normal expansion.  Argument is macro expanded first
-            elif ptype == 'e':
-                if argnum not in expanded:
-                    expanded[argnum] = self.expand_macros(args[argnum])
-                rep[i:i+1] = expanded[argnum]
-
-        # Get rid of removed comma if necessary
-        if comma_patch:
-            rep = [_i for _i in rep if _i]
-
-        return rep
-
-
-    # ----------------------------------------------------------------------
-    # expand_macros()
-    #
-    # Given a list of tokens, this function performs macro expansion.
-    # The expanded argument is a dictionary that contains macros already
-    # expanded.  This is used to prevent infinite recursion.
-    # ----------------------------------------------------------------------
-
-    def expand_macros(self,tokens,expanded=None):
-        if expanded is None:
-            expanded = {}
-        i = 0
-        while i < len(tokens):
-            t = tokens[i]
-            if t.type == self.t_ID:
-                if t.value in self.macros and t.value not in expanded:
-                    # Yes, we found a macro match
-                    expanded[t.value] = True
-                    
-                    m = self.macros[t.value]
-                    if not m.arglist:
-                        # A simple macro
-                        ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded)
-                        for e in ex:
-                            e.lineno = t.lineno
-                        tokens[i:i+1] = ex
-                        i += len(ex)
-                    else:
-                        # A macro with arguments
-                        j = i + 1
-                        while j < len(tokens) and tokens[j].type in self.t_WS:
-                            j += 1
-                        if tokens[j].value == '(':
-                            tokcount,args,positions = self.collect_args(tokens[j:])
-                            if not m.variadic and len(args) !=  len(m.arglist):
-                                self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist)))
-                                i = j + tokcount
-                            elif m.variadic and len(args) < len(m.arglist)-1:
-                                if len(m.arglist) > 2:
-                                    self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1))
-                                else:
-                                    self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1))
-                                i = j + tokcount
-                            else:
-                                if m.variadic:
-                                    if len(args) == len(m.arglist)-1:
-                                        args.append([])
-                                    else:
-                                        args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1]
-                                        del args[len(m.arglist):]
-                                        
-                                # Get macro replacement text
-                                rep = self.macro_expand_args(m,args)
-                                rep = self.expand_macros(rep,expanded)
-                                for r in rep:
-                                    r.lineno = t.lineno
-                                tokens[i:j+tokcount] = rep
-                                i += len(rep)
-                    del expanded[t.value]
-                    continue
-                elif t.value == '__LINE__':
-                    t.type = self.t_INTEGER
-                    t.value = self.t_INTEGER_TYPE(t.lineno)
-                
-            i += 1
-        return tokens
-
-    # ----------------------------------------------------------------------    
-    # evalexpr()
-    # 
-    # Evaluate an expression token sequence for the purposes of evaluating
-    # integral expressions.
-    # ----------------------------------------------------------------------
-
-    def evalexpr(self,tokens):
-        # tokens = tokenize(line)
-        # Search for defined macros
-        i = 0
-        while i < len(tokens):
-            if tokens[i].type == self.t_ID and tokens[i].value == 'defined':
-                j = i + 1
-                needparen = False
-                result = "0L"
-                while j < len(tokens):
-                    if tokens[j].type in self.t_WS:
-                        j += 1
-                        continue
-                    elif tokens[j].type == self.t_ID:
-                        if tokens[j].value in self.macros:
-                            result = "1L"
-                        else:
-                            result = "0L"
-                        if not needparen: break
-                    elif tokens[j].value == '(':
-                        needparen = True
-                    elif tokens[j].value == ')':
-                        break
-                    else:
-                        self.error(self.source,tokens[i].lineno,"Malformed defined()")
-                    j += 1
-                tokens[i].type = self.t_INTEGER
-                tokens[i].value = self.t_INTEGER_TYPE(result)
-                del tokens[i+1:j+1]
-            i += 1
-        tokens = self.expand_macros(tokens)
-        for i,t in enumerate(tokens):
-            if t.type == self.t_ID:
-                tokens[i] = copy.copy(t)
-                tokens[i].type = self.t_INTEGER
-                tokens[i].value = self.t_INTEGER_TYPE("0L")
-            elif t.type == self.t_INTEGER:
-                tokens[i] = copy.copy(t)
-                # Strip off any trailing suffixes
-                tokens[i].value = str(tokens[i].value)
-                while tokens[i].value[-1] not in "0123456789abcdefABCDEF":
-                    tokens[i].value = tokens[i].value[:-1]
-        
-        expr = "".join([str(x.value) for x in tokens])
-        expr = expr.replace("&&"," and ")
-        expr = expr.replace("||"," or ")
-        expr = expr.replace("!"," not ")
-        try:
-            result = eval(expr)
-        except StandardError:
-            self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression")
-            result = 0
-        return result
-
-    # ----------------------------------------------------------------------
-    # parsegen()
-    #
-    # Parse an input string/
-    # ----------------------------------------------------------------------
-    def parsegen(self,input,source=None):
-
-        # Replace trigraph sequences
-        t = trigraph(input)
-        lines = self.group_lines(t)
-
-        if not source:
-            source = ""
-            
-        self.define("__FILE__ \"%s\"" % source)
-
-        self.source = source
-        chunk = []
-        enable = True
-        iftrigger = False
-        ifstack = []
-
-        for x in lines:
-            for i,tok in enumerate(x):
-                if tok.type not in self.t_WS: break
-            if tok.value == '#':
-                # Preprocessor directive
-
-                # insert necessary whitespace instead of eaten tokens
-                for tok in x:
-                    if tok.type in self.t_WS and '\n' in tok.value:
-                        chunk.append(tok)
-                
-                dirtokens = self.tokenstrip(x[i+1:])
-                if dirtokens:
-                    name = dirtokens[0].value
-                    args = self.tokenstrip(dirtokens[1:])
-                else:
-                    name = ""
-                    args = []
-                
-                if name == 'define':
-                    if enable:
-                        for tok in self.expand_macros(chunk):
-                            yield tok
-                        chunk = []
-                        self.define(args)
-                elif name == 'include':
-                    if enable:
-                        for tok in self.expand_macros(chunk):
-                            yield tok
-                        chunk = []
-                        oldfile = self.macros['__FILE__']
-                        for tok in self.include(args):
-                            yield tok
-                        self.macros['__FILE__'] = oldfile
-                        self.source = source
-                elif name == 'undef':
-                    if enable:
-                        for tok in self.expand_macros(chunk):
-                            yield tok
-                        chunk = []
-                        self.undef(args)
-                elif name == 'ifdef':
-                    ifstack.append((enable,iftrigger))
-                    if enable:
-                        if not args[0].value in self.macros:
-                            enable = False
-                            iftrigger = False
-                        else:
-                            iftrigger = True
-                elif name == 'ifndef':
-                    ifstack.append((enable,iftrigger))
-                    if enable:
-                        if args[0].value in self.macros:
-                            enable = False
-                            iftrigger = False
-                        else:
-                            iftrigger = True
-                elif name == 'if':
-                    ifstack.append((enable,iftrigger))
-                    if enable:
-                        result = self.evalexpr(args)
-                        if not result:
-                            enable = False
-                            iftrigger = False
-                        else:
-                            iftrigger = True
-                elif name == 'elif':
-                    if ifstack:
-                        if ifstack[-1][0]:     # We only pay attention if outer "if" allows this
-                            if enable:         # If already true, we flip enable False
-                                enable = False
-                            elif not iftrigger:   # If False, but not triggered yet, we'll check expression
-                                result = self.evalexpr(args)
-                                if result:
-                                    enable  = True
-                                    iftrigger = True
-                    else:
-                        self.error(self.source,dirtokens[0].lineno,"Misplaced #elif")
-                        
-                elif name == 'else':
-                    if ifstack:
-                        if ifstack[-1][0]:
-                            if enable:
-                                enable = False
-                            elif not iftrigger:
-                                enable = True
-                                iftrigger = True
-                    else:
-                        self.error(self.source,dirtokens[0].lineno,"Misplaced #else")
-
-                elif name == 'endif':
-                    if ifstack:
-                        enable,iftrigger = ifstack.pop()
-                    else:
-                        self.error(self.source,dirtokens[0].lineno,"Misplaced #endif")
-                else:
-                    # Unknown preprocessor directive
-                    pass
-
-            else:
-                # Normal text
-                if enable:
-                    chunk.extend(x)
-
-        for tok in self.expand_macros(chunk):
-            yield tok
-        chunk = []
-
-    # ----------------------------------------------------------------------
-    # include()
-    #
-    # Implementation of file-inclusion
-    # ----------------------------------------------------------------------
-
-    def include(self,tokens):
-        # Try to extract the filename and then process an include file
-        if not tokens:
-            return
-        if tokens:
-            if tokens[0].value != '<' and tokens[0].type != self.t_STRING:
-                tokens = self.expand_macros(tokens)
-
-            if tokens[0].value == '<':
-                # Include <...>
-                i = 1
-                while i < len(tokens):
-                    if tokens[i].value == '>':
-                        break
-                    i += 1
-                else:
-                    print("Malformed #include <...>")
-                    return
-                filename = "".join([x.value for x in tokens[1:i]])
-                path = self.path + [""] + self.temp_path
-            elif tokens[0].type == self.t_STRING:
-                filename = tokens[0].value[1:-1]
-                path = self.temp_path + [""] + self.path
-            else:
-                print("Malformed #include statement")
-                return
-        for p in path:
-            iname = os.path.join(p,filename)
-            try:
-                data = open(iname,"r").read()
-                dname = os.path.dirname(iname)
-                if dname:
-                    self.temp_path.insert(0,dname)
-                for tok in self.parsegen(data,filename):
-                    yield tok
-                if dname:
-                    del self.temp_path[0]
-                break
-            except IOError:
-                pass
-        else:
-            print("Couldn't find '%s'" % filename)
-
-    # ----------------------------------------------------------------------
-    # define()
-    #
-    # Define a new macro
-    # ----------------------------------------------------------------------
-
-    def define(self,tokens):
-        if isinstance(tokens,(str,unicode)):
-            tokens = self.tokenize(tokens)
-
-        linetok = tokens
-        try:
-            name = linetok[0]
-            if len(linetok) > 1:
-                mtype = linetok[1]
-            else:
-                mtype = None
-            if not mtype:
-                m = Macro(name.value,[])
-                self.macros[name.value] = m
-            elif mtype.type in self.t_WS:
-                # A normal macro
-                m = Macro(name.value,self.tokenstrip(linetok[2:]))
-                self.macros[name.value] = m
-            elif mtype.value == '(':
-                # A macro with arguments
-                tokcount, args, positions = self.collect_args(linetok[1:])
-                variadic = False
-                for a in args:
-                    if variadic:
-                        print("No more arguments may follow a variadic argument")
-                        break
-                    astr = "".join([str(_i.value) for _i in a])
-                    if astr == "...":
-                        variadic = True
-                        a[0].type = self.t_ID
-                        a[0].value = '__VA_ARGS__'
-                        variadic = True
-                        del a[1:]
-                        continue
-                    elif astr[-3:] == "..." and a[0].type == self.t_ID:
-                        variadic = True
-                        del a[1:]
-                        # If, for some reason, "." is part of the identifier, strip off the name for the purposes
-                        # of macro expansion
-                        if a[0].value[-3:] == '...':
-                            a[0].value = a[0].value[:-3]
-                        continue
-                    if len(a) > 1 or a[0].type != self.t_ID:
-                        print("Invalid macro argument")
-                        break
-                else:
-                    mvalue = self.tokenstrip(linetok[1+tokcount:])
-                    i = 0
-                    while i < len(mvalue):
-                        if i+1 < len(mvalue):
-                            if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##':
-                                del mvalue[i]
-                                continue
-                            elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS:
-                                del mvalue[i+1]
-                        i += 1
-                    m = Macro(name.value,mvalue,[x[0].value for x in args],variadic)
-                    self.macro_prescan(m)
-                    self.macros[name.value] = m
-            else:
-                print("Bad macro definition")
-        except LookupError:
-            print("Bad macro definition")
-
-    # ----------------------------------------------------------------------
-    # undef()
-    #
-    # Undefine a macro
-    # ----------------------------------------------------------------------
-
-    def undef(self,tokens):
-        id = tokens[0].value
-        try:
-            del self.macros[id]
-        except LookupError:
-            pass
-
-    # ----------------------------------------------------------------------
-    # parse()
-    #
-    # Parse input text.
-    # ----------------------------------------------------------------------
-    def parse(self,input,source=None,ignore={}):
-        self.ignore = ignore
-        self.parser = self.parsegen(input,source)
-        
-    # ----------------------------------------------------------------------
-    # token()
-    #
-    # Method to return individual tokens
-    # ----------------------------------------------------------------------
-    def token(self):
-        try:
-            while True:
-                tok = next(self.parser)
-                if tok.type not in self.ignore: return tok
-        except StopIteration:
-            self.parser = None
-            return None
-
-if __name__ == '__main__':
-    import ply.lex as lex
-    lexer = lex.lex()
-
-    # Run a preprocessor
-    import sys
-    f = open(sys.argv[1])
-    input = f.read()
-
-    p = Preprocessor(lexer)
-    p.parse(input,sys.argv[1])
-    while True:
-        tok = p.token()
-        if not tok: break
-        print(p.source, tok)
-
-
-
-
-    
-
-
-
-
-
-
diff --git a/ply/ctokens.py b/ply/ctokens.py
deleted file mode 100644
index f6f6952..0000000
--- a/ply/ctokens.py
+++ /dev/null
@@ -1,133 +0,0 @@
-# ----------------------------------------------------------------------
-# ctokens.py
-#
-# Token specifications for symbols in ANSI C and C++.  This file is
-# meant to be used as a library in other tokenizers.
-# ----------------------------------------------------------------------
-
-# Reserved words
-
-tokens = [
-    # Literals (identifier, integer constant, float constant, string constant, char const)
-    'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER',
-
-    # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=)
-    'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO',
-    'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT',
-    'LOR', 'LAND', 'LNOT',
-    'LT', 'LE', 'GT', 'GE', 'EQ', 'NE',
-    
-    # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=)
-    'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL',
-    'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL',
-
-    # Increment/decrement (++,--)
-    'INCREMENT', 'DECREMENT',
-
-    # Structure dereference (->)
-    'ARROW',
-
-    # Ternary operator (?)
-    'TERNARY',
-    
-    # Delimeters ( ) [ ] { } , . ; :
-    'LPAREN', 'RPAREN',
-    'LBRACKET', 'RBRACKET',
-    'LBRACE', 'RBRACE',
-    'COMMA', 'PERIOD', 'SEMI', 'COLON',
-
-    # Ellipsis (...)
-    'ELLIPSIS',
-]
-    
-# Operators
-t_PLUS             = r'\+'
-t_MINUS            = r'-'
-t_TIMES            = r'\*'
-t_DIVIDE           = r'/'
-t_MODULO           = r'%'
-t_OR               = r'\|'
-t_AND              = r'&'
-t_NOT              = r'~'
-t_XOR              = r'\^'
-t_LSHIFT           = r'<<'
-t_RSHIFT           = r'>>'
-t_LOR              = r'\|\|'
-t_LAND             = r'&&'
-t_LNOT             = r'!'
-t_LT               = r'<'
-t_GT               = r'>'
-t_LE               = r'<='
-t_GE               = r'>='
-t_EQ               = r'=='
-t_NE               = r'!='
-
-# Assignment operators
-
-t_EQUALS           = r'='
-t_TIMESEQUAL       = r'\*='
-t_DIVEQUAL         = r'/='
-t_MODEQUAL         = r'%='
-t_PLUSEQUAL        = r'\+='
-t_MINUSEQUAL       = r'-='
-t_LSHIFTEQUAL      = r'<<='
-t_RSHIFTEQUAL      = r'>>='
-t_ANDEQUAL         = r'&='
-t_OREQUAL          = r'\|='
-t_XOREQUAL         = r'\^='
-
-# Increment/decrement
-t_INCREMENT        = r'\+\+'
-t_DECREMENT        = r'--'
-
-# ->
-t_ARROW            = r'->'
-
-# ?
-t_TERNARY          = r'\?'
-
-# Delimeters
-t_LPAREN           = r'\('
-t_RPAREN           = r'\)'
-t_LBRACKET         = r'\['
-t_RBRACKET         = r'\]'
-t_LBRACE           = r'\{'
-t_RBRACE           = r'\}'
-t_COMMA            = r','
-t_PERIOD           = r'\.'
-t_SEMI             = r';'
-t_COLON            = r':'
-t_ELLIPSIS         = r'\.\.\.'
-
-# Identifiers
-t_ID = r'[A-Za-z_][A-Za-z0-9_]*'
-
-# Integer literal
-t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?'
-
-# Floating literal
-t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?'
-
-# String literal
-t_STRING = r'\"([^\\\n]|(\\.))*?\"'
-
-# Character constant 'c' or L'c'
-t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\''
-
-# Comment (C-Style)
-def t_COMMENT(t):
-    r'/\*(.|\n)*?\*/'
-    t.lexer.lineno += t.value.count('\n')
-    return t
-
-# Comment (C++-Style)
-def t_CPPCOMMENT(t):
-    r'//.*\n'
-    t.lexer.lineno += 1
-    return t
-
-
-    
-
-
-
diff --git a/ply/lex.py b/ply/lex.py
deleted file mode 100644
index 8f05537..0000000
--- a/ply/lex.py
+++ /dev/null
@@ -1,1063 +0,0 @@
-# -----------------------------------------------------------------------------
-# ply: lex.py
-#
-# Copyright (C) 2001-2011,
-# David M. Beazley (Dabeaz LLC)
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-# 
-# * Redistributions of source code must retain the above copyright notice,
-#   this list of conditions and the following disclaimer.  
-# * Redistributions in binary form must reproduce the above copyright notice, 
-#   this list of conditions and the following disclaimer in the documentation
-#   and/or other materials provided with the distribution.  
-# * Neither the name of the David Beazley or Dabeaz LLC may be used to
-#   endorse or promote products derived from this software without
-#  specific prior written permission. 
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# -----------------------------------------------------------------------------
-
-__version__    = "3.5"
-__tabversion__ = "3.5"       # Version of table file used
-
-import re, sys, types, copy, os, inspect
-
-# This tuple contains known string types
-try:
-    # Python 2.6
-    StringTypes = (types.StringType, types.UnicodeType)
-except AttributeError:
-    # Python 3.0
-    StringTypes = (str, bytes)
-
-# Extract the code attribute of a function. Different implementations
-# are for Python 2/3 compatibility.
-
-if sys.version_info[0] < 3:
-    def func_code(f):
-        return f.func_code
-else:
-    def func_code(f):
-        return f.__code__
-
-# This regular expression is used to match valid token names
-_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')
-
-# Exception thrown when invalid token encountered and no default error
-# handler is defined.
-
-class LexError(Exception):
-    def __init__(self,message,s):
-         self.args = (message,)
-         self.text = s
-
-# Token class.  This class is used to represent the tokens produced.
-class LexToken(object):
-    def __str__(self):
-        return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos)
-    def __repr__(self):
-        return str(self)
-
-# This object is a stand-in for a logging object created by the 
-# logging module.  
-
-class PlyLogger(object):
-    def __init__(self,f):
-        self.f = f
-    def critical(self,msg,*args,**kwargs):
-        self.f.write((msg % args) + "\n")
-
-    def warning(self,msg,*args,**kwargs):
-        self.f.write("WARNING: "+ (msg % args) + "\n")
-
-    def error(self,msg,*args,**kwargs):
-        self.f.write("ERROR: " + (msg % args) + "\n")
-
-    info = critical
-    debug = critical
-
-# Null logger is used when no output is generated. Does nothing.
-class NullLogger(object):
-    def __getattribute__(self,name):
-        return self
-    def __call__(self,*args,**kwargs):
-        return self
-
-# -----------------------------------------------------------------------------
-#                        === Lexing Engine ===
-#
-# The following Lexer class implements the lexer runtime.   There are only
-# a few public methods and attributes:
-#
-#    input()          -  Store a new string in the lexer
-#    token()          -  Get the next token
-#    clone()          -  Clone the lexer
-#
-#    lineno           -  Current line number
-#    lexpos           -  Current position in the input string
-# -----------------------------------------------------------------------------
-
-class Lexer:
-    def __init__(self):
-        self.lexre = None             # Master regular expression. This is a list of
-                                      # tuples (re,findex) where re is a compiled
-                                      # regular expression and findex is a list
-                                      # mapping regex group numbers to rules
-        self.lexretext = None         # Current regular expression strings
-        self.lexstatere = {}          # Dictionary mapping lexer states to master regexs
-        self.lexstateretext = {}      # Dictionary mapping lexer states to regex strings
-        self.lexstaterenames = {}     # Dictionary mapping lexer states to symbol names
-        self.lexstate = "INITIAL"     # Current lexer state
-        self.lexstatestack = []       # Stack of lexer states
-        self.lexstateinfo = None      # State information
-        self.lexstateignore = {}      # Dictionary of ignored characters for each state
-        self.lexstateerrorf = {}      # Dictionary of error functions for each state
-        self.lexreflags = 0           # Optional re compile flags
-        self.lexdata = None           # Actual input data (as a string)
-        self.lexpos = 0               # Current position in input text
-        self.lexlen = 0               # Length of the input text
-        self.lexerrorf = None         # Error rule (if any)
-        self.lextokens = None         # List of valid tokens
-        self.lexignore = ""           # Ignored characters
-        self.lexliterals = ""         # Literal characters that can be passed through
-        self.lexmodule = None         # Module
-        self.lineno = 1               # Current line number
-        self.lexoptimize = 0          # Optimized mode
-
-    def clone(self,object=None):
-        c = copy.copy(self)
-
-        # If the object parameter has been supplied, it means we are attaching the
-        # lexer to a new object.  In this case, we have to rebind all methods in
-        # the lexstatere and lexstateerrorf tables.
-
-        if object:
-            newtab = { }
-            for key, ritem in self.lexstatere.items():
-                newre = []
-                for cre, findex in ritem:
-                     newfindex = []
-                     for f in findex:
-                         if not f or not f[0]:
-                             newfindex.append(f)
-                             continue
-                         newfindex.append((getattr(object,f[0].__name__),f[1]))
-                newre.append((cre,newfindex))
-                newtab[key] = newre
-            c.lexstatere = newtab
-            c.lexstateerrorf = { }
-            for key, ef in self.lexstateerrorf.items():
-                c.lexstateerrorf[key] = getattr(object,ef.__name__)
-            c.lexmodule = object
-        return c
-
-    # ------------------------------------------------------------
-    # writetab() - Write lexer information to a table file
-    # ------------------------------------------------------------
-    def writetab(self,tabfile,outputdir=""):
-        if isinstance(tabfile,types.ModuleType):
-            return
-        basetabfilename = tabfile.split(".")[-1]
-        filename = os.path.join(outputdir,basetabfilename)+".py"
-        tf = open(filename,"w")
-        tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__))
-        tf.write("_tabversion   = %s\n" % repr(__tabversion__))
-        tf.write("_lextokens    = %s\n" % repr(self.lextokens))
-        tf.write("_lexreflags   = %s\n" % repr(self.lexreflags))
-        tf.write("_lexliterals  = %s\n" % repr(self.lexliterals))
-        tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo))
-
-        tabre = { }
-        # Collect all functions in the initial state
-        initial = self.lexstatere["INITIAL"]
-        initialfuncs = []
-        for part in initial:
-            for f in part[1]:
-                if f and f[0]:
-                    initialfuncs.append(f)
-
-        for key, lre in self.lexstatere.items():
-             titem = []
-             for i in range(len(lre)):
-                  titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i])))
-             tabre[key] = titem
-
-        tf.write("_lexstatere   = %s\n" % repr(tabre))
-        tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore))
-
-        taberr = { }
-        for key, ef in self.lexstateerrorf.items():
-             if ef:
-                  taberr[key] = ef.__name__
-             else:
-                  taberr[key] = None
-        tf.write("_lexstateerrorf = %s\n" % repr(taberr))
-        tf.close()
-
-    # ------------------------------------------------------------
-    # readtab() - Read lexer information from a tab file
-    # ------------------------------------------------------------
-    def readtab(self,tabfile,fdict):
-        if isinstance(tabfile,types.ModuleType):
-            lextab = tabfile
-        else:
-            if sys.version_info[0] < 3:
-                exec("import %s as lextab" % tabfile)
-            else:
-                env = { }
-                exec("import %s as lextab" % tabfile, env,env)
-                lextab = env['lextab']
-
-        if getattr(lextab,"_tabversion","0.0") != __tabversion__:
-            raise ImportError("Inconsistent PLY version")
-
-        self.lextokens      = lextab._lextokens
-        self.lexreflags     = lextab._lexreflags
-        self.lexliterals    = lextab._lexliterals
-        self.lexstateinfo   = lextab._lexstateinfo
-        self.lexstateignore = lextab._lexstateignore
-        self.lexstatere     = { }
-        self.lexstateretext = { }
-        for key,lre in lextab._lexstatere.items():
-             titem = []
-             txtitem = []
-             for i in range(len(lre)):
-                  titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict)))
-                  txtitem.append(lre[i][0])
-             self.lexstatere[key] = titem
-             self.lexstateretext[key] = txtitem
-        self.lexstateerrorf = { }
-        for key,ef in lextab._lexstateerrorf.items():
-             self.lexstateerrorf[key] = fdict[ef]
-        self.begin('INITIAL')
-
-    # ------------------------------------------------------------
-    # input() - Push a new string into the lexer
-    # ------------------------------------------------------------
-    def input(self,s):
-        # Pull off the first character to see if s looks like a string
-        c = s[:1]
-        if not isinstance(c,StringTypes):
-            raise ValueError("Expected a string")
-        self.lexdata = s
-        self.lexpos = 0
-        self.lexlen = len(s)
-
-    # ------------------------------------------------------------
-    # begin() - Changes the lexing state
-    # ------------------------------------------------------------
-    def begin(self,state):
-        if not state in self.lexstatere:
-            raise ValueError("Undefined state")
-        self.lexre = self.lexstatere[state]
-        self.lexretext = self.lexstateretext[state]
-        self.lexignore = self.lexstateignore.get(state,"")
-        self.lexerrorf = self.lexstateerrorf.get(state,None)
-        self.lexstate = state
-
-    # ------------------------------------------------------------
-    # push_state() - Changes the lexing state and saves old on stack
-    # ------------------------------------------------------------
-    def push_state(self,state):
-        self.lexstatestack.append(self.lexstate)
-        self.begin(state)
-
-    # ------------------------------------------------------------
-    # pop_state() - Restores the previous state
-    # ------------------------------------------------------------
-    def pop_state(self):
-        self.begin(self.lexstatestack.pop())
-
-    # ------------------------------------------------------------
-    # current_state() - Returns the current lexing state
-    # ------------------------------------------------------------
-    def current_state(self):
-        return self.lexstate
-
-    # ------------------------------------------------------------
-    # skip() - Skip ahead n characters
-    # ------------------------------------------------------------
-    def skip(self,n):
-        self.lexpos += n
-
-    # ------------------------------------------------------------
-    # opttoken() - Return the next token from the Lexer
-    #
-    # Note: This function has been carefully implemented to be as fast
-    # as possible.  Don't make changes unless you really know what
-    # you are doing
-    # ------------------------------------------------------------
-    def token(self):
-        # Make local copies of frequently referenced attributes
-        lexpos    = self.lexpos
-        lexlen    = self.lexlen
-        lexignore = self.lexignore
-        lexdata   = self.lexdata
-
-        while lexpos < lexlen:
-            # This code provides some short-circuit code for whitespace, tabs, and other ignored characters
-            if lexdata[lexpos] in lexignore:
-                lexpos += 1
-                continue
-
-            # Look for a regular expression match
-            for lexre,lexindexfunc in self.lexre:
-                m = lexre.match(lexdata,lexpos)
-                if not m: continue
-
-                # Create a token for return
-                tok = LexToken()
-                tok.value = m.group()
-                tok.lineno = self.lineno
-                tok.lexpos = lexpos
-
-                i = m.lastindex
-                func,tok.type = lexindexfunc[i]
-
-                if not func:
-                   # If no token type was set, it's an ignored token
-                   if tok.type:
-                      self.lexpos = m.end()
-                      return tok
-                   else:
-                      lexpos = m.end()
-                      break
-
-                lexpos = m.end()
-
-                # If token is processed by a function, call it
-
-                tok.lexer = self      # Set additional attributes useful in token rules
-                self.lexmatch = m
-                self.lexpos = lexpos
-
-                newtok = func(tok)
-
-                # Every function must return a token, if nothing, we just move to next token
-                if not newtok:
-                    lexpos    = self.lexpos         # This is here in case user has updated lexpos.
-                    lexignore = self.lexignore      # This is here in case there was a state change
-                    break
-
-                # Verify type of the token.  If not in the token map, raise an error
-                if not self.lexoptimize:
-                    if not newtok.type in self.lextokens:
-                        raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
-                            func_code(func).co_filename, func_code(func).co_firstlineno,
-                            func.__name__, newtok.type),lexdata[lexpos:])
-
-                return newtok
-            else:
-                # No match, see if in literals
-                if lexdata[lexpos] in self.lexliterals:
-                    tok = LexToken()
-                    tok.value = lexdata[lexpos]
-                    tok.lineno = self.lineno
-                    tok.type = tok.value
-                    tok.lexpos = lexpos
-                    self.lexpos = lexpos + 1
-                    return tok
-
-                # No match. Call t_error() if defined.
-                if self.lexerrorf:
-                    tok = LexToken()
-                    tok.value = self.lexdata[lexpos:]
-                    tok.lineno = self.lineno
-                    tok.type = "error"
-                    tok.lexer = self
-                    tok.lexpos = lexpos
-                    self.lexpos = lexpos
-                    newtok = self.lexerrorf(tok)
-                    if lexpos == self.lexpos:
-                        # Error method didn't change text position at all. This is an error.
-                        raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
-                    lexpos = self.lexpos
-                    if not newtok: continue
-                    return newtok
-
-                self.lexpos = lexpos
-                raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:])
-
-        self.lexpos = lexpos + 1
-        if self.lexdata is None:
-             raise RuntimeError("No input string given with input()")
-        return None
-
-    # Iterator interface
-    def __iter__(self):
-        return self
-
-    def next(self):
-        t = self.token()
-        if t is None:
-            raise StopIteration
-        return t
-
-    __next__ = next
-
-# -----------------------------------------------------------------------------
-#                           ==== Lex Builder ===
-#
-# The functions and classes below are used to collect lexing information
-# and build a Lexer object from it.
-# -----------------------------------------------------------------------------
-
-# -----------------------------------------------------------------------------
-# _get_regex(func)
-#
-# Returns the regular expression assigned to a function either as a doc string
-# or as a .regex attribute attached by the @TOKEN decorator.
-# -----------------------------------------------------------------------------
-
-def _get_regex(func):
-    return getattr(func,"regex",func.__doc__)
-
-# -----------------------------------------------------------------------------
-# get_caller_module_dict()
-#
-# This function returns a dictionary containing all of the symbols defined within
-# a caller further down the call stack.  This is used to get the environment
-# associated with the yacc() call if none was provided.
-# -----------------------------------------------------------------------------
-
-def get_caller_module_dict(levels):
-    try:
-        raise RuntimeError
-    except RuntimeError:
-        e,b,t = sys.exc_info()
-        f = t.tb_frame
-        while levels > 0:
-            f = f.f_back                   
-            levels -= 1
-        ldict = f.f_globals.copy()
-        if f.f_globals != f.f_locals:
-            ldict.update(f.f_locals)
-
-        return ldict
-
-# -----------------------------------------------------------------------------
-# _funcs_to_names()
-#
-# Given a list of regular expression functions, this converts it to a list
-# suitable for output to a table file
-# -----------------------------------------------------------------------------
-
-def _funcs_to_names(funclist,namelist):
-    result = []
-    for f,name in zip(funclist,namelist):
-         if f and f[0]:
-             result.append((name, f[1]))
-         else:
-             result.append(f)
-    return result
-
-# -----------------------------------------------------------------------------
-# _names_to_funcs()
-#
-# Given a list of regular expression function names, this converts it back to
-# functions.
-# -----------------------------------------------------------------------------
-
-def _names_to_funcs(namelist,fdict):
-     result = []
-     for n in namelist:
-          if n and n[0]:
-              result.append((fdict[n[0]],n[1]))
-          else:
-              result.append(n)
-     return result
-
-# -----------------------------------------------------------------------------
-# _form_master_re()
-#
-# This function takes a list of all of the regex components and attempts to
-# form the master regular expression.  Given limitations in the Python re
-# module, it may be necessary to break the master regex into separate expressions.
-# -----------------------------------------------------------------------------
-
-def _form_master_re(relist,reflags,ldict,toknames):
-    if not relist: return []
-    regex = "|".join(relist)
-    try:
-        lexre = re.compile(regex,re.VERBOSE | reflags)
-
-        # Build the index to function map for the matching engine
-        lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1)
-        lexindexnames = lexindexfunc[:]
-
-        for f,i in lexre.groupindex.items():
-            handle = ldict.get(f,None)
-            if type(handle) in (types.FunctionType, types.MethodType):
-                lexindexfunc[i] = (handle,toknames[f])
-                lexindexnames[i] = f
-            elif handle is not None:
-                lexindexnames[i] = f
-                if f.find("ignore_") > 0:
-                    lexindexfunc[i] = (None,None)
-                else:
-                    lexindexfunc[i] = (None, toknames[f])
-        
-        return [(lexre,lexindexfunc)],[regex],[lexindexnames]
-    except Exception:
-        m = int(len(relist)/2)
-        if m == 0: m = 1
-        llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames)
-        rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames)
-        return llist+rlist, lre+rre, lnames+rnames
-
-# -----------------------------------------------------------------------------
-# def _statetoken(s,names)
-#
-# Given a declaration name s of the form "t_" and a dictionary whose keys are
-# state names, this function returns a tuple (states,tokenname) where states
-# is a tuple of state names and tokenname is the name of the token.  For example,
-# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM')
-# -----------------------------------------------------------------------------
-
-def _statetoken(s,names):
-    nonstate = 1
-    parts = s.split("_")
-    for i in range(1,len(parts)):
-         if not parts[i] in names and parts[i] != 'ANY': break
-    if i > 1:
-       states = tuple(parts[1:i])
-    else:
-       states = ('INITIAL',)
-
-    if 'ANY' in states:
-       states = tuple(names)
-
-    tokenname = "_".join(parts[i:])
-    return (states,tokenname)
-
-
-# -----------------------------------------------------------------------------
-# LexerReflect()
-#
-# This class represents information needed to build a lexer as extracted from a
-# user's input file.
-# -----------------------------------------------------------------------------
-class LexerReflect(object):
-    def __init__(self,ldict,log=None,reflags=0):
-        self.ldict      = ldict
-        self.error_func = None
-        self.tokens     = []
-        self.reflags    = reflags
-        self.stateinfo  = { 'INITIAL' : 'inclusive'}
-        self.modules    = {}
-        self.error      = 0
-
-        if log is None:
-            self.log = PlyLogger(sys.stderr)
-        else:
-            self.log = log
-
-    # Get all of the basic information
-    def get_all(self):
-        self.get_tokens()
-        self.get_literals()
-        self.get_states()
-        self.get_rules()
-        
-    # Validate all of the information
-    def validate_all(self):
-        self.validate_tokens()
-        self.validate_literals()
-        self.validate_rules()
-        return self.error
-
-    # Get the tokens map
-    def get_tokens(self):
-        tokens = self.ldict.get("tokens",None)
-        if not tokens:
-            self.log.error("No token list is defined")
-            self.error = 1
-            return
-
-        if not isinstance(tokens,(list, tuple)):
-            self.log.error("tokens must be a list or tuple")
-            self.error = 1
-            return
-        
-        if not tokens:
-            self.log.error("tokens is empty")
-            self.error = 1
-            return
-
-        self.tokens = tokens
-
-    # Validate the tokens
-    def validate_tokens(self):
-        terminals = {}
-        for n in self.tokens:
-            if not _is_identifier.match(n):
-                self.log.error("Bad token name '%s'",n)
-                self.error = 1
-            if n in terminals:
-                self.log.warning("Token '%s' multiply defined", n)
-            terminals[n] = 1
-
-    # Get the literals specifier
-    def get_literals(self):
-        self.literals = self.ldict.get("literals","")
-        if not self.literals:
-            self.literals = ""
-
-    # Validate literals
-    def validate_literals(self):
-        try:
-            for c in self.literals:
-                if not isinstance(c,StringTypes) or len(c) > 1:
-                    self.log.error("Invalid literal %s. Must be a single character", repr(c))
-                    self.error = 1
-
-        except TypeError:
-            self.log.error("Invalid literals specification. literals must be a sequence of characters")
-            self.error = 1
-
-    def get_states(self):
-        self.states = self.ldict.get("states",None)
-        # Build statemap
-        if self.states:
-             if not isinstance(self.states,(tuple,list)):
-                  self.log.error("states must be defined as a tuple or list")
-                  self.error = 1
-             else:
-                  for s in self.states:
-                        if not isinstance(s,tuple) or len(s) != 2:
-                               self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s))
-                               self.error = 1
-                               continue
-                        name, statetype = s
-                        if not isinstance(name,StringTypes):
-                               self.log.error("State name %s must be a string", repr(name))
-                               self.error = 1
-                               continue
-                        if not (statetype == 'inclusive' or statetype == 'exclusive'):
-                               self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name)
-                               self.error = 1
-                               continue
-                        if name in self.stateinfo:
-                               self.log.error("State '%s' already defined",name)
-                               self.error = 1
-                               continue
-                        self.stateinfo[name] = statetype
-
-    # Get all of the symbols with a t_ prefix and sort them into various
-    # categories (functions, strings, error functions, and ignore characters)
-
-    def get_rules(self):
-        tsymbols = [f for f in self.ldict if f[:2] == 't_' ]
-
-        # Now build up a list of functions and a list of strings
-
-        self.toknames = { }        # Mapping of symbols to token names
-        self.funcsym =  { }        # Symbols defined as functions
-        self.strsym =   { }        # Symbols defined as strings
-        self.ignore   = { }        # Ignore strings by state
-        self.errorf   = { }        # Error functions by state
-
-        for s in self.stateinfo:
-             self.funcsym[s] = []
-             self.strsym[s] = []
-
-        if len(tsymbols) == 0:
-            self.log.error("No rules of the form t_rulename are defined")
-            self.error = 1
-            return
-
-        for f in tsymbols:
-            t = self.ldict[f]
-            states, tokname = _statetoken(f,self.stateinfo)
-            self.toknames[f] = tokname
-
-            if hasattr(t,"__call__"):
-                if tokname == 'error':
-                    for s in states:
-                        self.errorf[s] = t
-                elif tokname == 'ignore':
-                    line = func_code(t).co_firstlineno
-                    file = func_code(t).co_filename
-                    self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__)
-                    self.error = 1
-                else:
-                    for s in states: 
-                        self.funcsym[s].append((f,t))
-            elif isinstance(t, StringTypes):
-                if tokname == 'ignore':
-                    for s in states:
-                        self.ignore[s] = t
-                    if "\\" in t:
-                        self.log.warning("%s contains a literal backslash '\\'",f)
-
-                elif tokname == 'error':
-                    self.log.error("Rule '%s' must be defined as a function", f)
-                    self.error = 1
-                else:
-                    for s in states: 
-                        self.strsym[s].append((f,t))
-            else:
-                self.log.error("%s not defined as a function or string", f)
-                self.error = 1
-
-        # Sort the functions by line number
-        for f in self.funcsym.values():
-            if sys.version_info[0] < 3:
-                f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno))
-            else:
-                # Python 3.0
-                f.sort(key=lambda x: func_code(x[1]).co_firstlineno)
-
-        # Sort the strings by regular expression length
-        for s in self.strsym.values():
-            if sys.version_info[0] < 3:
-                s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1])))
-            else:
-                # Python 3.0
-                s.sort(key=lambda x: len(x[1]),reverse=True)
-
-    # Validate all of the t_rules collected 
-    def validate_rules(self):
-        for state in self.stateinfo:
-            # Validate all rules defined by functions
-
-            
-
-            for fname, f in self.funcsym[state]:
-                line = func_code(f).co_firstlineno
-                file = func_code(f).co_filename
-                module = inspect.getmodule(f)
-                self.modules[module] = 1
-
-                tokname = self.toknames[fname]
-                if isinstance(f, types.MethodType):
-                    reqargs = 2
-                else:
-                    reqargs = 1
-                nargs = func_code(f).co_argcount
-                if nargs > reqargs:
-                    self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
-                    self.error = 1
-                    continue
-
-                if nargs < reqargs:
-                    self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
-                    self.error = 1
-                    continue
-
-                if not _get_regex(f):
-                    self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__)
-                    self.error = 1
-                    continue
-
-                try:
-                    c = re.compile("(?P<%s>%s)" % (fname, _get_regex(f)), re.VERBOSE | self.reflags)
-                    if c.match(""):
-                        self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__)
-                        self.error = 1
-                except re.error:
-                    _etype, e, _etrace = sys.exc_info()
-                    self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e)
-                    if '#' in _get_regex(f):
-                        self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__)
-                    self.error = 1
-
-            # Validate all rules defined by strings
-            for name,r in self.strsym[state]:
-                tokname = self.toknames[name]
-                if tokname == 'error':
-                    self.log.error("Rule '%s' must be defined as a function", name)
-                    self.error = 1
-                    continue
-
-                if not tokname in self.tokens and tokname.find("ignore_") < 0:
-                    self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname)
-                    self.error = 1
-                    continue
-
-                try:
-                    c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags)
-                    if (c.match("")):
-                         self.log.error("Regular expression for rule '%s' matches empty string",name)
-                         self.error = 1
-                except re.error:
-                    _etype, e, _etrace = sys.exc_info()
-                    self.log.error("Invalid regular expression for rule '%s'. %s",name,e)
-                    if '#' in r:
-                         self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name)
-                    self.error = 1
-
-            if not self.funcsym[state] and not self.strsym[state]:
-                self.log.error("No rules defined for state '%s'",state)
-                self.error = 1
-
-            # Validate the error function
-            efunc = self.errorf.get(state,None)
-            if efunc:
-                f = efunc
-                line = func_code(f).co_firstlineno
-                file = func_code(f).co_filename
-                module = inspect.getmodule(f)
-                self.modules[module] = 1
-
-                if isinstance(f, types.MethodType):
-                    reqargs = 2
-                else:
-                    reqargs = 1
-                nargs = func_code(f).co_argcount
-                if nargs > reqargs:
-                    self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
-                    self.error = 1
-
-                if nargs < reqargs:
-                    self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
-                    self.error = 1
-
-        for module in self.modules:
-            self.validate_module(module)
-
-
-    # -----------------------------------------------------------------------------
-    # validate_module()
-    #
-    # This checks to see if there are duplicated t_rulename() functions or strings
-    # in the parser input file.  This is done using a simple regular expression
-    # match on each line in the source code of the given module.
-    # -----------------------------------------------------------------------------
-
-    def validate_module(self, module):
-        lines, linen = inspect.getsourcelines(module)
-
-        fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
-        sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')
-
-        counthash = { }
-        linen += 1
-        for l in lines:
-            m = fre.match(l)
-            if not m:
-                m = sre.match(l)
-            if m:
-                name = m.group(1)
-                prev = counthash.get(name)
-                if not prev:
-                    counthash[name] = linen
-                else:
-                    filename = inspect.getsourcefile(module)
-                    self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev)
-                    self.error = 1
-            linen += 1
-            
-# -----------------------------------------------------------------------------
-# lex(module)
-#
-# Build all of the regular expression rules from definitions in the supplied module
-# -----------------------------------------------------------------------------
-def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None):
-    global lexer
-    ldict = None
-    stateinfo  = { 'INITIAL' : 'inclusive'}
-    lexobj = Lexer()
-    lexobj.lexoptimize = optimize
-    global token,input
-
-    if errorlog is None:
-        errorlog = PlyLogger(sys.stderr)
-
-    if debug:
-        if debuglog is None:
-            debuglog = PlyLogger(sys.stderr)
-
-    # Get the module dictionary used for the lexer
-    if object: module = object
-
-    if module:
-        _items = [(k,getattr(module,k)) for k in dir(module)]
-        ldict = dict(_items)
-    else:
-        ldict = get_caller_module_dict(2)
-
-    # Collect parser information from the dictionary
-    linfo = LexerReflect(ldict,log=errorlog,reflags=reflags)
-    linfo.get_all()
-    if not optimize:
-        if linfo.validate_all():
-            raise SyntaxError("Can't build lexer")
-
-    if optimize and lextab:
-        try:
-            lexobj.readtab(lextab,ldict)
-            token = lexobj.token
-            input = lexobj.input
-            lexer = lexobj
-            return lexobj
-
-        except ImportError:
-            pass
-
-    # Dump some basic debugging information
-    if debug:
-        debuglog.info("lex: tokens   = %r", linfo.tokens)
-        debuglog.info("lex: literals = %r", linfo.literals)
-        debuglog.info("lex: states   = %r", linfo.stateinfo)
-
-    # Build a dictionary of valid token names
-    lexobj.lextokens = { }
-    for n in linfo.tokens:
-        lexobj.lextokens[n] = 1
-
-    # Get literals specification
-    if isinstance(linfo.literals,(list,tuple)):
-        lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
-    else:
-        lexobj.lexliterals = linfo.literals
-
-    # Get the stateinfo dictionary
-    stateinfo = linfo.stateinfo
-
-    regexs = { }
-    # Build the master regular expressions
-    for state in stateinfo:
-        regex_list = []
-
-        # Add rules defined by functions first
-        for fname, f in linfo.funcsym[state]:
-            line = func_code(f).co_firstlineno
-            file = func_code(f).co_filename
-            regex_list.append("(?P<%s>%s)" % (fname,_get_regex(f)))
-            if debug:
-                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,_get_regex(f), state)
-
-        # Now add all of the simple rules
-        for name,r in linfo.strsym[state]:
-            regex_list.append("(?P<%s>%s)" % (name,r))
-            if debug:
-                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state)
-
-        regexs[state] = regex_list
-
-    # Build the master regular expressions
-
-    if debug:
-        debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====")
-
-    for state in regexs:
-        lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames)
-        lexobj.lexstatere[state] = lexre
-        lexobj.lexstateretext[state] = re_text
-        lexobj.lexstaterenames[state] = re_names
-        if debug:
-            for i in range(len(re_text)):
-                debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i])
-
-    # For inclusive states, we need to add the regular expressions from the INITIAL state
-    for state,stype in stateinfo.items():
-        if state != "INITIAL" and stype == 'inclusive':
-             lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
-             lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
-             lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
-
-    lexobj.lexstateinfo = stateinfo
-    lexobj.lexre = lexobj.lexstatere["INITIAL"]
-    lexobj.lexretext = lexobj.lexstateretext["INITIAL"]
-    lexobj.lexreflags = reflags
-
-    # Set up ignore variables
-    lexobj.lexstateignore = linfo.ignore
-    lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","")
-
-    # Set up error functions
-    lexobj.lexstateerrorf = linfo.errorf
-    lexobj.lexerrorf = linfo.errorf.get("INITIAL",None)
-    if not lexobj.lexerrorf:
-        errorlog.warning("No t_error rule is defined")
-
-    # Check state information for ignore and error rules
-    for s,stype in stateinfo.items():
-        if stype == 'exclusive':
-              if not s in linfo.errorf:
-                   errorlog.warning("No error rule is defined for exclusive state '%s'", s)
-              if not s in linfo.ignore and lexobj.lexignore:
-                   errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
-        elif stype == 'inclusive':
-              if not s in linfo.errorf:
-                   linfo.errorf[s] = linfo.errorf.get("INITIAL",None)
-              if not s in linfo.ignore:
-                   linfo.ignore[s] = linfo.ignore.get("INITIAL","")
-
-    # Create global versions of the token() and input() functions
-    token = lexobj.token
-    input = lexobj.input
-    lexer = lexobj
-
-    # If in optimize mode, we write the lextab
-    if lextab and optimize:
-        lexobj.writetab(lextab,outputdir)
-
-    return lexobj
-
-# -----------------------------------------------------------------------------
-# runmain()
-#
-# This runs the lexer as a main program
-# -----------------------------------------------------------------------------
-
-def runmain(lexer=None,data=None):
-    if not data:
-        try:
-            filename = sys.argv[1]
-            f = open(filename)
-            data = f.read()
-            f.close()
-        except IndexError:
-            sys.stdout.write("Reading from standard input (type EOF to end):\n")
-            data = sys.stdin.read()
-
-    if lexer:
-        _input = lexer.input
-    else:
-        _input = input
-    _input(data)
-    if lexer:
-        _token = lexer.token
-    else:
-        _token = token
-
-    while 1:
-        tok = _token()
-        if not tok: break
-        sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos))
-
-# -----------------------------------------------------------------------------
-# @TOKEN(regex)
-#
-# This decorator function can be used to set the regex expression on a function
-# when its docstring might need to be set in an alternative way
-# -----------------------------------------------------------------------------
-
-def TOKEN(r):
-    def set_regex(f):
-        if hasattr(r,"__call__"):
-            f.regex = _get_regex(r)
-        else:
-            f.regex = r
-        return f
-    return set_regex
-
-# Alternative spelling of the TOKEN decorator
-Token = TOKEN
-
diff --git a/ply/yacc.py b/ply/yacc.py
deleted file mode 100644
index 49d83d7..0000000
--- a/ply/yacc.py
+++ /dev/null
@@ -1,3331 +0,0 @@
-# -----------------------------------------------------------------------------
-# ply: yacc.py
-#
-# Copyright (C) 2001-2011,
-# David M. Beazley (Dabeaz LLC)
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-# 
-# * Redistributions of source code must retain the above copyright notice,
-#   this list of conditions and the following disclaimer.  
-# * Redistributions in binary form must reproduce the above copyright notice, 
-#   this list of conditions and the following disclaimer in the documentation
-#   and/or other materials provided with the distribution.  
-# * Neither the name of the David Beazley or Dabeaz LLC may be used to
-#   endorse or promote products derived from this software without
-#  specific prior written permission. 
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-# -----------------------------------------------------------------------------
-#
-# This implements an LR parser that is constructed from grammar rules defined
-# as Python functions. The grammer is specified by supplying the BNF inside
-# Python documentation strings.  The inspiration for this technique was borrowed
-# from John Aycock's Spark parsing system.  PLY might be viewed as cross between
-# Spark and the GNU bison utility.
-#
-# The current implementation is only somewhat object-oriented. The
-# LR parser itself is defined in terms of an object (which allows multiple
-# parsers to co-exist).  However, most of the variables used during table
-# construction are defined in terms of global variables.  Users shouldn't
-# notice unless they are trying to define multiple parsers at the same
-# time using threads (in which case they should have their head examined).
-#
-# This implementation supports both SLR and LALR(1) parsing.  LALR(1)
-# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu),
-# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles,
-# Techniques, and Tools" (The Dragon Book).  LALR(1) has since been replaced
-# by the more efficient DeRemer and Pennello algorithm.
-#
-# :::::::: WARNING :::::::
-#
-# Construction of LR parsing tables is fairly complicated and expensive.
-# To make this module run fast, a *LOT* of work has been put into
-# optimization---often at the expensive of readability and what might
-# consider to be good Python "coding style."   Modify the code at your
-# own risk!
-# ----------------------------------------------------------------------------
-
-__version__    = "3.5"
-__tabversion__ = "3.2"       # Table version
-
-#-----------------------------------------------------------------------------
-#                     === User configurable parameters ===
-#
-# Change these to modify the default behavior of yacc (if you wish)
-#-----------------------------------------------------------------------------
-
-yaccdebug   = 1                # Debugging mode.  If set, yacc generates a
-                               # a 'parser.out' file in the current directory
-
-debug_file  = 'parser.out'     # Default name of the debugging file
-tab_module  = 'parsetab'       # Default name of the table module
-default_lr  = 'LALR'           # Default LR table generation method
-
-error_count = 3                # Number of symbols that must be shifted to leave recovery mode
-
-yaccdevel   = 0                # Set to True if developing yacc.  This turns off optimized
-                               # implementations of certain functions.
-
-resultlimit = 40               # Size limit of results when running in debug mode.
-
-pickle_protocol = 0            # Protocol to use when writing pickle files
-
-import re, types, sys, os.path, inspect
-
-# Compatibility function for python 2.6/3.0
-if sys.version_info[0] < 3:
-    def func_code(f):
-        return f.func_code
-else:
-    def func_code(f):
-        return f.__code__
-
-# String type-checking compatibility
-if sys.version_info[0] < 3:
-    string_types = basestring
-else:
-    string_types = str
-
-# Compatibility
-try:
-    MAXINT = sys.maxint
-except AttributeError:
-    MAXINT = sys.maxsize
-
-# Python 2.x/3.0 compatibility.
-def load_ply_lex():
-    if sys.version_info[0] < 3:
-        import lex
-    else:
-        import ply.lex as lex
-    return lex
-
-# This object is a stand-in for a logging object created by the 
-# logging module.   PLY will use this by default to create things
-# such as the parser.out file.  If a user wants more detailed
-# information, they can create their own logging object and pass
-# it into PLY.
-
-class PlyLogger(object):
-    def __init__(self,f):
-        self.f = f
-    def debug(self,msg,*args,**kwargs):
-        self.f.write((msg % args) + "\n")
-    info     = debug
-
-    def warning(self,msg,*args,**kwargs):
-        self.f.write("WARNING: "+ (msg % args) + "\n")
-
-    def error(self,msg,*args,**kwargs):
-        self.f.write("ERROR: " + (msg % args) + "\n")
-
-    critical = debug
-
-# Null logger is used when no output is generated. Does nothing.
-class NullLogger(object):
-    def __getattribute__(self,name):
-        return self
-    def __call__(self,*args,**kwargs):
-        return self
-        
-# Exception raised for yacc-related errors
-class YaccError(Exception):   pass
-
-# Format the result message that the parser produces when running in debug mode.
-def format_result(r):
-    repr_str = repr(r)
-    if '\n' in repr_str: repr_str = repr(repr_str)
-    if len(repr_str) > resultlimit:
-        repr_str = repr_str[:resultlimit]+" ..."
-    result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str)
-    return result
-
-
-# Format stack entries when the parser is running in debug mode
-def format_stack_entry(r):
-    repr_str = repr(r)
-    if '\n' in repr_str: repr_str = repr(repr_str)
-    if len(repr_str) < 16:
-        return repr_str
-    else:
-        return "<%s @ 0x%x>" % (type(r).__name__,id(r))
-
-# Panic mode error recovery support.   This feature is being reworked--much of the
-# code here is to offer a deprecation/backwards compatible transition
-
-_errok = None
-_token = None
-_restart = None
-_warnmsg = """PLY: Don't use global functions errok(), token(), and restart() in p_error().
-Instead, invoke the methods on the associated parser instance:
-
-    def p_error(p):
-        ...
-        # Use parser.errok(), parser.token(), parser.restart()
-        ...
-
-    parser = yacc.yacc()
-"""
-import warnings
-def errok():
-    warnings.warn(_warnmsg)
-    return _errok()
-
-def restart():
-    warnings.warn(_warnmsg)
-    return _restart()
-
-def token():
-    warnings.warn(_warnmsg)
-    return _token()
-
-# Utility function to call the p_error() function with some deprecation hacks
-def call_errorfunc(errorfunc,token,parser):
-    global _errok, _token, _restart
-    _errok = parser.errok
-    _token = parser.token
-    _restart = parser.restart
-    r = errorfunc(token)
-    del _errok, _token, _restart
-
-#-----------------------------------------------------------------------------
-#                        ===  LR Parsing Engine ===
-#
-# The following classes are used for the LR parser itself.  These are not
-# used during table construction and are independent of the actual LR
-# table generation algorithm
-#-----------------------------------------------------------------------------
-
-# This class is used to hold non-terminal grammar symbols during parsing.
-# It normally has the following attributes set:
-#        .type       = Grammar symbol type
-#        .value      = Symbol value
-#        .lineno     = Starting line number
-#        .endlineno  = Ending line number (optional, set automatically)
-#        .lexpos     = Starting lex position
-#        .endlexpos  = Ending lex position (optional, set automatically)
-
-class YaccSymbol:
-    def __str__(self):    return self.type
-    def __repr__(self):   return str(self)
-
-# This class is a wrapper around the objects actually passed to each
-# grammar rule.   Index lookup and assignment actually assign the
-# .value attribute of the underlying YaccSymbol object.
-# The lineno() method returns the line number of a given
-# item (or 0 if not defined).   The linespan() method returns
-# a tuple of (startline,endline) representing the range of lines
-# for a symbol.  The lexspan() method returns a tuple (lexpos,endlexpos)
-# representing the range of positional information for a symbol.
-
-class YaccProduction:
-    def __init__(self,s,stack=None):
-        self.slice = s
-        self.stack = stack
-        self.lexer = None
-        self.parser= None
-    def __getitem__(self,n):
-        if isinstance(n, slice):
-            return [s.value for s in self.slice[n]]
-        elif n >= 0: 
-            return self.slice[n].value
-        else: 
-            return self.stack[n].value
-
-    def __setitem__(self,n,v):
-        self.slice[n].value = v
-
-    def __getslice__(self,i,j):
-        return [s.value for s in self.slice[i:j]]
-
-    def __len__(self):
-        return len(self.slice)
-
-    def lineno(self,n):
-        return getattr(self.slice[n],"lineno",0)
-
-    def set_lineno(self,n,lineno):
-        self.slice[n].lineno = lineno
-
-    def linespan(self,n):
-        startline = getattr(self.slice[n],"lineno",0)
-        endline = getattr(self.slice[n],"endlineno",startline)
-        return startline,endline
-
-    def lexpos(self,n):
-        return getattr(self.slice[n],"lexpos",0)
-
-    def lexspan(self,n):
-        startpos = getattr(self.slice[n],"lexpos",0)
-        endpos = getattr(self.slice[n],"endlexpos",startpos)
-        return startpos,endpos
-
-    def error(self):
-       raise SyntaxError
-
-
-# -----------------------------------------------------------------------------
-#                               == LRParser ==
-#
-# The LR Parsing engine.
-# -----------------------------------------------------------------------------
-
-class LRParser:
-    def __init__(self,lrtab,errorf):
-        self.productions = lrtab.lr_productions
-        self.action      = lrtab.lr_action
-        self.goto        = lrtab.lr_goto
-        self.errorfunc   = errorf
-
-    def errok(self):
-        self.errorok     = 1
-
-    def restart(self):
-        del self.statestack[:]
-        del self.symstack[:]
-        sym = YaccSymbol()
-        sym.type = '$end'
-        self.symstack.append(sym)
-        self.statestack.append(0)
-
-    def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
-        if debug or yaccdevel:
-            if isinstance(debug,int):
-                debug = PlyLogger(sys.stderr)
-            return self.parsedebug(input,lexer,debug,tracking,tokenfunc)
-        elif tracking:
-            return self.parseopt(input,lexer,debug,tracking,tokenfunc)
-        else:
-            return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc)
-        
-
-    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    # parsedebug().
-    #
-    # This is the debugging enabled version of parse().  All changes made to the
-    # parsing engine should be made here.   For the non-debugging version,
-    # copy this code to a method parseopt() and delete all of the sections
-    # enclosed in:
-    #
-    #      #--! DEBUG
-    #      statements
-    #      #--! DEBUG
-    #
-    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-    def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None):
-        lookahead = None                 # Current lookahead symbol
-        lookaheadstack = [ ]             # Stack of lookahead symbols
-        actions = self.action            # Local reference to action table (to avoid lookup on self.)
-        goto    = self.goto              # Local reference to goto table (to avoid lookup on self.)
-        prod    = self.productions       # Local reference to production list (to avoid lookup on self.)
-        pslice  = YaccProduction(None)   # Production object passed to grammar rules
-        errorcount = 0                   # Used during error recovery 
-
-        # --! DEBUG
-        debug.info("PLY: PARSE DEBUG START")
-        # --! DEBUG
-
-        # If no lexer was given, we will try to use the lex module
-        if not lexer:
-            lex = load_ply_lex()
-            lexer = lex.lexer
-
-        # Set up the lexer and parser objects on pslice
-        pslice.lexer = lexer
-        pslice.parser = self
-
-        # If input was supplied, pass to lexer
-        if input is not None:
-            lexer.input(input)
-
-        if tokenfunc is None:
-           # Tokenize function
-           get_token = lexer.token
-        else:
-           get_token = tokenfunc
-
-        # Set the parser() token method (sometimes used in error recovery)
-        self.token = get_token
-
-        # Set up the state and symbol stacks
-
-        statestack = [ ]                # Stack of parsing states
-        self.statestack = statestack
-        symstack   = [ ]                # Stack of grammar symbols
-        self.symstack = symstack
-
-        pslice.stack = symstack         # Put in the production
-        errtoken   = None               # Err token
-
-        # The start state is assumed to be (0,$end)
-
-        statestack.append(0)
-        sym = YaccSymbol()
-        sym.type = "$end"
-        symstack.append(sym)
-        state = 0
-        while 1:
-            # Get the next symbol on the input.  If a lookahead symbol
-            # is already set, we just use that. Otherwise, we'll pull
-            # the next token off of the lookaheadstack or from the lexer
-
-            # --! DEBUG
-            debug.debug('')
-            debug.debug('State  : %s', state)
-            # --! DEBUG
-
-            if not lookahead:
-                if not lookaheadstack:
-                    lookahead = get_token()     # Get the next token
-                else:
-                    lookahead = lookaheadstack.pop()
-                if not lookahead:
-                    lookahead = YaccSymbol()
-                    lookahead.type = "$end"
-
-            # --! DEBUG
-            debug.debug('Stack  : %s',
-                        ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
-            # --! DEBUG
-
-            # Check the action table
-            ltype = lookahead.type
-            t = actions[state].get(ltype)
-
-            if t is not None:
-                if t > 0:
-                    # shift a symbol on the stack
-                    statestack.append(t)
-                    state = t
-                    
-                    # --! DEBUG
-                    debug.debug("Action : Shift and goto state %s", t)
-                    # --! DEBUG
-
-                    symstack.append(lookahead)
-                    lookahead = None
-
-                    # Decrease error count on successful shift
-                    if errorcount: errorcount -=1
-                    continue
-
-                if t < 0:
-                    # reduce a symbol on the stack, emit a production
-                    p = prod[-t]
-                    pname = p.name
-                    plen  = p.len
-
-                    # Get production function
-                    sym = YaccSymbol()
-                    sym.type = pname       # Production name
-                    sym.value = None
-
-                    # --! DEBUG
-                    if plen:
-                        debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, "["+",".join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+"]",-t)
-                    else:
-                        debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t)
-                        
-                    # --! DEBUG
-
-                    if plen:
-                        targ = symstack[-plen-1:]
-                        targ[0] = sym
-
-                        # --! TRACKING
-                        if tracking:
-                           t1 = targ[1]
-                           sym.lineno = t1.lineno
-                           sym.lexpos = t1.lexpos
-                           t1 = targ[-1]
-                           sym.endlineno = getattr(t1,"endlineno",t1.lineno)
-                           sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
-
-                        # --! TRACKING
-
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
-                        # below as a performance optimization.  Make sure
-                        # changes get made in both locations.
-
-                        pslice.slice = targ
-                        
-                        try:
-                            # Call the grammar rule with our special slice object
-                            del symstack[-plen:]
-                            del statestack[-plen:]
-                            p.callable(pslice)
-                            # --! DEBUG
-                            debug.info("Result : %s", format_result(pslice[0]))
-                            # --! DEBUG
-                            symstack.append(sym)
-                            state = goto[statestack[-1]][pname]
-                            statestack.append(state)
-                        except SyntaxError:
-                            # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
-                            state = statestack[-1]
-                            sym.type = 'error'
-                            lookahead = sym
-                            errorcount = error_count
-                            self.errorok = 0
-                        continue
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    
-                    else:
-
-                        # --! TRACKING
-                        if tracking:
-                           sym.lineno = lexer.lineno
-                           sym.lexpos = lexer.lexpos
-                        # --! TRACKING
-
-                        targ = [ sym ]
-
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
-                        # above as a performance optimization.  Make sure
-                        # changes get made in both locations.
-
-                        pslice.slice = targ
-
-                        try:
-                            # Call the grammar rule with our special slice object
-                            p.callable(pslice)
-                            # --! DEBUG
-                            debug.info("Result : %s", format_result(pslice[0]))
-                            # --! DEBUG
-                            symstack.append(sym)
-                            state = goto[statestack[-1]][pname]
-                            statestack.append(state)
-                        except SyntaxError:
-                            # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
-                            state = statestack[-1]
-                            sym.type = 'error'
-                            lookahead = sym
-                            errorcount = error_count
-                            self.errorok = 0
-                        continue
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-                if t == 0:
-                    n = symstack[-1]
-                    result = getattr(n,"value",None)
-                    # --! DEBUG
-                    debug.info("Done   : Returning %s", format_result(result))
-                    debug.info("PLY: PARSE DEBUG END")
-                    # --! DEBUG
-                    return result
-
-            if t == None:
-
-                # --! DEBUG
-                debug.error('Error  : %s',
-                            ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
-                # --! DEBUG
-
-                # We have some kind of parsing error here.  To handle
-                # this, we are going to push the current token onto
-                # the tokenstack and replace it with an 'error' token.
-                # If there are any synchronization rules, they may
-                # catch it.
-                #
-                # In addition to pushing the error token, we call call
-                # the user defined p_error() function if this is the
-                # first syntax error.  This function is only called if
-                # errorcount == 0.
-                if errorcount == 0 or self.errorok:
-                    errorcount = error_count
-                    self.errorok = 0
-                    errtoken = lookahead
-                    if errtoken.type == "$end":
-                        errtoken = None               # End of file!
-                    if self.errorfunc:
-                        if errtoken and not hasattr(errtoken,'lexer'):
-                            errtoken.lexer = lexer
-                        tok = call_errorfunc(self.errorfunc, errtoken, self)
-                        if self.errorok:
-                            # User must have done some kind of panic
-                            # mode recovery on their own.  The
-                            # returned token is the next lookahead
-                            lookahead = tok
-                            errtoken = None
-                            continue
-                    else:
-                        if errtoken:
-                            if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
-                            else: lineno = 0
-                            if lineno:
-                                sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
-                            else:
-                                sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
-                        else:
-                            sys.stderr.write("yacc: Parse error in input. EOF\n")
-                            return
-
-                else:
-                    errorcount = error_count
-
-                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
-                # entire parse has been rolled back and we're completely hosed.   The token is
-                # discarded and we just keep going.
-
-                if len(statestack) <= 1 and lookahead.type != "$end":
-                    lookahead = None
-                    errtoken = None
-                    state = 0
-                    # Nuke the pushback stack
-                    del lookaheadstack[:]
-                    continue
-
-                # case 2: the statestack has a couple of entries on it, but we're
-                # at the end of the file. nuke the top entry and generate an error token
-
-                # Start nuking entries on the stack
-                if lookahead.type == "$end":
-                    # Whoa. We're really hosed here. Bail out
-                    return
-
-                if lookahead.type != 'error':
-                    sym = symstack[-1]
-                    if sym.type == 'error':
-                        # Hmmm. Error is on top of stack, we'll just nuke input
-                        # symbol and continue
-                        if tracking:
-                            sym.endlineno = getattr(lookahead,"lineno", sym.lineno)
-                            sym.endlexpos = getattr(lookahead,"lexpos", sym.lexpos)
-                        lookahead = None
-                        continue
-                    t = YaccSymbol()
-                    t.type = 'error'
-                    if hasattr(lookahead,"lineno"):
-                        t.lineno = lookahead.lineno
-                    if hasattr(lookahead,"lexpos"):
-                        t.lexpos = lookahead.lexpos
-                    t.value = lookahead
-                    lookaheadstack.append(lookahead)
-                    lookahead = t
-                else:
-                    sym = symstack.pop()
-                    if tracking:
-                        lookahead.lineno = sym.lineno
-                        lookahead.lexpos = sym.lexpos
-                    statestack.pop()
-                    state = statestack[-1]       # Potential bug fix
-
-                continue
-
-            # Call an error function here
-            raise RuntimeError("yacc: internal parser error!!!\n")
-
-    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    # parseopt().
-    #
-    # Optimized version of parse() method.  DO NOT EDIT THIS CODE DIRECTLY.
-    # Edit the debug version above, then copy any modifications to the method
-    # below while removing #--! DEBUG sections.
-    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-
-    def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
-        lookahead = None                 # Current lookahead symbol
-        lookaheadstack = [ ]             # Stack of lookahead symbols
-        actions = self.action            # Local reference to action table (to avoid lookup on self.)
-        goto    = self.goto              # Local reference to goto table (to avoid lookup on self.)
-        prod    = self.productions       # Local reference to production list (to avoid lookup on self.)
-        pslice  = YaccProduction(None)   # Production object passed to grammar rules
-        errorcount = 0                   # Used during error recovery 
-
-        # If no lexer was given, we will try to use the lex module
-        if not lexer:
-            lex = load_ply_lex()
-            lexer = lex.lexer
-        
-        # Set up the lexer and parser objects on pslice
-        pslice.lexer = lexer
-        pslice.parser = self
-
-        # If input was supplied, pass to lexer
-        if input is not None:
-            lexer.input(input)
-
-        if tokenfunc is None:
-           # Tokenize function
-           get_token = lexer.token
-        else:
-           get_token = tokenfunc
-
-        # Set the parser() token method (sometimes used in error recovery)
-        self.token = get_token
-
-        # Set up the state and symbol stacks
-
-        statestack = [ ]                # Stack of parsing states
-        self.statestack = statestack
-        symstack   = [ ]                # Stack of grammar symbols
-        self.symstack = symstack
-
-        pslice.stack = symstack         # Put in the production
-        errtoken   = None               # Err token
-
-        # The start state is assumed to be (0,$end)
-
-        statestack.append(0)
-        sym = YaccSymbol()
-        sym.type = '$end'
-        symstack.append(sym)
-        state = 0
-        while 1:
-            # Get the next symbol on the input.  If a lookahead symbol
-            # is already set, we just use that. Otherwise, we'll pull
-            # the next token off of the lookaheadstack or from the lexer
-
-            if not lookahead:
-                if not lookaheadstack:
-                    lookahead = get_token()     # Get the next token
-                else:
-                    lookahead = lookaheadstack.pop()
-                if not lookahead:
-                    lookahead = YaccSymbol()
-                    lookahead.type = '$end'
-
-            # Check the action table
-            ltype = lookahead.type
-            t = actions[state].get(ltype)
-
-            if t is not None:
-                if t > 0:
-                    # shift a symbol on the stack
-                    statestack.append(t)
-                    state = t
-
-                    symstack.append(lookahead)
-                    lookahead = None
-
-                    # Decrease error count on successful shift
-                    if errorcount: errorcount -=1
-                    continue
-
-                if t < 0:
-                    # reduce a symbol on the stack, emit a production
-                    p = prod[-t]
-                    pname = p.name
-                    plen  = p.len
-
-                    # Get production function
-                    sym = YaccSymbol()
-                    sym.type = pname       # Production name
-                    sym.value = None
-
-                    if plen:
-                        targ = symstack[-plen-1:]
-                        targ[0] = sym
-
-                        # --! TRACKING
-                        if tracking:
-                           t1 = targ[1]
-                           sym.lineno = t1.lineno
-                           sym.lexpos = t1.lexpos
-                           t1 = targ[-1]
-                           sym.endlineno = getattr(t1,"endlineno",t1.lineno)
-                           sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos)
-
-                        # --! TRACKING
-
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
-                        # below as a performance optimization.  Make sure
-                        # changes get made in both locations.
-
-                        pslice.slice = targ
-                        
-                        try:
-                            # Call the grammar rule with our special slice object
-                            del symstack[-plen:]
-                            del statestack[-plen:]
-                            p.callable(pslice)
-                            symstack.append(sym)
-                            state = goto[statestack[-1]][pname]
-                            statestack.append(state)
-                        except SyntaxError:
-                            # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
-                            state = statestack[-1]
-                            sym.type = 'error'
-                            lookahead = sym
-                            errorcount = error_count
-                            self.errorok = 0
-                        continue
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    
-                    else:
-
-                        # --! TRACKING
-                        if tracking:
-                           sym.lineno = lexer.lineno
-                           sym.lexpos = lexer.lexpos
-                        # --! TRACKING
-
-                        targ = [ sym ]
-
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
-                        # above as a performance optimization.  Make sure
-                        # changes get made in both locations.
-
-                        pslice.slice = targ
-
-                        try:
-                            # Call the grammar rule with our special slice object
-                            p.callable(pslice)
-                            symstack.append(sym)
-                            state = goto[statestack[-1]][pname]
-                            statestack.append(state)
-                        except SyntaxError:
-                            # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
-                            state = statestack[-1]
-                            sym.type = 'error'
-                            lookahead = sym
-                            errorcount = error_count
-                            self.errorok = 0
-                        continue
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-                if t == 0:
-                    n = symstack[-1]
-                    return getattr(n,"value",None)
-
-            if t == None:
-
-                # We have some kind of parsing error here.  To handle
-                # this, we are going to push the current token onto
-                # the tokenstack and replace it with an 'error' token.
-                # If there are any synchronization rules, they may
-                # catch it.
-                #
-                # In addition to pushing the error token, we call call
-                # the user defined p_error() function if this is the
-                # first syntax error.  This function is only called if
-                # errorcount == 0.
-                if errorcount == 0 or self.errorok:
-                    errorcount = error_count
-                    self.errorok = 0
-                    errtoken = lookahead
-                    if errtoken.type == '$end':
-                        errtoken = None               # End of file!
-                    if self.errorfunc:
-                        if errtoken and not hasattr(errtoken,'lexer'):
-                            errtoken.lexer = lexer
-                        tok = call_errorfunc(self.errorfunc, errtoken, self)
-
-                        if self.errorok:
-                            # User must have done some kind of panic
-                            # mode recovery on their own.  The
-                            # returned token is the next lookahead
-                            lookahead = tok
-                            errtoken = None
-                            continue
-                    else:
-                        if errtoken:
-                            if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
-                            else: lineno = 0
-                            if lineno:
-                                sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
-                            else:
-                                sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
-                        else:
-                            sys.stderr.write("yacc: Parse error in input. EOF\n")
-                            return
-
-                else:
-                    errorcount = error_count
-
-                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
-                # entire parse has been rolled back and we're completely hosed.   The token is
-                # discarded and we just keep going.
-
-                if len(statestack) <= 1 and lookahead.type != '$end':
-                    lookahead = None
-                    errtoken = None
-                    state = 0
-                    # Nuke the pushback stack
-                    del lookaheadstack[:]
-                    continue
-
-                # case 2: the statestack has a couple of entries on it, but we're
-                # at the end of the file. nuke the top entry and generate an error token
-
-                # Start nuking entries on the stack
-                if lookahead.type == '$end':
-                    # Whoa. We're really hosed here. Bail out
-                    return
-
-                if lookahead.type != 'error':
-                    sym = symstack[-1]
-                    if sym.type == 'error':
-                        # Hmmm. Error is on top of stack, we'll just nuke input
-                        # symbol and continue
-                        if tracking:
-                            sym.endlineno = getattr(lookahead,"lineno", sym.lineno)
-                            sym.endlexpos = getattr(lookahead,"lexpos", sym.lexpos)
-                        lookahead = None
-                        continue
-                    t = YaccSymbol()
-                    t.type = 'error'
-                    if hasattr(lookahead,"lineno"):
-                        t.lineno = lookahead.lineno
-                    if hasattr(lookahead,"lexpos"):
-                        t.lexpos = lookahead.lexpos
-                    t.value = lookahead
-                    lookaheadstack.append(lookahead)
-                    lookahead = t
-                else:
-                    sym = symstack.pop()
-                    if tracking:
-                        lookahead.lineno = sym.lineno
-                        lookahead.lexpos = sym.lexpos
-                    statestack.pop()
-                    state = statestack[-1]       # Potential bug fix
-
-                continue
-
-            # Call an error function here
-            raise RuntimeError("yacc: internal parser error!!!\n")
-
-    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    # parseopt_notrack().
-    #
-    # Optimized version of parseopt() with line number tracking removed. 
-    # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove
-    # code in the #--! TRACKING sections
-    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-    def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None):
-        lookahead = None                 # Current lookahead symbol
-        lookaheadstack = [ ]             # Stack of lookahead symbols
-        actions = self.action            # Local reference to action table (to avoid lookup on self.)
-        goto    = self.goto              # Local reference to goto table (to avoid lookup on self.)
-        prod    = self.productions       # Local reference to production list (to avoid lookup on self.)
-        pslice  = YaccProduction(None)   # Production object passed to grammar rules
-        errorcount = 0                   # Used during error recovery 
-
-        # If no lexer was given, we will try to use the lex module
-        if not lexer:
-            lex = load_ply_lex()
-            lexer = lex.lexer
-        
-        # Set up the lexer and parser objects on pslice
-        pslice.lexer = lexer
-        pslice.parser = self
-
-        # If input was supplied, pass to lexer
-        if input is not None:
-            lexer.input(input)
-
-        if tokenfunc is None:
-           # Tokenize function
-           get_token = lexer.token
-        else:
-           get_token = tokenfunc
-
-        # Set the parser() token method (sometimes used in error recovery)
-        self.token = get_token
-
-        # Set up the state and symbol stacks
-
-        statestack = [ ]                # Stack of parsing states
-        self.statestack = statestack
-        symstack   = [ ]                # Stack of grammar symbols
-        self.symstack = symstack
-
-        pslice.stack = symstack         # Put in the production
-        errtoken   = None               # Err token
-
-        # The start state is assumed to be (0,$end)
-
-        statestack.append(0)
-        sym = YaccSymbol()
-        sym.type = '$end'
-        symstack.append(sym)
-        state = 0
-        while 1:
-            # Get the next symbol on the input.  If a lookahead symbol
-            # is already set, we just use that. Otherwise, we'll pull
-            # the next token off of the lookaheadstack or from the lexer
-
-            if not lookahead:
-                if not lookaheadstack:
-                    lookahead = get_token()     # Get the next token
-                else:
-                    lookahead = lookaheadstack.pop()
-                if not lookahead:
-                    lookahead = YaccSymbol()
-                    lookahead.type = '$end'
-
-            # Check the action table
-            ltype = lookahead.type
-            t = actions[state].get(ltype)
-
-            if t is not None:
-                if t > 0:
-                    # shift a symbol on the stack
-                    statestack.append(t)
-                    state = t
-
-                    symstack.append(lookahead)
-                    lookahead = None
-
-                    # Decrease error count on successful shift
-                    if errorcount: errorcount -=1
-                    continue
-
-                if t < 0:
-                    # reduce a symbol on the stack, emit a production
-                    p = prod[-t]
-                    pname = p.name
-                    plen  = p.len
-
-                    # Get production function
-                    sym = YaccSymbol()
-                    sym.type = pname       # Production name
-                    sym.value = None
-
-                    if plen:
-                        targ = symstack[-plen-1:]
-                        targ[0] = sym
-
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
-                        # below as a performance optimization.  Make sure
-                        # changes get made in both locations.
-
-                        pslice.slice = targ
-                        
-                        try:
-                            # Call the grammar rule with our special slice object
-                            del symstack[-plen:]
-                            del statestack[-plen:]
-                            p.callable(pslice)
-                            symstack.append(sym)
-                            state = goto[statestack[-1]][pname]
-                            statestack.append(state)
-                        except SyntaxError:
-                            # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
-                            state = statestack[-1]
-                            sym.type = 'error'
-                            lookahead = sym
-                            errorcount = error_count
-                            self.errorok = 0
-                        continue
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-    
-                    else:
-
-                        targ = [ sym ]
-
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-                        # The code enclosed in this section is duplicated 
-                        # above as a performance optimization.  Make sure
-                        # changes get made in both locations.
-
-                        pslice.slice = targ
-
-                        try:
-                            # Call the grammar rule with our special slice object
-                            p.callable(pslice)
-                            symstack.append(sym)
-                            state = goto[statestack[-1]][pname]
-                            statestack.append(state)
-                        except SyntaxError:
-                            # If an error was set. Enter error recovery state
-                            lookaheadstack.append(lookahead)
-                            symstack.pop()
-                            statestack.pop()
-                            state = statestack[-1]
-                            sym.type = 'error'
-                            lookahead = sym
-                            errorcount = error_count
-                            self.errorok = 0
-                        continue
-                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-                if t == 0:
-                    n = symstack[-1]
-                    return getattr(n,"value",None)
-
-            if t == None:
-
-                # We have some kind of parsing error here.  To handle
-                # this, we are going to push the current token onto
-                # the tokenstack and replace it with an 'error' token.
-                # If there are any synchronization rules, they may
-                # catch it.
-                #
-                # In addition to pushing the error token, we call call
-                # the user defined p_error() function if this is the
-                # first syntax error.  This function is only called if
-                # errorcount == 0.
-                if errorcount == 0 or self.errorok:
-                    errorcount = error_count
-                    self.errorok = 0
-                    errtoken = lookahead
-                    if errtoken.type == '$end':
-                        errtoken = None               # End of file!
-                    if self.errorfunc:
-                        if errtoken and not hasattr(errtoken,'lexer'):
-                            errtoken.lexer = lexer
-                        tok = call_errorfunc(self.errorfunc, errtoken, self)
-
-                        if self.errorok:
-                            # User must have done some kind of panic
-                            # mode recovery on their own.  The
-                            # returned token is the next lookahead
-                            lookahead = tok
-                            errtoken = None
-                            continue
-                    else:
-                        if errtoken:
-                            if hasattr(errtoken,"lineno"): lineno = lookahead.lineno
-                            else: lineno = 0
-                            if lineno:
-                                sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type))
-                            else:
-                                sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type)
-                        else:
-                            sys.stderr.write("yacc: Parse error in input. EOF\n")
-                            return
-
-                else:
-                    errorcount = error_count
-
-                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
-                # entire parse has been rolled back and we're completely hosed.   The token is
-                # discarded and we just keep going.
-
-                if len(statestack) <= 1 and lookahead.type != '$end':
-                    lookahead = None
-                    errtoken = None
-                    state = 0
-                    # Nuke the pushback stack
-                    del lookaheadstack[:]
-                    continue
-
-                # case 2: the statestack has a couple of entries on it, but we're
-                # at the end of the file. nuke the top entry and generate an error token
-
-                # Start nuking entries on the stack
-                if lookahead.type == '$end':
-                    # Whoa. We're really hosed here. Bail out
-                    return
-
-                if lookahead.type != 'error':
-                    sym = symstack[-1]
-                    if sym.type == 'error':
-                        # Hmmm. Error is on top of stack, we'll just nuke input
-                        # symbol and continue
-                        lookahead = None
-                        continue
-                    t = YaccSymbol()
-                    t.type = 'error'
-                    if hasattr(lookahead,"lineno"):
-                        t.lineno = lookahead.lineno
-                    if hasattr(lookahead,"lexpos"):
-                        t.lexpos = lookahead.lexpos
-                    t.value = lookahead
-                    lookaheadstack.append(lookahead)
-                    lookahead = t
-                else:
-                    symstack.pop()
-                    statestack.pop()
-                    state = statestack[-1]       # Potential bug fix
-
-                continue
-
-            # Call an error function here
-            raise RuntimeError("yacc: internal parser error!!!\n")
-
-# -----------------------------------------------------------------------------
-#                          === Grammar Representation ===
-#
-# The following functions, classes, and variables are used to represent and
-# manipulate the rules that make up a grammar. 
-# -----------------------------------------------------------------------------
-
-import re
-
-# regex matching identifiers
-_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
-
-# -----------------------------------------------------------------------------
-# class Production:
-#
-# This class stores the raw information about a single production or grammar rule.
-# A grammar rule refers to a specification such as this:
-#
-#       expr : expr PLUS term 
-#
-# Here are the basic attributes defined on all productions
-#
-#       name     - Name of the production.  For example 'expr'
-#       prod     - A list of symbols on the right side ['expr','PLUS','term']
-#       prec     - Production precedence level
-#       number   - Production number.
-#       func     - Function that executes on reduce
-#       file     - File where production function is defined
-#       lineno   - Line number where production function is defined
-#
-# The following attributes are defined or optional.
-#
-#       len       - Length of the production (number of symbols on right hand side)
-#       usyms     - Set of unique symbols found in the production
-# -----------------------------------------------------------------------------
-
-class Production(object):
-    reduced = 0
-    def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0):
-        self.name     = name
-        self.prod     = tuple(prod)
-        self.number   = number
-        self.func     = func
-        self.callable = None
-        self.file     = file
-        self.line     = line
-        self.prec     = precedence
-
-        # Internal settings used during table construction
-        
-        self.len  = len(self.prod)   # Length of the production
-
-        # Create a list of unique production symbols used in the production
-        self.usyms = [ ]             
-        for s in self.prod:
-            if s not in self.usyms:
-                self.usyms.append(s)
-
-        # List of all LR items for the production
-        self.lr_items = []
-        self.lr_next = None
-
-        # Create a string representation
-        if self.prod:
-            self.str = "%s -> %s" % (self.name," ".join(self.prod))
-        else:
-            self.str = "%s -> <empty>" % self.name
-
-    def __str__(self):
-        return self.str
-
-    def __repr__(self):
-        return "Production("+str(self)+")"
-
-    def __len__(self):
-        return len(self.prod)
-
-    def __nonzero__(self):
-        return 1
-
-    def __getitem__(self,index):
-        return self.prod[index]
-            
-    # Return the nth lr_item from the production (or None if at the end)
-    def lr_item(self,n):
-        if n > len(self.prod): return None
-        p = LRItem(self,n)
-
-        # Precompute the list of productions immediately following.  Hack. Remove later
-        try:
-            p.lr_after = Prodnames[p.prod[n+1]]
-        except (IndexError,KeyError):
-            p.lr_after = []
-        try:
-            p.lr_before = p.prod[n-1]
-        except IndexError:
-            p.lr_before = None
-
-        return p
-    
-    # Bind the production function name to a callable
-    def bind(self,pdict):
-        if self.func:
-            self.callable = pdict[self.func]
-
-# This class serves as a minimal standin for Production objects when
-# reading table data from files.   It only contains information
-# actually used by the LR parsing engine, plus some additional
-# debugging information.
-class MiniProduction(object):
-    def __init__(self,str,name,len,func,file,line):
-        self.name     = name
-        self.len      = len
-        self.func     = func
-        self.callable = None
-        self.file     = file
-        self.line     = line
-        self.str      = str
-    def __str__(self):
-        return self.str
-    def __repr__(self):
-        return "MiniProduction(%s)" % self.str
-
-    # Bind the production function name to a callable
-    def bind(self,pdict):
-        if self.func:
-            self.callable = pdict[self.func]
-
-
-# -----------------------------------------------------------------------------
-# class LRItem
-#
-# This class represents a specific stage of parsing a production rule.  For
-# example: 
-#
-#       expr : expr . PLUS term 
-#
-# In the above, the "." represents the current location of the parse.  Here
-# basic attributes:
-#
-#       name       - Name of the production.  For example 'expr'
-#       prod       - A list of symbols on the right side ['expr','.', 'PLUS','term']
-#       number     - Production number.
-#
-#       lr_next      Next LR item. Example, if we are ' expr -> expr . PLUS term'
-#                    then lr_next refers to 'expr -> expr PLUS . term'
-#       lr_index   - LR item index (location of the ".") in the prod list.
-#       lookaheads - LALR lookahead symbols for this item
-#       len        - Length of the production (number of symbols on right hand side)
-#       lr_after    - List of all productions that immediately follow
-#       lr_before   - Grammar symbol immediately before
-# -----------------------------------------------------------------------------
-
-class LRItem(object):
-    def __init__(self,p,n):
-        self.name       = p.name
-        self.prod       = list(p.prod)
-        self.number     = p.number
-        self.lr_index   = n
-        self.lookaheads = { }
-        self.prod.insert(n,".")
-        self.prod       = tuple(self.prod)
-        self.len        = len(self.prod)
-        self.usyms      = p.usyms
-
-    def __str__(self):
-        if self.prod:
-            s = "%s -> %s" % (self.name," ".join(self.prod))
-        else:
-            s = "%s -> <empty>" % self.name
-        return s
-
-    def __repr__(self):
-        return "LRItem("+str(self)+")"
-
-# -----------------------------------------------------------------------------
-# rightmost_terminal()
-#
-# Return the rightmost terminal from a list of symbols.  Used in add_production()
-# -----------------------------------------------------------------------------
-def rightmost_terminal(symbols, terminals):
-    i = len(symbols) - 1
-    while i >= 0:
-        if symbols[i] in terminals:
-            return symbols[i]
-        i -= 1
-    return None
-
-# -----------------------------------------------------------------------------
-#                           === GRAMMAR CLASS ===
-#
-# The following class represents the contents of the specified grammar along
-# with various computed properties such as first sets, follow sets, LR items, etc.
-# This data is used for critical parts of the table generation process later.
-# -----------------------------------------------------------------------------
-
-class GrammarError(YaccError): pass
-
-class Grammar(object):
-    def __init__(self,terminals):
-        self.Productions  = [None]  # A list of all of the productions.  The first
-                                    # entry is always reserved for the purpose of
-                                    # building an augmented grammar
-
-        self.Prodnames    = { }     # A dictionary mapping the names of nonterminals to a list of all
-                                    # productions of that nonterminal.
-
-        self.Prodmap      = { }     # A dictionary that is only used to detect duplicate
-                                    # productions.
-
-        self.Terminals    = { }     # A dictionary mapping the names of terminal symbols to a
-                                    # list of the rules where they are used.
-
-        for term in terminals:
-            self.Terminals[term] = []
-
-        self.Terminals['error'] = []
-
-        self.Nonterminals = { }     # A dictionary mapping names of nonterminals to a list
-                                    # of rule numbers where they are used.
-
-        self.First        = { }     # A dictionary of precomputed FIRST(x) symbols
-
-        self.Follow       = { }     # A dictionary of precomputed FOLLOW(x) symbols
-
-        self.Precedence   = { }     # Precedence rules for each terminal. Contains tuples of the
-                                    # form ('right',level) or ('nonassoc', level) or ('left',level)
-
-        self.UsedPrecedence = { }   # Precedence rules that were actually used by the grammer.
-                                    # This is only used to provide error checking and to generate
-                                    # a warning about unused precedence rules.
-
-        self.Start = None           # Starting symbol for the grammar
-
-
-    def __len__(self):
-        return len(self.Productions)
-
-    def __getitem__(self,index):
-        return self.Productions[index]
-
-    # -----------------------------------------------------------------------------
-    # set_precedence()
-    #
-    # Sets the precedence for a given terminal. assoc is the associativity such as
-    # 'left','right', or 'nonassoc'.  level is a numeric level.
-    #
-    # -----------------------------------------------------------------------------
-
-    def set_precedence(self,term,assoc,level):
-        assert self.Productions == [None],"Must call set_precedence() before add_production()"
-        if term in self.Precedence:
-            raise GrammarError("Precedence already specified for terminal %r" % term)
-        if assoc not in ['left','right','nonassoc']:
-            raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
-        self.Precedence[term] = (assoc,level)
- 
-    # -----------------------------------------------------------------------------
-    # add_production()
-    #
-    # Given an action function, this function assembles a production rule and
-    # computes its precedence level.
-    #
-    # The production rule is supplied as a list of symbols.   For example,
-    # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and
-    # symbols ['expr','PLUS','term'].
-    #
-    # Precedence is determined by the precedence of the right-most non-terminal
-    # or the precedence of a terminal specified by %prec.
-    #
-    # A variety of error checks are performed to make sure production symbols
-    # are valid and that %prec is used correctly.
-    # -----------------------------------------------------------------------------
-
-    def add_production(self,prodname,syms,func=None,file='',line=0):
-
-        if prodname in self.Terminals:
-            raise GrammarError("%s:%d: Illegal rule name %r. Already defined as a token" % (file,line,prodname))
-        if prodname == 'error':
-            raise GrammarError("%s:%d: Illegal rule name %r. error is a reserved word" % (file,line,prodname))
-        if not _is_identifier.match(prodname):
-            raise GrammarError("%s:%d: Illegal rule name %r" % (file,line,prodname))
-
-        # Look for literal tokens 
-        for n,s in enumerate(syms):
-            if s[0] in "'\"":
-                 try:
-                     c = eval(s)
-                     if (len(c) > 1):
-                          raise GrammarError("%s:%d: Literal token %s in rule %r may only be a single character" % (file,line,s, prodname))
-                     if not c in self.Terminals:
-                          self.Terminals[c] = []
-                     syms[n] = c
-                     continue
-                 except SyntaxError:
-                     pass
-            if not _is_identifier.match(s) and s != '%prec':
-                raise GrammarError("%s:%d: Illegal name %r in rule %r" % (file,line,s, prodname))
-        
-        # Determine the precedence level
-        if '%prec' in syms:
-            if syms[-1] == '%prec':
-                raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line))
-            if syms[-2] != '%prec':
-                raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line))
-            precname = syms[-1]
-            prodprec = self.Precedence.get(precname)
-            if not prodprec:
-                raise GrammarError("%s:%d: Nothing known about the precedence of %r" % (file,line,precname))
-            else:
-                self.UsedPrecedence[precname] = 1
-            del syms[-2:]     # Drop %prec from the rule
-        else:
-            # If no %prec, precedence is determined by the rightmost terminal symbol
-            precname = rightmost_terminal(syms,self.Terminals)
-            prodprec = self.Precedence.get(precname,('right',0)) 
-            
-        # See if the rule is already in the rulemap
-        map = "%s -> %s" % (prodname,syms)
-        if map in self.Prodmap:
-            m = self.Prodmap[map]
-            raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) +
-                               "Previous definition at %s:%d" % (m.file, m.line))
-
-        # From this point on, everything is valid.  Create a new Production instance
-        pnumber  = len(self.Productions)
-        if not prodname in self.Nonterminals:
-            self.Nonterminals[prodname] = [ ]
-
-        # Add the production number to Terminals and Nonterminals
-        for t in syms:
-            if t in self.Terminals:
-                self.Terminals[t].append(pnumber)
-            else:
-                if not t in self.Nonterminals:
-                    self.Nonterminals[t] = [ ]
-                self.Nonterminals[t].append(pnumber)
-
-        # Create a production and add it to the list of productions
-        p = Production(pnumber,prodname,syms,prodprec,func,file,line)
-        self.Productions.append(p)
-        self.Prodmap[map] = p
-
-        # Add to the global productions list
-        try:
-            self.Prodnames[prodname].append(p)
-        except KeyError:
-            self.Prodnames[prodname] = [ p ]
-        return 0
-
-    # -----------------------------------------------------------------------------
-    # set_start()
-    #
-    # Sets the starting symbol and creates the augmented grammar.  Production 
-    # rule 0 is S' -> start where start is the start symbol.
-    # -----------------------------------------------------------------------------
-
-    def set_start(self,start=None):
-        if not start:
-            start = self.Productions[1].name
-        if start not in self.Nonterminals:
-            raise GrammarError("start symbol %s undefined" % start)
-        self.Productions[0] = Production(0,"S'",[start])
-        self.Nonterminals[start].append(0)
-        self.Start = start
-
-    # -----------------------------------------------------------------------------
-    # find_unreachable()
-    #
-    # Find all of the nonterminal symbols that can't be reached from the starting
-    # symbol.  Returns a list of nonterminals that can't be reached.
-    # -----------------------------------------------------------------------------
-
-    def find_unreachable(self):
-        
-        # Mark all symbols that are reachable from a symbol s
-        def mark_reachable_from(s):
-            if reachable[s]:
-                # We've already reached symbol s.
-                return
-            reachable[s] = 1
-            for p in self.Prodnames.get(s,[]):
-                for r in p.prod:
-                    mark_reachable_from(r)
-
-        reachable   = { }
-        for s in list(self.Terminals) + list(self.Nonterminals):
-            reachable[s] = 0
-
-        mark_reachable_from( self.Productions[0].prod[0] )
-
-        return [s for s in list(self.Nonterminals)
-                        if not reachable[s]]
-    
-    # -----------------------------------------------------------------------------
-    # infinite_cycles()
-    #
-    # This function looks at the various parsing rules and tries to detect
-    # infinite recursion cycles (grammar rules where there is no possible way
-    # to derive a string of only terminals).
-    # -----------------------------------------------------------------------------
-
-    def infinite_cycles(self):
-        terminates = {}
-
-        # Terminals:
-        for t in self.Terminals:
-            terminates[t] = 1
-
-        terminates['$end'] = 1
-
-        # Nonterminals:
-
-        # Initialize to false:
-        for n in self.Nonterminals:
-            terminates[n] = 0
-
-        # Then propagate termination until no change:
-        while 1:
-            some_change = 0
-            for (n,pl) in self.Prodnames.items():
-                # Nonterminal n terminates iff any of its productions terminates.
-                for p in pl:
-                    # Production p terminates iff all of its rhs symbols terminate.
-                    for s in p.prod:
-                        if not terminates[s]:
-                            # The symbol s does not terminate,
-                            # so production p does not terminate.
-                            p_terminates = 0
-                            break
-                    else:
-                        # didn't break from the loop,
-                        # so every symbol s terminates
-                        # so production p terminates.
-                        p_terminates = 1
-
-                    if p_terminates:
-                        # symbol n terminates!
-                        if not terminates[n]:
-                            terminates[n] = 1
-                            some_change = 1
-                        # Don't need to consider any more productions for this n.
-                        break
-
-            if not some_change:
-                break
-
-        infinite = []
-        for (s,term) in terminates.items():
-            if not term:
-                if not s in self.Prodnames and not s in self.Terminals and s != 'error':
-                    # s is used-but-not-defined, and we've already warned of that,
-                    # so it would be overkill to say that it's also non-terminating.
-                    pass
-                else:
-                    infinite.append(s)
-
-        return infinite
-
-
-    # -----------------------------------------------------------------------------
-    # undefined_symbols()
-    #
-    # Find all symbols that were used the grammar, but not defined as tokens or
-    # grammar rules.  Returns a list of tuples (sym, prod) where sym in the symbol
-    # and prod is the production where the symbol was used. 
-    # -----------------------------------------------------------------------------
-    def undefined_symbols(self):
-        result = []
-        for p in self.Productions:
-            if not p: continue
-
-            for s in p.prod:
-                if not s in self.Prodnames and not s in self.Terminals and s != 'error':
-                    result.append((s,p))
-        return result
-
-    # -----------------------------------------------------------------------------
-    # unused_terminals()
-    #
-    # Find all terminals that were defined, but not used by the grammar.  Returns
-    # a list of all symbols.
-    # -----------------------------------------------------------------------------
-    def unused_terminals(self):
-        unused_tok = []
-        for s,v in self.Terminals.items():
-            if s != 'error' and not v:
-                unused_tok.append(s)
-
-        return unused_tok
-
-    # ------------------------------------------------------------------------------
-    # unused_rules()
-    #
-    # Find all grammar rules that were defined,  but not used (maybe not reachable)
-    # Returns a list of productions.
-    # ------------------------------------------------------------------------------
-
-    def unused_rules(self):
-        unused_prod = []
-        for s,v in self.Nonterminals.items():
-            if not v:
-                p = self.Prodnames[s][0]
-                unused_prod.append(p)
-        return unused_prod
-
-    # -----------------------------------------------------------------------------
-    # unused_precedence()
-    #
-    # Returns a list of tuples (term,precedence) corresponding to precedence
-    # rules that were never used by the grammar.  term is the name of the terminal
-    # on which precedence was applied and precedence is a string such as 'left' or
-    # 'right' corresponding to the type of precedence. 
-    # -----------------------------------------------------------------------------
-
-    def unused_precedence(self):
-        unused = []
-        for termname in self.Precedence:
-            if not (termname in self.Terminals or termname in self.UsedPrecedence):
-                unused.append((termname,self.Precedence[termname][0]))
-                
-        return unused
-
-    # -------------------------------------------------------------------------
-    # _first()
-    #
-    # Compute the value of FIRST1(beta) where beta is a tuple of symbols.
-    #
-    # During execution of compute_first1, the result may be incomplete.
-    # Afterward (e.g., when called from compute_follow()), it will be complete.
-    # -------------------------------------------------------------------------
-    def _first(self,beta):
-
-        # We are computing First(x1,x2,x3,...,xn)
-        result = [ ]
-        for x in beta:
-            x_produces_empty = 0
-
-            # Add all the non-<empty> symbols of First[x] to the result.
-            for f in self.First[x]:
-                if f == '<empty>':
-                    x_produces_empty = 1
-                else:
-                    if f not in result: result.append(f)
-
-            if x_produces_empty:
-                # We have to consider the next x in beta,
-                # i.e. stay in the loop.
-                pass
-            else:
-                # We don't have to consider any further symbols in beta.
-                break
-        else:
-            # There was no 'break' from the loop,
-            # so x_produces_empty was true for all x in beta,
-            # so beta produces empty as well.
-            result.append('<empty>')
-
-        return result
-
-    # -------------------------------------------------------------------------
-    # compute_first()
-    #
-    # Compute the value of FIRST1(X) for all symbols
-    # -------------------------------------------------------------------------
-    def compute_first(self):
-        if self.First:
-            return self.First
-
-        # Terminals:
-        for t in self.Terminals:
-            self.First[t] = [t]
-
-        self.First['$end'] = ['$end']
-
-        # Nonterminals:
-
-        # Initialize to the empty set:
-        for n in self.Nonterminals:
-            self.First[n] = []
-
-        # Then propagate symbols until no change:
-        while 1:
-            some_change = 0
-            for n in self.Nonterminals:
-                for p in self.Prodnames[n]:
-                    for f in self._first(p.prod):
-                        if f not in self.First[n]:
-                            self.First[n].append( f )
-                            some_change = 1
-            if not some_change:
-                break
-        
-        return self.First
-
-    # ---------------------------------------------------------------------
-    # compute_follow()
-    #
-    # Computes all of the follow sets for every non-terminal symbol.  The
-    # follow set is the set of all symbols that might follow a given
-    # non-terminal.  See the Dragon book, 2nd Ed. p. 189.
-    # ---------------------------------------------------------------------
-    def compute_follow(self,start=None):
-        # If already computed, return the result
-        if self.Follow:
-            return self.Follow
-
-        # If first sets not computed yet, do that first.
-        if not self.First:
-            self.compute_first()
-
-        # Add '$end' to the follow list of the start symbol
-        for k in self.Nonterminals:
-            self.Follow[k] = [ ]
-
-        if not start:
-            start = self.Productions[1].name
-
-        self.Follow[start] = [ '$end' ]
-
-        while 1:
-            didadd = 0
-            for p in self.Productions[1:]:
-                # Here is the production set
-                for i in range(len(p.prod)):
-                    B = p.prod[i]
-                    if B in self.Nonterminals:
-                        # Okay. We got a non-terminal in a production
-                        fst = self._first(p.prod[i+1:])
-                        hasempty = 0
-                        for f in fst:
-                            if f != '<empty>' and f not in self.Follow[B]:
-                                self.Follow[B].append(f)
-                                didadd = 1
-                            if f == '<empty>':
-                                hasempty = 1
-                        if hasempty or i == (len(p.prod)-1):
-                            # Add elements of follow(a) to follow(b)
-                            for f in self.Follow[p.name]:
-                                if f not in self.Follow[B]:
-                                    self.Follow[B].append(f)
-                                    didadd = 1
-            if not didadd: break
-        return self.Follow
-
-
-    # -----------------------------------------------------------------------------
-    # build_lritems()
-    #
-    # This function walks the list of productions and builds a complete set of the
-    # LR items.  The LR items are stored in two ways:  First, they are uniquely
-    # numbered and placed in the list _lritems.  Second, a linked list of LR items
-    # is built for each production.  For example:
-    #
-    #   E -> E PLUS E
-    #
-    # Creates the list
-    #
-    #  [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ]
-    # -----------------------------------------------------------------------------
-
-    def build_lritems(self):
-        for p in self.Productions:
-            lastlri = p
-            i = 0
-            lr_items = []
-            while 1:
-                if i > len(p):
-                    lri = None
-                else:
-                    lri = LRItem(p,i)
-                    # Precompute the list of productions immediately following
-                    try:
-                        lri.lr_after = self.Prodnames[lri.prod[i+1]]
-                    except (IndexError,KeyError):
-                        lri.lr_after = []
-                    try:
-                        lri.lr_before = lri.prod[i-1]
-                    except IndexError:
-                        lri.lr_before = None
-
-                lastlri.lr_next = lri
-                if not lri: break
-                lr_items.append(lri)
-                lastlri = lri
-                i += 1
-            p.lr_items = lr_items
-
-# -----------------------------------------------------------------------------
-#                            == Class LRTable ==
-#
-# This basic class represents a basic table of LR parsing information.  
-# Methods for generating the tables are not defined here.  They are defined
-# in the derived class LRGeneratedTable.
-# -----------------------------------------------------------------------------
-
-class VersionError(YaccError): pass
-
-class LRTable(object):
-    def __init__(self):
-        self.lr_action = None
-        self.lr_goto = None
-        self.lr_productions = None
-        self.lr_method = None
-
-    def read_table(self,module):
-        if isinstance(module,types.ModuleType):
-            parsetab = module
-        else:
-            if sys.version_info[0] < 3:
-                exec("import %s as parsetab" % module)
-            else:
-                env = { }
-                exec("import %s as parsetab" % module, env, env)
-                parsetab = env['parsetab']
-
-        if parsetab._tabversion != __tabversion__:
-            raise VersionError("yacc table file version is out of date")
-
-        self.lr_action = parsetab._lr_action
-        self.lr_goto = parsetab._lr_goto
-
-        self.lr_productions = []
-        for p in parsetab._lr_productions:
-            self.lr_productions.append(MiniProduction(*p))
-
-        self.lr_method = parsetab._lr_method
-        return parsetab._lr_signature
-
-    def read_pickle(self,filename):
-        try:
-            import cPickle as pickle
-        except ImportError:
-            import pickle
-
-        in_f = open(filename,"rb")
-
-        tabversion = pickle.load(in_f)
-        if tabversion != __tabversion__:
-            raise VersionError("yacc table file version is out of date")
-        self.lr_method = pickle.load(in_f)
-        signature      = pickle.load(in_f)
-        self.lr_action = pickle.load(in_f)
-        self.lr_goto   = pickle.load(in_f)
-        productions    = pickle.load(in_f)
-
-        self.lr_productions = []
-        for p in productions:
-            self.lr_productions.append(MiniProduction(*p))
-
-        in_f.close()
-        return signature
-
-    # Bind all production function names to callable objects in pdict
-    def bind_callables(self,pdict):
-        for p in self.lr_productions:
-            p.bind(pdict)
-    
-# -----------------------------------------------------------------------------
-#                           === LR Generator ===
-#
-# The following classes and functions are used to generate LR parsing tables on 
-# a grammar.
-# -----------------------------------------------------------------------------
-
-# -----------------------------------------------------------------------------
-# digraph()
-# traverse()
-#
-# The following two functions are used to compute set valued functions
-# of the form:
-#
-#     F(x) = F'(x) U U{F(y) | x R y}
-#
-# This is used to compute the values of Read() sets as well as FOLLOW sets
-# in LALR(1) generation.
-#
-# Inputs:  X    - An input set
-#          R    - A relation
-#          FP   - Set-valued function
-# ------------------------------------------------------------------------------
-
-def digraph(X,R,FP):
-    N = { }
-    for x in X:
-       N[x] = 0
-    stack = []
-    F = { }
-    for x in X:
-        if N[x] == 0: traverse(x,N,stack,F,X,R,FP)
-    return F
-
-def traverse(x,N,stack,F,X,R,FP):
-    stack.append(x)
-    d = len(stack)
-    N[x] = d
-    F[x] = FP(x)             # F(X) <- F'(x)
-
-    rel = R(x)               # Get y's related to x
-    for y in rel:
-        if N[y] == 0:
-             traverse(y,N,stack,F,X,R,FP)
-        N[x] = min(N[x],N[y])
-        for a in F.get(y,[]):
-            if a not in F[x]: F[x].append(a)
-    if N[x] == d:
-       N[stack[-1]] = MAXINT
-       F[stack[-1]] = F[x]
-       element = stack.pop()
-       while element != x:
-           N[stack[-1]] = MAXINT
-           F[stack[-1]] = F[x]
-           element = stack.pop()
-
-class LALRError(YaccError): pass
-
-# -----------------------------------------------------------------------------
-#                             == LRGeneratedTable ==
-#
-# This class implements the LR table generation algorithm.  There are no
-# public methods except for write()
-# -----------------------------------------------------------------------------
-
-class LRGeneratedTable(LRTable):
-    def __init__(self,grammar,method='LALR',log=None):
-        if method not in ['SLR','LALR']:
-            raise LALRError("Unsupported method %s" % method)
-
-        self.grammar = grammar
-        self.lr_method = method
-
-        # Set up the logger
-        if not log:
-            log = NullLogger()
-        self.log = log
-
-        # Internal attributes
-        self.lr_action     = {}        # Action table
-        self.lr_goto       = {}        # Goto table
-        self.lr_productions  = grammar.Productions    # Copy of grammar Production array
-        self.lr_goto_cache = {}        # Cache of computed gotos
-        self.lr0_cidhash   = {}        # Cache of closures
-
-        self._add_count    = 0         # Internal counter used to detect cycles
-
-        # Diagonistic information filled in by the table generator
-        self.sr_conflict   = 0
-        self.rr_conflict   = 0
-        self.conflicts     = []        # List of conflicts
-
-        self.sr_conflicts  = []
-        self.rr_conflicts  = []
-
-        # Build the tables
-        self.grammar.build_lritems()
-        self.grammar.compute_first()
-        self.grammar.compute_follow()
-        self.lr_parse_table()
-
-    # Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
-
-    def lr0_closure(self,I):
-        self._add_count += 1
-
-        # Add everything in I to J
-        J = I[:]
-        didadd = 1
-        while didadd:
-            didadd = 0
-            for j in J:
-                for x in j.lr_after:
-                    if getattr(x,"lr0_added",0) == self._add_count: continue
-                    # Add B --> .G to J
-                    J.append(x.lr_next)
-                    x.lr0_added = self._add_count
-                    didadd = 1
-
-        return J
-
-    # Compute the LR(0) goto function goto(I,X) where I is a set
-    # of LR(0) items and X is a grammar symbol.   This function is written
-    # in a way that guarantees uniqueness of the generated goto sets
-    # (i.e. the same goto set will never be returned as two different Python
-    # objects).  With uniqueness, we can later do fast set comparisons using
-    # id(obj) instead of element-wise comparison.
-
-    def lr0_goto(self,I,x):
-        # First we look for a previously cached entry
-        g = self.lr_goto_cache.get((id(I),x))
-        if g: return g
-
-        # Now we generate the goto set in a way that guarantees uniqueness
-        # of the result
-
-        s = self.lr_goto_cache.get(x)
-        if not s:
-            s = { }
-            self.lr_goto_cache[x] = s
-
-        gs = [ ]
-        for p in I:
-            n = p.lr_next
-            if n and n.lr_before == x:
-                s1 = s.get(id(n))
-                if not s1:
-                    s1 = { }
-                    s[id(n)] = s1
-                gs.append(n)
-                s = s1
-        g = s.get('$end')
-        if not g:
-            if gs:
-                g = self.lr0_closure(gs)
-                s['$end'] = g
-            else:
-                s['$end'] = gs
-        self.lr_goto_cache[(id(I),x)] = g
-        return g
-
-    # Compute the LR(0) sets of item function
-    def lr0_items(self):
-
-        C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ]
-        i = 0
-        for I in C:
-            self.lr0_cidhash[id(I)] = i
-            i += 1
-
-        # Loop over the items in C and each grammar symbols
-        i = 0
-        while i < len(C):
-            I = C[i]
-            i += 1
-
-            # Collect all of the symbols that could possibly be in the goto(I,X) sets
-            asyms = { }
-            for ii in I:
-                for s in ii.usyms:
-                    asyms[s] = None
-
-            for x in asyms:
-                g = self.lr0_goto(I,x)
-                if not g:  continue
-                if id(g) in self.lr0_cidhash: continue
-                self.lr0_cidhash[id(g)] = len(C)
-                C.append(g)
-
-        return C
-
-    # -----------------------------------------------------------------------------
-    #                       ==== LALR(1) Parsing ====
-    #
-    # LALR(1) parsing is almost exactly the same as SLR except that instead of
-    # relying upon Follow() sets when performing reductions, a more selective
-    # lookahead set that incorporates the state of the LR(0) machine is utilized.
-    # Thus, we mainly just have to focus on calculating the lookahead sets.
-    #
-    # The method used here is due to DeRemer and Pennelo (1982).
-    #
-    # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1)
-    #     Lookahead Sets", ACM Transactions on Programming Languages and Systems,
-    #     Vol. 4, No. 4, Oct. 1982, pp. 615-649
-    #
-    # Further details can also be found in:
-    #
-    #  J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing",
-    #      McGraw-Hill Book Company, (1985).
-    #
-    # -----------------------------------------------------------------------------
-
-    # -----------------------------------------------------------------------------
-    # compute_nullable_nonterminals()
-    #
-    # Creates a dictionary containing all of the non-terminals that might produce
-    # an empty production.
-    # -----------------------------------------------------------------------------
-
-    def compute_nullable_nonterminals(self):
-        nullable = {}
-        num_nullable = 0
-        while 1:
-           for p in self.grammar.Productions[1:]:
-               if p.len == 0:
-                    nullable[p.name] = 1
-                    continue
-               for t in p.prod:
-                    if not t in nullable: break
-               else:
-                    nullable[p.name] = 1
-           if len(nullable) == num_nullable: break
-           num_nullable = len(nullable)
-        return nullable
-
-    # -----------------------------------------------------------------------------
-    # find_nonterminal_trans(C)
-    #
-    # Given a set of LR(0) items, this functions finds all of the non-terminal
-    # transitions.    These are transitions in which a dot appears immediately before
-    # a non-terminal.   Returns a list of tuples of the form (state,N) where state
-    # is the state number and N is the nonterminal symbol.
-    #
-    # The input C is the set of LR(0) items.
-    # -----------------------------------------------------------------------------
-
-    def find_nonterminal_transitions(self,C):
-         trans = []
-         for state in range(len(C)):
-             for p in C[state]:
-                 if p.lr_index < p.len - 1:
-                      t = (state,p.prod[p.lr_index+1])
-                      if t[1] in self.grammar.Nonterminals:
-                            if t not in trans: trans.append(t)
-             state = state + 1
-         return trans
-
-    # -----------------------------------------------------------------------------
-    # dr_relation()
-    #
-    # Computes the DR(p,A) relationships for non-terminal transitions.  The input
-    # is a tuple (state,N) where state is a number and N is a nonterminal symbol.
-    #
-    # Returns a list of terminals.
-    # -----------------------------------------------------------------------------
-
-    def dr_relation(self,C,trans,nullable):
-        dr_set = { }
-        state,N = trans
-        terms = []
-
-        g = self.lr0_goto(C[state],N)
-        for p in g:
-           if p.lr_index < p.len - 1:
-               a = p.prod[p.lr_index+1]
-               if a in self.grammar.Terminals:
-                   if a not in terms: terms.append(a)
-
-        # This extra bit is to handle the start state
-        if state == 0 and N == self.grammar.Productions[0].prod[0]:
-           terms.append('$end')
-
-        return terms
-
-    # -----------------------------------------------------------------------------
-    # reads_relation()
-    #
-    # Computes the READS() relation (p,A) READS (t,C).
-    # -----------------------------------------------------------------------------
-
-    def reads_relation(self,C, trans, empty):
-        # Look for empty transitions
-        rel = []
-        state, N = trans
-
-        g = self.lr0_goto(C[state],N)
-        j = self.lr0_cidhash.get(id(g),-1)
-        for p in g:
-            if p.lr_index < p.len - 1:
-                 a = p.prod[p.lr_index + 1]
-                 if a in empty:
-                      rel.append((j,a))
-
-        return rel
-
-    # -----------------------------------------------------------------------------
-    # compute_lookback_includes()
-    #
-    # Determines the lookback and includes relations
-    #
-    # LOOKBACK:
-    #
-    # This relation is determined by running the LR(0) state machine forward.
-    # For example, starting with a production "N : . A B C", we run it forward
-    # to obtain "N : A B C ."   We then build a relationship between this final
-    # state and the starting state.   These relationships are stored in a dictionary
-    # lookdict.
-    #
-    # INCLUDES:
-    #
-    # Computes the INCLUDE() relation (p,A) INCLUDES (p',B).
-    #
-    # This relation is used to determine non-terminal transitions that occur
-    # inside of other non-terminal transition states.   (p,A) INCLUDES (p', B)
-    # if the following holds:
-    #
-    #       B -> LAT, where T -> epsilon and p' -L-> p
-    #
-    # L is essentially a prefix (which may be empty), T is a suffix that must be
-    # able to derive an empty string.  State p' must lead to state p with the string L.
-    #
-    # -----------------------------------------------------------------------------
-
-    def compute_lookback_includes(self,C,trans,nullable):
-
-        lookdict = {}          # Dictionary of lookback relations
-        includedict = {}       # Dictionary of include relations
-
-        # Make a dictionary of non-terminal transitions
-        dtrans = {}
-        for t in trans:
-            dtrans[t] = 1
-
-        # Loop over all transitions and compute lookbacks and includes
-        for state,N in trans:
-            lookb = []
-            includes = []
-            for p in C[state]:
-                if p.name != N: continue
-
-                # Okay, we have a name match.  We now follow the production all the way
-                # through the state machine until we get the . on the right hand side
-
-                lr_index = p.lr_index
-                j = state
-                while lr_index < p.len - 1:
-                     lr_index = lr_index + 1
-                     t = p.prod[lr_index]
-
-                     # Check to see if this symbol and state are a non-terminal transition
-                     if (j,t) in dtrans:
-                           # Yes.  Okay, there is some chance that this is an includes relation
-                           # the only way to know for certain is whether the rest of the
-                           # production derives empty
-
-                           li = lr_index + 1
-                           while li < p.len:
-                                if p.prod[li] in self.grammar.Terminals: break      # No forget it
-                                if not p.prod[li] in nullable: break
-                                li = li + 1
-                           else:
-                                # Appears to be a relation between (j,t) and (state,N)
-                                includes.append((j,t))
-
-                     g = self.lr0_goto(C[j],t)               # Go to next set
-                     j = self.lr0_cidhash.get(id(g),-1)     # Go to next state
-
-                # When we get here, j is the final state, now we have to locate the production
-                for r in C[j]:
-                     if r.name != p.name: continue
-                     if r.len != p.len:   continue
-                     i = 0
-                     # This look is comparing a production ". A B C" with "A B C ."
-                     while i < r.lr_index:
-                          if r.prod[i] != p.prod[i+1]: break
-                          i = i + 1
-                     else:
-                          lookb.append((j,r))
-            for i in includes:
-                 if not i in includedict: includedict[i] = []
-                 includedict[i].append((state,N))
-            lookdict[(state,N)] = lookb
-
-        return lookdict,includedict
-
-    # -----------------------------------------------------------------------------
-    # compute_read_sets()
-    #
-    # Given a set of LR(0) items, this function computes the read sets.
-    #
-    # Inputs:  C        =  Set of LR(0) items
-    #          ntrans   = Set of nonterminal transitions
-    #          nullable = Set of empty transitions
-    #
-    # Returns a set containing the read sets
-    # -----------------------------------------------------------------------------
-
-    def compute_read_sets(self,C, ntrans, nullable):
-        FP = lambda x: self.dr_relation(C,x,nullable)
-        R =  lambda x: self.reads_relation(C,x,nullable)
-        F = digraph(ntrans,R,FP)
-        return F
-
-    # -----------------------------------------------------------------------------
-    # compute_follow_sets()
-    #
-    # Given a set of LR(0) items, a set of non-terminal transitions, a readset,
-    # and an include set, this function computes the follow sets
-    #
-    # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)}
-    #
-    # Inputs:
-    #            ntrans     = Set of nonterminal transitions
-    #            readsets   = Readset (previously computed)
-    #            inclsets   = Include sets (previously computed)
-    #
-    # Returns a set containing the follow sets
-    # -----------------------------------------------------------------------------
-
-    def compute_follow_sets(self,ntrans,readsets,inclsets):
-         FP = lambda x: readsets[x]
-         R  = lambda x: inclsets.get(x,[])
-         F = digraph(ntrans,R,FP)
-         return F
-
-    # -----------------------------------------------------------------------------
-    # add_lookaheads()
-    #
-    # Attaches the lookahead symbols to grammar rules.
-    #
-    # Inputs:    lookbacks         -  Set of lookback relations
-    #            followset         -  Computed follow set
-    #
-    # This function directly attaches the lookaheads to productions contained
-    # in the lookbacks set
-    # -----------------------------------------------------------------------------
-
-    def add_lookaheads(self,lookbacks,followset):
-        for trans,lb in lookbacks.items():
-            # Loop over productions in lookback
-            for state,p in lb:
-                 if not state in p.lookaheads:
-                      p.lookaheads[state] = []
-                 f = followset.get(trans,[])
-                 for a in f:
-                      if a not in p.lookaheads[state]: p.lookaheads[state].append(a)
-
-    # -----------------------------------------------------------------------------
-    # add_lalr_lookaheads()
-    #
-    # This function does all of the work of adding lookahead information for use
-    # with LALR parsing
-    # -----------------------------------------------------------------------------
-
-    def add_lalr_lookaheads(self,C):
-        # Determine all of the nullable nonterminals
-        nullable = self.compute_nullable_nonterminals()
-
-        # Find all non-terminal transitions
-        trans = self.find_nonterminal_transitions(C)
-
-        # Compute read sets
-        readsets = self.compute_read_sets(C,trans,nullable)
-
-        # Compute lookback/includes relations
-        lookd, included = self.compute_lookback_includes(C,trans,nullable)
-
-        # Compute LALR FOLLOW sets
-        followsets = self.compute_follow_sets(trans,readsets,included)
-
-        # Add all of the lookaheads
-        self.add_lookaheads(lookd,followsets)
-
-    # -----------------------------------------------------------------------------
-    # lr_parse_table()
-    #
-    # This function constructs the parse tables for SLR or LALR
-    # -----------------------------------------------------------------------------
-    def lr_parse_table(self):
-        Productions = self.grammar.Productions
-        Precedence  = self.grammar.Precedence
-        goto   = self.lr_goto         # Goto array
-        action = self.lr_action       # Action array
-        log    = self.log             # Logger for output
-
-        actionp = { }                 # Action production array (temporary)
-        
-        log.info("Parsing method: %s", self.lr_method)
-
-        # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items
-        # This determines the number of states
-
-        C = self.lr0_items()
-
-        if self.lr_method == 'LALR':
-            self.add_lalr_lookaheads(C)
-
-        # Build the parser table, state by state
-        st = 0
-        for I in C:
-            # Loop over each production in I
-            actlist = [ ]              # List of actions
-            st_action  = { }
-            st_actionp = { }
-            st_goto    = { }
-            log.info("")
-            log.info("state %d", st)
-            log.info("")
-            for p in I:
-                log.info("    (%d) %s", p.number, str(p))
-            log.info("")
-
-            for p in I:
-                    if p.len == p.lr_index + 1:
-                        if p.name == "S'":
-                            # Start symbol. Accept!
-                            st_action["$end"] = 0
-                            st_actionp["$end"] = p
-                        else:
-                            # We are at the end of a production.  Reduce!
-                            if self.lr_method == 'LALR':
-                                laheads = p.lookaheads[st]
-                            else:
-                                laheads = self.grammar.Follow[p.name]
-                            for a in laheads:
-                                actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p)))
-                                r = st_action.get(a)
-                                if r is not None:
-                                    # Whoa. Have a shift/reduce or reduce/reduce conflict
-                                    if r > 0:
-                                        # Need to decide on shift or reduce here
-                                        # By default we favor shifting. Need to add
-                                        # some precedence rules here.
-                                        sprec,slevel = Productions[st_actionp[a].number].prec
-                                        rprec,rlevel = Precedence.get(a,('right',0))
-                                        if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
-                                            # We really need to reduce here.
-                                            st_action[a] = -p.number
-                                            st_actionp[a] = p
-                                            if not slevel and not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as reduce",a)
-                                                self.sr_conflicts.append((st,a,'reduce'))
-                                            Productions[p.number].reduced += 1
-                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
-                                            st_action[a] = None
-                                        else:
-                                            # Hmmm. Guess we'll keep the shift
-                                            if not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as shift",a)
-                                                self.sr_conflicts.append((st,a,'shift'))
-                                    elif r < 0:
-                                        # Reduce/reduce conflict.   In this case, we favor the rule
-                                        # that was defined first in the grammar file
-                                        oldp = Productions[-r]
-                                        pp = Productions[p.number]
-                                        if oldp.line > pp.line:
-                                            st_action[a] = -p.number
-                                            st_actionp[a] = p
-                                            chosenp,rejectp = pp,oldp
-                                            Productions[p.number].reduced += 1
-                                            Productions[oldp.number].reduced -= 1
-                                        else:
-                                            chosenp,rejectp = oldp,pp
-                                        self.rr_conflicts.append((st,chosenp,rejectp))
-                                        log.info("  ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a])
-                                    else:
-                                        raise LALRError("Unknown conflict in state %d" % st)
-                                else:
-                                    st_action[a] = -p.number
-                                    st_actionp[a] = p
-                                    Productions[p.number].reduced += 1
-                    else:
-                        i = p.lr_index
-                        a = p.prod[i+1]       # Get symbol right after the "."
-                        if a in self.grammar.Terminals:
-                            g = self.lr0_goto(I,a)
-                            j = self.lr0_cidhash.get(id(g),-1)
-                            if j >= 0:
-                                # We are in a shift state
-                                actlist.append((a,p,"shift and go to state %d" % j))
-                                r = st_action.get(a)
-                                if r is not None:
-                                    # Whoa have a shift/reduce or shift/shift conflict
-                                    if r > 0:
-                                        if r != j:
-                                            raise LALRError("Shift/shift conflict in state %d" % st)
-                                    elif r < 0:
-                                        # Do a precedence check.
-                                        #   -  if precedence of reduce rule is higher, we reduce.
-                                        #   -  if precedence of reduce is same and left assoc, we reduce.
-                                        #   -  otherwise we shift
-                                        rprec,rlevel = Productions[st_actionp[a].number].prec
-                                        sprec,slevel = Precedence.get(a,('right',0))
-                                        if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
-                                            # We decide to shift here... highest precedence to shift
-                                            Productions[st_actionp[a].number].reduced -= 1
-                                            st_action[a] = j
-                                            st_actionp[a] = p
-                                            if not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as shift",a)
-                                                self.sr_conflicts.append((st,a,'shift'))
-                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
-                                            st_action[a] = None
-                                        else:
-                                            # Hmmm. Guess we'll keep the reduce
-                                            if not slevel and not rlevel:
-                                                log.info("  ! shift/reduce conflict for %s resolved as reduce",a)
-                                                self.sr_conflicts.append((st,a,'reduce'))
-
-                                    else:
-                                        raise LALRError("Unknown conflict in state %d" % st)
-                                else:
-                                    st_action[a] = j
-                                    st_actionp[a] = p
-
-            # Print the actions associated with each terminal
-            _actprint = { }
-            for a,p,m in actlist:
-                if a in st_action:
-                    if p is st_actionp[a]:
-                        log.info("    %-15s %s",a,m)
-                        _actprint[(a,m)] = 1
-            log.info("")
-            # Print the actions that were not used. (debugging)
-            not_used = 0
-            for a,p,m in actlist:
-                if a in st_action:
-                    if p is not st_actionp[a]:
-                        if not (a,m) in _actprint:
-                            log.debug("  ! %-15s [ %s ]",a,m)
-                            not_used = 1
-                            _actprint[(a,m)] = 1
-            if not_used:
-                log.debug("")
-
-            # Construct the goto table for this state
-
-            nkeys = { }
-            for ii in I:
-                for s in ii.usyms:
-                    if s in self.grammar.Nonterminals:
-                        nkeys[s] = None
-            for n in nkeys:
-                g = self.lr0_goto(I,n)
-                j = self.lr0_cidhash.get(id(g),-1)
-                if j >= 0:
-                    st_goto[n] = j
-                    log.info("    %-30s shift and go to state %d",n,j)
-
-            action[st] = st_action
-            actionp[st] = st_actionp
-            goto[st] = st_goto
-            st += 1
-
-
-    # -----------------------------------------------------------------------------
-    # write()
-    #
-    # This function writes the LR parsing tables to a file
-    # -----------------------------------------------------------------------------
-
-    def write_table(self,modulename,outputdir='',signature=""):
-        basemodulename = modulename.split(".")[-1]
-        filename = os.path.join(outputdir,basemodulename) + ".py"
-        try:
-            f = open(filename,"w")
-
-            f.write("""
-# %s
-# This file is automatically generated. Do not edit.
-_tabversion = %r
-
-_lr_method = %r
-
-_lr_signature = %r
-    """ % (filename, __tabversion__, self.lr_method, signature))
-
-            # Change smaller to 0 to go back to original tables
-            smaller = 1
-
-            # Factor out names to try and make smaller
-            if smaller:
-                items = { }
-
-                for s,nd in self.lr_action.items():
-                   for name,v in nd.items():
-                      i = items.get(name)
-                      if not i:
-                         i = ([],[])
-                         items[name] = i
-                      i[0].append(s)
-                      i[1].append(v)
-
-                f.write("\n_lr_action_items = {")
-                for k,v in items.items():
-                    f.write("%r:([" % k)
-                    for i in v[0]:
-                        f.write("%r," % i)
-                    f.write("],[")
-                    for i in v[1]:
-                        f.write("%r," % i)
-
-                    f.write("]),")
-                f.write("}\n")
-
-                f.write("""
-_lr_action = { }
-for _k, _v in _lr_action_items.items():
-   for _x,_y in zip(_v[0],_v[1]):
-      if not _x in _lr_action:  _lr_action[_x] = { }
-      _lr_action[_x][_k] = _y
-del _lr_action_items
-""")
-
-            else:
-                f.write("\n_lr_action = { ");
-                for k,v in self.lr_action.items():
-                    f.write("(%r,%r):%r," % (k[0],k[1],v))
-                f.write("}\n");
-
-            if smaller:
-                # Factor out names to try and make smaller
-                items = { }
-
-                for s,nd in self.lr_goto.items():
-                   for name,v in nd.items():
-                      i = items.get(name)
-                      if not i:
-                         i = ([],[])
-                         items[name] = i
-                      i[0].append(s)
-                      i[1].append(v)
-
-                f.write("\n_lr_goto_items = {")
-                for k,v in items.items():
-                    f.write("%r:([" % k)
-                    for i in v[0]:
-                        f.write("%r," % i)
-                    f.write("],[")
-                    for i in v[1]:
-                        f.write("%r," % i)
-
-                    f.write("]),")
-                f.write("}\n")
-
-                f.write("""
-_lr_goto = { }
-for _k, _v in _lr_goto_items.items():
-   for _x,_y in zip(_v[0],_v[1]):
-       if not _x in _lr_goto: _lr_goto[_x] = { }
-       _lr_goto[_x][_k] = _y
-del _lr_goto_items
-""")
-            else:
-                f.write("\n_lr_goto = { ");
-                for k,v in self.lr_goto.items():
-                    f.write("(%r,%r):%r," % (k[0],k[1],v))
-                f.write("}\n");
-
-            # Write production table
-            f.write("_lr_productions = [\n")
-            for p in self.lr_productions:
-                if p.func:
-                    f.write("  (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line))
-                else:
-                    f.write("  (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len))
-            f.write("]\n")
-            f.close()
-
-        except IOError:
-            e = sys.exc_info()[1]
-            sys.stderr.write("Unable to create %r\n" % filename)
-            sys.stderr.write(str(e)+"\n")
-            return
-
-
-    # -----------------------------------------------------------------------------
-    # pickle_table()
-    #
-    # This function pickles the LR parsing tables to a supplied file object
-    # -----------------------------------------------------------------------------
-
-    def pickle_table(self,filename,signature=""):
-        try:
-            import cPickle as pickle
-        except ImportError:
-            import pickle
-        outf = open(filename,"wb")
-        pickle.dump(__tabversion__,outf,pickle_protocol)
-        pickle.dump(self.lr_method,outf,pickle_protocol)
-        pickle.dump(signature,outf,pickle_protocol)
-        pickle.dump(self.lr_action,outf,pickle_protocol)
-        pickle.dump(self.lr_goto,outf,pickle_protocol)
-
-        outp = []
-        for p in self.lr_productions:
-            if p.func:
-                outp.append((p.str,p.name, p.len, p.func,p.file,p.line))
-            else:
-                outp.append((str(p),p.name,p.len,None,None,None))
-        pickle.dump(outp,outf,pickle_protocol)
-        outf.close()
-
-# -----------------------------------------------------------------------------
-#                            === INTROSPECTION ===
-#
-# The following functions and classes are used to implement the PLY
-# introspection features followed by the yacc() function itself.
-# -----------------------------------------------------------------------------
-
-# -----------------------------------------------------------------------------
-# get_caller_module_dict()
-#
-# This function returns a dictionary containing all of the symbols defined within
-# a caller further down the call stack.  This is used to get the environment
-# associated with the yacc() call if none was provided.
-# -----------------------------------------------------------------------------
-
-def get_caller_module_dict(levels):
-    try:
-        raise RuntimeError
-    except RuntimeError:
-        e,b,t = sys.exc_info()
-        f = t.tb_frame
-        while levels > 0:
-            f = f.f_back                   
-            levels -= 1
-        ldict = f.f_globals.copy()
-        if f.f_globals != f.f_locals:
-            ldict.update(f.f_locals)
-
-        return ldict
-
-# -----------------------------------------------------------------------------
-# parse_grammar()
-#
-# This takes a raw grammar rule string and parses it into production data
-# -----------------------------------------------------------------------------
-def parse_grammar(doc,file,line):
-    grammar = []
-    # Split the doc string into lines
-    pstrings = doc.splitlines()
-    lastp = None
-    dline = line
-    for ps in pstrings:
-        dline += 1
-        p = ps.split()
-        if not p: continue
-        try:
-            if p[0] == '|':
-                # This is a continuation of a previous rule
-                if not lastp:
-                    raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline))
-                prodname = lastp
-                syms = p[1:]
-            else:
-                prodname = p[0]
-                lastp = prodname
-                syms   = p[2:]
-                assign = p[1]
-                if assign != ':' and assign != '::=':
-                    raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline))
-
-            grammar.append((file,dline,prodname,syms))
-        except SyntaxError:
-            raise
-        except Exception:
-            raise SyntaxError("%s:%d: Syntax error in rule %r" % (file,dline,ps.strip()))
-
-    return grammar
-
-# -----------------------------------------------------------------------------
-# ParserReflect()
-#
-# This class represents information extracted for building a parser including
-# start symbol, error function, tokens, precedence list, action functions,
-# etc.
-# -----------------------------------------------------------------------------
-class ParserReflect(object):
-    def __init__(self,pdict,log=None):
-        self.pdict      = pdict
-        self.start      = None
-        self.error_func = None
-        self.tokens     = None
-        self.modules    = {}
-        self.grammar    = []
-        self.error      = 0
-
-        if log is None:
-            self.log = PlyLogger(sys.stderr)
-        else:
-            self.log = log
-
-    # Get all of the basic information
-    def get_all(self):
-        self.get_start()
-        self.get_error_func()
-        self.get_tokens()
-        self.get_precedence()
-        self.get_pfunctions()
-        
-    # Validate all of the information
-    def validate_all(self):
-        self.validate_start()
-        self.validate_error_func()
-        self.validate_tokens()
-        self.validate_precedence()
-        self.validate_pfunctions()
-        self.validate_modules()
-        return self.error
-
-    # Compute a signature over the grammar
-    def signature(self):
-        try:
-            from hashlib import md5
-        except ImportError:
-            from md5 import md5
-        try:
-            sig = md5()
-            if self.start:
-                sig.update(self.start.encode('latin-1'))
-            if self.prec:
-                sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1'))
-            if self.tokens:
-                sig.update(" ".join(self.tokens).encode('latin-1'))
-            for f in self.pfuncs:
-                if f[3]:
-                    sig.update(f[3].encode('latin-1'))
-        except (TypeError,ValueError):
-            pass
-        return sig.digest()
-
-    # -----------------------------------------------------------------------------
-    # validate_modules()
-    #
-    # This method checks to see if there are duplicated p_rulename() functions
-    # in the parser module file.  Without this function, it is really easy for
-    # users to make mistakes by cutting and pasting code fragments (and it's a real
-    # bugger to try and figure out why the resulting parser doesn't work).  Therefore,
-    # we just do a little regular expression pattern matching of def statements
-    # to try and detect duplicates.
-    # -----------------------------------------------------------------------------
-
-    def validate_modules(self):
-        # Match def p_funcname(
-        fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(')
-
-        for module in self.modules.keys():
-            lines, linen = inspect.getsourcelines(module)
-
-            counthash = { }
-            for linen,l in enumerate(lines):
-                linen += 1
-                m = fre.match(l)
-                if m:
-                    name = m.group(1)
-                    prev = counthash.get(name)
-                    if not prev:
-                        counthash[name] = linen
-                    else:
-                        filename = inspect.getsourcefile(module)
-                        self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev)
-
-    # Get the start symbol
-    def get_start(self):
-        self.start = self.pdict.get('start')
-
-    # Validate the start symbol
-    def validate_start(self):
-        if self.start is not None:
-            if not isinstance(self.start, string_types):
-                self.log.error("'start' must be a string")
-
-    # Look for error handler
-    def get_error_func(self):
-        self.error_func = self.pdict.get('p_error')
-
-    # Validate the error function
-    def validate_error_func(self):
-        if self.error_func:
-            if isinstance(self.error_func,types.FunctionType):
-                ismethod = 0
-            elif isinstance(self.error_func, types.MethodType):
-                ismethod = 1
-            else:
-                self.log.error("'p_error' defined, but is not a function or method")
-                self.error = 1
-                return
-
-            eline = func_code(self.error_func).co_firstlineno
-            efile = func_code(self.error_func).co_filename
-            module = inspect.getmodule(self.error_func)
-            self.modules[module] = 1
-
-            argcount = func_code(self.error_func).co_argcount - ismethod
-            if argcount != 1:
-                self.log.error("%s:%d: p_error() requires 1 argument",efile,eline)
-                self.error = 1
-
-    # Get the tokens map
-    def get_tokens(self):
-        tokens = self.pdict.get("tokens")
-        if not tokens:
-            self.log.error("No token list is defined")
-            self.error = 1
-            return
-
-        if not isinstance(tokens,(list, tuple)):
-            self.log.error("tokens must be a list or tuple")
-            self.error = 1
-            return
-        
-        if not tokens:
-            self.log.error("tokens is empty")
-            self.error = 1
-            return
-
-        self.tokens = tokens
-
-    # Validate the tokens
-    def validate_tokens(self):
-        # Validate the tokens.
-        if 'error' in self.tokens:
-            self.log.error("Illegal token name 'error'. Is a reserved word")
-            self.error = 1
-            return
-
-        terminals = {}
-        for n in self.tokens:
-            if n in terminals:
-                self.log.warning("Token %r multiply defined", n)
-            terminals[n] = 1
-
-    # Get the precedence map (if any)
-    def get_precedence(self):
-        self.prec = self.pdict.get("precedence")
-
-    # Validate and parse the precedence map
-    def validate_precedence(self):
-        preclist = []
-        if self.prec:
-            if not isinstance(self.prec,(list,tuple)):
-                self.log.error("precedence must be a list or tuple")
-                self.error = 1
-                return
-            for level,p in enumerate(self.prec):
-                if not isinstance(p,(list,tuple)):
-                    self.log.error("Bad precedence table")
-                    self.error = 1
-                    return
-
-                if len(p) < 2:
-                    self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p)
-                    self.error = 1
-                    return
-                assoc = p[0]
-                if not isinstance(assoc, string_types):
-                    self.log.error("precedence associativity must be a string")
-                    self.error = 1
-                    return
-                for term in p[1:]:
-                    if not isinstance(term, string_types):
-                        self.log.error("precedence items must be strings")
-                        self.error = 1
-                        return
-                    preclist.append((term, assoc, level+1))
-        self.preclist = preclist
-
-    # Get all p_functions from the grammar
-    def get_pfunctions(self):
-        p_functions = []
-        for name, item in self.pdict.items():
-            if not name.startswith('p_'): continue
-            if name == 'p_error': continue
-            if isinstance(item,(types.FunctionType,types.MethodType)):
-                line = func_code(item).co_firstlineno
-                module = inspect.getmodule(item)
-                p_functions.append((line,module,name,item.__doc__))
-
-        # Sort all of the actions by line number
-        p_functions.sort()
-        self.pfuncs = p_functions
-
-
-    # Validate all of the p_functions
-    def validate_pfunctions(self):
-        grammar = []
-        # Check for non-empty symbols
-        if len(self.pfuncs) == 0:
-            self.log.error("no rules of the form p_rulename are defined")
-            self.error = 1
-            return
-
-        for line, module, name, doc in self.pfuncs:
-            file = inspect.getsourcefile(module)
-            func = self.pdict[name]
-            if isinstance(func, types.MethodType):
-                reqargs = 2
-            else:
-                reqargs = 1
-            if func_code(func).co_argcount > reqargs:
-                self.log.error("%s:%d: Rule %r has too many arguments",file,line,func.__name__)
-                self.error = 1
-            elif func_code(func).co_argcount < reqargs:
-                self.log.error("%s:%d: Rule %r requires an argument",file,line,func.__name__)
-                self.error = 1
-            elif not func.__doc__:
-                self.log.warning("%s:%d: No documentation string specified in function %r (ignored)",file,line,func.__name__)
-            else:
-                try:
-                    parsed_g = parse_grammar(doc,file,line)
-                    for g in parsed_g:
-                        grammar.append((name, g))
-                except SyntaxError:
-                    e = sys.exc_info()[1]
-                    self.log.error(str(e))
-                    self.error = 1
-
-                # Looks like a valid grammar rule
-                # Mark the file in which defined.
-                self.modules[module] = 1
-
-        # Secondary validation step that looks for p_ definitions that are not functions
-        # or functions that look like they might be grammar rules.
-
-        for n,v in self.pdict.items():
-            if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)): continue
-            if n.startswith('t_'): continue
-            if n.startswith('p_') and n != 'p_error':
-                self.log.warning("%r not defined as a function", n)
-            if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or
-                (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)):
-                try:
-                    doc = v.__doc__.split(" ")
-                    if doc[1] == ':':
-                        self.log.warning("%s:%d: Possible grammar rule %r defined without p_ prefix",
-                                         func_code(v).co_filename, func_code(v).co_firstlineno,n)
-                except Exception:
-                    pass
-
-        self.grammar = grammar
-
-# -----------------------------------------------------------------------------
-# yacc(module)
-#
-# Build a parser
-# -----------------------------------------------------------------------------
-
-def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, 
-         check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='',
-         debuglog=None, errorlog = None, picklefile=None):
-
-    global parse                 # Reference to the parsing method of the last built parser
-
-    # If pickling is enabled, table files are not created
-
-    if picklefile:
-        write_tables = 0
-
-    if errorlog is None:
-        errorlog = PlyLogger(sys.stderr)
-
-    # Get the module dictionary used for the parser
-    if module:
-        _items = [(k,getattr(module,k)) for k in dir(module)]
-        pdict = dict(_items)
-    else:
-        pdict = get_caller_module_dict(2)
-
-    # Collect parser information from the dictionary
-    pinfo = ParserReflect(pdict,log=errorlog)
-    pinfo.get_all()
-
-    if pinfo.error:
-        raise YaccError("Unable to build parser")
-
-    # Check signature against table files (if any)
-    signature = pinfo.signature()
-
-    # Read the tables
-    try:
-        lr = LRTable()
-        if picklefile:
-            read_signature = lr.read_pickle(picklefile)
-        else:
-            read_signature = lr.read_table(tabmodule)
-        if optimize or (read_signature == signature):
-            try:
-                lr.bind_callables(pinfo.pdict)
-                parser = LRParser(lr,pinfo.error_func)
-                parse = parser.parse
-                return parser
-            except Exception:
-                e = sys.exc_info()[1]
-                errorlog.warning("There was a problem loading the table file: %s", repr(e))
-    except VersionError:
-        e = sys.exc_info()
-        errorlog.warning(str(e))
-    except Exception:
-        pass
-
-    if debuglog is None:
-        if debug:
-            debuglog = PlyLogger(open(debugfile,"w"))
-        else:
-            debuglog = NullLogger()
-
-    debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__)
-
-
-    errors = 0
-
-    # Validate the parser information
-    if pinfo.validate_all():
-        raise YaccError("Unable to build parser")
-    
-    if not pinfo.error_func:
-        errorlog.warning("no p_error() function is defined")
-
-    # Create a grammar object
-    grammar = Grammar(pinfo.tokens)
-
-    # Set precedence level for terminals
-    for term, assoc, level in pinfo.preclist:
-        try:
-            grammar.set_precedence(term,assoc,level)
-        except GrammarError:
-            e = sys.exc_info()[1]
-            errorlog.warning("%s",str(e))
-
-    # Add productions to the grammar
-    for funcname, gram in pinfo.grammar:
-        file, line, prodname, syms = gram
-        try:
-            grammar.add_production(prodname,syms,funcname,file,line)
-        except GrammarError:
-            e = sys.exc_info()[1]
-            errorlog.error("%s",str(e))
-            errors = 1
-
-    # Set the grammar start symbols
-    try:
-        if start is None:
-            grammar.set_start(pinfo.start)
-        else:
-            grammar.set_start(start)
-    except GrammarError:
-        e = sys.exc_info()[1]
-        errorlog.error(str(e))
-        errors = 1
-
-    if errors:
-        raise YaccError("Unable to build parser")
-
-    # Verify the grammar structure
-    undefined_symbols = grammar.undefined_symbols()
-    for sym, prod in undefined_symbols:
-        errorlog.error("%s:%d: Symbol %r used, but not defined as a token or a rule",prod.file,prod.line,sym)
-        errors = 1
-
-    unused_terminals = grammar.unused_terminals()
-    if unused_terminals:
-        debuglog.info("")
-        debuglog.info("Unused terminals:")
-        debuglog.info("")
-        for term in unused_terminals:
-            errorlog.warning("Token %r defined, but not used", term)
-            debuglog.info("    %s", term)
-
-    # Print out all productions to the debug log
-    if debug:
-        debuglog.info("")
-        debuglog.info("Grammar")
-        debuglog.info("")
-        for n,p in enumerate(grammar.Productions):
-            debuglog.info("Rule %-5d %s", n, p)
-
-    # Find unused non-terminals
-    unused_rules = grammar.unused_rules()
-    for prod in unused_rules:
-        errorlog.warning("%s:%d: Rule %r defined, but not used", prod.file, prod.line, prod.name)
-
-    if len(unused_terminals) == 1:
-        errorlog.warning("There is 1 unused token")
-    if len(unused_terminals) > 1:
-        errorlog.warning("There are %d unused tokens", len(unused_terminals))
-
-    if len(unused_rules) == 1:
-        errorlog.warning("There is 1 unused rule")
-    if len(unused_rules) > 1:
-        errorlog.warning("There are %d unused rules", len(unused_rules))
-
-    if debug:
-        debuglog.info("")
-        debuglog.info("Terminals, with rules where they appear")
-        debuglog.info("")
-        terms = list(grammar.Terminals)
-        terms.sort()
-        for term in terms:
-            debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]]))
-        
-        debuglog.info("")
-        debuglog.info("Nonterminals, with rules where they appear")
-        debuglog.info("")
-        nonterms = list(grammar.Nonterminals)
-        nonterms.sort()
-        for nonterm in nonterms:
-            debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]]))
-        debuglog.info("")
-
-    if check_recursion:
-        unreachable = grammar.find_unreachable()
-        for u in unreachable:
-            errorlog.warning("Symbol %r is unreachable",u)
-
-        infinite = grammar.infinite_cycles()
-        for inf in infinite:
-            errorlog.error("Infinite recursion detected for symbol %r", inf)
-            errors = 1
-        
-    unused_prec = grammar.unused_precedence()
-    for term, assoc in unused_prec:
-        errorlog.error("Precedence rule %r defined for unknown symbol %r", assoc, term)
-        errors = 1
-
-    if errors:
-        raise YaccError("Unable to build parser")
-    
-    # Run the LRGeneratedTable on the grammar
-    if debug:
-        errorlog.debug("Generating %s tables", method)
-            
-    lr = LRGeneratedTable(grammar,method,debuglog)
-
-    if debug:
-        num_sr = len(lr.sr_conflicts)
-
-        # Report shift/reduce and reduce/reduce conflicts
-        if num_sr == 1:
-            errorlog.warning("1 shift/reduce conflict")
-        elif num_sr > 1:
-            errorlog.warning("%d shift/reduce conflicts", num_sr)
-
-        num_rr = len(lr.rr_conflicts)
-        if num_rr == 1:
-            errorlog.warning("1 reduce/reduce conflict")
-        elif num_rr > 1:
-            errorlog.warning("%d reduce/reduce conflicts", num_rr)
-
-    # Write out conflicts to the output file
-    if debug and (lr.sr_conflicts or lr.rr_conflicts):
-        debuglog.warning("")
-        debuglog.warning("Conflicts:")
-        debuglog.warning("")
-
-        for state, tok, resolution in lr.sr_conflicts:
-            debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s",  tok, state, resolution)
-        
-        already_reported = {}
-        for state, rule, rejected in lr.rr_conflicts:
-            if (state,id(rule),id(rejected)) in already_reported:
-                continue
-            debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
-            debuglog.warning("rejected rule (%s) in state %d", rejected,state)
-            errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule)
-            errorlog.warning("rejected rule (%s) in state %d", rejected, state)
-            already_reported[state,id(rule),id(rejected)] = 1
-        
-        warned_never = []
-        for state, rule, rejected in lr.rr_conflicts:
-            if not rejected.reduced and (rejected not in warned_never):
-                debuglog.warning("Rule (%s) is never reduced", rejected)
-                errorlog.warning("Rule (%s) is never reduced", rejected)
-                warned_never.append(rejected)
-
-    # Write the table file if requested
-    if write_tables:
-        lr.write_table(tabmodule,outputdir,signature)
-
-    # Write a pickled version of the tables
-    if picklefile:
-        lr.pickle_table(picklefile,signature)
-
-    # Build the parser
-    lr.bind_callables(pinfo.pdict)
-    parser = LRParser(lr,pinfo.error_func)
-
-    parse = parser.parse
-    return parser
diff --git a/plyxproto/__init__.py b/plyxproto/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/plyxproto/__init__.py
+++ /dev/null
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 79bc678..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-[bdist_wheel]
-# This flag says that the code is written to work on both Python 2 and Python
-# 3. If at all possible, it is good practice to do this. If you cannot, you
-# will need to generate wheels for each Python version that you support.
-universal=1
diff --git a/setup.py b/setup.py
index ad1947f..4f15ae1 100644
--- a/setup.py
+++ b/setup.py
@@ -1,29 +1,63 @@
-#!/usr/bin/env python
+# Copyright 2017-present Open Networking Foundation and others
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
 
-"""plyxproto xproto parser
+"""
+plyxproto xproto parser
 
 See:
-https://github.com/sb98052/plyprotobuf
-https://github.com/opencord/xos
+ https://gerrit.opencord.org/gitweb?p=plyxproto.git
+ https://guide.xosproject.org/
 """
 
-from setuptools import setup
+from setuptools import setup, find_packages
+
+
+def readme():
+    with open('README.rst') as f:
+        return f.read()
+
+
+def version():
+    with open('VERSION') as f:
+        return f.read()
+
 
 setup(
     name='plyxproto',
-    version='3.1.0',
+    version=version(),
     description='xproto parser and processor',
-    author='Dusan Klinec (original protobuf parser), Sapan Bhatia (xproto extensions), Zack Williams (maintenance), Scott Baker (maintenance)',
-    author_email='sapan@opennetworking.org',
-    url='https://github.com/sb98052/plyprotobuf',
+    long_description=readme(),
+    author='''
+      Dusan Klinec (original plyprotobuf code),
+      Sapan Bhatia (xproto extensions),
+      Zack Williams (maintenance),
+      Scott Baker (maintenance),
+    ''',
+    author_email='support@opencord.org',
+    url='https://gerrit.opencord.org/gitweb?p=plyxproto.git',
     license='Apache Software License',
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Intended Audience :: Developers',
         'Topic :: Software Development :: Build Tools',
         'License :: OSI Approved :: Apache Software License',
-        'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 2.7'],
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+    ],
     keywords='xproto protobuf xos parser',
-    packages=['plyxproto'],
-    install_requires=['ply'])
+    packages=find_packages(where="src"),
+    package_dir={"": "src"},
+    install_requires=['ply'],
+    include_package_data=True,
+)
diff --git a/src/plyxproto/__init__.py b/src/plyxproto/__init__.py
new file mode 100644
index 0000000..b0fb0b2
--- /dev/null
+++ b/src/plyxproto/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/plyxproto/helpers.py b/src/plyxproto/helpers.py
similarity index 88%
rename from plyxproto/helpers.py
rename to src/plyxproto/helpers.py
index 2cfc4c3..15136f5 100644
--- a/plyxproto/helpers.py
+++ b/src/plyxproto/helpers.py
@@ -1,3 +1,21 @@
+# Copyright 2017-present Open Networking Foundation and others
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+from six.moves import range
+
+
 class LexHelper:
     offset = 0
 
@@ -178,7 +196,7 @@
         def f(element):
             if self.verbose:
                 msg = 'unimplemented call to {}; ignoring ({})'
-                print(msg.format(name, element))
+                print((msg.format(name, element)))
             return True
         return f
 
diff --git a/plyxproto/logicparser.py b/src/plyxproto/logicparser.py
similarity index 92%
rename from plyxproto/logicparser.py
rename to src/plyxproto/logicparser.py
index ae8444e..1c4922b 100644
--- a/plyxproto/logicparser.py
+++ b/src/plyxproto/logicparser.py
@@ -1,9 +1,11 @@
+from __future__ import absolute_import
+from __future__ import print_function
 __author__ = "Sapan Bhatia"
 
 __copyright__ = "Copyright (C) 2017 Open Networking Lab"
 __version__ = "1.0"
 
-from helpers import LexHelper
+from .helpers import LexHelper
 
 
 class FOLParsingError(Exception):
@@ -69,14 +71,14 @@
         t.lexer.lineno += t.value.count('\n')
 
     def t_SYMBOL(self, t):
-        '[A-Za-z_$][\.A-Za-z0-9_+$]*(\(\))?'
+        r'[A-Za-z_$][\.A-Za-z0-9_+$]*(\(\))?'
         if t.value in FOLLexer.keywords:
             t.type = t.value.upper()
         return t
 
     def t_error(self, t):
-        print("Illegal character '{}' ({}) in line {}".format(
-            t.value[0], hex(ord(t.value[0])), t.lexer.lineno))
+        print(("Illegal character '{}' ({}) in line {}".format(
+            t.value[0], hex(ord(t.value[0])), t.lexer.lineno)))
         t.lexer.skip(1)
 
 
diff --git a/plyxproto/model.py b/src/plyxproto/model.py
similarity index 95%
rename from plyxproto/model.py
rename to src/plyxproto/model.py
index f5fe8bd..40bab35 100644
--- a/plyxproto/model.py
+++ b/src/plyxproto/model.py
@@ -1,4 +1,19 @@
-from helpers import Base, SourceElement
+# Copyright 2017-present Open Networking Foundation and others
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import absolute_import
+from .helpers import Base, SourceElement
 
 
 class PackageStatement(SourceElement):
@@ -491,7 +506,7 @@
     def append_name(self, name):
         try:
             self.value = self.value + '.' + name.value
-        except:
+        except BaseException:
             self.value = self.value + '.' + name
 
     def deriveLex(self):
diff --git a/plyxproto/parser.py b/src/plyxproto/parser.py
similarity index 93%
rename from plyxproto/parser.py
rename to src/plyxproto/parser.py
index a6c7dc8..aabf2eb 100755
--- a/plyxproto/parser.py
+++ b/src/plyxproto/parser.py
@@ -1,8 +1,9 @@
-__author__ = "Dusan (Ph4r05) Klinec"
+from __future__ import absolute_import
+from __future__ import print_function
 
-__copyright__ = "Copyright (C) 2014 Dusan (ph4r05) Klinec"
+__author__ = "Dusan (Ph4r05) Klinec, Sapan Bhatia, ONF"
+__copyright__ = "Copyright (C) 2014 Dusan (ph4r05) Klinec, 2017-2019 ONF"
 __license__ = "Apache License, Version 2.0"
-__version__ = "1.0"
 
 import ply.lex as lex
 import ply.yacc as yacc
@@ -33,8 +34,8 @@
     ServiceDefinition,
 )
 
-from helpers import LexHelper, LU
-from logicparser import FOLParser, FOLLexer, FOLParsingError
+from .helpers import LexHelper, LU
+from .logicparser import FOLParser, FOLLexer, FOLParsingError
 import ast
 
 
@@ -153,8 +154,8 @@
         t.lexer.lineno += len(t.value) / 2
 
     def t_error(self, t):
-        print("Illegal character '{}' ({}) in line {}".format(
-            t.value[0], hex(ord(t.value[0])), t.lexer.lineno))
+        print(("Illegal character '{}' ({}) in line {}".format(
+            t.value[0], hex(ord(t.value[0])), t.lexer.lineno)))
         t.lexer.skip(1)
 
 
@@ -347,7 +348,10 @@
 
     # TODO: Add directives to link definition
     def p_link_definition(self, p):
-        '''link_definition : field_modifier link_type field_name policy_opt ARROW dotname slash_name colon_fieldname EQ field_id field_directives SEMI'''
+        '''
+        link_definition : field_modifier link_type field_name policy_opt ARROW dotname slash_name colon_fieldname EQ field_id field_directives SEMI
+        '''
+
         p[0] = LinkSpec(
             FieldDefinition(
                 LU.i(
@@ -372,8 +376,10 @@
         self.lh.set_parse_object(p[0], p)
 
     # TODO: Add directives to link definition
-    def p_link_definition_with_reverse (self, p):
-        '''link_definition_with_reverse : field_modifier link_type field_name policy_opt ARROW dotname slash_name colon_fieldname EQ field_id COLON reverse_id field_directives SEMI'''
+    def p_link_definition_with_reverse(self, p):
+        '''
+        link_definition_with_reverse : field_modifier link_type field_name policy_opt ARROW dotname slash_name colon_fieldname EQ field_id COLON reverse_id field_directives SEMI
+        '''
         p[0] = LinkSpec(
             FieldDefinition(
                 LU.i(
@@ -393,7 +399,7 @@
                                                                 p, 3), LU.i(
                                                                     p, 6), LU.i(
                                                                         p, 7), LU.i(
-                                                                            p, 8), reverse_id= LU.i(p, 12)))
+                                                                            p, 8), reverse_id=LU.i(p, 12)))
 
         self.lh.set_parse_object(p[0], p)
 
@@ -440,10 +446,10 @@
     def p_reduce_definition(self, p):
         '''reduce_definition : REDUCE NAME POLICYBODY'''
         ltxt = p[3].lstrip('<').rstrip('>')
-        l = ast.parse(ltxt).body[0]
-        if not isinstance(l, ast.Expr):
+        al = ast.parse(ltxt).body[0]
+        if not isinstance(al, ast.Expr):
             raise PythonError("reduce operator needs to be an expression")
-        elif not isinstance(l.value, ast.Lambda):
+        elif not isinstance(al.value, ast.Lambda):
             raise PythonError("reduce operator needs to be a lambda")
 
         p[0] = ReduceDefinition(Name(LU.i(p, 2)), ltxt)
@@ -452,10 +458,10 @@
     def p_map_definition(self, p):
         '''map_definition : MAP NAME POLICYBODY'''
         ltxt = p[3].lstrip('<').rstrip('>')
-        l = ast.parse(ltxt).body[0]
-        if not isinstance(l, ast.Expr):
+        al = ast.parse(ltxt).body[0]
+        if not isinstance(al, ast.Expr):
             raise PythonError("map operator needs to be an expression")
-        elif not isinstance(l.value, ast.Lambda):
+        elif not isinstance(al.value, ast.Lambda):
             raise PythonError("map operator needs to be a lambda")
 
         p[0] = MapDefinition(Name(LU.i(p, 2)), ltxt)
@@ -606,7 +612,8 @@
         p[0] = OptionStatement(Name(LU.i(p, 2)), LU.i(p, 4))
         self.lh.set_parse_object(p[0], p)
 
-    # topLevelStatement = Group(message_definition | message_extension | enum_definition | service_definition | import_directive | option_directive | package_definition)
+    # topLevelStatement = Group(message_definition | message_extension | enum_definition | service_definition |
+    #                           import_directive | option_directive | package_definition)
     def p_topLevel(self, p):
         '''topLevel : message_definition
                     | message_extension
@@ -664,7 +671,7 @@
 
     def tokenize_file(self, _file):
         if isinstance(_file, str):
-            _file = file(_file)
+            _file = open(_file)
         content = ''
         for line in _file:
             content += line
@@ -677,7 +684,7 @@
 
     def parse_file(self, _file, debug=0):
         if isinstance(_file, str):
-            _file = file(_file)
+            _file = open(_file)
         content = ''
         for line in _file:
             content += line
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e42366d
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017-present Open Networking Foundation and others
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/tests/test_logicparser.py b/tests/test_logicparser.py
new file mode 100644
index 0000000..bb3107d
--- /dev/null
+++ b/tests/test_logicparser.py
@@ -0,0 +1,72 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+import unittest
+
+import ply.lex as lex
+import ply.yacc as yacc
+from plyxproto.logicparser import FOLLexer, FOLParser
+
+
+class TestFOL(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(self):
+
+        self.DEBUG = 0
+        self.lexer = lex.lex(module=FOLLexer(), debug=self.DEBUG)
+        self.parser = yacc.yacc(module=FOLParser(), start='goal', debug=self.DEBUG)
+
+    def test_true(self):
+        '''verify basic truth statement'''
+
+        t_in = "<true>"
+        p_out = self.parser.parse(t_in, lexer=self.lexer, debug=self.DEBUG)
+        self.assertEqual(p_out, "true")
+
+    def test_or(self):
+        '''verify or statement'''
+
+        t_in = "<a | b>"
+        p_out = self.parser.parse(t_in, lexer=self.lexer, debug=self.DEBUG)
+        self.assertEqual(p_out, {'|': ['a', 'b']})
+
+    def test_exists(self):
+        '''verify exists statement'''
+
+        t_in = "<exists a: x=y>"
+        p_out = self.parser.parse(t_in, lexer=self.lexer, debug=self.DEBUG)
+        self.assertEqual(p_out, {'exists': ['a', {'=': ('x', 'y')}]})
+
+    def test_forall(self):
+        '''verify forall statement'''
+
+        t_in = "<forall a: exists b: x.b=y.b>"
+        p_out = self.parser.parse(t_in, lexer=self.lexer, debug=self.DEBUG)
+        self.assertEqual(p_out, {'forall': ['a', {'exists': ['b', {'=': ('x.b', 'y.b')}]}]})
+
+    def test_endswith(self):
+        '''verify endswith statement'''
+
+        t_in = "<forall a: {{ a.endswith('good') }}>"
+        p_out = self.parser.parse(t_in, lexer=self.lexer, debug=self.DEBUG)
+        self.assertEqual(p_out, {'forall': ['a', {'python': "a.endswith('good')"}]})
+
+    def test_function(self):
+        '''verify policy function calls'''
+
+        t_in = "< *doit(foo) >"
+        p_out = self.parser.parse(t_in, lexer=self.lexer, debug=self.DEBUG)
+        self.assertEqual(p_out, {'policy': ['doit', 'foo']})
diff --git a/tests/test_parser.py b/tests/test_parser.py
new file mode 100644
index 0000000..fc7d8c7
--- /dev/null
+++ b/tests/test_parser.py
@@ -0,0 +1,431 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+import unittest
+
+import plyxproto.parser as plyxparser
+import plyxproto.model as plyxmodel
+import plyxproto.helpers as plyxhelpers
+
+# Example of creating unittests on PLY code:
+#  https://github.com/dabeaz/ply/tree/master/test
+#  http://www.dalkescientific.com/writings/NBN/parsing_with_ply.html
+
+
+def get_name(element):
+    '''returns name of element'''
+    return element.name.value.pval
+
+
+def get_field_type(ft):
+    ''' returns field type'''
+
+    # FIXME: these are confusing
+    if isinstance(ft, plyxmodel.FieldType):
+        return ft.name.pval
+
+    elif isinstance(ft, plyxmodel.DotName):
+        return ft.value
+
+    else:
+        print("unknown field type: %s" % ft)
+        raise BaseException
+
+
+def get_field_directives(field):
+    ''' navigate the morass that is fieldDirective '''
+
+    fds = {}
+
+    for fieldd in field.fieldDirective:
+
+        fd_key = ""
+        fd_val = ""
+
+        # FIXME: Do these differences in heirarchy have value? They seem
+        # arbitrary and maybe the parser should handle it instead?
+
+        if hasattr(fieldd, 'name'):
+            fd_key = fieldd.name.value
+        elif hasattr(fieldd.pval.name, 'pval'):
+            fd_key = fieldd.pval.name.pval
+        elif hasattr(fieldd.pval.name.value, 'pval'):
+            fd_key = fieldd.pval.name.value.pval
+        else:
+            print("problem with key in fieldDirective: ", fieldd)
+            raise BaseException
+
+        if type(fieldd) is list:
+            fd_val = [item.pval for item in fieldd.value]
+        elif hasattr(fieldd, 'value'):
+            if type(fieldd.value) is list:
+                fd_val = [item.pval for item in fieldd.value]
+            elif hasattr(fieldd.value, 'pval'):
+                fd_val = fieldd.value.pval
+            elif hasattr(fieldd.value, 'value'):
+                if hasattr(fieldd.value.value, 'pval'):
+                    fd_val = fieldd.value.value.pval
+                else:
+                    fd_val = fieldd.value.value
+            else:
+                print("problem with value.value in fieldDirective: %s" % fieldd)
+                raise BaseException
+        elif hasattr(fieldd.pval.value, 'pval'):
+            fd_val = fieldd.pval.value.pval
+        elif hasattr(fieldd.pval.value.value, 'pval'):
+            fd_val = fieldd.pval.value.value.pval
+        else:
+            print("problem with value in fieldDirective: ", fieldd)
+            raise BaseException
+
+        fds[fd_key] = fd_val
+
+    return fds
+
+
+def msg_dict(msgdef):
+    '''
+    Given a MessageDefinition object, returns a dict describing it
+    Should probably really be a recursive __dict__ method on that object
+    '''
+
+    fd = {}  # FieldDefinition
+    md = {}  # sub-ModelDefinition
+    en = {}  # EnumDefinitons
+    ed = {}  # ExtensionsDirective
+    os = {}  # OptionStatement
+
+    assert isinstance(msgdef, plyxmodel.MessageDefinition)
+
+    for field in msgdef.body:
+
+        if isinstance(field, plyxmodel.FieldDefinition):
+
+            fd[field.name.value.pval] = {
+                'id': int(field.fieldId.pval),
+                'modifier': field.field_modifier.pval,
+                'type': get_field_type(field.ftype),
+                'policy': field.policy,
+                'directives': get_field_directives(field),
+            }
+
+        elif isinstance(field, plyxmodel.LinkSpec):
+
+            fd[field.field_def.name.value.pval] = {
+                'id': int(field.field_def.fieldId.pval),
+                'modifier': field.field_def.field_modifier.pval,
+                'type': field.field_def.ftype.value,  # Why different (not in a LU as in FieldDefinition)?
+                'link_type': field.link_def.link_type.pval,
+                'link_name': field.link_def.name[0].pval,  # Why in a list?
+                'link_src_port': field.link_def.src_port.value.pval,  # confusingly named - why include "port" ?
+                'link_dst_port': field.link_def.dst_port.value.pval,
+                'directives': get_field_directives(field.field_def),
+            }
+
+        elif isinstance(field, plyxmodel.MessageDefinition):
+
+            md[field.name.value.pval] = msg_dict(field)
+
+        elif isinstance(field, plyxmodel.EnumDefinition):
+
+            enumopts = {}
+
+            for enumdef in field.body:
+                enumopts[int(enumdef.fieldId.pval)] = get_name(enumdef)
+
+            en[field.name.value.pval] = enumopts
+
+        elif isinstance(field, plyxmodel.ExtensionsDirective):
+
+            # FIXME: models.ExtensionsMax() isn't well defined, so
+            # omit to/from when non-LU is found
+
+            if type(field.fromVal) is plyxhelpers.LU:
+                ed['from'] = field.fromVal.pval
+
+            if type(field.toVal) is plyxhelpers.LU:
+                ed['to'] = field.toVal.pval
+
+        elif isinstance(field, plyxmodel.OptionStatement):
+
+            os[field.name.value.pval] = field.value.value.pval
+
+        else:
+            print("Unknown message type: %s" % type(field))
+            raise BaseException
+
+    return fd, md, en, ed, os
+
+
+def options_dict(protofile):
+    '''returns a dictionary of name:value options from a ProtoFile'''
+
+    options = {}
+
+    for item in protofile.body:
+        if isinstance(item, plyxmodel.OptionStatement):
+            options[item.name.value.pval] = item.value.value.pval
+
+    return options
+
+
+class TestParser(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(self):
+        self.parser = plyxparser.ProtobufAnalyzer()
+
+    def test_invalid_input(self):
+        '''invalid input'''
+
+        t_in = """this is invalid"""
+
+        with self.assertRaises(plyxparser.ParsingError):
+            self.parser.parse_string(t_in)
+
+    def test_package(self):
+        '''creating a package'''
+
+        t_in = """package tutorial;"""
+        p_out = self.parser.parse_string(t_in)
+
+        self.assertIsInstance(p_out, plyxmodel.ProtoFile)
+        self.assertIsInstance(p_out.body[0], plyxmodel.PackageStatement)
+        self.assertEqual(p_out.body[0].name.value[0].pval, "tutorial")
+
+    def test_def_policy(self):
+        '''defining a policy'''
+
+        t_in = """policy foo <exists foo: foo.x=foo.y>"""
+        p_out = self.parser.parse_string(t_in)
+
+        self.assertIsInstance(p_out, plyxmodel.ProtoFile)
+        self.assertIsInstance(p_out.body[0], plyxmodel.PolicyDefinition)
+        self.assertEqual(get_name(p_out.body[0]), "foo")
+        self.assertDictEqual(p_out.body[0].body, {'exists': ['foo', {'=': ('foo.x', 'foo.y')}]})
+
+    def test_def_message(self):
+        '''defining a message'''
+
+        t_in = """package tutorial;
+
+message Person {
+  required string name = 1;
+  required int32 id = 2;
+  optional string email = 3;
+}
+"""
+        p_out = self.parser.parse_string(t_in)
+
+        self.assertIsInstance(p_out, plyxmodel.ProtoFile)
+        # see test_package for testing `package ...` statement
+        self.assertIsInstance(p_out.body[1], plyxmodel.MessageDefinition)
+        self.assertEqual(get_name(p_out.body[1]), 'Person')
+
+        fd, md, en, ed, os = msg_dict(p_out.body[1])
+
+        self.assertDictEqual(fd['name'],
+                             {'id': 1, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {}})
+        self.assertDictEqual(fd['id'],
+                             {'id': 2, 'modifier': 'required', 'type': 'int32', 'policy': None, 'directives': {}})
+        self.assertDictEqual(fd['email'],
+                             {'id': 3, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {}})
+
+    def test_options(self):
+        '''setting options'''
+
+        t_in = """package tutorial;
+option java_outer_classname = "PushNotifications";
+option optimize_for = SPEED;
+"""
+        p_out = self.parser.parse_string(t_in)
+        od = options_dict(p_out)
+
+        self.assertDictEqual(od, {'java_outer_classname': '"PushNotifications"', 'optimize_for': 'SPEED'})
+
+    def test_def_relation(self):
+        '''test defining related messages'''
+
+        t_in = """package tutorial;
+
+message Person(core.Actor) {
+  required string name = 1;
+  required int32 id = 2;
+  optional string email = 3;
+
+  required manytoone work_location->Location/types.Company:employees = 4;
+
+  enum PhoneType {
+    MOBILE = 0;
+    HOME = 1;
+    WORK = 2;
+  }
+
+  message PhoneNumber {
+    required string number = 1;
+    optional PhoneType type = 2 [default = HOME];
+  }
+
+  repeated PhoneNumber phone = 4;
+  extensions 500 to 990;
+}
+
+message AddressBook {
+  repeated Person person = 1;
+
+  // Possible extension numbers.
+  extensions 500 to max;
+}
+"""
+        p_out = self.parser.parse_string(t_in)
+
+        fd, md, en, ed, os = msg_dict(p_out.body[1])
+
+        self.assertDictEqual(fd, {'name': {'id': 1, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {}}, 'id': {'id': 2, 'modifier': 'required', 'type': 'int32', 'policy': None, 'directives': {}}, 'email': {'id': 3, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {}}, 'work_location': {'id': 4, 'modifier': 'required', 'type': 'int32', 'link_type': 'manytoone', 'link_name': 'Location', 'link_src_port': 'work_location', 'link_dst_port': 'employees', 'directives': {'type': 'link', 'model': ['Location'], 'port': 'employees'}}, 'phone': {'id': 4, 'modifier': 'repeated', 'type': 'PhoneNumber', 'policy': None, 'directives': {}}})
+
+        self.assertDictEqual(md, {'PhoneNumber': ({'number': {'id': 1, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {}}, 'type': {'id': 2, 'modifier': 'optional', 'type': 'PhoneType', 'policy': None, 'directives': {'default': 'HOME'}}}, {}, {}, {}, {})})
+        self.assertDictEqual(en, {'PhoneType': {0: 'MOBILE', 1: 'HOME', 2: 'WORK'}})
+        self.assertDictEqual(ed, {'from': '500', 'to': '990'})
+        self.assertDictEqual(os, {})
+
+        fd, md, en, ed, os = msg_dict(p_out.body[2])
+
+        self.assertDictEqual(fd, {'person': {'id': 1, 'modifier': 'repeated', 'type': 'Person', 'policy': None, 'directives': {}}})
+        self.assertDictEqual(md, {})
+        self.assertDictEqual(en, {})
+        self.assertDictEqual(ed, {'from': '500'})
+        self.assertDictEqual(os, {})
+
+    def test_xos_core(self):
+        '''test chunk of xos core xproto'''
+
+        t_in = """
+option app_label = "core";
+option legacy="True";
+
+// use thi policy to allow access to admins only
+policy admin_policy < ctx.user.is_admin >
+
+message XOSBase {
+     option skip_init = True;
+     option custom_header = "xosbase_header";
+     option abstract = True;
+
+     // field 1 is reserved for "id"
+     required string created = 2 [content_type = "date", auto_now_add = True, help_text = "Time this model was created"];
+     required string updated = 3 [default = "now()", content_type = "date", help_text = "Time this model was changed by a non-synchronizer"];
+     optional string enacted = 4 [null = True, content_type = "date", blank = True, default = None, help_text = "When synced, set to the timestamp of the data that was synced"];
+     optional string policed = 5 [null = True, content_type = "date", blank = True, default = None, help_text = "When policed, set to the timestamp of the data that was policed"];
+     optional string backend_register = 6 [default = "{}", max_length = 1024, feedback_state = True];
+     required bool backend_need_delete = 7 [default = False, blank = True];
+     required bool backend_need_reap = 8 [default = False, blank = True];
+     required string backend_status = 9 [default = "Provisioning in progress", max_length = 1024, null = True, feedback_state = True];
+     required int32 backend_code = 10 [default = 0, feedback_state = True];
+     required bool deleted = 11 [default = False, blank = True];
+     required bool write_protect = 12 [default = False, blank = True];
+     required bool lazy_blocked = 13 [default = False, blank = True];
+     required bool no_sync = 14 [default = False, blank = True];
+     required bool no_policy = 15 [default = False, blank = True];
+     optional string policy_status = 16 [default = "Policy in process", max_length = 1024, feedback_state = True];
+     optional int32 policy_code = 17 [default = 0, feedback_state = True];
+     required string leaf_model_name = 18 [null = False, max_length = 1024, help_text = "The most specialized model in this chain of inheritance, often defined by a service developer"];
+     required bool backend_need_delete_policy = 19 [default = False, help_text = "True if delete model_policy must be run before object can be reaped", blank = True];
+     required bool xos_managed = 20 [default = True, help_text = "True if xos is responsible for creating/deleting this object", blank = True, gui_hidden = True];
+     optional string backend_handle = 21 [max_length = 1024, feedback_state = True, blank=True, null=True, help_text = "Handle used by the backend to track this object", gui_hidden = True];
+     optional string changed_by_step = 22 [null = True, content_type = "date", blank = True, default = None, gui_hidden = True, help_text = "Time this model was changed by a sync step"];
+     optional string changed_by_policy = 23 [null = True, content_type = "date", blank = True, default = None, gui_hidden = True, help_text = "Time this model was changed by a model policy"];
+}
+
+// A user may give a permission that he has to another user
+policy grant_policy < ctx.user.is_admin
+                      | exists Privilege:Privilege.object_type = obj.object_type
+                        & Privilege.object_id = obj.object_id
+                        & Privilege.accessor_type = "User"
+                        & Privilege.accessor_id = ctx.user.id
+                        & Privilege.permission = "role:admin" >
+
+message Privilege::grant_policy (XOSBase) {
+     required int32 accessor_id = 1 [null = False, blank=False];
+     required string accessor_type = 2 [null = False, max_length=1024, blank = False];
+     optional int32 controller_id = 3 [null = True, blank = True];
+     required int32 object_id = 4 [null = False, blank=False];
+     required string object_type = 5 [null = False, max_length=1024, blank = False];
+     required string permission = 6 [null = False, default = "all", max_length=1024, tosca_key=True];
+     required string granted = 7 [content_type = "date", auto_now_add = True, max_length=1024];
+     required string expires = 8 [content_type = "date", null = True, max_length=1024];
+}
+"""
+
+        p_out = self.parser.parse_string(t_in)
+
+        # check options
+        od = options_dict(p_out)
+        self.assertDictEqual(od, {'app_label': '"core"', 'legacy': '"True"'})
+
+        self.assertIsInstance(p_out.body[2], plyxmodel.PolicyDefinition)
+        self.assertEqual(get_name(p_out.body[2]), "admin_policy")
+        self.assertEqual(p_out.body[2].body, 'ctx.user.is_admin')
+
+        fd, md, en, ed, os = msg_dict(p_out.body[3])
+
+        self.assertDictEqual(fd, {'created': {'id': 2, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'content_type': '"date"', 'auto_now_add': 'True', 'help_text': '"Time this model was created"'}}, 'updated': {'id': 3, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'default': '"now()"', 'content_type': '"date"', 'help_text': '"Time this model was changed by a non-synchronizer"'}}, 'enacted': {'id': 4, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {'null': 'True', 'content_type': '"date"', 'blank': 'True', 'default': 'None', 'help_text': '"When synced, set to the timestamp of the data that was synced"'}}, 'policed': {'id': 5, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {'null': 'True', 'content_type': '"date"', 'blank': 'True', 'default': 'None', 'help_text': '"When policed, set to the timestamp of the data that was policed"'}}, 'backend_register': {'id': 6, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {'default': '"{}"', 'max_length': '1024', 'feedback_state': 'True'}}, 'backend_need_delete': {'id': 7, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'blank': 'True'}}, 'backend_need_reap': {'id': 8, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'blank': 'True'}}, 'backend_status': {'id': 9, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'default': '"Provisioning in progress"', 'max_length': '1024', 'null': 'True', 'feedback_state': 'True'}}, 'backend_code': {'id': 10, 'modifier': 'required', 'type': 'int32', 'policy': None, 'directives': {'default': '0', 'feedback_state': 'True'}}, 'deleted': {'id': 11, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'blank': 'True'}}, 'write_protect': {'id': 12, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'blank': 'True'}}, 'lazy_blocked': {'id': 13, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'blank': 'True'}}, 'no_sync': {'id': 14, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'blank': 'True'}}, 'no_policy': {'id': 15, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'blank': 'True'}}, 'policy_status': {'id': 16, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {'default': '"Policy in process"', 'max_length': '1024', 'feedback_state': 'True'}}, 'policy_code': {'id': 17, 'modifier': 'optional', 'type': 'int32', 'policy': None, 'directives': {'default': '0', 'feedback_state': 'True'}}, 'leaf_model_name': {'id': 18, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'null': 'False', 'max_length': '1024', 'help_text': '"The most specialized model in this chain of inheritance, often defined by a service developer"'}}, 'backend_need_delete_policy': {'id': 19, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'False', 'help_text': '"True if delete model_policy must be run before object can be reaped"', 'blank': 'True'}}, 'xos_managed': {'id': 20, 'modifier': 'required', 'type': 'bool', 'policy': None, 'directives': {'default': 'True', 'help_text': '"True if xos is responsible for creating/deleting this object"', 'blank': 'True', 'gui_hidden': 'True'}}, 'backend_handle': {'id': 21, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {'max_length': '1024', 'feedback_state': 'True', 'blank': 'True', 'null': 'True', 'help_text': '"Handle used by the backend to track this object"', 'gui_hidden': 'True'}}, 'changed_by_step': {'id': 22, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {'null': 'True', 'content_type': '"date"', 'blank': 'True', 'default': 'None', 'gui_hidden': 'True', 'help_text': '"Time this model was changed by a sync step"'}}, 'changed_by_policy': {'id': 23, 'modifier': 'optional', 'type': 'string', 'policy': None, 'directives': {'null': 'True', 'content_type': '"date"', 'blank': 'True', 'default': 'None', 'gui_hidden': 'True', 'help_text': '"Time this model was changed by a model policy"'}}})
+
+        self.assertDictEqual(md, {})
+        self.assertDictEqual(en, {})
+        self.assertDictEqual(ed, {})
+        self.assertDictEqual(os, {'skip_init': 'True', 'custom_header': '"xosbase_header"', 'abstract': 'True'})
+
+        self.assertIsInstance(p_out.body[4], plyxmodel.PolicyDefinition)
+
+        self.assertEqual(get_name(p_out.body[4]), "grant_policy")
+        self.assertDictEqual(p_out.body[4].body, {'|':
+            ['ctx.user.is_admin', {'&':
+                [{'&': [{'&': [{'&': [{'exists': ['Privilege', {'=': ('Privilege.object_type', 'obj.object_type')}]},
+                               {'=': ('Privilege.object_id', 'obj.object_id')}]},
+                               {'=': ('Privilege.accessor_type', '"User"')}]},
+                               {'=': ('Privilege.accessor_id', 'ctx.user.id')}]},
+                               {'=': ('Privilege.permission', '"role:admin"')}]}]}
+                )
+        fd, md, en, ed, os = msg_dict(p_out.body[5])
+
+        self.assertDictEqual(fd, {'accessor_id': {'id': 1, 'modifier': 'required', 'type': 'int32', 'policy': None, 'directives': {'null': 'False', 'blank': 'False'}}, 'accessor_type': {'id': 2, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'null': 'False', 'max_length': '1024', 'blank': 'False'}}, 'controller_id': {'id': 3, 'modifier': 'optional', 'type': 'int32', 'policy': None, 'directives': {'null': 'True', 'blank': 'True'}}, 'object_id': {'id': 4, 'modifier': 'required', 'type': 'int32', 'policy': None, 'directives': {'null': 'False', 'blank': 'False'}}, 'object_type': {'id': 5, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'null': 'False', 'max_length': '1024', 'blank': 'False'}}, 'permission': {'id': 6, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'null': 'False', 'default': '"all"', 'max_length': '1024', 'tosca_key': 'True'}}, 'granted': {'id': 7, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'content_type': '"date"', 'auto_now_add': 'True', 'max_length': '1024'}}, 'expires': {'id': 8, 'modifier': 'required', 'type': 'string', 'policy': None, 'directives': {'content_type': '"date"', 'null': 'True', 'max_length': '1024'}}})
+
+        self.assertDictEqual(md, {})
+        self.assertDictEqual(en, {})
+        self.assertDictEqual(ed, {})
+        self.assertDictEqual(os, {})
+
+# FIXME: these sorts of simple validations should fail but currently don't
+#
+#       def test_invalid_message(self):
+#           '''an invalid message'''
+#
+#           t_in = """message BadMessage {
+#     required string name = 0;
+#     required int32 id = -2;
+#   }"""
+#           p_out = self.parser.parse_string(t_in)
+#           print(p_out)
+#
+#
+#       def test_duplicate_id(self):
+#           '''duplicate id's in a message'''
+#
+#           t_in = """message BadMessage2 {
+#     required string name = 1;
+#     required int32 id = 1;
+#   }"""
+#           p_out = self.parser.parse_string(t_in)
+#           print(p_out)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..d10c13f
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,42 @@
+; Copyright 2017-present the Author
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+[tox]
+envlist = py27,py35,py36,py37
+skip_missing_interpreters = true
+
+[testenv]
+deps =
+  nose2
+  ply
+
+commands =
+  nose2 -c tox.ini --verbose --junit-xml
+
+[flake8]
+max-line-length = 119
+
+[unittest]
+plugins=nose2.plugins.junitxml
+
+[junit-xml]
+path=nose2-results.xml
+
+[coverage]
+always-on = True
+coverage =
+  plyxproto
+coverage-report =
+  xml
+  term