pywriter: support RANGE helper
[openpower-isa.git] / src / openpower / 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 from copy import deepcopy
15
16 from openpower.decoder.power_decoder import create_pdecode
17 from openpower.decoder.pseudo.lexer import IndentLexer
18 from openpower.decoder.orderedset import OrderedSet
19
20 # I use the Python AST
21 #from compiler import ast
22 import ast
23
24 # Helper function
25
26 regs = ['RA', 'RS', 'RB', 'RC', 'RT']
27 fregs = ['FRA', 'FRS', 'FRB', 'FRC', 'FRT', 'FRS']
28
29 def Assign(autoassign, assignname, left, right, iea_mode):
30 names = []
31 print("Assign", assignname, left, right)
32 if isinstance(left, ast.Name):
33 # Single assignment on left
34 # XXX when doing IntClass, which will have an "eq" function,
35 # this is how to access it
36 # eq = ast.Attribute(left, "eq") # get eq fn
37 # return ast.Call(eq, [right], []) # now call left.eq(right)
38 return ast.Assign([ast.Name(left.id, ast.Store())], right)
39 elif isinstance(left, ast.Tuple):
40 # List of things - make sure they are Name nodes
41 names = []
42 for child in left.getChildren():
43 if not isinstance(child, ast.Name):
44 raise SyntaxError("that assignment not supported")
45 names.append(child.name)
46 ass_list = [ast.AssName(name, 'OP_ASSIGN') for name in names]
47 return ast.Assign([ast.AssTuple(ass_list)], right)
48 elif isinstance(left, ast.Subscript):
49 ls = left.slice
50 # XXX changing meaning of "undefined" to a function
51 #if (isinstance(ls, ast.Slice) and isinstance(right, ast.Name) and
52 # right.id == 'undefined'):
53 # # undefined needs to be copied the exact same slice
54 # right = ast.Subscript(right, ls, ast.Load())
55 # return ast.Assign([left], right)
56 res = ast.Assign([left], right)
57 if autoassign and isinstance(ls, ast.Slice):
58 # hack to create a variable pre-declared based on a slice.
59 # dividend[0:32] = (RA)[0:32] will create
60 # dividend = [0] * 32
61 # dividend[0:32] = (RA)[0:32]
62 # the declaration makes the slice-assignment "work"
63 lower, upper, step = ls.lower, ls.upper, ls.step
64 print("lower, upper, step", repr(lower), repr(upper), step)
65 if not isinstance(lower, ast.Constant) or \
66 not isinstance(upper, ast.Constant):
67 return res
68 qty = ast.Num(upper.value-lower.value)
69 keywords = [ast.keyword(arg='repeat', value=qty)]
70 l = [ast.Num(0)]
71 right = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
72 declare = ast.Assign([ast.Name(assignname, ast.Store())], right)
73 return [declare, res]
74 return res
75 # XXX HMMM probably not needed...
76 ls = left.slice
77 if isinstance(ls, ast.Slice):
78 lower, upper, step = ls.lower, ls.upper, ls.step
79 print("slice assign", lower, upper, step)
80 if step is None:
81 ls = (lower, upper, None)
82 else:
83 ls = (lower, upper, step)
84 ls = ast.Tuple(ls)
85 return ast.Call(ast.Name("selectassign", ast.Load()),
86 [left.value, ls, right], [])
87 else:
88 print("Assign fail")
89 raise SyntaxError("Can't do that yet")
90
91
92 # I implemented INDENT / DEDENT generation as a post-processing filter
93
94 # The original lex token stream contains WS and NEWLINE characters.
95 # WS will only occur before any other tokens on a line.
96
97 # I have three filters. One tags tokens by adding two attributes.
98 # "must_indent" is True if the token must be indented from the
99 # previous code. The other is "at_line_start" which is True for WS
100 # and the first non-WS/non-NEWLINE on a line. It flags the check so
101 # see if the new line has changed indication level.
102
103
104 # No using Python's approach because Ply supports precedence
105
106 # comparison: expr (comp_op expr)*
107 # arith_expr: term (('+'|'-') term)*
108 # term: factor (('*'|'/'|'%'|'//') factor)*
109 # factor: ('+'|'-'|'~') factor | power
110 # comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
111
112 def make_le_compare(arg):
113 (left, right) = arg
114 return ast.Call(ast.Name("le", ast.Load()), (left, right), [])
115
116
117 def make_ge_compare(arg):
118 (left, right) = arg
119 return ast.Call(ast.Name("ge", ast.Load()), (left, right), [])
120
121
122 def make_lt_compare(arg):
123 (left, right) = arg
124 return ast.Call(ast.Name("lt", ast.Load()), (left, right), [])
125
126
127 def make_gt_compare(arg):
128 (left, right) = arg
129 return ast.Call(ast.Name("gt", ast.Load()), (left, right), [])
130
131
132 def make_eq_compare(arg):
133 (left, right) = arg
134 return ast.Call(ast.Name("eq", ast.Load()), (left, right), [])
135
136
137 def make_ne_compare(arg):
138 (left, right) = arg
139 return ast.Call(ast.Name("ne", ast.Load()), (left, right), [])
140
141
142 binary_ops = {
143 "^": ast.BitXor(),
144 "&": ast.BitAnd(),
145 "|": ast.BitOr(),
146 "+": ast.Add(),
147 "-": ast.Sub(),
148 "*": ast.Mult(),
149 "/": ast.FloorDiv(),
150 "%": ast.Mod(),
151 "<=": make_le_compare,
152 ">=": make_ge_compare,
153 "<": make_lt_compare,
154 ">": make_gt_compare,
155 "=": make_eq_compare,
156 "!=": make_ne_compare,
157 }
158 unary_ops = {
159 "+": ast.UAdd(),
160 "-": ast.USub(),
161 "¬": ast.Invert(),
162 }
163
164
165 def check_concat(node): # checks if the comparison is already a concat
166 print("check concat", node)
167 if not isinstance(node, ast.Call):
168 return [node]
169 print("func", node.func.id)
170 if node.func.id != 'concat':
171 return [node]
172 if node.keywords: # a repeated list-constant, don't optimise
173 return [node]
174 return node.args
175
176
177 # identify SelectableInt pattern [something] * N
178 # must return concat(something, repeat=N)
179 def identify_sint_mul_pattern(p):
180 if p[2] != '*': # multiply
181 return False
182 if not isinstance(p[3], ast.Constant): # rhs = Num
183 return False
184 if not isinstance(p[1], ast.List): # lhs is a list
185 return False
186 l = p[1].elts
187 if len(l) != 1: # lhs is a list of length 1
188 return False
189 return True # yippee!
190
191
192 def apply_trailer(atom, trailer, read_regs):
193 if trailer[0] == "TLIST":
194 # assume depth of one
195 atom = apply_trailer(atom, trailer[1], read_regs)
196 trailer = trailer[2]
197 if trailer[0] == "CALL":
198 #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
199 for arg in trailer[1]:
200 if isinstance(arg, ast.Name):
201 name = arg.id
202 if name in regs + fregs:
203 read_regs.add(name)
204 # special-case, function named "SVSTATE_NEXT" must be made "self.xxxx"
205 if atom.id == 'SVSTATE_NEXT':
206 name = ast.Name("self", ast.Load())
207 atom = ast.Attribute(name, atom, ast.Load())
208 return ast.Call(atom, trailer[1], [])
209 # if p[1].id == 'print':
210 # p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
211 # else:
212 # p[0] = ast.CallFunc(p[1], p[2][1], None, None)
213 else:
214 print("subscript atom", trailer[1])
215 #raise AssertionError("not implemented %s" % p[2][0])
216 subs = trailer[1]
217 if len(subs) == 1:
218 idx = subs[0]
219 if isinstance(idx, ast.Name) and idx.id in regs + fregs:
220 read_regs.add(idx.id)
221 if isinstance(idx, ast.Name) and idx.id in regs:
222 print ("single atom subscript, underscored", idx.id)
223 idx = ast.Name("_%s" % idx.id, ast.Load())
224 else:
225 idx = ast.Slice(subs[0], subs[1], None)
226 # if isinstance(atom, ast.Name) and atom.id == 'CR':
227 # atom.id = 'CR' # bad hack
228 #print ("apply_trailer Subscript", atom.id, idx)
229 return ast.Subscript(atom, idx, ast.Load())
230
231 ########## Parser (tokens -> AST) ######
232
233 # also part of Ply
234 #import yacc
235
236 # https://www.mathcs.emory.edu/~valerie/courses/fall10/155/resources/op_precedence.html
237 # python operator precedence
238 # Highest precedence at top, lowest at bottom.
239 # Operators in the same box evaluate left to right.
240 #
241 # Operator Description
242 # () Parentheses (grouping)
243 # f(args...) Function call
244 # x[index:index] Slicing
245 # x[index] Subscription
246 # x.attribute Attribute reference
247 # ** Exponentiation
248 # ~x Bitwise not
249 # +x, -x Positive, negative
250 # *, /, % mul, div, remainder
251 # +, - Addition, subtraction
252 # <<, >> Bitwise shifts
253 # & Bitwise AND
254 # ^ Bitwise XOR
255 # | Bitwise OR
256 # in, not in, is, is not, <, <=, >, >=, <>, !=, == comp, membership, ident
257 # not x Boolean NOT
258 # and Boolean AND
259 # or Boolean OR
260 # lambda Lambda expression
261
262
263 class PowerParser:
264
265 precedence = (
266 ("left", "EQ", "NE", "GT", "LT", "LE", "GE", "LTU", "GTU"),
267 ("left", "BITOR"),
268 ("left", "BITXOR"),
269 ("left", "BITAND"),
270 ("left", "PLUS", "MINUS"),
271 ("left", "MULT", "DIV", "MOD"),
272 ("left", "INVERT"),
273 )
274
275 def __init__(self, form, include_carry_in_write=False):
276 self.include_ca_in_write = include_carry_in_write
277 self.gprs = {}
278 if form is not None:
279 form = self.sd.sigforms[form]
280 print(form)
281 formkeys = form._asdict().keys()
282 else:
283 formkeys = []
284 self.declared_vars = set()
285 self.fnparm_vars = set()
286 for rname in regs + fregs:
287 self.gprs[rname] = None
288 self.declared_vars.add(rname)
289 self.available_op_fields = set()
290 for k in formkeys:
291 if k not in self.gprs:
292 if k == 'SPR': # sigh, lower-case to not conflict
293 k = k.lower()
294 self.available_op_fields.add(k)
295 self.op_fields = OrderedSet()
296 self.read_regs = OrderedSet()
297 self.uninit_regs = OrderedSet()
298 self.write_regs = OrderedSet()
299 self.special_regs = OrderedSet() # see p_atom_name
300
301 # The grammar comments come from Python's Grammar/Grammar file
302
303 # NB: compound_stmt in single_input is followed by extra NEWLINE!
304 # file_input: (NEWLINE | stmt)* ENDMARKER
305
306 def p_file_input_end(self, p):
307 """file_input_end : file_input ENDMARKER"""
308 print("end", p[1])
309 p[0] = p[1]
310
311 def p_file_input(self, p):
312 """file_input : file_input NEWLINE
313 | file_input stmt
314 | NEWLINE
315 | stmt"""
316 if isinstance(p[len(p)-1], str):
317 if len(p) == 3:
318 p[0] = p[1]
319 else:
320 p[0] = [] # p == 2 --> only a blank line
321 else:
322 if len(p) == 3:
323 p[0] = p[1] + p[2]
324 else:
325 p[0] = p[1]
326
327 # funcdef: [decorators] 'def' NAME parameters ':' suite
328 # ignoring decorators
329
330 def p_funcdef(self, p):
331 "funcdef : DEF NAME parameters COLON suite"
332 p[0] = ast.FunctionDef(p[2], p[3], p[5], ())
333 # reset function parameters after suite is identified
334 self.fnparm_vars = set()
335
336 # parameters: '(' [varargslist] ')'
337 def p_parameters(self, p):
338 """parameters : LPAR RPAR
339 | LPAR varargslist RPAR"""
340 if len(p) == 3:
341 args = []
342 else:
343 args = p[2]
344 p[0] = ast.arguments(args=args, vararg=None, kwarg=None, defaults=[])
345 # during the time between parameters identified and suite is not
346 # there is a window of opportunity to declare the function parameters
347 # in-scope, for use to not overwrite them with auto-assign
348 self.fnparm_vars = set()
349 for arg in args:
350 print ("adding fn parm", arg)
351 self.fnparm_vars.add(arg)
352
353 # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
354 # '**' NAME) |
355 # highly simplified
356
357 def p_varargslist(self, p):
358 """varargslist : varargslist COMMA NAME
359 | NAME"""
360 if len(p) == 4:
361 print (p[1], p[3])
362 p[0] = p[1] + [p[3]]
363 else:
364 p[0] = [p[1]]
365
366 # stmt: simple_stmt | compound_stmt
367 def p_stmt_simple(self, p):
368 """stmt : simple_stmt"""
369 # simple_stmt is a list
370 p[0] = p[1]
371
372 def p_stmt_compound(self, p):
373 """stmt : compound_stmt"""
374 p[0] = [p[1]]
375
376 # simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
377 def p_simple_stmt(self, p):
378 """simple_stmt : small_stmts NEWLINE
379 | small_stmts SEMICOLON NEWLINE"""
380 p[0] = p[1]
381
382 def p_small_stmts(self, p):
383 """small_stmts : small_stmts SEMICOLON small_stmt
384 | small_stmt"""
385 if len(p) == 4:
386 p[0] = p[1] + [p[3]]
387 elif isinstance(p[1], list):
388 p[0] = p[1]
389 else:
390 p[0] = [p[1]]
391
392 # small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
393 # import_stmt | global_stmt | exec_stmt | assert_stmt
394 def p_small_stmt(self, p):
395 """small_stmt : flow_stmt
396 | break_stmt
397 | expr_stmt"""
398 if isinstance(p[1], ast.Call):
399 p[0] = ast.Expr(p[1])
400 elif isinstance(p[1], ast.Name) and p[1].id in ['TRAP', 'SVSTATE_NEXT']:
401 fname = p[1].id
402 # TRAP and SVSTATE_NEXT needs to actually be a function
403 name = ast.Name("self", ast.Load())
404 name = ast.Attribute(name, fname, ast.Load())
405 p[0] = ast.Call(name, [], [])
406 else:
407 p[0] = p[1]
408
409 # expr_stmt: testlist (augassign (yield_expr|testlist) |
410 # ('=' (yield_expr|testlist))*)
411 # augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
412 # '<<=' | '>>=' | '**=' | '//=')
413 def p_expr_stmt(self, p):
414 """expr_stmt : testlist ASSIGNEA testlist
415 | testlist ASSIGN testlist
416 | testlist """
417 print("expr_stmt", p)
418 if len(p) == 2:
419 # a list of expressions
420 #p[0] = ast.Discard(p[1])
421 p[0] = p[1]
422 else:
423 iea_mode = p[2] == '<-iea'
424 name = None
425 autoassign = False
426 if isinstance(p[1], ast.Name):
427 name = p[1].id
428 elif isinstance(p[1], ast.Subscript):
429 if isinstance(p[1].value, ast.Name):
430 name = p[1].value.id
431 if name in self.gprs:
432 # add to list of uninitialised
433 self.uninit_regs.add(name)
434 # work out if this is an ininitialised variable
435 # that should be auto-assigned simply by being "sliced"
436 autoassign = (name not in self.declared_vars and
437 name not in self.fnparm_vars and
438 name not in self.special_regs)
439 elif isinstance(p[1], ast.Call) and p[1].func.id in \
440 ['GPR', 'FPR', 'SPR']:
441 print(astor.dump_tree(p[1]))
442 # replace GPR(x) with GPR[x]
443 idx = p[1].args[0].id
444 if idx in regs + fregs:
445 ridx = ast.Name("_%s" % idx, ast.Load())
446 else:
447 ridx = ast.Name(idx, ast.Load())
448 p[1] = ast.Subscript(p[1].func, ridx, ast.Load())
449 if idx in self.gprs:
450 self.read_regs.add(idx) # add to list of regs to read
451 elif isinstance(p[1], ast.Call) and p[1].func.id == 'MEM':
452 print("mem assign")
453 print(astor.dump_tree(p[1]))
454 p[1].func.id = "memassign" # change function name to set
455 p[1].args.append(p[3])
456 p[0] = p[1]
457 print("mem rewrite")
458 print(astor.dump_tree(p[0]))
459 return
460 else:
461 print("help, help")
462 print(astor.dump_tree(p[1]))
463 print("expr assign", name, p[1], "to", p[3])
464 if isinstance(p[3], ast.Name):
465 toname = p[3].id
466 if toname in self.gprs:
467 self.read_regs.add(toname)
468 if name and name in self.gprs:
469 self.write_regs.add(name) # add to list of regs to write
470 p[0] = Assign(autoassign, name, p[1], p[3], iea_mode)
471 if name:
472 self.declared_vars.add(name)
473
474 def p_flow_stmt(self, p):
475 "flow_stmt : return_stmt"
476 p[0] = p[1]
477
478 # return_stmt: 'return' [testlist]
479 def p_return_stmt(self, p):
480 "return_stmt : RETURN testlist"
481 p[0] = ast.Return(p[2])
482
483 def p_compound_stmt(self, p):
484 """compound_stmt : if_stmt
485 | while_stmt
486 | switch_stmt
487 | for_stmt
488 | funcdef
489 """
490 p[0] = p[1]
491
492 def p_break_stmt(self, p):
493 """break_stmt : BREAK
494 """
495 p[0] = ast.Break()
496
497 def p_for_stmt(self, p):
498 """for_stmt : FOR atom EQ comparison TO comparison COLON suite
499 | DO atom EQ comparison TO comparison COLON suite
500 """
501 start = p[4]
502 end = p[6]
503 it = ast.Call(ast.Name("RANGE", ast.Load()), (start, end), [])
504 p[0] = ast.For(p[2], it, p[8], [])
505
506 def p_while_stmt(self, p):
507 """while_stmt : DO WHILE test COLON suite ELSE COLON suite
508 | DO WHILE test COLON suite
509 """
510 if len(p) == 6:
511 p[0] = ast.While(p[3], p[5], [])
512 else:
513 p[0] = ast.While(p[3], p[5], p[8])
514
515 def p_switch_smt(self, p):
516 """switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT switches DEDENT
517 """
518 switchon = p[3]
519 print("switch stmt")
520 print(astor.dump_tree(p[1]))
521
522 cases = []
523 current_cases = [] # for deferral
524 for (case, suite) in p[8]:
525 print("for", case, suite)
526 if suite is None:
527 for c in case:
528 current_cases.append(ast.Num(c))
529 continue
530 if case == 'default': # last
531 break
532 for c in case:
533 current_cases.append(ast.Num(c))
534 print("cases", current_cases)
535 compare = ast.Compare(switchon, [ast.In()],
536 [ast.List(current_cases, ast.Load())])
537 current_cases = []
538 cases.append((compare, suite))
539
540 print("ended", case, current_cases)
541 if case == 'default':
542 if current_cases:
543 compare = ast.Compare(switchon, [ast.In()],
544 [ast.List(current_cases, ast.Load())])
545 cases.append((compare, suite))
546 cases.append((None, suite))
547
548 cases.reverse()
549 res = []
550 for compare, suite in cases:
551 print("after rev", compare, suite)
552 if compare is None:
553 assert len(res) == 0, "last case should be default"
554 res = suite
555 else:
556 if not isinstance(res, list):
557 res = [res]
558 res = ast.If(compare, suite, res)
559 p[0] = res
560
561 def p_switches(self, p):
562 """switches : switch_list switch_default
563 | switch_default
564 """
565 if len(p) == 3:
566 p[0] = p[1] + [p[2]]
567 else:
568 p[0] = [p[1]]
569
570 def p_switch_list(self, p):
571 """switch_list : switch_case switch_list
572 | switch_case
573 """
574 if len(p) == 3:
575 p[0] = [p[1]] + p[2]
576 else:
577 p[0] = [p[1]]
578
579 def p_switch_case(self, p):
580 """switch_case : CASE LPAR atomlist RPAR COLON suite
581 """
582 # XXX bad hack
583 if isinstance(p[6][0], ast.Name) and p[6][0].id == 'fallthrough':
584 p[6] = None
585 p[0] = (p[3], p[6])
586
587 def p_switch_default(self, p):
588 """switch_default : DEFAULT COLON suite
589 """
590 p[0] = ('default', p[3])
591
592 def p_atomlist(self, p):
593 """atomlist : atom COMMA atomlist
594 | atom
595 """
596 assert isinstance(p[1], ast.Constant), "case must be numbers"
597 if len(p) == 4:
598 p[0] = [p[1].value] + p[3]
599 else:
600 p[0] = [p[1].value]
601
602 def p_if_stmt(self, p):
603 """if_stmt : IF test COLON suite ELSE COLON if_stmt
604 | IF test COLON suite ELSE COLON suite
605 | IF test COLON suite
606 """
607 if len(p) == 8 and isinstance(p[7], ast.If):
608 p[0] = ast.If(p[2], p[4], [p[7]])
609 elif len(p) == 5:
610 p[0] = ast.If(p[2], p[4], [])
611 else:
612 p[0] = ast.If(p[2], p[4], p[7])
613
614 def p_suite(self, p):
615 """suite : simple_stmt
616 | NEWLINE INDENT stmts DEDENT"""
617 if len(p) == 2:
618 p[0] = p[1]
619 else:
620 p[0] = p[3]
621
622 def p_stmts(self, p):
623 """stmts : stmts stmt
624 | stmt"""
625 if len(p) == 3:
626 p[0] = p[1] + p[2]
627 else:
628 p[0] = p[1]
629
630 def p_comparison(self, p):
631 """comparison : comparison PLUS comparison
632 | comparison MINUS comparison
633 | comparison MULT comparison
634 | comparison DIV comparison
635 | comparison MOD comparison
636 | comparison EQ comparison
637 | comparison NE comparison
638 | comparison LE comparison
639 | comparison GE comparison
640 | comparison LTU comparison
641 | comparison GTU comparison
642 | comparison LT comparison
643 | comparison GT comparison
644 | comparison BITOR comparison
645 | comparison BITXOR comparison
646 | comparison BITAND comparison
647 | PLUS comparison
648 | MINUS comparison
649 | INVERT comparison
650 | comparison APPEND comparison
651 | power"""
652 if len(p) == 4:
653 print(list(p))
654 if p[2] == '<u':
655 p[0] = ast.Call(ast.Name("ltu", ast.Load()), (p[1], p[3]), [])
656 elif p[2] == '>u':
657 p[0] = ast.Call(ast.Name("gtu", ast.Load()), (p[1], p[3]), [])
658 elif p[2] == '||':
659 l = check_concat(p[1]) + check_concat(p[3])
660 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, [])
661 elif p[2] in ['/', '%']:
662 # bad hack: if % or / used anywhere other than div/mod ops,
663 # do % or /. however if the argument names are "dividend"
664 # we must call the special trunc_divs and trunc_rems functions
665 l, r = p[1], p[3]
666 # actual call will be "dividend / divisor" - just check
667 # LHS name
668 # XXX DISABLE BAD HACK (False)
669 if False and isinstance(l, ast.Name) and l.id == 'dividend':
670 if p[2] == '/':
671 fn = 'trunc_divs'
672 else:
673 fn = 'trunc_rems'
674 # return "function trunc_xxx(l, r)"
675 p[0] = ast.Call(ast.Name(fn, ast.Load()), (l, r), [])
676 else:
677 # return "l {binop} r"
678 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
679 elif p[2] in ['<', '>', '=', '<=', '>=', '!=']:
680 p[0] = binary_ops[p[2]]((p[1], p[3]))
681 elif identify_sint_mul_pattern(p):
682 keywords = [ast.keyword(arg='repeat', value=p[3])]
683 l = p[1].elts
684 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
685 else:
686 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
687 elif len(p) == 3:
688 if isinstance(p[2], str) and p[2] == '-':
689 p[0] = ast.UnaryOp(unary_ops[p[2]], p[1])
690 else:
691 p[0] = ast.UnaryOp(unary_ops[p[1]], p[2])
692 else:
693 p[0] = p[1]
694
695 # power: atom trailer* ['**' factor]
696 # trailers enables function calls (and subscripts).
697 # so this is 'trailerlist'
698 def p_power(self, p):
699 """power : atom
700 | atom trailerlist"""
701 if len(p) == 2:
702 print("power dump atom notrailer")
703 print(astor.dump_tree(p[1]))
704 p[0] = p[1]
705 else:
706 print("power dump atom")
707 print(astor.dump_tree(p[1]))
708 print("power dump trailerlist")
709 print(astor.dump_tree(p[2]))
710 p[0] = apply_trailer(p[1], p[2], self.read_regs)
711 if isinstance(p[1], ast.Name):
712 name = p[1].id
713 if name in regs + fregs:
714 self.read_regs.add(name)
715
716 def p_atom_name(self, p):
717 """atom : NAME"""
718 name = p[1]
719 if name in self.available_op_fields:
720 self.op_fields.add(name)
721 if name == 'overflow':
722 self.write_regs.add(name)
723 if self.include_ca_in_write:
724 if name in ['CA', 'CA32']:
725 self.write_regs.add(name)
726 if name in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR',
727 'SVSTATE', 'SVREMAP',
728 'SVSHAPE0', 'SVSHAPE1', 'SVSHAPE2', 'SVSHAPE3']:
729 self.special_regs.add(name)
730 self.write_regs.add(name) # and add to list to write
731 p[0] = ast.Name(id=name, ctx=ast.Load())
732
733 def p_atom_number(self, p):
734 """atom : BINARY
735 | NUMBER
736 | HEX
737 | STRING"""
738 p[0] = ast.Constant(p[1])
739
740 # '[' [listmaker] ']' |
741
742 def p_atom_listmaker(self, p):
743 """atom : LBRACK listmaker RBRACK"""
744 p[0] = p[2]
745
746 def p_listmaker(self, p):
747 """listmaker : test COMMA listmaker
748 | test
749 """
750 if len(p) == 2:
751 p[0] = ast.List([p[1]], ast.Load())
752 else:
753 p[0] = ast.List([p[1]] + p[3].nodes, ast.Load())
754
755 def p_atom_tuple(self, p):
756 """atom : LPAR testlist RPAR"""
757 print("tuple", p[2])
758 print("astor dump")
759 print(astor.dump_tree(p[2]))
760
761 if isinstance(p[2], ast.Name):
762 name = p[2].id
763 print("tuple name", name)
764 if name in self.gprs:
765 self.read_regs.add(name) # add to list of regs to read
766 #p[0] = ast.Subscript(ast.Name("GPR", ast.Load()), ast.Str(p[2].id))
767 # return
768 p[0] = p[2]
769 elif isinstance(p[2], ast.BinOp):
770 if isinstance(p[2].left, ast.Name) and \
771 isinstance(p[2].right, ast.Constant) and \
772 p[2].right.value == 0 and \
773 p[2].left.id in self.gprs:
774 rid = p[2].left.id
775 self.read_regs.add(rid) # add to list of regs to read
776 # create special call to GPR.getz or FPR.getz
777 if rid in fregs:
778 gprz = ast.Name("FPR", ast.Load())
779 else:
780 gprz = ast.Name("GPR", ast.Load())
781 # get testzero function
782 gprz = ast.Attribute(gprz, "getz", ast.Load())
783 # *sigh* see class GPR. we need index itself not reg value
784 ridx = ast.Name("_%s" % rid, ast.Load())
785 p[0] = ast.Call(gprz, [ridx], [])
786 print("tree", astor.dump_tree(p[0]))
787 else:
788 p[0] = p[2]
789 else:
790 p[0] = p[2]
791
792 def p_trailerlist(self, p):
793 """trailerlist : trailer trailerlist
794 | trailer
795 """
796 if len(p) == 2:
797 p[0] = p[1]
798 else:
799 p[0] = ("TLIST", p[1], p[2])
800
801 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
802 def p_trailer(self, p):
803 """trailer : trailer_arglist
804 | trailer_subscript
805 """
806 p[0] = p[1]
807
808 def p_trailer_arglist(self, p):
809 "trailer_arglist : LPAR arglist RPAR"
810 p[0] = ("CALL", p[2])
811
812 def p_trailer_subscript(self, p):
813 "trailer_subscript : LBRACK subscript RBRACK"
814 p[0] = ("SUBS", p[2])
815
816 # subscript: '.' '.' '.' | test | [test] ':' [test]
817
818 def p_subscript(self, p):
819 """subscript : test COLON test
820 | test
821 """
822 if len(p) == 4:
823 # add one to end
824 if isinstance(p[3], ast.Constant):
825 end = ast.Constant(p[3].value+1)
826 else:
827 end = ast.BinOp(p[3], ast.Add(), ast.Constant(1))
828 p[0] = [p[1], end]
829 else:
830 p[0] = [p[1]]
831
832 # testlist: test (',' test)* [',']
833 # Contains shift/reduce error
834
835 def p_testlist(self, p):
836 """testlist : testlist_multi COMMA
837 | testlist_multi """
838 if len(p) == 2:
839 p[0] = p[1]
840 else:
841 # May need to promote singleton to tuple
842 if isinstance(p[1], list):
843 p[0] = p[1]
844 else:
845 p[0] = [p[1]]
846 # Convert into a tuple?
847 if isinstance(p[0], list):
848 p[0] = ast.Tuple(p[0])
849
850 def p_testlist_multi(self, p):
851 """testlist_multi : testlist_multi COMMA test
852 | test"""
853 if len(p) == 2:
854 # singleton
855 p[0] = p[1]
856 else:
857 if isinstance(p[1], list):
858 p[0] = p[1] + [p[3]]
859 else:
860 # singleton -> tuple
861 p[0] = [p[1], p[3]]
862
863 # test: or_test ['if' or_test 'else' test] | lambdef
864 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
865
866 def p_test(self, p):
867 "test : comparison"
868 p[0] = p[1]
869
870 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
871 # | '**' test)
872 # XXX INCOMPLETE: this doesn't allow the trailing comma
873
874 def p_arglist(self, p):
875 """arglist : arglist COMMA argument
876 | argument"""
877 if len(p) == 4:
878 p[0] = p[1] + [p[3]]
879 else:
880 p[0] = [p[1]]
881
882 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
883 def p_argument(self, p):
884 "argument : test"
885 p[0] = p[1]
886
887 def p_error(self, p):
888 # print "Error!", repr(p)
889 raise SyntaxError(p)
890
891
892 class GardenSnakeParser(PowerParser):
893 def __init__(self, lexer=None, debug=False, form=None, incl_carry=False):
894 if form is not None:
895 self.sd = create_pdecode()
896 PowerParser.__init__(self, form, incl_carry)
897 self.debug = debug
898 if lexer is None:
899 lexer = IndentLexer(debug=0)
900 self.lexer = lexer
901 self.tokens = lexer.tokens
902 self.parser = yacc.yacc(module=self, start="file_input_end",
903 debug=debug, write_tables=False)
904
905 def parse(self, code):
906 # self.lexer.input(code)
907 result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
908 return ast.Module(result)
909
910
911 ###### Code generation ######
912
913 #from compiler import misc, syntax, pycodegen
914
915 _CACHED_PARSERS = {}
916 _CACHE_PARSERS = True
917
918
919 class GardenSnakeCompiler(object):
920 def __init__(self, debug=False, form=None, incl_carry=False):
921 if _CACHE_PARSERS:
922 try:
923 parser = _CACHED_PARSERS[debug, form, incl_carry]
924 except KeyError:
925 parser = GardenSnakeParser(debug=debug, form=form,
926 incl_carry=incl_carry)
927 _CACHED_PARSERS[debug, form, incl_carry] = parser
928
929 self.parser = deepcopy(parser)
930 else:
931 self.parser = GardenSnakeParser(debug=debug, form=form,
932 incl_carry=incl_carry)
933
934 def compile(self, code, mode="exec", filename="<string>"):
935 tree = self.parser.parse(code)
936 print("snake")
937 pprint(tree)
938 return tree
939 #misc.set_filename(filename, tree)
940 return compile(tree, mode="exec", filename="<string>")
941 # syntax.check(tree)
942 gen = pycodegen.ModuleCodeGenerator(tree)
943 code = gen.getCode()
944 return code