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