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