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