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