1 # Based on GardenSnake - a parser generator demonstration program
2 # GardenSnake was released into the Public Domain by Andrew Dalke.
4 # Portions of this work are derived from Python's Grammar definition
5 # and may be covered under the Python copyright and license
7 # Andrew Dalke / Dalke Scientific Software, LLC
8 # 30 August 2006 / Cape Town, South Africa
10 # Modifications for inclusion in PLY distribution
11 from pprint
import pprint
12 from ply
import lex
, yacc
14 from copy
import deepcopy
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
22 # I use the Python AST
23 #from compiler import ast
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'}
34 # I implemented INDENT / DEDENT generation as a post-processing filter
36 # The original lex token stream contains WS and NEWLINE characters.
37 # WS will only occur before any other tokens on a line.
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.
46 # No using Python's approach because Ply supports precedence
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'
54 def make_le_compare(arg
):
56 return ast
.Call(ast
.Name("le", ast
.Load()), [left
, right
], [])
59 def make_ge_compare(arg
):
61 return ast
.Call(ast
.Name("ge", ast
.Load()), [left
, right
], [])
64 def make_lt_compare(arg
):
66 return ast
.Call(ast
.Name("lt", ast
.Load()), [left
, right
], [])
69 def make_gt_compare(arg
):
71 return ast
.Call(ast
.Name("gt", ast
.Load()), [left
, right
], [])
74 def make_eq_compare(arg
):
76 return ast
.Call(ast
.Name("eq", ast
.Load()), [left
, right
], [])
79 def make_ne_compare(arg
):
81 return ast
.Call(ast
.Name("ne", ast
.Load()), [left
, right
], [])
95 "<=": make_le_compare
,
96 ">=": make_ge_compare
,
100 "!=": make_ne_compare
,
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
):
113 node_func_id
= getattr(node
.func
, "id", None)
114 print("func", node_func_id
)
115 if node_func_id
!= 'concat':
117 if node
.keywords
: # a repeated list-constant, don't optimise
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:
130 these must specifically be returned as concat(item, repeat=something)
132 #print ("identify_sint_mul_pattern")
134 # print(" ", astor.dump_tree(pat))
135 if p
[2] != '*': # multiply
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
142 if not isinstance(p
[1], ast
.List
): # lhs is a list
145 if len(l
) != 1: # lhs is a list of length 1
147 return True # yippee!
150 ########## Parser (tokens -> AST) ######
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.
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
168 # +x, -x Positive, negative
169 # *, /, % mul, div, remainder
170 # +, - Addition, subtraction
171 # <<, >> Bitwise shifts
175 # in, not in, is, is not, <, <=, >, >=, <>, !=, == comp, membership, ident
179 # lambda Lambda expression
185 ("left", "EQ", "NE", "GT", "LT", "LE", "GE", "LTU", "GTU"),
189 ("left", "LSHIFT", "RSHIFT"),
190 ("left", "PLUS", "MINUS"),
191 ("left", "MULT", "DIV", "MOD"),
197 if self
.form
is not None:
198 form
= self
.sd
.sigforms
[self
.form
]
200 formkeys
= form
._asdict
().keys()
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()
210 if k
not in self
.gprs
:
211 if k
== 'SPR': # sigh, lower-case to not conflict
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
220 def __init__(self
, form
, include_carry_in_write
=False, helper
=False):
221 self
.include_ca_in_write
= include_carry_in_write
225 self
.input_text
= None
228 # The grammar comments come from Python's Grammar/Grammar file
230 # NB: compound_stmt in single_input is followed by extra NEWLINE!
231 # file_input: (NEWLINE | stmt)* ENDMARKER
233 def p_file_input_end(self
, p
):
234 """file_input_end : file_input ENDMARKER"""
238 def p_file_input(self
, p
):
239 """file_input : file_input NEWLINE
243 if isinstance(p
[len(p
)-1], str):
247 p
[0] = [] # p == 2 --> only a blank line
254 # funcdef: [decorators] 'def' NAME parameters ':' suite
255 # ignoring decorators
257 def p_funcdef(self
, p
):
258 "funcdef : DEF NAME parameters COLON suite"
260 p
[0] = ast
.FunctionDef()
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()
269 # parameters: '(' [varargslist] ')'
270 def p_parameters(self
, p
):
271 """parameters : LPAR RPAR
272 | LPAR varargslist RPAR"""
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()
284 print("adding fn parm", arg
)
285 self
.fnparm_vars
.add(arg
)
287 # varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] |
291 def p_varargslist(self
, p
):
292 """varargslist : varargslist COMMA NAME
300 # stmt: simple_stmt | compound_stmt
301 def p_stmt_simple(self
, p
):
302 """stmt : simple_stmt"""
303 # simple_stmt is a list
306 def p_stmt_compound(self
, p
):
307 """stmt : compound_stmt"""
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"""
316 def p_small_stmts(self
, p
):
317 """small_stmts : small_stmts SEMICOLON small_stmt
321 elif isinstance(p
[1], list):
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
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
:
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])
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
352 print("expr_stmt", p
)
354 # a list of expressions
355 #p[0] = ast.Discard(p[1])
358 iea_mode
= p
[2] == '<-iea'
363 if isinstance(p
[1], ast
.Name
):
365 elif isinstance(p
[1], ast
.Subscript
):
366 print("assign subscript", p
[1].value
,
370 print(astor
.dump_tree(p
[1]))
371 print(astor
.dump_tree(p
[3]))
372 if isinstance(p
[1].value
, ast
.Name
):
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
)):
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())
393 ridx
= ast
.Name(idx
, ast
.Load())
394 p
[1] = ast
.Subscript(p
[1].func
, ridx
, ast
.Load())
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':
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])
404 print(astor
.dump_tree(p
[0]))
407 print(astor
.dump_tree(p
[1]))
408 raise_syntax_error("help, help", self
.filename
,
409 p
.slice[2].lineno
, p
.slice[2].lexpos
,
411 print("expr assign", name
, p
[1], "to", p
[3])
412 print(astor
.dump_tree(p
[3]))
413 if isinstance(p
[3], ast
.Name
):
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
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
,
427 self
.declared_vars
.add(name
)
429 def p_flow_stmt(self
, p
):
430 "flow_stmt : return_stmt"
433 # return_stmt: 'return' [testlist]
434 def p_return_stmt(self
, p
):
435 "return_stmt : RETURN testlist"
436 p
[0] = ast
.Return(p
[2])
438 def p_compound_stmt(self
, p
):
439 """compound_stmt : if_stmt
447 def p_break_stmt(self
, p
):
448 """break_stmt : BREAK
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
458 it
= ast
.Call(ast
.Name("RANGE", ast
.Load()), (start
, end
), [])
459 p
[0] = ast
.For(p
[2], it
, p
[8], [])
461 def p_while_stmt(self
, p
):
462 """while_stmt : DO WHILE test COLON suite ELSE COLON suite
463 | DO WHILE test COLON suite
466 p
[0] = ast
.While(p
[3], p
[5], [])
468 p
[0] = ast
.While(p
[3], p
[5], p
[8])
470 def p_switch_smt(self
, p
):
471 """switch_stmt : SWITCH LPAR atom RPAR COLON NEWLINE INDENT switches DEDENT
475 print(astor
.dump_tree(p
[1]))
478 current_cases
= [] # for deferral
479 for (case
, suite
) in p
[8]:
480 print("for", case
, suite
)
483 current_cases
.append(ast
.Num(c
))
485 if case
== 'default': # last
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())])
493 cases
.append((compare
, suite
))
495 print("ended", case
, current_cases
)
496 if case
== 'default':
498 compare
= ast
.Compare(switchon
, [ast
.In()],
499 [ast
.List(current_cases
, ast
.Load())])
500 cases
.append((compare
, suite
))
501 cases
.append((None, suite
))
505 for compare
, suite
in cases
:
506 print("after rev", compare
, suite
)
508 assert len(res
) == 0, "last case should be default"
511 if not isinstance(res
, list):
513 res
= ast
.If(compare
, suite
, res
)
516 def p_switches(self
, p
):
517 """switches : switch_list switch_default
525 def p_switch_list(self
, p
):
526 """switch_list : switch_case switch_list
534 def p_switch_case(self
, p
):
535 """switch_case : CASE LPAR atomlist RPAR COLON suite
538 if isinstance(p
[6][0], ast
.Name
) and p
[6][0].id == 'fallthrough':
542 def p_switch_default(self
, p
):
543 """switch_default : DEFAULT COLON suite
545 p
[0] = ('default', p
[3])
547 def p_atomlist(self
, p
):
548 """atomlist : atom COMMA atomlist
551 assert isinstance(p
[1], ast
.Constant
), "case must be numbers"
553 p
[0] = [p
[1].value
] + p
[3]
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
562 if len(p
) == 8 and isinstance(p
[7], ast
.If
):
563 p
[0] = ast
.If(p
[2], p
[4], [p
[7]])
565 p
[0] = ast
.If(p
[2], p
[4], [])
567 p
[0] = ast
.If(p
[2], p
[4], p
[7])
569 def p_suite(self
, p
):
570 """suite : simple_stmt
571 | NEWLINE INDENT stmts DEDENT"""
577 def p_stmts(self
, p
):
578 """stmts : stmts stmt
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
607 | comparison APPEND comparison
612 p
[0] = ast
.Call(ast
.Name("ltu", ast
.Load()), (p
[1], p
[3]), [])
614 p
[0] = ast
.Call(ast
.Name("gtu", ast
.Load()), (p
[1], p
[3]), [])
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
623 # actual call will be "dividend / divisor" - just check
625 # XXX DISABLE BAD HACK (False)
626 if False and isinstance(l
, ast
.Name
) and l
.id == 'dividend':
631 # return "function trunc_xxx(l, r)"
632 p
[0] = ast
.Call(ast
.Name(fn
, ast
.Load()), (l
, r
), [])
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])]
641 p
[0] = ast
.Call(ast
.Name("concat", ast
.Load()), l
, keywords
)
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]], [])
650 if isinstance(p
[2], str) and p
[2] == '-':
651 p
[0] = ast
.UnaryOp(unary_ops
[p
[2]], p
[1])
653 p
[0] = ast
.UnaryOp(unary_ops
[p
[1]], p
[2])
657 # power: atom trailer* ['**' factor]
658 # trailers enables function calls (and subscripts).
659 # so this is 'trailerlist'
660 def p_power(self
, p
):
662 | atom trailerlist"""
664 print("power dump atom notrailer")
665 print(astor
.dump_tree(p
[1]))
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
):
675 if name
in regs
+ fregs
:
676 self
.read_regs
.add(name
)
678 def p_atom_name(self
, p
):
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))
699 p
[0] = ast
.Name(id=name
, ctx
=ast
.Load(), lineno
=p
.lineno(1))
701 def p_atom_number(self
, p
):
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
712 p
[0] = ast
.Constant(p
[1])
714 # '[' [listmaker] ']' |
716 def p_atom_listmaker(self
, p
):
717 """atom : LBRACK listmaker RBRACK"""
720 def p_listmaker(self
, p
):
721 """listmaker : test COMMA listmaker
725 p
[0] = ast
.List([p
[1]], ast
.Load())
727 p
[0] = ast
.List([p
[1]] + p
[3].nodes
, ast
.Load())
729 def p_atom_tuple(self
, p
):
730 """atom : LPAR testlist RPAR"""
733 print(astor
.dump_tree(p
[2]))
735 if isinstance(p
[2], ast
.Name
):
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))
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
:
749 self
.read_regs
.add(rid
) # add to list of regs to read
750 # create special call to GPR.getz or FPR.getz
752 gprz
= ast
.Name("FPR", ast
.Load())
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]))
767 def p_trailerlist(self
, p
):
768 """trailerlist : trailer trailerlist
774 p
[0] = ("TLIST", p
[1], p
[2])
776 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
777 def p_trailer(self
, p
):
778 """trailer : trailer_arglist
784 def p_trailer_arglist(self
, p
):
785 """trailer_arglist : LPAR arglist RPAR
788 args
= [] if len(p
) == 3 else p
[2]
789 p
[0] = ("CALL", args
, p
.slice[1])
791 def p_trailer_subscript(self
, p
):
792 "trailer_subscript : LBRACK subscript RBRACK"
793 p
[0] = ("SUBS", p
[2])
795 def p_trailer_attr(self
, p
):
796 "trailer_attr : PERIOD NAME"
797 p
[0] = ("ATTR", p
[2])
799 # subscript: '.' '.' '.' | test | [test] ':' [test]
801 def p_subscript(self
, p
):
802 """subscript : test COLON test
807 if isinstance(p
[3], ast
.Constant
):
808 end
= ast
.Constant(p
[3].value
+1)
810 end
= ast
.BinOp(p
[3], ast
.Add(), ast
.Constant(1))
815 # testlist: test (',' test)* [',']
816 # Contains shift/reduce error
818 def p_testlist(self
, p
):
819 """testlist : testlist_multi COMMA
824 # May need to promote singleton to tuple
825 if isinstance(p
[1], list):
829 # Convert into a tuple?
830 if isinstance(p
[0], list):
831 v
= ast
.Tuple() # pypy workaround
835 def p_testlist_multi(self
, p
):
836 """testlist_multi : testlist_multi COMMA test
842 if isinstance(p
[1], list):
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'
853 | comparison QMARK test COLON test"""
857 p
[0] = ast
.IfExp(test
=p
[1], body
=p
[3], orelse
=p
[5])
859 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
861 # XXX INCOMPLETE: this doesn't allow the trailing comma
863 def p_arglist(self
, p
):
864 """arglist : arglist COMMA argument
871 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
872 def p_argument(self
, p
):
876 def p_error(self
, p
):
877 # print "Error!", repr(p)
878 raise_syntax_error(str(p
), self
.filename
, p
.lineno
, p
.lexpos
,
881 def Assign(self
, autoassign
, assignname
, left
, right
, iea_mode
, eq_tok
):
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
894 for child
in left
.elts
:
895 if not isinstance(child
, ast
.Name
):
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
903 return ast
.Assign([v
], right
)
904 elif isinstance(left
, ast
.Attribute
):
906 ast
.Attribute(left
.value
, left
.attr
, ast
.Store())], right
)
907 elif isinstance(left
, ast
.Subscript
):
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):
929 qty
= ast
.BinOp(upper
, binary_ops
['-'], lower
)
930 keywords
= [ast
.keyword(arg
='repeat', value
=qty
)]
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
]
937 # XXX HMMM probably not needed...
939 if isinstance(ls
, ast
.Slice
):
940 lower
, upper
, step
= ls
.lower
, ls
.upper
, ls
.step
941 print("slice assign", lower
, upper
, step
)
943 ls
= (lower
, upper
, None)
945 ls
= (lower
, upper
, step
)
947 return ast
.Call(ast
.Name("selectassign", ast
.Load()),
948 [left
.value
, ls
, right
], [])
951 raise_syntax_error("Can't do that yet", self
.filename
,
952 eq_tok
.lineno
, eq_tok
.lexpos
, self
.input_text
)
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":
959 if isinstance(atom
, ast
.Name
):
963 def try_extract_uppercase_name(self
, atom
):
964 name
= self
.try_extract_name(atom
)
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
:
973 def try_get_attr(self
, atom
, trailer
):
974 if trailer
[0] == "ATTR":
976 if trailer
[0] != "SUBS":
978 base_name
= self
.try_extract_uppercase_name(atom
)
979 if base_name
is None or base_name
in SUBS_TO_ATTR_EXCEPTIONS
:
981 if len(trailer
[1]) != 1:
983 return self
.try_extract_uppercase_name(trailer
[1][0])
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
)
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
):
995 if name
in regs
+ fregs
:
997 if atom
.id == "SetFX":
998 if len(trailer
[1]) != 1 or \
999 not isinstance(trailer
[1][0], ast
.Attribute
):
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
,
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)
1018 # p[0] = ast.CallFunc(p[1], p[2][1], None, None)
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])
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())
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())
1042 _CACHE_DECODER
= True
1043 _CACHED_DECODER
= None
1046 def _create_cached_decoder():
1047 global _CACHED_DECODER
1049 if _CACHED_DECODER
is None:
1050 _CACHED_DECODER
= create_pdecode()
1051 return _CACHED_DECODER
1052 return create_pdecode()
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
)
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)
1066 def parse(self
, code
):
1068 self
.lexer
.filename
= self
.filename
1069 result
= self
.parser
.parse(code
, lexer
=self
.lexer
, debug
=self
.debug
)
1071 v
= ast
.ClassDef() # pypy workaround
1072 v
.name
= "ISACallerFnHelper"
1073 v
.bases
= ["ISACallerHelper"]
1076 v
.decorator_list
= []
1078 return ast
.Module(result
)
1081 ###### Code generation ######
1083 #from compiler import misc, syntax, pycodegen
1085 _CACHED_PARSERS
= {}
1086 _CACHE_PARSERS
= True
1089 class GardenSnakeCompiler(object):
1090 def __init__(self
, debug
=False, form
=None, incl_carry
=False, helper
=False):
1093 self
.parser
= _CACHED_PARSERS
[debug
, form
, incl_carry
, helper
]
1095 self
.parser
= GardenSnakeParser(
1096 debug
=debug
, form
=form
, incl_carry
=incl_carry
,
1098 _CACHED_PARSERS
[debug
, form
, incl_carry
, helper
] = self
.parser
1100 self
.parser
= GardenSnakeParser(debug
=debug
, form
=form
,
1101 incl_carry
=incl_carry
, helper
=helper
)
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
1109 tree
= self
.parser
.parse(code
)
1110 except SyntaxError2
as e
:
1111 e
.raise_syntax_error()
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()