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