gardensnake.py
来自「M5,一个功能强大的多处理器系统模拟器.很多针对处理器架构,性能的研究都使用它作」· Python 代码 · 共 710 行 · 第 1/2 页
PY
710 行
# The grammar comments come from Python's Grammar/Grammar file## NB: compound_stmt in single_input is followed by extra NEWLINE!# file_input: (NEWLINE | stmt)* ENDMARKERdef p_file_input_end(p): """file_input_end : file_input ENDMARKER""" p[0] = ast.Stmt(p[1])def p_file_input(p): """file_input : file_input NEWLINE | file_input stmt | NEWLINE | stmt""" if isinstance(p[len(p)-1], basestring): if len(p) == 3: p[0] = p[1] else: p[0] = [] # p == 2 --> only a blank line else: if len(p) == 3: p[0] = p[1] + p[2] else: p[0] = p[1]# funcdef: [decorators] 'def' NAME parameters ':' suite# ignoring decoratorsdef p_funcdef(p): "funcdef : DEF NAME parameters COLON suite" p[0] = ast.Function(None, p[2], tuple(p[3]), (), 0, None, p[5])# parameters: '(' [varargslist] ')'def p_parameters(p): """parameters : LPAR RPAR | LPAR varargslist RPAR""" if len(p) == 3: p[0] = [] else: p[0] = p[2]# varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME) |# highly simplifieddef p_varargslist(p): """varargslist : varargslist COMMA NAME | NAME""" if len(p) == 4: p[0] = p[1] + p[3] else: p[0] = [p[1]]# stmt: simple_stmt | compound_stmtdef p_stmt_simple(p): """stmt : simple_stmt""" # simple_stmt is a list p[0] = p[1]def p_stmt_compound(p): """stmt : compound_stmt""" p[0] = [p[1]]# simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINEdef p_simple_stmt(p): """simple_stmt : small_stmts NEWLINE | small_stmts SEMICOLON NEWLINE""" p[0] = p[1]def p_small_stmts(p): """small_stmts : small_stmts SEMICOLON small_stmt | small_stmt""" if len(p) == 4: p[0] = p[1] + [p[3]] else: p[0] = [p[1]]# small_stmt: expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |# import_stmt | global_stmt | exec_stmt | assert_stmtdef p_small_stmt(p): """small_stmt : flow_stmt | expr_stmt""" p[0] = p[1]# expr_stmt: testlist (augassign (yield_expr|testlist) |# ('=' (yield_expr|testlist))*)# augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |# '<<=' | '>>=' | '**=' | '//=')def p_expr_stmt(p): """expr_stmt : testlist ASSIGN testlist | testlist """ if len(p) == 2: # a list of expressions p[0] = ast.Discard(p[1]) else: p[0] = Assign(p[1], p[3])def p_flow_stmt(p): "flow_stmt : return_stmt" p[0] = p[1]# return_stmt: 'return' [testlist]def p_return_stmt(p): "return_stmt : RETURN testlist" p[0] = ast.Return(p[2])def p_compound_stmt(p): """compound_stmt : if_stmt | funcdef""" p[0] = p[1]def p_if_stmt(p): 'if_stmt : IF test COLON suite' p[0] = ast.If([(p[2], p[4])], None)def p_suite(p): """suite : simple_stmt | NEWLINE INDENT stmts DEDENT""" if len(p) == 2: p[0] = ast.Stmt(p[1]) else: p[0] = ast.Stmt(p[3])def p_stmts(p): """stmts : stmts stmt | stmt""" if len(p) == 3: p[0] = p[1] + p[2] else: p[0] = p[1]## No using Python's approach because Ply supports precedence# comparison: expr (comp_op expr)*# arith_expr: term (('+'|'-') term)*# term: factor (('*'|'/'|'%'|'//') factor)*# factor: ('+'|'-'|'~') factor | power# comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'def make_lt_compare((left, right)): return ast.Compare(left, [('<', right),])def make_gt_compare((left, right)): return ast.Compare(left, [('>', right),])def make_eq_compare((left, right)): return ast.Compare(left, [('==', right),])binary_ops = { "+": ast.Add, "-": ast.Sub, "*": ast.Mul, "/": ast.Div, "<": make_lt_compare, ">": make_gt_compare, "==": make_eq_compare,}unary_ops = { "+": ast.UnaryAdd, "-": ast.UnarySub, }precedence = ( ("left", "EQ", "GT", "LT"), ("left", "PLUS", "MINUS"), ("left", "MULT", "DIV"), )def p_comparison(p): """comparison : comparison PLUS comparison | comparison MINUS comparison | comparison MULT comparison | comparison DIV comparison | comparison LT comparison | comparison EQ comparison | comparison GT comparison | PLUS comparison | MINUS comparison | power""" if len(p) == 4: p[0] = binary_ops[p[2]]((p[1], p[3])) elif len(p) == 3: p[0] = unary_ops[p[1]](p[2]) else: p[0] = p[1]# power: atom trailer* ['**' factor]# trailers enables function calls. I only allow one level of calls# so this is 'trailer'def p_power(p): """power : atom | atom trailer""" if len(p) == 2: p[0] = p[1] else: if p[2][0] == "CALL": p[0] = ast.CallFunc(p[1], p[2][1], None, None) else: raise AssertionError("not implemented")def p_atom_name(p): """atom : NAME""" p[0] = ast.Name(p[1])def p_atom_number(p): """atom : NUMBER | STRING""" p[0] = ast.Const(p[1])def p_atom_tuple(p): """atom : LPAR testlist RPAR""" p[0] = p[2]# trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAMEdef p_trailer(p): "trailer : LPAR arglist RPAR" p[0] = ("CALL", p[2])# testlist: test (',' test)* [',']# Contains shift/reduce errordef p_testlist(p): """testlist : testlist_multi COMMA | testlist_multi """ if len(p) == 2: p[0] = p[1] else: # May need to promote singleton to tuple if isinstance(p[1], list): p[0] = p[1] else: p[0] = [p[1]] # Convert into a tuple? if isinstance(p[0], list): p[0] = ast.Tuple(p[0])def p_testlist_multi(p): """testlist_multi : testlist_multi COMMA test | test""" if len(p) == 2: # singleton p[0] = p[1] else: if isinstance(p[1], list): p[0] = p[1] + [p[3]] else: # singleton -> tuple p[0] = [p[1], p[3]]# test: or_test ['if' or_test 'else' test] | lambdef# as I don't support 'and', 'or', and 'not' this works down to 'comparison'def p_test(p): "test : comparison" p[0] = p[1]# arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)# XXX INCOMPLETE: this doesn't allow the trailing commadef p_arglist(p): """arglist : arglist COMMA argument | argument""" if len(p) == 4: p[0] = p[1] + [p[3]] else: p[0] = [p[1]]# argument: test [gen_for] | test '=' test # Really [keyword '='] testdef p_argument(p): "argument : test" p[0] = p[1]def p_error(p): #print "Error!", repr(p) raise SyntaxError(p)class GardenSnakeParser(object): def __init__(self, lexer = None): if lexer is None: lexer = IndentLexer() self.lexer = lexer self.parser = yacc.yacc(start="file_input_end") def parse(self, code): self.lexer.input(code) result = self.parser.parse(lexer = self.lexer) return ast.Module(None, result)###### Code generation ######from compiler import misc, syntax, pycodegenclass GardenSnakeCompiler(object): def __init__(self): self.parser = GardenSnakeParser() def compile(self, code, filename="<string>"): tree = self.parser.parse(code) #print tree misc.set_filename(filename, tree) syntax.check(tree) gen = pycodegen.ModuleCodeGenerator(tree) code = gen.getCode() return code####### Test code #######compile = GardenSnakeCompiler().compilecode = r"""print('LET\'S TRY THIS \\OUT')#Comment heredef x(a): print('called with',a) if a == 1: return 2 if a*2 > 10: return 999 / 4 # Another comment here return a+2*3ints = (1, 2, 3, 4,5)print('mutiline-expression', ints)t = 4+1/3*2+6*(9-5+1)print('predence test; should be 34+2/3:', t, t==(34+2/3))print('numbers', 1,2,3,4,5)if 1: 8 a=9 print(x(a))print(x(1))print(x(2))print(x(8),'3')print('this is decimal', 1/5)print('BIG DECIMAL', 1.234567891234567e12345)"""# Set up the GardenSnake run-time environmentdef print_(*args): print "-->", " ".join(map(str,args))globals()["print"] = print_compiled_code = compile(code)exec compiled_code in globals()print "Done"
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?