format code
[soc.git] / src / soc / decoder / pseudo / parser.py
1 # Based on GardenSnake - a parser generator demonstration program
2 # GardenSnake was released into the Public Domain by Andrew Dalke.
3
4 # Portions of this work are derived from Python's Grammar definition
5 # and may be covered under the Python copyright and license
6 #
7 # Andrew Dalke / Dalke Scientific Software, LLC
8 # 30 August 2006 / Cape Town, South Africa
9
10 # Modifications for inclusion in PLY distribution
11 from pprint import pprint
12 from ply import lex, yacc
13 import astor
14
15 from soc.decoder.power_decoder import create_pdecode
16 from soc.decoder.pseudo.lexer import IndentLexer
17 from soc.decoder.orderedset import OrderedSet
18
19 # I use the Python AST
20 #from compiler import ast
21 import ast
22
23 # Helper function
24
25
26 def Assign(autoassign, assignname, left, right, iea_mode):
27 names = []
28 print("Assign", assignname, left, right)
29 if isinstance(left, ast.Name):
30 # Single assignment on left
31 # XXX when doing IntClass, which will have an "eq" function,
32 # this is how to access it
33 # eq = ast.Attribute(left, "eq") # get eq fn
34 # return ast.Call(eq, [right], []) # now call left.eq(right)
35 return ast.Assign([ast.Name(left.id, ast.Store())], right)
36 elif isinstance(left, ast.Tuple):
37 # List of things - make sure they are Name nodes
38 names = []
39 for child in left.getChildren():
40 if not isinstance(child, ast.Name):
41 raise SyntaxError("that assignment not supported")
42 names.append(child.name)
43 ass_list = [ast.AssName(name, 'OP_ASSIGN') for name in names]
44 return ast.Assign([ast.AssTuple(ass_list)], right)
45 elif isinstance(left, ast.Subscript):
46 ls = left.slice
47 if (isinstance(ls, ast.Slice) and isinstance(right, ast.Name) and
48 right.id == 'undefined'):
49 # undefined needs to be copied the exact same slice
50 right = ast.Subscript(right, ls, ast.Load())
51 return ast.Assign([left], right)
52 res = ast.Assign([left], right)
53 if autoassign and isinstance(ls, ast.Slice):
54 # hack to create a variable pre-declared based on a slice.
55 # dividend[0:32] = (RA)[0:32] will create
56 # dividend = [0] * 32
57 # dividend[0:32] = (RA)[0:32]
58 # the declaration makes the slice-assignment "work"
59 lower, upper, step = ls.lower, ls.upper, ls.step
60 print("lower, upper, step", repr(lower), repr(upper), step)
61 if not isinstance(lower, ast.Constant) or \
62 not isinstance(upper, ast.Constant):
63 return res
64 qty = ast.Num(upper.value-lower.value)
65 keywords = [ast.keyword(arg='repeat', value=qty)]
66 l = [ast.Num(0)]
67 right = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
68 declare = ast.Assign([ast.Name(assignname, ast.Store())], right)
69 return [declare, res]
70 return res
71 # XXX HMMM probably not needed...
72 ls = left.slice
73 if isinstance(ls, ast.Slice):
74 lower, upper, step = ls.lower, ls.upper, ls.step
75 print("slice assign", lower, upper, step)
76 if step is None:
77 ls = (lower, upper, None)
78 else:
79 ls = (lower, upper, step)
80 ls = ast.Tuple(ls)
81 return ast.Call(ast.Name("selectassign", ast.Load()),
82 [left.value, ls, right], [])
83 else:
84 print("Assign fail")
85 raise SyntaxError("Can't do that yet")
86
87
88 # I implemented INDENT / DEDENT generation as a post-processing filter
89
90 # The original lex token stream contains WS and NEWLINE characters.
91 # WS will only occur before any other tokens on a line.
92
93 # I have three filters. One tags tokens by adding two attributes.
94 # "must_indent" is True if the token must be indented from the
95 # previous code. The other is "at_line_start" which is True for WS
96 # and the first non-WS/non-NEWLINE on a line. It flags the check so
97 # see if the new line has changed indication level.
98
99
100 # No using Python's approach because Ply supports precedence
101
102 # comparison: expr (comp_op expr)*
103 # arith_expr: term (('+'|'-') term)*
104 # term: factor (('*'|'/'|'%'|'//') factor)*
105 # factor: ('+'|'-'|'~') factor | power
106 # comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
107
108 def make_le_compare(arg):
109 (left, right) = arg
110 return ast.Call(ast.Name("le", ast.Load()), (left, right), [])
111
112
113 def make_ge_compare(arg):
114 (left, right) = arg
115 return ast.Call(ast.Name("ge", ast.Load()), (left, right), [])
116
117
118 def make_lt_compare(arg):
119 (left, right) = arg
120 return ast.Call(ast.Name("lt", ast.Load()), (left, right), [])
121
122
123 def make_gt_compare(arg):
124 (left, right) = arg
125 return ast.Call(ast.Name("gt", ast.Load()), (left, right), [])
126
127
128 def make_eq_compare(arg):
129 (left, right) = arg
130 return ast.Call(ast.Name("eq", ast.Load()), (left, right), [])
131
132
133 def make_ne_compare(arg):
134 (left, right) = arg
135 return ast.Call(ast.Name("ne", ast.Load()), (left, right), [])
136
137
138 binary_ops = {
139 "^": ast.BitXor(),
140 "&": ast.BitAnd(),
141 "|": ast.BitOr(),
142 "+": ast.Add(),
143 "-": ast.Sub(),
144 "*": ast.Mult(),
145 "/": ast.FloorDiv(),
146 "%": ast.Mod(),
147 "<=": make_le_compare,
148 ">=": make_ge_compare,
149 "<": make_lt_compare,
150 ">": make_gt_compare,
151 "=": make_eq_compare,
152 "!=": make_ne_compare,
153 }
154 unary_ops = {
155 "+": ast.UAdd(),
156 "-": ast.USub(),
157 "¬": ast.Invert(),
158 }
159
160
161 def check_concat(node): # checks if the comparison is already a concat
162 print("check concat", node)
163 if not isinstance(node, ast.Call):
164 return [node]
165 print("func", node.func.id)
166 if node.func.id != 'concat':
167 return [node]
168 if node.keywords: # a repeated list-constant, don't optimise
169 return [node]
170 return node.args
171
172
173 # identify SelectableInt pattern [something] * N
174 # must return concat(something, repeat=N)
175 def identify_sint_mul_pattern(p):
176 if p[2] != '*': # multiply
177 return False
178 if not isinstance(p[3], ast.Constant): # rhs = Num
179 return False
180 if not isinstance(p[1], ast.List): # lhs is a list
181 return False
182 l = p[1].elts
183 if len(l) != 1: # lhs is a list of length 1
184 return False
185 return True # yippee!
186
187
188 def apply_trailer(atom, trailer):
189 if trailer[0] == "TLIST":
190 # assume depth of one
191 atom = apply_trailer(atom, trailer[1])
192 trailer = trailer[2]
193 if trailer[0] == "CALL":
194 #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
195 return ast.Call(atom, trailer[1], [])
196 # if p[1].id == 'print':
197 # p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
198 # else:
199 # p[0] = ast.CallFunc(p[1], p[2][1], None, None)
200 else:
201 print("subscript atom", trailer[1])
202 #raise AssertionError("not implemented %s" % p[2][0])
203 subs = trailer[1]
204 if len(subs) == 1:
205 idx = subs[0]
206 else:
207 idx = ast.Slice(subs[0], subs[1], None)
208 # if isinstance(atom, ast.Name) and atom.id == 'CR':
209 # atom.id = 'CR' # bad hack
210 #print ("apply_trailer Subscript", atom.id, idx)
211 return ast.Subscript(atom, idx, ast.Load())
212
213 ########## Parser (tokens -> AST) ######
214
215 # also part of Ply
216 #import yacc
217
218 # https://www.mathcs.emory.edu/~valerie/courses/fall10/155/resources/op_precedence.html
219 # python operator precedence
220 # Highest precedence at top, lowest at bottom.
221 # Operators in the same box evaluate left to right.
222 #
223 # Operator Description
224 # () Parentheses (grouping)
225 # f(args...) Function call
226 # x[index:index] Slicing
227 # x[index] Subscription
228 # x.attribute Attribute reference
229 # ** Exponentiation
230 # ~x Bitwise not
231 # +x, -x Positive, negative
232 # *, /, % mul, div, remainder
233 # +, - Addition, subtraction
234 # <<, >> Bitwise shifts
235 # & Bitwise AND
236 # ^ Bitwise XOR
237 # | Bitwise OR
238 # in, not in, is, is not, <, <=, >, >=, <>, !=, == comp, membership, ident
239 # not x Boolean NOT
240 # and Boolean AND
241 # or Boolean OR
242 # lambda Lambda expression
243
244
245 class PowerParser:
246
247 precedence = (
248 ("left", "EQ", "NE", "GT", "LT", "LE", "GE", "LTU", "GTU"),
249 ("left", "BITOR"),
250 ("left", "BITXOR"),
251 ("left", "BITAND"),
252 ("left", "PLUS", "MINUS"),
253 ("left", "MULT", "DIV", "MOD"),
254 ("left", "INVERT"),
255 )
256
257 def __init__(self, form, include_carry_in_write=False):
258 self.include_ca_in_write = include_carry_in_write
259 self.gprs = {}
260 form = self.sd.sigforms[form]
261 print(form)
262 formkeys = form._asdict().keys()
263 self.declared_vars = set()
264 for rname in ['RA', 'RB', 'RC', 'RT', 'RS']:
265 self.gprs[rname] = None
266 self.declared_vars.add(rname)
267 self.available_op_fields = set()
268 for k in formkeys:
269 if k not in self.gprs:
270 if k == 'SPR': # sigh, lower-case to not conflict
271 k = k.lower()
272 self.available_op_fields.add(k)
273 self.op_fields = OrderedSet()
274 self.read_regs = OrderedSet()
275 self.uninit_regs = OrderedSet()
276 self.write_regs = OrderedSet()
277 self.special_regs = OrderedSet() # see p_atom_name
278
279 # The grammar comments come from Python's Grammar/Grammar file
280
281 # NB: compound_stmt in single_input is followed by extra NEWLINE!
282 # file_input: (NEWLINE | stmt)* ENDMARKER
283
284 def p_file_input_end(self, p):
285 """file_input_end : file_input ENDMARKER"""
286 print("end", p[1])
287 p[0] = p[1]
288
289 def p_file_input(self, p):
290 """file_input : file_input NEWLINE
291 | file_input stmt
292 | NEWLINE
293 | stmt"""
294 if isinstance(p[len(p)-1], str):
295 if len(p) == 3:
296 p[0] = p[1]
297 else:
298 p[0] = [] # p == 2 --> only a blank line
299 else:
300 if len(p) == 3:
301 p[0] = p[1] + p[2]
302 else:
303 p[0] = p[1]
304
305 # funcdef: [decorators] 'def' NAME parameters ':' suite
306 # ignoring decorators
307
308 def p_funcdef(self, p):
309 "funcdef : DEF NAME parameters COLON suite"
310 p[0] = ast.FunctionDef(p[2], p[3], p[5], ())
311
312 # parameters: '(' [varargslist] ')'
313 def p_parameters(self, p):
314 """parameters : LPAR RPAR
315 | LPAR varargslist RPAR"""
316 if len(p) == 3:
317 args = []
318 else:
319 args = p[2]
320 p[0] = ast.arguments(args=args, vararg=None, kwarg=None, defaults=[])
321
322 # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
323 # '**' NAME) |
324 # highly simplified
325
326 def p_varargslist(self, p):
327 """varargslist : varargslist COMMA NAME
328 | NAME"""
329 if len(p) == 4:
330 p[0] = p[1] + p[3]
331 else:
332 p[0] = [p[1]]
333
334 # stmt: simple_stmt | compound_stmt
335 def p_stmt_simple(self, p):
336 """stmt : simple_stmt"""
337 # simple_stmt is a list
338 p[0] = p[1]
339
340 def p_stmt_compound(self, p):
341 """stmt : compound_stmt"""
342 p[0] = [p[1]]
343
344 # simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
345 def p_simple_stmt(self, p):
346 """simple_stmt : small_stmts NEWLINE
347 | small_stmts SEMICOLON NEWLINE"""
348 p[0] = p[1]
349
350 def p_small_stmts(self, p):
351 """small_stmts : small_stmts SEMICOLON small_stmt
352 | small_stmt"""
353 if len(p) == 4:
354 p[0] = p[1] + [p[3]]
355 elif isinstance(p[1], list):
356 p[0] = p[1]
357 else:
358 p[0] = [p[1]]
359
360 # small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
361 # import_stmt | global_stmt | exec_stmt | assert_stmt
362 def p_small_stmt(self, p):
363 """small_stmt : flow_stmt
364 | break_stmt
365 | expr_stmt"""
366 if isinstance(p[1], ast.Call):
367 p[0] = ast.Expr(p[1])
368 elif isinstance(p[1], ast.Name) and p[1].id == 'TRAP':
369 # TRAP needs to actually be a function
370 name = ast.Name("self", ast.Load())
371 name = ast.Attribute(name, "TRAP", ast.Load())
372 p[0] = ast.Call(name, [], [])
373 else:
374 p[0] = p[1]
375
376 # expr_stmt: testlist (augassign (yield_expr|testlist) |
377 # ('=' (yield_expr|testlist))*)
378 # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
379 # '<<=' | '>>=' | '**=' | '//=')
380 def p_expr_stmt(self, p):
381 """expr_stmt : testlist ASSIGNEA testlist
382 | testlist ASSIGN testlist
383 | testlist """
384 print("expr_stmt", p)
385 if len(p) == 2:
386 # a list of expressions
387 #p[0] = ast.Discard(p[1])
388 p[0] = p[1]
389 else:
390 iea_mode = p[2] == '<-iea'
391 name = None
392 autoassign = False
393 if isinstance(p[1], ast.Name):
394 name = p[1].id
395 elif isinstance(p[1], ast.Subscript):
396 if isinstance(p[1].value, ast.Name):
397 name = p[1].value.id
398 if name in self.gprs:
399 # add to list of uninitialised
400 self.uninit_regs.add(name)
401 autoassign = (name not in self.declared_vars and
402 name not in self.special_regs)
403 elif isinstance(p[1], ast.Call) and p[1].func.id in ['GPR', 'SPR']:
404 print(astor.dump_tree(p[1]))
405 # replace GPR(x) with GPR[x]
406 idx = p[1].args[0]
407 p[1] = ast.Subscript(p[1].func, idx, ast.Load())
408 elif isinstance(p[1], ast.Call) and p[1].func.id == 'MEM':
409 print("mem assign")
410 print(astor.dump_tree(p[1]))
411 p[1].func.id = "memassign" # change function name to set
412 p[1].args.append(p[3])
413 p[0] = p[1]
414 print("mem rewrite")
415 print(astor.dump_tree(p[0]))
416 return
417 else:
418 print("help, help")
419 print(astor.dump_tree(p[1]))
420 print("expr assign", name, p[1])
421 if name and name in self.gprs:
422 self.write_regs.add(name) # add to list of regs to write
423 p[0] = Assign(autoassign, name, p[1], p[3], iea_mode)
424 if name:
425 self.declared_vars.add(name)
426
427 def p_flow_stmt(self, p):
428 "flow_stmt : return_stmt"
429 p[0] = p[1]
430
431 # return_stmt: 'return' [testlist]
432 def p_return_stmt(self, p):
433 "return_stmt : RETURN testlist"
434 p[0] = ast.Return(p[2])
435
436 def p_compound_stmt(self, p):
437 """compound_stmt : if_stmt
438 | while_stmt
439 | switch_stmt
440 | for_stmt
441 | funcdef
442 """
443 p[0] = p[1]
444
445 def p_break_stmt(self, p):
446 """break_stmt : BREAK
447 """
448 p[0] = ast.Break()
449
450 def p_for_stmt(self, p):
451 """for_stmt : FOR atom EQ test TO test COLON suite
452 | DO atom EQ test TO test COLON suite
453 """
454 start = p[4]
455 end = p[6]
456 if start.value > end.value: # start greater than end, must go -ve
457 # auto-subtract-one (sigh) due to python range
458 end = ast.BinOp(p[6], ast.Add(), ast.Constant(-1))
459 arange = [start, end, ast.Constant(-1)]
460 else:
461 # auto-add-one (sigh) due to python range
462 end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
463 arange = [start, end]
464 it = ast.Call(ast.Name("range", ast.Load()), arange, [])
465 p[0] = ast.For(p[2], it, p[8], [])
466
467 def p_while_stmt(self, p):
468 """while_stmt : DO WHILE test COLON suite ELSE COLON suite
469 | DO WHILE test COLON suite
470 """
471 if len(p) == 6:
472 p[0] = ast.While(p[3], p[5], [])
473 else:
474 p[0] = ast.While(p[3], p[5], p[8])
475
476 def p_switch_smt(self, p):
477 """switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT switches DEDENT
478 """
479 switchon = p[3]
480 print("switch stmt")
481 print(astor.dump_tree(p[1]))
482
483 cases = []
484 current_cases = [] # for deferral
485 for (case, suite) in p[8]:
486 print("for", case, suite)
487 if suite is None:
488 for c in case:
489 current_cases.append(ast.Num(c))
490 continue
491 if case == 'default': # last
492 break
493 for c in case:
494 current_cases.append(ast.Num(c))
495 print("cases", current_cases)
496 compare = ast.Compare(switchon, [ast.In()],
497 [ast.List(current_cases, ast.Load())])
498 current_cases = []
499 cases.append((compare, suite))
500
501 print("ended", case, current_cases)
502 if case == 'default':
503 if current_cases:
504 compare = ast.Compare(switchon, [ast.In()],
505 [ast.List(current_cases, ast.Load())])
506 cases.append((compare, suite))
507 cases.append((None, suite))
508
509 cases.reverse()
510 res = []
511 for compare, suite in cases:
512 print("after rev", compare, suite)
513 if compare is None:
514 assert len(res) == 0, "last case should be default"
515 res = suite
516 else:
517 if not isinstance(res, list):
518 res = [res]
519 res = ast.If(compare, suite, res)
520 p[0] = res
521
522 def p_switches(self, p):
523 """switches : switch_list switch_default
524 | switch_default
525 """
526 if len(p) == 3:
527 p[0] = p[1] + [p[2]]
528 else:
529 p[0] = [p[1]]
530
531 def p_switch_list(self, p):
532 """switch_list : switch_case switch_list
533 | switch_case
534 """
535 if len(p) == 3:
536 p[0] = [p[1]] + p[2]
537 else:
538 p[0] = [p[1]]
539
540 def p_switch_case(self, p):
541 """switch_case : CASE LPAR atomlist RPAR COLON suite
542 """
543 # XXX bad hack
544 if isinstance(p[6][0], ast.Name) and p[6][0].id == 'fallthrough':
545 p[6] = None
546 p[0] = (p[3], p[6])
547
548 def p_switch_default(self, p):
549 """switch_default : DEFAULT COLON suite
550 """
551 p[0] = ('default', p[3])
552
553 def p_atomlist(self, p):
554 """atomlist : atom COMMA atomlist
555 | atom
556 """
557 assert isinstance(p[1], ast.Constant), "case must be numbers"
558 if len(p) == 4:
559 p[0] = [p[1].value] + p[3]
560 else:
561 p[0] = [p[1].value]
562
563 def p_if_stmt(self, p):
564 """if_stmt : IF test COLON suite ELSE COLON if_stmt
565 | IF test COLON suite ELSE COLON suite
566 | IF test COLON suite
567 """
568 if len(p) == 8 and isinstance(p[7], ast.If):
569 p[0] = ast.If(p[2], p[4], [p[7]])
570 elif len(p) == 5:
571 p[0] = ast.If(p[2], p[4], [])
572 else:
573 p[0] = ast.If(p[2], p[4], p[7])
574
575 def p_suite(self, p):
576 """suite : simple_stmt
577 | NEWLINE INDENT stmts DEDENT"""
578 if len(p) == 2:
579 p[0] = p[1]
580 else:
581 p[0] = p[3]
582
583 def p_stmts(self, p):
584 """stmts : stmts stmt
585 | stmt"""
586 if len(p) == 3:
587 p[0] = p[1] + p[2]
588 else:
589 p[0] = p[1]
590
591 def p_comparison(self, p):
592 """comparison : comparison PLUS comparison
593 | comparison MINUS comparison
594 | comparison MULT comparison
595 | comparison DIV comparison
596 | comparison MOD comparison
597 | comparison EQ comparison
598 | comparison NE comparison
599 | comparison LE comparison
600 | comparison GE comparison
601 | comparison LTU comparison
602 | comparison GTU comparison
603 | comparison LT comparison
604 | comparison GT comparison
605 | comparison BITOR comparison
606 | comparison BITXOR comparison
607 | comparison BITAND comparison
608 | PLUS comparison
609 | comparison MINUS
610 | INVERT comparison
611 | comparison APPEND comparison
612 | power"""
613 if len(p) == 4:
614 print(list(p))
615 if p[2] == '<u':
616 p[0] = ast.Call(ast.Name("ltu", ast.Load()), (p[1], p[3]), [])
617 elif p[2] == '>u':
618 p[0] = ast.Call(ast.Name("gtu", ast.Load()), (p[1], p[3]), [])
619 elif p[2] == '||':
620 l = check_concat(p[1]) + check_concat(p[3])
621 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, [])
622 elif p[2] in ['/', '%']:
623 # bad hack: if % or / used anywhere other than div/mod ops,
624 # do % or /. however if the argument names are "dividend"
625 # we must call the special trunc_divs and trunc_rems functions
626 l, r = p[1], p[3]
627 # actual call will be "dividend / divisor" - just check
628 # LHS name
629 # XXX DISABLE BAD HACK (False)
630 if False and isinstance(l, ast.Name) and l.id == 'dividend':
631 if p[2] == '/':
632 fn = 'trunc_divs'
633 else:
634 fn = 'trunc_rems'
635 # return "function trunc_xxx(l, r)"
636 p[0] = ast.Call(ast.Name(fn, ast.Load()), (l, r), [])
637 else:
638 # return "l {binop} r"
639 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
640 elif p[2] in ['<', '>', '=', '<=', '>=', '!=']:
641 p[0] = binary_ops[p[2]]((p[1], p[3]))
642 elif identify_sint_mul_pattern(p):
643 keywords = [ast.keyword(arg='repeat', value=p[3])]
644 l = p[1].elts
645 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
646 else:
647 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
648 elif len(p) == 3:
649 if isinstance(p[2], str) and p[2] == '-':
650 p[0] = ast.UnaryOp(unary_ops[p[2]], p[1])
651 else:
652 p[0] = ast.UnaryOp(unary_ops[p[1]], p[2])
653 else:
654 p[0] = p[1]
655
656 # power: atom trailer* ['**' factor]
657 # trailers enables function calls (and subscripts).
658 # so this is 'trailerlist'
659 def p_power(self, p):
660 """power : atom
661 | atom trailerlist"""
662 if len(p) == 2:
663 p[0] = p[1]
664 else:
665 print("power dump atom")
666 print(astor.dump_tree(p[1]))
667 print("power dump trailerlist")
668 print(astor.dump_tree(p[2]))
669 p[0] = apply_trailer(p[1], p[2])
670 if isinstance(p[1], ast.Name):
671 name = p[1].id
672 if name in ['RA', 'RS', 'RB', 'RC']:
673 self.read_regs.add(name)
674
675 def p_atom_name(self, p):
676 """atom : NAME"""
677 name = p[1]
678 if name in self.available_op_fields:
679 self.op_fields.add(name)
680 if name == 'overflow':
681 self.write_regs.add(name)
682 if self.include_ca_in_write:
683 if name in ['CA', 'CA32']:
684 self.write_regs.add(name)
685 if name in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR']:
686 self.special_regs.add(name)
687 self.write_regs.add(name) # and add to list to write
688 p[0] = ast.Name(id=name, ctx=ast.Load())
689
690 def p_atom_number(self, p):
691 """atom : BINARY
692 | NUMBER
693 | HEX
694 | STRING"""
695 p[0] = ast.Constant(p[1])
696
697 # '[' [listmaker] ']' |
698
699 def p_atom_listmaker(self, p):
700 """atom : LBRACK listmaker RBRACK"""
701 p[0] = p[2]
702
703 def p_listmaker(self, p):
704 """listmaker : test COMMA listmaker
705 | test
706 """
707 if len(p) == 2:
708 p[0] = ast.List([p[1]], ast.Load())
709 else:
710 p[0] = ast.List([p[1]] + p[3].nodes, ast.Load())
711
712 def p_atom_tuple(self, p):
713 """atom : LPAR testlist RPAR"""
714 print("tuple", p[2])
715 print("astor dump")
716 print(astor.dump_tree(p[2]))
717
718 if isinstance(p[2], ast.Name):
719 name = p[2].id
720 print("tuple name", name)
721 if name in self.gprs:
722 self.read_regs.add(name) # add to list of regs to read
723 #p[0] = ast.Subscript(ast.Name("GPR", ast.Load()), ast.Str(p[2].id))
724 # return
725 p[0] = p[2]
726 elif isinstance(p[2], ast.BinOp):
727 if isinstance(p[2].left, ast.Name) and \
728 isinstance(p[2].right, ast.Constant) and \
729 p[2].right.value == 0 and \
730 p[2].left.id in self.gprs:
731 rid = p[2].left.id
732 self.read_regs.add(rid) # add to list of regs to read
733 # create special call to GPR.getz
734 gprz = ast.Name("GPR", ast.Load())
735 # get testzero function
736 gprz = ast.Attribute(gprz, "getz", ast.Load())
737 # *sigh* see class GPR. we need index itself not reg value
738 ridx = ast.Name("_%s" % rid, ast.Load())
739 p[0] = ast.Call(gprz, [ridx], [])
740 print("tree", astor.dump_tree(p[0]))
741 else:
742 p[0] = p[2]
743 else:
744 p[0] = p[2]
745
746 def p_trailerlist(self, p):
747 """trailerlist : trailer trailerlist
748 | trailer
749 """
750 if len(p) == 2:
751 p[0] = p[1]
752 else:
753 p[0] = ("TLIST", p[1], p[2])
754
755 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
756 def p_trailer(self, p):
757 """trailer : trailer_arglist
758 | trailer_subscript
759 """
760 p[0] = p[1]
761
762 def p_trailer_arglist(self, p):
763 "trailer_arglist : LPAR arglist RPAR"
764 p[0] = ("CALL", p[2])
765
766 def p_trailer_subscript(self, p):
767 "trailer_subscript : LBRACK subscript RBRACK"
768 p[0] = ("SUBS", p[2])
769
770 # subscript: '.' '.' '.' | test | [test] ':' [test]
771
772 def p_subscript(self, p):
773 """subscript : test COLON test
774 | test
775 """
776 if len(p) == 4:
777 # add one to end
778 if isinstance(p[3], ast.Constant):
779 end = ast.Constant(p[3].value+1)
780 else:
781 end = ast.BinOp(p[3], ast.Add(), ast.Constant(1))
782 p[0] = [p[1], end]
783 else:
784 p[0] = [p[1]]
785
786 # testlist: test (',' test)* [',']
787 # Contains shift/reduce error
788
789 def p_testlist(self, p):
790 """testlist : testlist_multi COMMA
791 | testlist_multi """
792 if len(p) == 2:
793 p[0] = p[1]
794 else:
795 # May need to promote singleton to tuple
796 if isinstance(p[1], list):
797 p[0] = p[1]
798 else:
799 p[0] = [p[1]]
800 # Convert into a tuple?
801 if isinstance(p[0], list):
802 p[0] = ast.Tuple(p[0])
803
804 def p_testlist_multi(self, p):
805 """testlist_multi : testlist_multi COMMA test
806 | test"""
807 if len(p) == 2:
808 # singleton
809 p[0] = p[1]
810 else:
811 if isinstance(p[1], list):
812 p[0] = p[1] + [p[3]]
813 else:
814 # singleton -> tuple
815 p[0] = [p[1], p[3]]
816
817 # test: or_test ['if' or_test 'else' test] | lambdef
818 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
819
820 def p_test(self, p):
821 "test : comparison"
822 p[0] = p[1]
823
824 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
825 # | '**' test)
826 # XXX INCOMPLETE: this doesn't allow the trailing comma
827
828 def p_arglist(self, p):
829 """arglist : arglist COMMA argument
830 | argument"""
831 if len(p) == 4:
832 p[0] = p[1] + [p[3]]
833 else:
834 p[0] = [p[1]]
835
836 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
837 def p_argument(self, p):
838 "argument : test"
839 p[0] = p[1]
840
841 def p_error(self, p):
842 # print "Error!", repr(p)
843 raise SyntaxError(p)
844
845
846 class GardenSnakeParser(PowerParser):
847 def __init__(self, lexer=None, debug=False, form=None, incl_carry=False):
848 self.sd = create_pdecode()
849 PowerParser.__init__(self, form, incl_carry)
850 self.debug = debug
851 if lexer is None:
852 lexer = IndentLexer(debug=0)
853 self.lexer = lexer
854 self.tokens = lexer.tokens
855 self.parser = yacc.yacc(module=self, start="file_input_end",
856 debug=debug, write_tables=False)
857
858 def parse(self, code):
859 # self.lexer.input(code)
860 result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
861 return ast.Module(result)
862
863
864 ###### Code generation ######
865
866 #from compiler import misc, syntax, pycodegen
867
868 class GardenSnakeCompiler(object):
869 def __init__(self, debug=False, form=None, incl_carry=False):
870 self.parser = GardenSnakeParser(debug=debug, form=form,
871 incl_carry=incl_carry)
872
873 def compile(self, code, mode="exec", filename="<string>"):
874 tree = self.parser.parse(code)
875 print("snake")
876 pprint(tree)
877 return tree
878 #misc.set_filename(filename, tree)
879 return compile(tree, mode="exec", filename="<string>")
880 # syntax.check(tree)
881 gen = pycodegen.ModuleCodeGenerator(tree)
882 code = gen.getCode()
883 return code