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