`deepcopy` from cache instead of recreating parsers for `GardenSnakeCompiler`
[soc.git] / src / soc / decoder / pseudo / parser.py
index ffb8ec0fcdf1a5a48e2610adecb857230001b2f7..1466f87cf1f63a0526e91910f93534e46716675e 100644 (file)
 from pprint import pprint
 from ply import lex, yacc
 import astor
+from copy import deepcopy
 
 from soc.decoder.power_decoder import create_pdecode
 from soc.decoder.pseudo.lexer import IndentLexer
+from soc.decoder.orderedset import OrderedSet
 
 # I use the Python AST
 #from compiler import ast
 import ast
 
 # Helper function
-def Assign(left, right):
+
+
+def Assign(autoassign, assignname, left, right, iea_mode):
     names = []
-    print ("Assign", left, right)
+    print("Assign", assignname, left, right)
     if isinstance(left, ast.Name):
         # Single assignment on left
         # XXX when doing IntClass, which will have an "eq" function,
@@ -40,25 +44,49 @@ def Assign(left, right):
         ass_list = [ast.AssName(name, 'OP_ASSIGN') for name in names]
         return ast.Assign([ast.AssTuple(ass_list)], right)
     elif isinstance(left, ast.Subscript):
-        return ast.Assign([left], right)
+        ls = left.slice
+        if (isinstance(ls, ast.Slice) and isinstance(right, ast.Name) and
+                right.id == 'undefined'):
+            # undefined needs to be copied the exact same slice
+            right = ast.Subscript(right, ls, ast.Load())
+            return ast.Assign([left], right)
+        res = ast.Assign([left], right)
+        if autoassign and isinstance(ls, ast.Slice):
+            # hack to create a variable pre-declared based on a slice.
+            # dividend[0:32] = (RA)[0:32] will create
+            #       dividend = [0] * 32
+            #       dividend[0:32] = (RA)[0:32]
+            # the declaration makes the slice-assignment "work"
+            lower, upper, step = ls.lower, ls.upper, ls.step
+            print("lower, upper, step", repr(lower), repr(upper), step)
+            if not isinstance(lower, ast.Constant) or \
+               not isinstance(upper, ast.Constant):
+                return res
+            qty = ast.Num(upper.value-lower.value)
+            keywords = [ast.keyword(arg='repeat', value=qty)]
+            l = [ast.Num(0)]
+            right = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
+            declare = ast.Assign([ast.Name(assignname, ast.Store())], right)
+            return [declare, res]
+        return res
         # XXX HMMM probably not needed...
         ls = left.slice
         if isinstance(ls, ast.Slice):
             lower, upper, step = ls.lower, ls.upper, ls.step
-            print ("slice assign", lower, upper, step)
+            print("slice assign", lower, upper, step)
             if step is None:
                 ls = (lower, upper, None)
             else:
                 ls = (lower, upper, step)
             ls = ast.Tuple(ls)
-        return ast.Call(ast.Name("selectassign"),
+        return ast.Call(ast.Name("selectassign", ast.Load()),
                         [left.value, ls, right], [])
     else:
-        print ("Assign fail")
+        print("Assign fail")
         raise SyntaxError("Can't do that yet")
 
 
-## I implemented INDENT / DEDENT generation as a post-processing filter
+# I implemented INDENT / DEDENT generation as a post-processing filter
 
 # The original lex token stream contains WS and NEWLINE characters.
 # WS will only occur before any other tokens on a line.
@@ -70,7 +98,7 @@ def Assign(left, right):
 # see if the new line has changed indication level.
 
 
-## No using Python's approach because Ply supports precedence
+# No using Python's approach because Ply supports precedence
 
 # comparison: expr (comp_op expr)*
 # arith_expr: term (('+'|'-') term)*
@@ -80,78 +108,183 @@ def Assign(left, right):
 
 def make_le_compare(arg):
     (left, right) = arg
-    return ast.Compare(left, [ast.LtE()], [right])
+    return ast.Call(ast.Name("le", ast.Load()), (left, right), [])
+
+
 def make_ge_compare(arg):
     (left, right) = arg
-    return ast.Compare(left, [ast.GtE()], [right])
+    return ast.Call(ast.Name("ge", ast.Load()), (left, right), [])
+
+
 def make_lt_compare(arg):
     (left, right) = arg
-    return ast.Compare(left, [ast.Lt()], [right])
+    return ast.Call(ast.Name("lt", ast.Load()), (left, right), [])
+
+
 def make_gt_compare(arg):
     (left, right) = arg
-    return ast.Compare(left, [ast.Gt()], [right])
+    return ast.Call(ast.Name("gt", ast.Load()), (left, right), [])
+
+
 def make_eq_compare(arg):
     (left, right) = arg
-    return ast.Compare(left, [ast.Eq()], [right])
+    return ast.Call(ast.Name("eq", ast.Load()), (left, right), [])
+
+
+def make_ne_compare(arg):
+    (left, right) = arg
+    return ast.Call(ast.Name("ne", ast.Load()), (left, right), [])
+
 
 binary_ops = {
+    "^": ast.BitXor(),
     "&": ast.BitAnd(),
     "|": ast.BitOr(),
     "+": ast.Add(),
     "-": ast.Sub(),
     "*": ast.Mult(),
-    "/": ast.Div(),
+    "/": ast.FloorDiv(),
+    "%": ast.Mod(),
     "<=": make_le_compare,
     ">=": make_ge_compare,
     "<": make_lt_compare,
     ">": make_gt_compare,
     "=": make_eq_compare,
+    "!=": make_ne_compare,
 }
 unary_ops = {
-    "+": ast.Add,
-    "-": ast.Sub,
-    }
+    "+": ast.UAdd(),
+    "-": ast.USub(),
+    "¬": ast.Invert(),
+}
 
-def check_concat(node): # checks if the comparison is already a concat
-    print (node)
+
+def check_concat(node):  # checks if the comparison is already a concat
+    print("check concat", node)
     if not isinstance(node, ast.Call):
         return [node]
-    print (node.func.id)
+    print("func", node.func.id)
     if node.func.id != 'concat':
         return [node]
-    return node[1]
-
+    if node.keywords:  # a repeated list-constant, don't optimise
+        return [node]
+    return node.args
+
+
+# identify SelectableInt pattern [something] * N
+# must return concat(something, repeat=N)
+def identify_sint_mul_pattern(p):
+    if p[2] != '*':  # multiply
+        return False
+    if not isinstance(p[3], ast.Constant):  # rhs = Num
+        return False
+    if not isinstance(p[1], ast.List):  # lhs is a list
+        return False
+    l = p[1].elts
+    if len(l) != 1:  # lhs is a list of length 1
+        return False
+    return True  # yippee!
+
+
+def apply_trailer(atom, trailer):
+    if trailer[0] == "TLIST":
+        # assume depth of one
+        atom = apply_trailer(atom, trailer[1])
+        trailer = trailer[2]
+    if trailer[0] == "CALL":
+        #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
+        return ast.Call(atom, trailer[1], [])
+        # if p[1].id == 'print':
+        #    p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
+        # else:
+        #    p[0] = ast.CallFunc(p[1], p[2][1], None, None)
+    else:
+        print("subscript atom", trailer[1])
+        #raise AssertionError("not implemented %s" % p[2][0])
+        subs = trailer[1]
+        if len(subs) == 1:
+            idx = subs[0]
+        else:
+            idx = ast.Slice(subs[0], subs[1], None)
+        # if isinstance(atom, ast.Name) and atom.id == 'CR':
+            # atom.id = 'CR' # bad hack
+            #print ("apply_trailer Subscript", atom.id, idx)
+        return ast.Subscript(atom, idx, ast.Load())
 
 ##########   Parser (tokens -> AST) ######
 
 # also part of Ply
 #import yacc
 
+# https://www.mathcs.emory.edu/~valerie/courses/fall10/155/resources/op_precedence.html
+# python operator precedence
+# Highest precedence at top, lowest at bottom.
+# Operators in the same box evaluate left to right.
+#
+# Operator Description
+# ()                                                     Parentheses (grouping)
+# f(args...)                                             Function call
+# x[index:index]                                         Slicing
+# x[index]                                               Subscription
+# x.attribute                                            Attribute reference
+# **                                                     Exponentiation
+# ~x                                                     Bitwise not
+# +x, -x                                                 Positive, negative
+# *, /, %                                                mul, div, remainder
+# +, -                                                   Addition, subtraction
+# <<, >>                                                 Bitwise shifts
+# &                                                      Bitwise AND
+# ^                                                      Bitwise XOR
+# |                                                      Bitwise OR
+# in, not in, is, is not, <, <=,  >,  >=, <>, !=, ==     comp, membership, ident
+# not x                                                  Boolean NOT
+# and                                                    Boolean AND
+# or                                                     Boolean OR
+# lambda                                                 Lambda expression
+
+
 class PowerParser:
 
     precedence = (
-        ("left", "BITOR", "BITAND"),
-        ("left", "EQ", "GT", "LT", "LE", "GE", "LTU", "GTU"),
+        ("left", "EQ", "NE", "GT", "LT", "LE", "GE", "LTU", "GTU"),
+        ("left", "BITOR"),
+        ("left", "BITXOR"),
+        ("left", "BITAND"),
         ("left", "PLUS", "MINUS"),
-        ("left", "MULT", "DIV"),
-        )
+        ("left", "MULT", "DIV", "MOD"),
+        ("left", "INVERT"),
+    )
 
-    def __init__(self):
+    def __init__(self, form, include_carry_in_write=False):
+        self.include_ca_in_write = include_carry_in_write
         self.gprs = {}
+        form = self.sd.sigforms[form]
+        print(form)
+        formkeys = form._asdict().keys()
+        self.declared_vars = set()
         for rname in ['RA', 'RB', 'RC', 'RT', 'RS']:
             self.gprs[rname] = None
-        self.read_regs = []
-        self.uninit_regs = []
-        self.write_regs = []
+            self.declared_vars.add(rname)
+        self.available_op_fields = set()
+        for k in formkeys:
+            if k not in self.gprs:
+                if k == 'SPR':  # sigh, lower-case to not conflict
+                    k = k.lower()
+                self.available_op_fields.add(k)
+        self.op_fields = OrderedSet()
+        self.read_regs = OrderedSet()
+        self.uninit_regs = OrderedSet()
+        self.write_regs = OrderedSet()
+        self.special_regs = OrderedSet()  # see p_atom_name
 
     # The grammar comments come from Python's Grammar/Grammar file
 
-    ## NB: compound_stmt in single_input is followed by extra NEWLINE!
+    # NB: compound_stmt in single_input is followed by extra NEWLINE!
     # file_input: (NEWLINE | stmt)* ENDMARKER
 
     def p_file_input_end(self, p):
         """file_input_end : file_input ENDMARKER"""
-        print ("end", p[1])
+        print("end", p[1])
         p[0] = p[1]
 
     def p_file_input(self, p):
@@ -163,16 +296,16 @@ class PowerParser:
             if len(p) == 3:
                 p[0] = p[1]
             else:
-                p[0] = [] # p == 2 --> only a blank line
+                p[0] = []  # p == 2 --> only a blank line
         else:
             if len(p) == 3:
                 p[0] = p[1] + p[2]
             else:
                 p[0] = p[1]
 
-
     # funcdef: [decorators] 'def' NAME parameters ':' suite
     # ignoring decorators
+
     def p_funcdef(self, p):
         "funcdef : DEF NAME parameters COLON suite"
         p[0] = ast.FunctionDef(p[2], p[3], p[5], ())
@@ -182,15 +315,15 @@ class PowerParser:
         """parameters : LPAR RPAR
                       | LPAR varargslist RPAR"""
         if len(p) == 3:
-            args=[]
+            args = []
         else:
             args = p[2]
         p[0] = ast.arguments(args=args, vararg=None, kwarg=None, defaults=[])
 
-
     # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
     # '**' NAME) |
     # highly simplified
+
     def p_varargslist(self, p):
         """varargslist : varargslist COMMA NAME
                        | NAME"""
@@ -220,6 +353,8 @@ class PowerParser:
                        | small_stmt"""
         if len(p) == 4:
             p[0] = p[1] + [p[3]]
+        elif isinstance(p[1], list):
+            p[0] = p[1]
         else:
             p[0] = [p[1]]
 
@@ -231,6 +366,11 @@ class PowerParser:
                       | expr_stmt"""
         if isinstance(p[1], ast.Call):
             p[0] = ast.Expr(p[1])
+        elif isinstance(p[1], ast.Name) and p[1].id == 'TRAP':
+            # TRAP needs to actually be a function
+            name = ast.Name("self", ast.Load())
+            name = ast.Attribute(name, "TRAP", ast.Load())
+            p[0] = ast.Call(name, [], [])
         else:
             p[0] = p[1]
 
@@ -239,24 +379,51 @@ class PowerParser:
     # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
     #             '<<=' | '>>=' | '**=' | '//=')
     def p_expr_stmt(self, p):
-        """expr_stmt : testlist ASSIGN testlist
+        """expr_stmt : testlist ASSIGNEA testlist
+                     | testlist ASSIGN testlist
                      | testlist """
-        print ("expr_stmt", p)
+        print("expr_stmt", p)
         if len(p) == 2:
             # a list of expressions
             #p[0] = ast.Discard(p[1])
             p[0] = p[1]
         else:
+            iea_mode = p[2] == '<-iea'
+            name = None
+            autoassign = False
             if isinstance(p[1], ast.Name):
                 name = p[1].id
             elif isinstance(p[1], ast.Subscript):
-                name = p[1].value.id
-                if name in self.gprs:
-                    self.uninit_regs.append(name) # add to list of uninitialised
-            print ("expr assign", name, p[1])
-            if name in self.gprs:
-                self.write_regs.append(name) # add to list of regs to write
-            p[0] = Assign(p[1], p[3])
+                if isinstance(p[1].value, ast.Name):
+                    name = p[1].value.id
+                    if name in self.gprs:
+                        # add to list of uninitialised
+                        self.uninit_regs.add(name)
+                    autoassign = (name not in self.declared_vars and
+                                  name not in self.special_regs)
+            elif isinstance(p[1], ast.Call) and p[1].func.id in ['GPR', 'SPR']:
+                print(astor.dump_tree(p[1]))
+                # replace GPR(x) with GPR[x]
+                idx = p[1].args[0]
+                p[1] = ast.Subscript(p[1].func, idx, ast.Load())
+            elif isinstance(p[1], ast.Call) and p[1].func.id == 'MEM':
+                print("mem assign")
+                print(astor.dump_tree(p[1]))
+                p[1].func.id = "memassign"  # change function name to set
+                p[1].args.append(p[3])
+                p[0] = p[1]
+                print("mem rewrite")
+                print(astor.dump_tree(p[0]))
+                return
+            else:
+                print("help, help")
+                print(astor.dump_tree(p[1]))
+            print("expr assign", name, p[1])
+            if name and name in self.gprs:
+                self.write_regs.add(name)  # add to list of regs to write
+            p[0] = Assign(autoassign, name, p[1], p[3], iea_mode)
+            if name:
+                self.declared_vars.add(name)
 
     def p_flow_stmt(self, p):
         "flow_stmt : return_stmt"
@@ -267,10 +434,10 @@ class PowerParser:
         "return_stmt : RETURN testlist"
         p[0] = ast.Return(p[2])
 
-
     def p_compound_stmt(self, p):
         """compound_stmt : if_stmt
                          | while_stmt
+                         | switch_stmt
                          | for_stmt
                          | funcdef
         """
@@ -282,13 +449,20 @@ class PowerParser:
         p[0] = ast.Break()
 
     def p_for_stmt(self, p):
-        """for_stmt : FOR test EQ test TO test COLON suite
+        """for_stmt : FOR atom EQ test TO test COLON suite
+                    | DO atom EQ test TO test COLON suite
         """
-        p[0] = ast.While(p[2], p[4], [])
-        # auto-add-one (sigh) due to python range
         start = p[4]
-        end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
-        it = ast.Call(ast.Name("range"), [start, end], [])
+        end = p[6]
+        if start.value > end.value:  # start greater than end, must go -ve
+            # auto-subtract-one (sigh) due to python range
+            end = ast.BinOp(p[6], ast.Add(), ast.Constant(-1))
+            arange = [start, end, ast.Constant(-1)]
+        else:
+            # auto-add-one (sigh) due to python range
+            end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
+            arange = [start, end]
+        it = ast.Call(ast.Name("range", ast.Load()), arange, [])
         p[0] = ast.For(p[2], it, p[8], [])
 
     def p_while_stmt(self, p):
@@ -300,6 +474,93 @@ class PowerParser:
         else:
             p[0] = ast.While(p[3], p[5], p[8])
 
+    def p_switch_smt(self, p):
+        """switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT switches DEDENT
+        """
+        switchon = p[3]
+        print("switch stmt")
+        print(astor.dump_tree(p[1]))
+
+        cases = []
+        current_cases = []  # for deferral
+        for (case, suite) in p[8]:
+            print("for", case, suite)
+            if suite is None:
+                for c in case:
+                    current_cases.append(ast.Num(c))
+                continue
+            if case == 'default':  # last
+                break
+            for c in case:
+                current_cases.append(ast.Num(c))
+            print("cases", current_cases)
+            compare = ast.Compare(switchon, [ast.In()],
+                                  [ast.List(current_cases, ast.Load())])
+            current_cases = []
+            cases.append((compare, suite))
+
+        print("ended", case, current_cases)
+        if case == 'default':
+            if current_cases:
+                compare = ast.Compare(switchon, [ast.In()],
+                                      [ast.List(current_cases, ast.Load())])
+                cases.append((compare, suite))
+            cases.append((None, suite))
+
+        cases.reverse()
+        res = []
+        for compare, suite in cases:
+            print("after rev", compare, suite)
+            if compare is None:
+                assert len(res) == 0, "last case should be default"
+                res = suite
+            else:
+                if not isinstance(res, list):
+                    res = [res]
+                res = ast.If(compare, suite, res)
+        p[0] = res
+
+    def p_switches(self, p):
+        """switches : switch_list switch_default
+                    | switch_default
+        """
+        if len(p) == 3:
+            p[0] = p[1] + [p[2]]
+        else:
+            p[0] = [p[1]]
+
+    def p_switch_list(self, p):
+        """switch_list : switch_case switch_list
+                       | switch_case
+        """
+        if len(p) == 3:
+            p[0] = [p[1]] + p[2]
+        else:
+            p[0] = [p[1]]
+
+    def p_switch_case(self, p):
+        """switch_case : CASE LPAR atomlist RPAR COLON suite
+        """
+        # XXX bad hack
+        if isinstance(p[6][0], ast.Name) and p[6][0].id == 'fallthrough':
+            p[6] = None
+        p[0] = (p[3], p[6])
+
+    def p_switch_default(self, p):
+        """switch_default : DEFAULT COLON suite
+        """
+        p[0] = ('default', p[3])
+
+    def p_atomlist(self, p):
+        """atomlist : atom COMMA atomlist
+                    | atom
+        """
+        assert isinstance(p[1], ast.Constant), "case must be numbers"
+        if len(p) == 4:
+            p[0] = [p[1].value] + p[3]
+        else:
+            p[0] = [p[1].value]
+
     def p_if_stmt(self, p):
         """if_stmt : IF test COLON suite ELSE COLON if_stmt
                    | IF test COLON suite ELSE COLON suite
@@ -320,7 +581,6 @@ class PowerParser:
         else:
             p[0] = p[3]
 
-
     def p_stmts(self, p):
         """stmts : stmts stmt
                  | stmt"""
@@ -334,7 +594,9 @@ class PowerParser:
                       | comparison MINUS comparison
                       | comparison MULT comparison
                       | comparison DIV comparison
+                      | comparison MOD comparison
                       | comparison EQ comparison
+                      | comparison NE comparison
                       | comparison LE comparison
                       | comparison GE comparison
                       | comparison LTU comparison
@@ -342,67 +604,98 @@ class PowerParser:
                       | comparison LT comparison
                       | comparison GT comparison
                       | comparison BITOR comparison
+                      | comparison BITXOR comparison
                       | comparison BITAND comparison
                       | PLUS comparison
-                      | MINUS comparison
+                      | comparison MINUS
+                      | INVERT comparison
                       | comparison APPEND comparison
                       | power"""
         if len(p) == 4:
-            print (list(p))
+            print(list(p))
             if p[2] == '<u':
-                p[0] = ast.Call(ast.Name("ltu"), (p[1], p[3]), [])
+                p[0] = ast.Call(ast.Name("ltu", ast.Load()), (p[1], p[3]), [])
             elif p[2] == '>u':
-                p[0] = ast.Call(ast.Name("gtu"), (p[1], p[3]), [])
+                p[0] = ast.Call(ast.Name("gtu", ast.Load()), (p[1], p[3]), [])
             elif p[2] == '||':
                 l = check_concat(p[1]) + check_concat(p[3])
-                p[0] = ast.Call(ast.Name("concat"), l, [])
-            elif p[2] in ['<', '>', '=', '<=', '>=']:
-                p[0] = binary_ops[p[2]]((p[1],p[3]))
+                p[0] = ast.Call(ast.Name("concat", ast.Load()), l, [])
+            elif p[2] in ['/', '%']:
+                # bad hack: if % or / used anywhere other than div/mod ops,
+                # do % or /.  however if the argument names are "dividend"
+                # we must call the special trunc_divs and trunc_rems functions
+                l, r = p[1], p[3]
+                # actual call will be "dividend / divisor" - just check
+                # LHS name
+                # XXX DISABLE BAD HACK (False)
+                if False and isinstance(l, ast.Name) and l.id == 'dividend':
+                    if p[2] == '/':
+                        fn = 'trunc_divs'
+                    else:
+                        fn = 'trunc_rems'
+                    # return "function trunc_xxx(l, r)"
+                    p[0] = ast.Call(ast.Name(fn, ast.Load()), (l, r), [])
+                else:
+                    # return "l {binop} r"
+                    p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
+            elif p[2] in ['<', '>', '=', '<=', '>=', '!=']:
+                p[0] = binary_ops[p[2]]((p[1], p[3]))
+            elif identify_sint_mul_pattern(p):
+                keywords = [ast.keyword(arg='repeat', value=p[3])]
+                l = p[1].elts
+                p[0] = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
             else:
                 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
         elif len(p) == 3:
-            p[0] = unary_ops[p[1]](p[2])
+            if isinstance(p[2], str) and p[2] == '-':
+                p[0] = ast.UnaryOp(unary_ops[p[2]], p[1])
+            else:
+                p[0] = ast.UnaryOp(unary_ops[p[1]], p[2])
         else:
             p[0] = p[1]
 
     # power: atom trailer* ['**' factor]
     # trailers enables function calls (and subscripts).
-    # I only allow one level of calls
-    # so this is 'trailer'
+    # so this is 'trailerlist'
     def p_power(self, p):
         """power : atom
-                 | atom trailer"""
+                 | atom trailerlist"""
         if len(p) == 2:
             p[0] = p[1]
         else:
-            if p[2][0] == "CALL":
-                #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
-                p[0] = ast.Call(p[1], p[2][1], [])
-                #if p[1].id == 'print':
-                #    p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
-                #else:
-                #    p[0] = ast.CallFunc(p[1], p[2][1], None, None)
-            else:
-                print ("subscript atom", p[2][1])
-                #raise AssertionError("not implemented %s" % p[2][0])
-                subs = p[2][1]
-                if len(subs) == 1:
-                    idx = subs[0]
-                else:
-                    idx = ast.Slice(subs[0], subs[1], None)
-                p[0] = ast.Subscript(p[1], idx)
+            print("power dump atom")
+            print(astor.dump_tree(p[1]))
+            print("power dump trailerlist")
+            print(astor.dump_tree(p[2]))
+            p[0] = apply_trailer(p[1], p[2])
+            if isinstance(p[1], ast.Name):
+                name = p[1].id
+                if name in ['RA', 'RS', 'RB', 'RC']:
+                    self.read_regs.add(name)
 
     def p_atom_name(self, p):
         """atom : NAME"""
-        p[0] = ast.Name(p[1], ctx=ast.Load())
+        name = p[1]
+        if name in self.available_op_fields:
+            self.op_fields.add(name)
+        if name == 'overflow':
+            self.write_regs.add(name)
+        if self.include_ca_in_write:
+            if name in ['CA', 'CA32']:
+                self.write_regs.add(name)
+        if name in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR']:
+            self.special_regs.add(name)
+            self.write_regs.add(name)  # and add to list to write
+        p[0] = ast.Name(id=name, ctx=ast.Load())
 
     def p_atom_number(self, p):
         """atom : BINARY
                 | NUMBER
+                | HEX
                 | STRING"""
         p[0] = ast.Constant(p[1])
 
-    #'[' [listmaker] ']' |
+    # '[' [listmaker] ']' |
 
     def p_atom_listmaker(self, p):
         """atom : LBRACK listmaker RBRACK"""
@@ -413,20 +706,52 @@ class PowerParser:
                      | test
         """
         if len(p) == 2:
-            p[0] = ast.List([p[1]])
+            p[0] = ast.List([p[1]], ast.Load())
         else:
-            p[0] = ast.List([p[1]] + p[3].nodes)
+            p[0] = ast.List([p[1]] + p[3].nodes, ast.Load())
 
     def p_atom_tuple(self, p):
         """atom : LPAR testlist RPAR"""
-        print ("tuple", p[2])
+        print("tuple", p[2])
+        print("astor dump")
+        print(astor.dump_tree(p[2]))
+
         if isinstance(p[2], ast.Name):
-            print ("tuple name", p[2].id)
-            if p[2].id in self.gprs:
-                self.read_regs.append(p[2].id) # add to list of regs to read
-                #p[0] = ast.Subscript(ast.Name("GPR"), ast.Str(p[2].id))
-                #return
-        p[0] = p[2]
+            name = p[2].id
+            print("tuple name", name)
+            if name in self.gprs:
+                self.read_regs.add(name)  # add to list of regs to read
+                #p[0] = ast.Subscript(ast.Name("GPR", ast.Load()), ast.Str(p[2].id))
+                # return
+            p[0] = p[2]
+        elif isinstance(p[2], ast.BinOp):
+            if isinstance(p[2].left, ast.Name) and \
+               isinstance(p[2].right, ast.Constant) and \
+                    p[2].right.value == 0 and \
+                    p[2].left.id in self.gprs:
+                rid = p[2].left.id
+                self.read_regs.add(rid)  # add to list of regs to read
+                # create special call to GPR.getz
+                gprz = ast.Name("GPR", ast.Load())
+                # get testzero function
+                gprz = ast.Attribute(gprz, "getz", ast.Load())
+                # *sigh* see class GPR.  we need index itself not reg value
+                ridx = ast.Name("_%s" % rid, ast.Load())
+                p[0] = ast.Call(gprz, [ridx], [])
+                print("tree", astor.dump_tree(p[0]))
+            else:
+                p[0] = p[2]
+        else:
+            p[0] = p[2]
+
+    def p_trailerlist(self, p):
+        """trailerlist : trailer trailerlist
+                       | trailer
+        """
+        if len(p) == 2:
+            p[0] = p[1]
+        else:
+            p[0] = ("TLIST", p[1], p[2])
 
     # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
     def p_trailer(self, p):
@@ -443,7 +768,7 @@ class PowerParser:
         "trailer_subscript : LBRACK subscript RBRACK"
         p[0] = ("SUBS", p[2])
 
-    #subscript: '.' '.' '.' | test | [test] ':' [test]
+    # subscript: '.' '.' '.' | test | [test] ':' [test]
 
     def p_subscript(self, p):
         """subscript : test COLON test
@@ -459,9 +784,9 @@ class PowerParser:
         else:
             p[0] = [p[1]]
 
-
     # testlist: test (',' test)* [',']
     # Contains shift/reduce error
+
     def p_testlist(self, p):
         """testlist : testlist_multi COMMA
                     | testlist_multi """
@@ -490,18 +815,17 @@ class PowerParser:
                 # singleton -> tuple
                 p[0] = [p[1], p[3]]
 
-
     # test: or_test ['if' or_test 'else' test] | lambdef
     #  as I don't support 'and', 'or', and 'not' this works down to 'comparison'
+
     def p_test(self, p):
         "test : comparison"
         p[0] = p[1]
 
-
-
     # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
     # | '**' test)
     # XXX INCOMPLETE: this doesn't allow the trailing comma
+
     def p_arglist(self, p):
         """arglist : arglist COMMA argument
                    | argument"""
@@ -516,25 +840,25 @@ class PowerParser:
         p[0] = p[1]
 
     def p_error(self, p):
-        #print "Error!", repr(p)
+        # print "Error!", repr(p)
         raise SyntaxError(p)
 
 
 class GardenSnakeParser(PowerParser):
-    def __init__(self, lexer = None):
-        PowerParser.__init__(self)
+    def __init__(self, lexer=None, debug=False, form=None, incl_carry=False):
+        self.sd = create_pdecode()
+        PowerParser.__init__(self, form, incl_carry)
+        self.debug = debug
         if lexer is None:
             lexer = IndentLexer(debug=0)
         self.lexer = lexer
         self.tokens = lexer.tokens
         self.parser = yacc.yacc(module=self, start="file_input_end",
-                                debug=False, write_tables=False)
-
-        self.sd = create_pdecode()
+                                debug=debug, write_tables=False)
 
     def parse(self, code):
-        #self.lexer.input(code)
-        result = self.parser.parse(code, lexer=self.lexer, debug=False)
+        # self.lexer.input(code)
+        result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
         return ast.Module(result)
 
 
@@ -542,19 +866,33 @@ class GardenSnakeParser(PowerParser):
 
 #from compiler import misc, syntax, pycodegen
 
+_CACHED_PARSERS = {}
+_CACHE_PARSERS = True
+
+
 class GardenSnakeCompiler(object):
-    def __init__(self):
-        self.parser = GardenSnakeParser()
+    def __init__(self, debug=False, form=None, incl_carry=False):
+        if _CACHE_PARSERS:
+            try:
+                parser = _CACHED_PARSERS[debug, form, incl_carry]
+            except KeyError:
+                parser = GardenSnakeParser(debug=debug, form=form,
+                                           incl_carry=incl_carry)
+                _CACHED_PARSERS[debug, form, incl_carry] = parser
+
+            self.parser = deepcopy(parser)
+        else:
+            self.parser = GardenSnakeParser(debug=debug, form=form,
+                                            incl_carry=incl_carry)
 
     def compile(self, code, mode="exec", filename="<string>"):
         tree = self.parser.parse(code)
-        print ("snake")
+        print("snake")
         pprint(tree)
         return tree
         #misc.set_filename(filename, tree)
         return compile(tree, mode="exec", filename="<string>")
-        #syntax.check(tree)
+        # syntax.check(tree)
         gen = pycodegen.ModuleCodeGenerator(tree)
         code = gen.getCode()
         return code
-