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