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