2d113f6ee3d9124b3d8a95e57e3639cbf8bf88e0
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
in ['overflow', 'CR0']:
684 self
.write_regs
.add(name
)
685 if self
.include_ca_in_write
:
686 if name
in ['CA', 'CA32']:
687 self
.write_regs
.add(name
)
688 if name
in ['CR', 'LR', 'CTR', 'TAR', 'FPSCR', 'MSR',
689 'SVSTATE', 'SVREMAP', 'SRR0', 'SRR1',
690 'SVSHAPE0', 'SVSHAPE1', 'SVSHAPE2', 'SVSHAPE3']:
691 self
.special_regs
.add(name
)
692 self
.write_regs
.add(name
) # and add to list to write
693 if name
in ('XLEN', ) or name
in BFP_FLAG_NAMES
:
694 attr
= ast
.Name("self", ast
.Load())
695 p
[0] = ast
.Attribute(attr
, name
, ast
.Load(), lineno
=p
.lineno(1))
697 p
[0] = ast
.Name(id=name
, ctx
=ast
.Load(), lineno
=p
.lineno(1))
699 def p_atom_number(self
, p
):
704 # SelectableInt isn't a valid ast.Constant,
705 # but we use it anyway since it produces nicer source (hex constants):
706 # if not isinstance(p[1], (str, int)):
707 # # SelectableInt isn't a valid ast.Constant
708 # p[0] = ast.parse(repr(p[1]), mode='eval').body
710 p
[0] = ast
.Constant(p
[1])
712 # '[' [listmaker] ']' |
714 def p_atom_listmaker(self
, p
):
715 """atom : LBRACK listmaker RBRACK"""
718 def p_listmaker(self
, p
):
719 """listmaker : test COMMA listmaker
723 p
[0] = ast
.List([p
[1]], ast
.Load())
725 p
[0] = ast
.List([p
[1]] + p
[3].nodes
, ast
.Load())
727 def p_atom_tuple(self
, p
):
728 """atom : LPAR testlist RPAR"""
731 print(astor
.dump_tree(p
[2]))
733 if isinstance(p
[2], ast
.Name
):
735 print("tuple name", name
)
736 if name
in self
.gprs
:
737 self
.read_regs
.add(name
) # add to list of regs to read
738 #p[0] = ast.Subscript(ast.Name("GPR", ast.Load()), ast.Str(p[2].id))
741 elif isinstance(p
[2], ast
.BinOp
):
742 if isinstance(p
[2].left
, ast
.Name
) and \
743 isinstance(p
[2].right
, ast
.Constant
) and \
744 p
[2].right
.value
== 0 and \
745 p
[2].left
.id in self
.gprs
:
747 self
.read_regs
.add(rid
) # add to list of regs to read
748 # create special call to GPR.getz or FPR.getz
750 gprz
= ast
.Name("FPR", ast
.Load())
752 gprz
= ast
.Name("GPR", ast
.Load())
753 # get testzero function
754 gprz
= ast
.Attribute(gprz
, "getz", ast
.Load())
755 # *sigh* see class GPR. we need index itself not reg value
756 ridx
= ast
.Name("_%s" % rid
, ast
.Load())
757 rvalue
= ast
.Name(rid
, ast
.Load())
758 p
[0] = ast
.Call(gprz
, [ridx
, rvalue
], [])
759 print("tree", astor
.dump_tree(p
[0]))
765 def p_trailerlist(self
, p
):
766 """trailerlist : trailer trailerlist
772 p
[0] = ("TLIST", p
[1], p
[2])
774 # trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
775 def p_trailer(self
, p
):
776 """trailer : trailer_arglist
782 def p_trailer_arglist(self
, p
):
783 """trailer_arglist : LPAR arglist RPAR
786 args
= [] if len(p
) == 3 else p
[2]
787 p
[0] = ("CALL", args
, p
.slice[1])
789 def p_trailer_subscript(self
, p
):
790 "trailer_subscript : LBRACK subscript RBRACK"
791 p
[0] = ("SUBS", p
[2])
793 def p_trailer_attr(self
, p
):
794 "trailer_attr : PERIOD NAME"
795 p
[0] = ("ATTR", p
[2])
797 # subscript: '.' '.' '.' | test | [test] ':' [test]
799 def p_subscript(self
, p
):
800 """subscript : test COLON test
805 if isinstance(p
[3], ast
.Constant
):
806 end
= ast
.Constant(p
[3].value
+1)
808 end
= ast
.BinOp(p
[3], ast
.Add(), ast
.Constant(1))
813 # testlist: test (',' test)* [',']
814 # Contains shift/reduce error
816 def p_testlist(self
, p
):
817 """testlist : testlist_multi COMMA
822 # May need to promote singleton to tuple
823 if isinstance(p
[1], list):
827 # Convert into a tuple?
828 if isinstance(p
[0], list):
829 v
= ast
.Tuple() # pypy workaround
833 def p_testlist_multi(self
, p
):
834 """testlist_multi : testlist_multi COMMA test
840 if isinstance(p
[1], list):
846 # test: or_test ['if' or_test 'else' test] | lambdef
847 # as I don't support 'and', 'or', and 'not' this works down to 'comparison'
851 | comparison QMARK test COLON test"""
855 p
[0] = ast
.IfExp(test
=p
[1], body
=p
[3], orelse
=p
[5])
857 # arglist: (argument ',')* (argument [',']| '*' test [',' '**' test]
859 # XXX INCOMPLETE: this doesn't allow the trailing comma
861 def p_arglist(self
, p
):
862 """arglist : arglist COMMA argument
869 # argument: test [gen_for] | test '=' test # Really [keyword '='] test
870 def p_argument(self
, p
):
874 def p_error(self
, p
):
875 # print "Error!", repr(p)
876 raise_syntax_error(str(p
), self
.filename
, p
.lineno
, p
.lexpos
,
879 def Assign(self
, autoassign
, assignname
, left
, right
, iea_mode
, eq_tok
):
881 print("Assign", autoassign
, assignname
, left
, right
)
882 if isinstance(left
, ast
.Name
):
883 # Single assignment on left
884 # XXX when doing IntClass, which will have an "eq" function,
885 # this is how to access it
886 # eq = ast.Attribute(left, "eq") # get eq fn
887 # return ast.Call(eq, [right], []) # now call left.eq(right)
888 return ast
.Assign([ast
.Name(left
.id, ast
.Store())], right
)
889 elif isinstance(left
, ast
.Tuple
):
890 # List of things - make sure they are Name nodes
892 for child
in left
.elts
:
893 if not isinstance(child
, ast
.Name
):
895 "that assignment not supported", self
.filename
,
896 eq_tok
.lineno
, eq_tok
.lexpos
, self
.input_text
)
897 names
.append(child
.id)
898 ass_list
= [ast
.Name(name
, ast
.Store()) for name
in names
]
899 v
= ast
.Tuple() # pypy workaround
901 return ast
.Assign([v
], right
)
902 elif isinstance(left
, ast
.Attribute
):
904 ast
.Attribute(left
.value
, left
.attr
, ast
.Store())], right
)
905 elif isinstance(left
, ast
.Subscript
):
907 # XXX changing meaning of "undefined" to a function
908 # if (isinstance(ls, ast.Slice) and isinstance(right, ast.Name) and
909 # right.id == 'undefined'):
910 # # undefined needs to be copied the exact same slice
911 # right = ast.Subscript(right, ls, ast.Load())
912 # return ast.Assign([left], right)
913 res
= ast
.Assign([left
], right
)
914 if autoassign
and isinstance(ls
, ast
.Slice
):
915 # hack to create a variable pre-declared based on a slice.
916 # dividend[0:32] = (RA)[0:32] will create
917 # dividend = [0] * 32
918 # dividend[0:32] = (RA)[0:32]
919 # the declaration makes the slice-assignment "work"
920 lower
, upper
, step
= ls
.lower
, ls
.upper
, ls
.step
921 print("lower, upper, step", repr(lower
), repr(upper
), step
)
922 # XXX relax constraint that indices on auto-assign have
923 # to be constants x[0:32]
924 # if not isinstance(lower, ast.Constant) or \
925 # not isinstance(upper, ast.Constant):
927 qty
= ast
.BinOp(upper
, binary_ops
['-'], lower
)
928 keywords
= [ast
.keyword(arg
='repeat', value
=qty
)]
930 right
= ast
.Call(ast
.Name("concat", ast
.Load()), l
, keywords
)
931 declare
= ast
.Assign(
932 [ast
.Name(assignname
, ast
.Store())], right
)
933 return [declare
, res
]
935 # XXX HMMM probably not needed...
937 if isinstance(ls
, ast
.Slice
):
938 lower
, upper
, step
= ls
.lower
, ls
.upper
, ls
.step
939 print("slice assign", lower
, upper
, step
)
941 ls
= (lower
, upper
, None)
943 ls
= (lower
, upper
, step
)
945 return ast
.Call(ast
.Name("selectassign", ast
.Load()),
946 [left
.value
, ls
, right
], [])
949 raise_syntax_error("Can't do that yet", self
.filename
,
950 eq_tok
.lineno
, eq_tok
.lexpos
, self
.input_text
)
952 def try_extract_name(self
, atom
):
953 if isinstance(atom
, ast
.Attribute
):
954 if isinstance(atom
.value
, ast
.Name
) and atom
.value
.id == "self":
957 if isinstance(atom
, ast
.Name
):
961 def try_extract_uppercase_name(self
, atom
):
962 name
= self
.try_extract_name(atom
)
965 # we want to filter out names like _123 that have no letters with
966 # casing at all, hence the lower() check
967 if name
.upper() != name
or name
.lower() == name
:
971 def try_get_attr(self
, atom
, trailer
):
972 if trailer
[0] == "ATTR":
974 if trailer
[0] != "SUBS":
976 base_name
= self
.try_extract_uppercase_name(atom
)
977 if base_name
is None or base_name
in SUBS_TO_ATTR_EXCEPTIONS
:
979 if len(trailer
[1]) != 1:
981 return self
.try_extract_uppercase_name(trailer
[1][0])
983 def apply_trailer(self
, atom
, trailer
, read_regs
):
984 if trailer
[0] == "TLIST":
985 # assume depth of one
986 atom
= self
.apply_trailer(atom
, trailer
[1], read_regs
)
988 if trailer
[0] == "CALL":
989 #p[0] = ast.Expr(ast.Call(p[1], p[2][1], []))
990 for arg
in trailer
[1]:
991 if isinstance(arg
, ast
.Name
):
993 if name
in regs
+ fregs
:
995 if atom
.id == "SetFX":
996 if len(trailer
[1]) != 1 or \
997 not isinstance(trailer
[1][0], ast
.Attribute
):
999 "SetFX call must have only one argument that is an "
1000 "attribute access -- like `SetFX(FPSCR.VXSNAN)`",
1001 self
.filename
, trailer
[2].lineno
, trailer
[2].lexpos
,
1004 args
= [arg
.value
, ast
.Str(arg
.attr
)]
1005 name
= ast
.Name("self", ast
.Load())
1006 atom
= ast
.Attribute(name
, atom
.id, ast
.Load())
1007 return ast
.Call(atom
, args
, [])
1008 # special-case, these functions must NOT be made "self.xxxx"
1009 if atom
.id not in SPECIAL_HELPERS
:
1010 name
= ast
.Name("self", ast
.Load())
1011 atom
= ast
.Attribute(name
, atom
.id, ast
.Load())
1012 return ast
.Call(atom
, trailer
[1], [])
1013 # if p[1].id == 'print':
1014 # p[0] = ast.Printnl(ast.Tuple(p[2][1]), None, None)
1016 # p[0] = ast.CallFunc(p[1], p[2][1], None, None)
1018 attr
= self
.try_get_attr(atom
, trailer
)
1019 if attr
is not None:
1020 return ast
.Attribute(atom
, attr
, ast
.Load())
1021 assert trailer
[0] == "SUBS"
1022 print("subscript atom", trailer
[1])
1023 #raise AssertionError("not implemented %s" % p[2][0])
1027 if isinstance(idx
, ast
.Name
) and idx
.id in regs
+ fregs
:
1028 read_regs
.add(idx
.id)
1029 if isinstance(idx
, ast
.Name
) and idx
.id in regs
:
1030 print("single atom subscript, underscored", idx
.id)
1031 idx
= ast
.Name("_%s" % idx
.id, ast
.Load())
1033 idx
= ast
.Slice(subs
[0], subs
[1], None)
1034 # if isinstance(atom, ast.Name) and atom.id == 'CR':
1035 # atom.id = 'CR' # bad hack
1036 #print ("apply_trailer Subscript", atom.id, idx)
1037 return ast
.Subscript(atom
, idx
, ast
.Load())
1040 _CACHE_DECODER
= True
1041 _CACHED_DECODER
= None
1044 def _create_cached_decoder():
1045 global _CACHED_DECODER
1047 if _CACHED_DECODER
is None:
1048 _CACHED_DECODER
= create_pdecode()
1049 return _CACHED_DECODER
1050 return create_pdecode()
1053 class GardenSnakeParser(PowerParser
):
1054 def __init__(self
, debug
=False, form
=None, incl_carry
=False, helper
=False):
1055 if form
is not None:
1056 self
.sd
= _create_cached_decoder()
1057 PowerParser
.__init
__(self
, form
, incl_carry
, helper
=helper
)
1059 self
.lexer
= IndentLexer(debug
=0)
1060 self
.tokens
= self
.lexer
.tokens
1061 self
.parser
= yacc
.yacc(module
=self
, start
="file_input_end",
1062 debug
=debug
, write_tables
=False)
1064 def parse(self
, code
):
1066 self
.lexer
.filename
= self
.filename
1067 result
= self
.parser
.parse(code
, lexer
=self
.lexer
, debug
=self
.debug
)
1069 v
= ast
.ClassDef() # pypy workaround
1070 v
.name
= "ISACallerFnHelper"
1071 v
.bases
= ["ISACallerHelper"]
1074 v
.decorator_list
= []
1076 return ast
.Module(result
)
1079 ###### Code generation ######
1081 #from compiler import misc, syntax, pycodegen
1083 _CACHED_PARSERS
= {}
1084 _CACHE_PARSERS
= True
1087 class GardenSnakeCompiler(object):
1088 def __init__(self
, debug
=False, form
=None, incl_carry
=False, helper
=False):
1091 self
.parser
= _CACHED_PARSERS
[debug
, form
, incl_carry
, helper
]
1093 self
.parser
= GardenSnakeParser(
1094 debug
=debug
, form
=form
, incl_carry
=incl_carry
,
1096 _CACHED_PARSERS
[debug
, form
, incl_carry
, helper
] = self
.parser
1098 self
.parser
= GardenSnakeParser(debug
=debug
, form
=form
,
1099 incl_carry
=incl_carry
, helper
=helper
)
1101 def compile(self
, code
, mode
="exec", filename
="<string>"):
1102 if filename
in ["string", "<string>"]:
1103 raise ValueError("missing filename")
1104 self
.parser
.filename
= filename
1105 self
.parser
.input_text
= code
1107 tree
= self
.parser
.parse(code
)
1108 except SyntaxError2
as e
:
1109 e
.raise_syntax_error()
1113 #misc.set_filename(filename, tree)
1114 return compile(tree
, mode
="exec", filename
="<string>")
1115 # syntax.check(tree)
1116 gen
= pycodegen
.ModuleCodeGenerator(tree
)
1117 code
= gen
.getCode()