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