e413f4d6c8f5fb6f73c9062d1b2a29c1e7e39251
[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 p[0] = ast.Name(id=name, ctx=ast.Load())
612
613 def p_atom_number(self, p):
614 """atom : BINARY
615 | NUMBER
616 | HEX
617 | STRING"""
618 p[0] = ast.Constant(p[1])
619
620 # '[' [listmaker] ']' |
621
622 def p_atom_listmaker(self, p):
623 """atom : LBRACK listmaker RBRACK"""
624 p[0] = p[2]
625
626 def p_listmaker(self, p):
627 """listmaker : test COMMA listmaker
628 | test
629 """
630 if len(p) == 2:
631 p[0] = ast.List([p[1]], ast.Load())
632 else:
633 p[0] = ast.List([p[1]] + p[3].nodes, ast.Load())
634
635 def p_atom_tuple(self, p):
636 """atom : LPAR testlist RPAR"""
637 print("tuple", p[2])
638 print("astor dump")
639 print(astor.dump_tree(p[2]))
640
641 if isinstance(p[2], ast.Name):
642 name = p[2].id
643 print("tuple name", name)
644 if name in self.gprs:
645 self.read_regs.add(name) # add to list of regs to read
646 #p[0] = ast.Subscript(ast.Name("GPR", ast.Load()), ast.Str(p[2].id))
647 # return
648 p[0] = p[2]
649 elif isinstance(p[2], ast.BinOp):
650 if isinstance(p[2].left, ast.Name) and \
651 isinstance(p[2].right, ast.Constant) and \
652 p[2].right.value == 0 and \
653 p[2].left.id in self.gprs:
654 rid = p[2].left.id
655 self.read_regs.add(rid) # add to list of regs to read
656 # create special call to GPR.getz
657 gprz = ast.Name("GPR", ast.Load())
658 # get testzero function
659 gprz = ast.Attribute(gprz, "getz", ast.Load())
660 # *sigh* see class GPR. we need index itself not reg value
661 ridx = ast.Name("_%s" % rid, ast.Load())
662 p[0] = ast.Call(gprz, [ridx], [])
663 print("tree", astor.dump_tree(p[0]))
664 else:
665 p[0] = p[2]
666 else:
667 p[0] = p[2]
668
669 def p_trailerlist(self, p):
670 """trailerlist : trailer trailerlist
671 | trailer
672 """
673 if len(p) == 2:
674 p[0] = p[1]
675 else:
676 p[0] = ("TLIST", p[1], p[2])
677
678 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
679 def p_trailer(self, p):
680 """trailer : trailer_arglist
681 | trailer_subscript
682 """
683 p[0] = p[1]
684
685 def p_trailer_arglist(self, p):
686 "trailer_arglist : LPAR arglist RPAR"
687 p[0] = ("CALL", p[2])
688
689 def p_trailer_subscript(self, p):
690 "trailer_subscript : LBRACK subscript RBRACK"
691 p[0] = ("SUBS", p[2])
692
693 # subscript: '.' '.' '.' | test | [test] ':' [test]
694
695 def p_subscript(self, p):
696 """subscript : test COLON test
697 | test
698 """
699 if len(p) == 4:
700 # add one to end
701 if isinstance(p[3], ast.Constant):
702 end = ast.Constant(p[3].value+1)
703 else:
704 end = ast.BinOp(p[3], ast.Add(), ast.Constant(1))
705 p[0] = [p[1], end]
706 else:
707 p[0] = [p[1]]
708
709 # testlist: test (',' test)* [',']
710 # Contains shift/reduce error
711
712 def p_testlist(self, p):
713 """testlist : testlist_multi COMMA
714 | testlist_multi """
715 if len(p) == 2:
716 p[0] = p[1]
717 else:
718 # May need to promote singleton to tuple
719 if isinstance(p[1], list):
720 p[0] = p[1]
721 else:
722 p[0] = [p[1]]
723 # Convert into a tuple?
724 if isinstance(p[0], list):
725 p[0] = ast.Tuple(p[0])
726
727 def p_testlist_multi(self, p):
728 """testlist_multi : testlist_multi COMMA test
729 | test"""
730 if len(p) == 2:
731 # singleton
732 p[0] = p[1]
733 else:
734 if isinstance(p[1], list):
735 p[0] = p[1] + [p[3]]
736 else:
737 # singleton -> tuple
738 p[0] = [p[1], p[3]]
739
740 # test: or_test ['if' or_test 'else' test] | lambdef
741 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
742
743 def p_test(self, p):
744 "test : comparison"
745 p[0] = p[1]
746
747 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
748 # | '**' test)
749 # XXX INCOMPLETE: this doesn't allow the trailing comma
750
751 def p_arglist(self, p):
752 """arglist : arglist COMMA argument
753 | argument"""
754 if len(p) == 4:
755 p[0] = p[1] + [p[3]]
756 else:
757 p[0] = [p[1]]
758
759 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
760 def p_argument(self, p):
761 "argument : test"
762 p[0] = p[1]
763
764 def p_error(self, p):
765 # print "Error!", repr(p)
766 raise SyntaxError(p)
767
768
769 class GardenSnakeParser(PowerParser):
770 def __init__(self, lexer=None, debug=False, form=None):
771 self.sd = create_pdecode()
772 PowerParser.__init__(self, form)
773 self.debug = debug
774 if lexer is None:
775 lexer = IndentLexer(debug=0)
776 self.lexer = lexer
777 self.tokens = lexer.tokens
778 self.parser = yacc.yacc(module=self, start="file_input_end",
779 debug=debug, write_tables=False)
780
781 def parse(self, code):
782 # self.lexer.input(code)
783 result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
784 return ast.Module(result)
785
786
787 ###### Code generation ######
788
789 #from compiler import misc, syntax, pycodegen
790
791 class GardenSnakeCompiler(object):
792 def __init__(self, debug=False, form=None):
793 self.parser = GardenSnakeParser(debug=debug, form=form)
794
795 def compile(self, code, mode="exec", filename="<string>"):
796 tree = self.parser.parse(code)
797 print("snake")
798 pprint(tree)
799 return tree
800 #misc.set_filename(filename, tree)
801 return compile(tree, mode="exec", filename="<string>")
802 # syntax.check(tree)
803 gen = pycodegen.ModuleCodeGenerator(tree)
804 code = gen.getCode()
805 return code