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