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