no python files to be committed in isafunctions
[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 test TO test COLON suite
499 | DO atom EQ test TO test COLON suite
500 """
501 start = p[4]
502 end = p[6]
503 if start.value > end.value: # start greater than end, must go -ve
504 # auto-subtract-one (sigh) due to python range
505 end = ast.BinOp(p[6], ast.Add(), ast.Constant(-1))
506 arange = [start, end, ast.Constant(-1)]
507 else:
508 # auto-add-one (sigh) due to python range
509 end = ast.BinOp(p[6], ast.Add(), ast.Constant(1))
510 arange = [start, end]
511 it = ast.Call(ast.Name("range", ast.Load()), arange, [])
512 p[0] = ast.For(p[2], it, p[8], [])
513
514 def p_while_stmt(self, p):
515 """while_stmt : DO WHILE test COLON suite ELSE COLON suite
516 | DO WHILE test COLON suite
517 """
518 if len(p) == 6:
519 p[0] = ast.While(p[3], p[5], [])
520 else:
521 p[0] = ast.While(p[3], p[5], p[8])
522
523 def p_switch_smt(self, p):
524 """switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT switches DEDENT
525 """
526 switchon = p[3]
527 print("switch stmt")
528 print(astor.dump_tree(p[1]))
529
530 cases = []
531 current_cases = [] # for deferral
532 for (case, suite) in p[8]:
533 print("for", case, suite)
534 if suite is None:
535 for c in case:
536 current_cases.append(ast.Num(c))
537 continue
538 if case == 'default': # last
539 break
540 for c in case:
541 current_cases.append(ast.Num(c))
542 print("cases", current_cases)
543 compare = ast.Compare(switchon, [ast.In()],
544 [ast.List(current_cases, ast.Load())])
545 current_cases = []
546 cases.append((compare, suite))
547
548 print("ended", case, current_cases)
549 if case == 'default':
550 if current_cases:
551 compare = ast.Compare(switchon, [ast.In()],
552 [ast.List(current_cases, ast.Load())])
553 cases.append((compare, suite))
554 cases.append((None, suite))
555
556 cases.reverse()
557 res = []
558 for compare, suite in cases:
559 print("after rev", compare, suite)
560 if compare is None:
561 assert len(res) == 0, "last case should be default"
562 res = suite
563 else:
564 if not isinstance(res, list):
565 res = [res]
566 res = ast.If(compare, suite, res)
567 p[0] = res
568
569 def p_switches(self, p):
570 """switches : switch_list switch_default
571 | switch_default
572 """
573 if len(p) == 3:
574 p[0] = p[1] + [p[2]]
575 else:
576 p[0] = [p[1]]
577
578 def p_switch_list(self, p):
579 """switch_list : switch_case switch_list
580 | switch_case
581 """
582 if len(p) == 3:
583 p[0] = [p[1]] + p[2]
584 else:
585 p[0] = [p[1]]
586
587 def p_switch_case(self, p):
588 """switch_case : CASE LPAR atomlist RPAR COLON suite
589 """
590 # XXX bad hack
591 if isinstance(p[6][0], ast.Name) and p[6][0].id == 'fallthrough':
592 p[6] = None
593 p[0] = (p[3], p[6])
594
595 def p_switch_default(self, p):
596 """switch_default : DEFAULT COLON suite
597 """
598 p[0] = ('default', p[3])
599
600 def p_atomlist(self, p):
601 """atomlist : atom COMMA atomlist
602 | atom
603 """
604 assert isinstance(p[1], ast.Constant), "case must be numbers"
605 if len(p) == 4:
606 p[0] = [p[1].value] + p[3]
607 else:
608 p[0] = [p[1].value]
609
610 def p_if_stmt(self, p):
611 """if_stmt : IF test COLON suite ELSE COLON if_stmt
612 | IF test COLON suite ELSE COLON suite
613 | IF test COLON suite
614 """
615 if len(p) == 8 and isinstance(p[7], ast.If):
616 p[0] = ast.If(p[2], p[4], [p[7]])
617 elif len(p) == 5:
618 p[0] = ast.If(p[2], p[4], [])
619 else:
620 p[0] = ast.If(p[2], p[4], p[7])
621
622 def p_suite(self, p):
623 """suite : simple_stmt
624 | NEWLINE INDENT stmts DEDENT"""
625 if len(p) == 2:
626 p[0] = p[1]
627 else:
628 p[0] = p[3]
629
630 def p_stmts(self, p):
631 """stmts : stmts stmt
632 | stmt"""
633 if len(p) == 3:
634 p[0] = p[1] + p[2]
635 else:
636 p[0] = p[1]
637
638 def p_comparison(self, p):
639 """comparison : comparison PLUS comparison
640 | comparison MINUS comparison
641 | comparison MULT comparison
642 | comparison DIV comparison
643 | comparison MOD comparison
644 | comparison EQ comparison
645 | comparison NE comparison
646 | comparison LE comparison
647 | comparison GE comparison
648 | comparison LTU comparison
649 | comparison GTU comparison
650 | comparison LT comparison
651 | comparison GT comparison
652 | comparison BITOR comparison
653 | comparison BITXOR comparison
654 | comparison BITAND comparison
655 | PLUS comparison
656 | comparison MINUS
657 | INVERT comparison
658 | comparison APPEND comparison
659 | power"""
660 if len(p) == 4:
661 print(list(p))
662 if p[2] == '<u':
663 p[0] = ast.Call(ast.Name("ltu", ast.Load()), (p[1], p[3]), [])
664 elif p[2] == '>u':
665 p[0] = ast.Call(ast.Name("gtu", ast.Load()), (p[1], p[3]), [])
666 elif p[2] == '||':
667 l = check_concat(p[1]) + check_concat(p[3])
668 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, [])
669 elif p[2] in ['/', '%']:
670 # bad hack: if % or / used anywhere other than div/mod ops,
671 # do % or /. however if the argument names are "dividend"
672 # we must call the special trunc_divs and trunc_rems functions
673 l, r = p[1], p[3]
674 # actual call will be "dividend / divisor" - just check
675 # LHS name
676 # XXX DISABLE BAD HACK (False)
677 if False and isinstance(l, ast.Name) and l.id == 'dividend':
678 if p[2] == '/':
679 fn = 'trunc_divs'
680 else:
681 fn = 'trunc_rems'
682 # return "function trunc_xxx(l, r)"
683 p[0] = ast.Call(ast.Name(fn, ast.Load()), (l, r), [])
684 else:
685 # return "l {binop} r"
686 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
687 elif p[2] in ['<', '>', '=', '<=', '>=', '!=']:
688 p[0] = binary_ops[p[2]]((p[1], p[3]))
689 elif identify_sint_mul_pattern(p):
690 keywords = [ast.keyword(arg='repeat', value=p[3])]
691 l = p[1].elts
692 p[0] = ast.Call(ast.Name("concat", ast.Load()), l, keywords)
693 else:
694 p[0] = ast.BinOp(p[1], binary_ops[p[2]], p[3])
695 elif len(p) == 3:
696 if isinstance(p[2], str) and p[2] == '-':
697 p[0] = ast.UnaryOp(unary_ops[p[2]], p[1])
698 else:
699 p[0] = ast.UnaryOp(unary_ops[p[1]], p[2])
700 else:
701 p[0] = p[1]
702
703 # power: atom trailer* ['**' factor]
704 # trailers enables function calls (and subscripts).
705 # so this is 'trailerlist'
706 def p_power(self, p):
707 """power : atom
708 | atom trailerlist"""
709 if len(p) == 2:
710 print("power dump atom notrailer")
711 print(astor.dump_tree(p[1]))
712 p[0] = p[1]
713 else:
714 print("power dump atom")
715 print(astor.dump_tree(p[1]))
716 print("power dump trailerlist")
717 print(astor.dump_tree(p[2]))
718 p[0] = apply_trailer(p[1], p[2], self.read_regs)
719 if isinstance(p[1], ast.Name):
720 name = p[1].id
721 if name in regs + fregs:
722 self.read_regs.add(name)
723
724 def p_atom_name(self, p):
725 """atom : NAME"""
726 name = p[1]
727 if name in self.available_op_fields:
728 self.op_fields.add(name)
729 if name == 'overflow':
730 self.write_regs.add(name)
731 if self.include_ca_in_write:
732 if name in ['CA', 'CA32']:
733 self.write_regs.add(name)
734 if name in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR',
735 'SVSTATE', 'SVREMAP',
736 'SVSHAPE0', 'SVSHAPE1', 'SVSHAPE2', 'SVSHAPE3']:
737 self.special_regs.add(name)
738 self.write_regs.add(name) # and add to list to write
739 p[0] = ast.Name(id=name, ctx=ast.Load())
740
741 def p_atom_number(self, p):
742 """atom : BINARY
743 | NUMBER
744 | HEX
745 | STRING"""
746 p[0] = ast.Constant(p[1])
747
748 # '[' [listmaker] ']' |
749
750 def p_atom_listmaker(self, p):
751 """atom : LBRACK listmaker RBRACK"""
752 p[0] = p[2]
753
754 def p_listmaker(self, p):
755 """listmaker : test COMMA listmaker
756 | test
757 """
758 if len(p) == 2:
759 p[0] = ast.List([p[1]], ast.Load())
760 else:
761 p[0] = ast.List([p[1]] + p[3].nodes, ast.Load())
762
763 def p_atom_tuple(self, p):
764 """atom : LPAR testlist RPAR"""
765 print("tuple", p[2])
766 print("astor dump")
767 print(astor.dump_tree(p[2]))
768
769 if isinstance(p[2], ast.Name):
770 name = p[2].id
771 print("tuple name", name)
772 if name in self.gprs:
773 self.read_regs.add(name) # add to list of regs to read
774 #p[0] = ast.Subscript(ast.Name("GPR", ast.Load()), ast.Str(p[2].id))
775 # return
776 p[0] = p[2]
777 elif isinstance(p[2], ast.BinOp):
778 if isinstance(p[2].left, ast.Name) and \
779 isinstance(p[2].right, ast.Constant) and \
780 p[2].right.value == 0 and \
781 p[2].left.id in self.gprs:
782 rid = p[2].left.id
783 self.read_regs.add(rid) # add to list of regs to read
784 # create special call to GPR.getz or FPR.getz
785 if rid in fregs:
786 gprz = ast.Name("FPR", ast.Load())
787 else:
788 gprz = ast.Name("GPR", ast.Load())
789 # get testzero function
790 gprz = ast.Attribute(gprz, "getz", ast.Load())
791 # *sigh* see class GPR. we need index itself not reg value
792 ridx = ast.Name("_%s" % rid, ast.Load())
793 p[0] = ast.Call(gprz, [ridx], [])
794 print("tree", astor.dump_tree(p[0]))
795 else:
796 p[0] = p[2]
797 else:
798 p[0] = p[2]
799
800 def p_trailerlist(self, p):
801 """trailerlist : trailer trailerlist
802 | trailer
803 """
804 if len(p) == 2:
805 p[0] = p[1]
806 else:
807 p[0] = ("TLIST", p[1], p[2])
808
809 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
810 def p_trailer(self, p):
811 """trailer : trailer_arglist
812 | trailer_subscript
813 """
814 p[0] = p[1]
815
816 def p_trailer_arglist(self, p):
817 "trailer_arglist : LPAR arglist RPAR"
818 p[0] = ("CALL", p[2])
819
820 def p_trailer_subscript(self, p):
821 "trailer_subscript : LBRACK subscript RBRACK"
822 p[0] = ("SUBS", p[2])
823
824 # subscript: '.' '.' '.' | test | [test] ':' [test]
825
826 def p_subscript(self, p):
827 """subscript : test COLON test
828 | test
829 """
830 if len(p) == 4:
831 # add one to end
832 if isinstance(p[3], ast.Constant):
833 end = ast.Constant(p[3].value+1)
834 else:
835 end = ast.BinOp(p[3], ast.Add(), ast.Constant(1))
836 p[0] = [p[1], end]
837 else:
838 p[0] = [p[1]]
839
840 # testlist: test (',' test)* [',']
841 # Contains shift/reduce error
842
843 def p_testlist(self, p):
844 """testlist : testlist_multi COMMA
845 | testlist_multi """
846 if len(p) == 2:
847 p[0] = p[1]
848 else:
849 # May need to promote singleton to tuple
850 if isinstance(p[1], list):
851 p[0] = p[1]
852 else:
853 p[0] = [p[1]]
854 # Convert into a tuple?
855 if isinstance(p[0], list):
856 p[0] = ast.Tuple(p[0])
857
858 def p_testlist_multi(self, p):
859 """testlist_multi : testlist_multi COMMA test
860 | test"""
861 if len(p) == 2:
862 # singleton
863 p[0] = p[1]
864 else:
865 if isinstance(p[1], list):
866 p[0] = p[1] + [p[3]]
867 else:
868 # singleton -> tuple
869 p[0] = [p[1], p[3]]
870
871 # test: or_test ['if' or_test 'else' test] | lambdef
872 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
873
874 def p_test(self, p):
875 "test : comparison"
876 p[0] = p[1]
877
878 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
879 # | '**' test)
880 # XXX INCOMPLETE: this doesn't allow the trailing comma
881
882 def p_arglist(self, p):
883 """arglist : arglist COMMA argument
884 | argument"""
885 if len(p) == 4:
886 p[0] = p[1] + [p[3]]
887 else:
888 p[0] = [p[1]]
889
890 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
891 def p_argument(self, p):
892 "argument : test"
893 p[0] = p[1]
894
895 def p_error(self, p):
896 # print "Error!", repr(p)
897 raise SyntaxError(p)
898
899
900 class GardenSnakeParser(PowerParser):
901 def __init__(self, lexer=None, debug=False, form=None, incl_carry=False):
902 if form is not None:
903 self.sd = create_pdecode()
904 PowerParser.__init__(self, form, incl_carry)
905 self.debug = debug
906 if lexer is None:
907 lexer = IndentLexer(debug=0)
908 self.lexer = lexer
909 self.tokens = lexer.tokens
910 self.parser = yacc.yacc(module=self, start="file_input_end",
911 debug=debug, write_tables=False)
912
913 def parse(self, code):
914 # self.lexer.input(code)
915 result = self.parser.parse(code, lexer=self.lexer, debug=self.debug)
916 return ast.Module(result)
917
918
919 ###### Code generation ######
920
921 #from compiler import misc, syntax, pycodegen
922
923 _CACHED_PARSERS = {}
924 _CACHE_PARSERS = True
925
926
927 class GardenSnakeCompiler(object):
928 def __init__(self, debug=False, form=None, incl_carry=False):
929 if _CACHE_PARSERS:
930 try:
931 parser = _CACHED_PARSERS[debug, form, incl_carry]
932 except KeyError:
933 parser = GardenSnakeParser(debug=debug, form=form,
934 incl_carry=incl_carry)
935 _CACHED_PARSERS[debug, form, incl_carry] = parser
936
937 self.parser = deepcopy(parser)
938 else:
939 self.parser = GardenSnakeParser(debug=debug, form=form,
940 incl_carry=incl_carry)
941
942 def compile(self, code, mode="exec", filename="<string>"):
943 tree = self.parser.parse(code)
944 print("snake")
945 pprint(tree)
946 return tree
947 #misc.set_filename(filename, tree)
948 return compile(tree, mode="exec", filename="<string>")
949 # syntax.check(tree)
950 gen = pycodegen.ModuleCodeGenerator(tree)
951 code = gen.getCode()
952 return code