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