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