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