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