add Form name parsing
[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"),
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 class PowerParser:
218
219 precedence = (
220 ("left", "EQ", "NE", "GT", "LT", "LE", "GE", "LTU", "GTU"),
221 ("left", "BITOR"),
222 ("left", "BITXOR"),
223 ("left", "BITAND"),
224 ("left", "PLUS", "MINUS"),
225 ("left", "MULT", "DIV", "MOD"),
226 ("left", "INVERT"),
227 )
228
229 def __init__(self, form):
230 self.gprs = {}
231 form = self.sd.sigforms[form]
232 print (form)
233 formkeys = form._asdict().keys()
234 for rname in ['RA', 'RB', 'RC', 'RT', 'RS']:
235 self.gprs[rname] = None
236 self.available_op_fields = set()
237 for k in formkeys:
238 if k not in self.gprs:
239 if k == 'SPR': # sigh, lower-case to not conflict
240 k = k.lower()
241 self.available_op_fields.add(k)
242 self.op_fields = OrderedSet()
243 self.read_regs = OrderedSet()
244 self.uninit_regs = OrderedSet()
245 self.write_regs = OrderedSet()
246
247 # The grammar comments come from Python's Grammar/Grammar file
248
249 # NB: compound_stmt in single_input is followed by extra NEWLINE!
250 # file_input: (NEWLINE | stmt)* ENDMARKER
251
252 def p_file_input_end(self, p):
253 """file_input_end : file_input ENDMARKER"""
254 print("end", p[1])
255 p[0] = p[1]
256
257 def p_file_input(self, p):
258 """file_input : file_input NEWLINE
259 | file_input stmt
260 | NEWLINE
261 | stmt"""
262 if isinstance(p[len(p)-1], str):
263 if len(p) == 3:
264 p[0] = p[1]
265 else:
266 p[0] = [] # p == 2 --> only a blank line
267 else:
268 if len(p) == 3:
269 p[0] = p[1] + p[2]
270 else:
271 p[0] = p[1]
272
273 # funcdef: [decorators] 'def' NAME parameters ':' suite
274 # ignoring decorators
275
276 def p_funcdef(self, p):
277 "funcdef : DEF NAME parameters COLON suite"
278 p[0] = ast.FunctionDef(p[2], p[3], p[5], ())
279
280 # parameters: '(' [varargslist] ')'
281 def p_parameters(self, p):
282 """parameters : LPAR RPAR
283 | LPAR varargslist RPAR"""
284 if len(p) == 3:
285 args = []
286 else:
287 args = p[2]
288 p[0] = ast.arguments(args=args, vararg=None, kwarg=None, defaults=[])
289
290 # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
291 # '**' NAME) |
292 # highly simplified
293
294 def p_varargslist(self, p):
295 """varargslist : varargslist COMMA NAME
296 | NAME"""
297 if len(p) == 4:
298 p[0] = p[1] + p[3]
299 else:
300 p[0] = [p[1]]
301
302 # stmt: simple_stmt | compound_stmt
303 def p_stmt_simple(self, p):
304 """stmt : simple_stmt"""
305 # simple_stmt is a list
306 p[0] = p[1]
307
308 def p_stmt_compound(self, p):
309 """stmt : compound_stmt"""
310 p[0] = [p[1]]
311
312 # simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
313 def p_simple_stmt(self, p):
314 """simple_stmt : small_stmts NEWLINE
315 | small_stmts SEMICOLON NEWLINE"""
316 p[0] = p[1]
317
318 def p_small_stmts(self, p):
319 """small_stmts : small_stmts SEMICOLON small_stmt
320 | small_stmt"""
321 if len(p) == 4:
322 p[0] = p[1] + [p[3]]
323 else:
324 p[0] = [p[1]]
325
326 # small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
327 # import_stmt | global_stmt | exec_stmt | assert_stmt
328 def p_small_stmt(self, p):
329 """small_stmt : flow_stmt
330 | break_stmt
331 | expr_stmt"""
332 if isinstance(p[1], ast.Call):
333 p[0] = ast.Expr(p[1])
334 else:
335 p[0] = p[1]
336
337 # expr_stmt: testlist (augassign (yield_expr|testlist) |
338 # ('=' (yield_expr|testlist))*)
339 # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
340 # '<<=' | '>>=' | '**=' | '//=')
341 def p_expr_stmt(self, p):
342 """expr_stmt : testlist ASSIGNEA testlist
343 | testlist ASSIGN testlist
344 | testlist """
345 print("expr_stmt", p)
346 if len(p) == 2:
347 # a list of expressions
348 #p[0] = ast.Discard(p[1])
349 p[0] = p[1]
350 else:
351 iea_mode = p[2] == '<-iea'
352 name = None
353 if isinstance(p[1], ast.Name):
354 name = p[1].id
355 elif isinstance(p[1], ast.Subscript):
356 if isinstance(p[1].value, ast.Name):
357 name = p[1].value.id
358 if name in self.gprs:
359 # add to list of uninitialised
360 self.uninit_regs.add(name)
361 elif isinstance(p[1], ast.Call) and p[1].func.id in ['GPR', 'SPR']:
362 print(astor.dump_tree(p[1]))
363 # replace GPR(x) with GPR[x]
364 idx = p[1].args[0]
365 p[1] = ast.Subscript(p[1].func, idx)
366 elif isinstance(p[1], ast.Call) and p[1].func.id == 'MEM':
367 print ("mem assign")
368 print(astor.dump_tree(p[1]))
369 p[1].func.id = "memassign" # change function name to set
370 p[1].args.append(p[3])
371 p[0] = p[1]
372 print ("mem rewrite")
373 print(astor.dump_tree(p[0]))
374 return
375 else:
376 print ("help, help")
377 print(astor.dump_tree(p[1]))
378 print("expr assign", name, p[1])
379 if name and name in self.gprs:
380 self.write_regs.add(name) # add to list of regs to write
381 p[0] = Assign(p[1], p[3], iea_mode)
382
383 def p_flow_stmt(self, p):
384 "flow_stmt : return_stmt"
385 p[0] = p[1]
386
387 # return_stmt: 'return' [testlist]
388 def p_return_stmt(self, p):
389 "return_stmt : RETURN testlist"
390 p[0] = ast.Return(p[2])
391
392 def p_compound_stmt(self, p):
393 """compound_stmt : if_stmt
394 | while_stmt
395 | switch_stmt
396 | for_stmt
397 | funcdef
398 """
399 p[0] = p[1]
400
401 def p_break_stmt(self, p):
402 """break_stmt : BREAK
403 """
404 p[0] = ast.Break()
405
406 def p_for_stmt(self, p):
407 """for_stmt : FOR atom EQ test TO test COLON suite
408 | DO atom EQ test TO test COLON suite
409 """
410 # auto-add-one (sigh) due to python range
411 start = p[4]
412 end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
413 it = ast.Call(ast.Name("range"), [start, end], [])
414 p[0] = ast.For(p[2], it, p[8], [])
415
416 def p_while_stmt(self, p):
417 """while_stmt : DO WHILE test COLON suite ELSE COLON suite
418 | DO WHILE test COLON suite
419 """
420 if len(p) == 6:
421 p[0] = ast.While(p[3], p[5], [])
422 else:
423 p[0] = ast.While(p[3], p[5], p[8])
424
425 def p_switch_smt(self, p):
426 """switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT switches DEDENT
427 """
428 switchon = p[3]
429 print("switch stmt")
430 print(astor.dump_tree(p[1]))
431
432 cases = []
433 current_cases = [] # for deferral
434 for (case, suite) in p[8]:
435 print ("for", case, suite)
436 if suite is None:
437 for c in case:
438 current_cases.append(ast.Num(c))
439 continue
440 if case == 'default': # last
441 break
442 for c in case:
443 current_cases.append(ast.Num(c))
444 print ("cases", current_cases)
445 compare = ast.Compare(switchon, [ast.In()],
446 [ast.List(current_cases)])
447 current_cases = []
448 cases.append((compare, suite))
449
450 print ("ended", case, current_cases)
451 if case == 'default':
452 if current_cases:
453 compare = ast.Compare(switchon, [ast.In()],
454 [ast.List(current_cases)])
455 cases.append((compare, suite))
456 cases.append((None, suite))
457
458 cases.reverse()
459 res = []
460 for compare, suite in cases:
461 print ("after rev", compare, suite)
462 if compare is None:
463 assert len(res) == 0, "last case should be default"
464 res = suite
465 else:
466 if not isinstance(res, list):
467 res = [res]
468 res = ast.If(compare, suite, res)
469 p[0] = res
470
471 def p_switches(self, p):
472 """switches : switch_list switch_default
473 | switch_default
474 """
475 if len(p) == 3:
476 p[0] = p[1] + [p[2]]
477 else:
478 p[0] = [p[1]]
479
480 def p_switch_list(self, p):
481 """switch_list : switch_case switch_list
482 | switch_case
483 """
484 if len(p) == 3:
485 p[0] = [p[1]] + p[2]
486 else:
487 p[0] = [p[1]]
488
489 def p_switch_case(self, p):
490 """switch_case : CASE LPAR atomlist RPAR COLON suite
491 """
492 # XXX bad hack
493 if isinstance(p[6][0], ast.Name) and p[6][0].id == 'fallthrough':
494 p[6] = None
495 p[0] = (p[3], p[6])
496
497 def p_switch_default(self, p):
498 """switch_default : DEFAULT COLON suite
499 """
500 p[0] = ('default', p[3])
501
502 def p_atomlist(self, p):
503 """atomlist : atom COMMA atomlist
504 | atom
505 """
506 assert isinstance(p[1], ast.Constant), "case must be numbers"
507 if len(p) == 4:
508 p[0] = [p[1].value] + p[3]
509 else:
510 p[0] = [p[1].value]
511
512 def p_if_stmt(self, p):
513 """if_stmt : IF test COLON suite ELSE COLON if_stmt
514 | IF test COLON suite ELSE COLON suite
515 | IF test COLON suite
516 """
517 if len(p) == 8 and isinstance(p[7], ast.If):
518 p[0] = ast.If(p[2], p[4], [p[7]])
519 elif len(p) == 5:
520 p[0] = ast.If(p[2], p[4], [])
521 else:
522 p[0] = ast.If(p[2], p[4], p[7])
523
524 def p_suite(self, p):
525 """suite : simple_stmt
526 | NEWLINE INDENT stmts DEDENT"""
527 if len(p) == 2:
528 p[0] = p[1]
529 else:
530 p[0] = p[3]
531
532 def p_stmts(self, p):
533 """stmts : stmts stmt
534 | stmt"""
535 if len(p) == 3:
536 p[0] = p[1] + p[2]
537 else:
538 p[0] = p[1]
539
540 def p_comparison(self, p):
541 """comparison : comparison PLUS comparison
542 | comparison MINUS comparison
543 | comparison MULT comparison
544 | comparison DIV comparison
545 | comparison MOD comparison
546 | comparison EQ comparison
547 | comparison NE comparison
548 | comparison LE comparison
549 | comparison GE comparison
550 | comparison LTU comparison
551 | comparison GTU comparison
552 | comparison LT comparison
553 | comparison GT comparison
554 | comparison BITOR comparison
555 | comparison BITXOR comparison
556 | comparison BITAND comparison
557 | PLUS comparison
558 | comparison MINUS
559 | INVERT comparison
560 | comparison APPEND comparison
561 | power"""
562 if len(p) == 4:
563 print(list(p))
564 if p[2] == '<u':
565 p[0] = ast.Call(ast.Name("ltu"), (p[1], p[3]), [])
566 elif p[2] == '>u':
567 p[0] = ast.Call(ast.Name("gtu"), (p[1], p[3]), [])
568 elif p[2] == '||':
569 l = check_concat(p[1]) + check_concat(p[3])
570 p[0] = ast.Call(ast.Name("concat"), l, [])
571 elif p[2] in ['<', '>', '=', '<=', '>=', '!=']:
572 p[0] = binary_ops[p[2]]((p[1], p[3]))
573 elif identify_sint_mul_pattern(p):
574 keywords=[ast.keyword(arg='repeat', value=p[3])]
575 l = p[1].elts
576 p[0] = ast.Call(ast.Name("concat"), l, keywords)
577 else:
578 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
579 elif len(p) == 3:
580 if isinstance(p[2], str) and p[2] == '-':
581 p[0] = ast.UnaryOp(unary_ops[p[2]], p[1])
582 else:
583 p[0] = ast.UnaryOp(unary_ops[p[1]], p[2])
584 else:
585 p[0] = p[1]
586
587 # power: atom trailer* ['**' factor]
588 # trailers enables function calls (and subscripts).
589 # so this is 'trailerlist'
590 def p_power(self, p):
591 """power : atom
592 | atom trailerlist"""
593 if len(p) == 2:
594 p[0] = p[1]
595 else:
596 print("power dump atom")
597 print(astor.dump_tree(p[1]))
598 print("power dump trailerlist")
599 print(astor.dump_tree(p[2]))
600 p[0] = apply_trailer(p[1], p[2])
601
602 def p_atom_name(self, p):
603 """atom : NAME"""
604 name = p[1]
605 if name in self.available_op_fields:
606 self.op_fields.add(name)
607 p[0] = ast.Name(id=name, ctx=ast.Load())
608
609 def p_atom_number(self, p):
610 """atom : BINARY
611 | NUMBER
612 | HEX
613 | STRING"""
614 p[0] = ast.Constant(p[1])
615
616 # '[' [listmaker] ']' |
617
618 def p_atom_listmaker(self, p):
619 """atom : LBRACK listmaker RBRACK"""
620 p[0] = p[2]
621
622 def p_listmaker(self, p):
623 """listmaker : test COMMA listmaker
624 | test
625 """
626 if len(p) == 2:
627 p[0] = ast.List([p[1]])
628 else:
629 p[0] = ast.List([p[1]] + p[3].nodes)
630
631 def p_atom_tuple(self, p):
632 """atom : LPAR testlist RPAR"""
633 print("tuple", p[2])
634 print("astor dump")
635 print(astor.dump_tree(p[2]))
636
637 if isinstance(p[2], ast.Name):
638 name = p[2].id
639 print("tuple name", name)
640 if name in self.gprs:
641 self.read_regs.add(name) # add to list of regs to read
642 #p[0] = ast.Subscript(ast.Name("GPR"), ast.Str(p[2].id))
643 # return
644 p[0] = p[2]
645 elif isinstance(p[2], ast.BinOp):
646 if isinstance(p[2].left, ast.Name) and \
647 isinstance(p[2].right, ast.Constant) and \
648 p[2].right.value == 0 and \
649 p[2].left.id in self.gprs:
650 rid = p[2].left.id
651 self.read_regs.add(rid) # add to list of regs to read
652 # create special call to GPR.getz
653 gprz = ast.Name("GPR")
654 gprz = ast.Attribute(gprz, "getz") # get testzero function
655 # *sigh* see class GPR. we need index itself not reg value
656 ridx = ast.Name("_%s" % rid)
657 p[0] = ast.Call(gprz, [ridx], [])
658 print("tree", astor.dump_tree(p[0]))
659 else:
660 p[0] = p[2]
661 else:
662 p[0] = p[2]
663
664 def p_trailerlist(self, p):
665 """trailerlist : trailer trailerlist
666 | trailer
667 """
668 if len(p) == 2:
669 p[0] = p[1]
670 else:
671 p[0] = ("TLIST", p[1], p[2])
672
673 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
674 def p_trailer(self, p):
675 """trailer : trailer_arglist
676 | trailer_subscript
677 """
678 p[0] = p[1]
679
680 def p_trailer_arglist(self, p):
681 "trailer_arglist : LPAR arglist RPAR"
682 p[0] = ("CALL", p[2])
683
684 def p_trailer_subscript(self, p):
685 "trailer_subscript : LBRACK subscript RBRACK"
686 p[0] = ("SUBS", p[2])
687
688 # subscript: '.' '.' '.' | test | [test] ':' [test]
689
690 def p_subscript(self, p):
691 """subscript : test COLON test
692 | test
693 """
694 if len(p) == 4:
695 # add one to end
696 if isinstance(p[3], ast.Constant):
697 end = ast.Constant(p[3].value+1)
698 else:
699 end = ast.BinOp(p[3], ast.Add(), ast.Constant(1))
700 p[0] = [p[1], end]
701 else:
702 p[0] = [p[1]]
703
704 # testlist: test (',' test)* [',']
705 # Contains shift/reduce error
706
707 def p_testlist(self, p):
708 """testlist : testlist_multi COMMA
709 | testlist_multi """
710 if len(p) == 2:
711 p[0] = p[1]
712 else:
713 # May need to promote singleton to tuple
714 if isinstance(p[1], list):
715 p[0] = p[1]
716 else:
717 p[0] = [p[1]]
718 # Convert into a tuple?
719 if isinstance(p[0], list):
720 p[0] = ast.Tuple(p[0])
721
722 def p_testlist_multi(self, p):
723 """testlist_multi : testlist_multi COMMA test
724 | test"""
725 if len(p) == 2:
726 # singleton
727 p[0] = p[1]
728 else:
729 if isinstance(p[1], list):
730 p[0] = p[1] + [p[3]]
731 else:
732 # singleton -> tuple
733 p[0] = [p[1], p[3]]
734
735 # test: or_test ['if' or_test 'else' test] | lambdef
736 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
737
738 def p_test(self, p):
739 "test : comparison"
740 p[0] = p[1]
741
742 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
743 # | '**' test)
744 # XXX INCOMPLETE: this doesn't allow the trailing comma
745
746 def p_arglist(self, p):
747 """arglist : arglist COMMA argument
748 | argument"""
749 if len(p) == 4:
750 p[0] = p[1] + [p[3]]
751 else:
752 p[0] = [p[1]]
753
754 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
755 def p_argument(self, p):
756 "argument : test"
757 p[0] = p[1]
758
759 def p_error(self, p):
760 # print "Error!", repr(p)
761 raise SyntaxError(p)
762
763
764 class GardenSnakeParser(PowerParser):
765 def __init__(self, lexer=None, debug=False, form=None):
766 self.sd = create_pdecode()
767 PowerParser.__init__(self, form)
768 self.debug = debug
769 if lexer is None:
770 lexer = IndentLexer(debug=0)
771 self.lexer = lexer
772 self.tokens = lexer.tokens
773 self.parser = yacc.yacc(module=self, start="file_input_end",
774 debug=debug, write_tables=False)
775
776 def parse(self, code):
777 # self.lexer.input(code)
778 result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
779 return ast.Module(result)
780
781
782 ###### Code generation ######
783
784 #from compiler import misc, syntax, pycodegen
785
786 class GardenSnakeCompiler(object):
787 def __init__(self, debug=False, form=None):
788 self.parser = GardenSnakeParser(debug=debug, form=form)
789
790 def compile(self, code, mode="exec", filename="<string>"):
791 tree = self.parser.parse(code)
792 print("snake")
793 pprint(tree)
794 return tree
795 #misc.set_filename(filename, tree)
796 return compile(tree, mode="exec", filename="<string>")
797 # syntax.check(tree)
798 gen = pycodegen.ModuleCodeGenerator(tree)
799 code = gen.getCode()
800 return code