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