『Rubyを256倍使うための本 無道編』の 13.defun/intp.y を移植
関数定義可能に。ただし、変数スコープなし、グローバル
class Node: def exec_list(self, nodes): for x in nodes: x.evaluate() class RootNode(Node): def __init__(self, tree): self.tree = tree def evaluate(self): self.exec_list(self.tree) class DefNode(Node): def __init__(self, ftab, funcname, funcobj): self.ftab = ftab self.funcname = funcname self.funcobj = funcobj def evaluate(self): self.ftab[self.funcname] = self.funcobj class FuncallNode(Node): def __init__(self, ftab, func, args): self.ftab = ftab self.func = func self.args = args def evaluate(self): args = map(lambda x: x.evaluate(), self.args) if self.ftab.has_key(self.func): self.ftab[self.func].call(args) elif self.func in ["+", "-", "*", "/"]: cmd = "%s%s%s" % (args[0], self.func, args[1]) return eval(cmd) else: import string cmd_args = string.join([repr(x) for x in args], ",") cmd = self.func + "(" + cmd_args + ")" cmd_no_paren = self.func + " " + cmd_args try: return eval(cmd) except SyntaxError: exec(cmd_no_paren) class Function(Node): def __init__(self, vtable, fname, params, body): self.vtable = vtable self.fname = fname self.params = params self.body = body def call(self, args): size1 = len(args) size2 = len(self.params) if size1 != size2: print "wrong # of arg for %s() (%d for %d)" % (self.fname, size1, size2) sys.exit(1) i = 0 for x in args: self.vtable[self.params[i]] = x i += 1 self.exec_list(self.body) class IfNode(Node): def __init__(self, condition, tstmt, fstmt): self.condition = condition self.tstmt = tstmt self.fstmt = fstmt def evaluate(self): if self.condition.evaluate(): self.exec_list(self.tstmt) else: self.exec_list(self.fstmt) class WhileNode(Node): def __init__(self, condition, body): self.condition = condition self.body = body def evaluate(self): while self.condition.evaluate(): self.exec_list(self.body) class AssignNode(Node): def __init__(self, vtable, vname, val): self.vtable = vtable self.vname = vname self.val = val def evaluate(self): self.vtable[self.vname] = self.val.evaluate() class VarRefNode(Node): def __init__(self, vtable, ftab, vname): self.vtable = vtable self.ftab = ftab self.vname = vname def evaluate(self): if self.vtable.has_key(self.vname): return self.vtable[self.vname] elif self.ftab.has_key(self.vname): self.ftab[self.vname].call([]) else: try: return eval(self.vtable[self.vname]) except SyntaxError: print "unknown method or local variable %s" % self.vname class StringNode(Node): def __init__(self, val): self.val = val def evaluate(self): return self.val class LiteralNode(Node): def __init__(self, val): self.val = val def evaluate(self): return self.val
と
#!/usr/bin/env python import sys from math import * from m20090513 import * import ply.lex as lex tokens = ( 'NUMBER', 'STRING', 'IDENT', 'EOL', 'IF', 'THEN', 'ELSE', 'WHILE', 'DO', 'END', 'DEF', ) literals = "(),=+-*/" reserved = { 'if' : 'IF', 'then' : 'THEN', 'else' : 'ELSE', 'while' : 'WHILE', 'do' : 'DO', 'end' : 'END', 'def' : 'DEF', } def t_NUMBER(t): r'\d+' try: t.value = int(t.value) except ValueError: print "Integer value too large", t.value t.value = 0 return t def t_STRING(t): r""""(?:[^"\\]+|\\.)*"|'(?:[^'\\]+|\\.)*'""" t.value = eval(t.value) return t def t_EOL(t): r'\n' return t def t_IDENT(t): r'[a-zA-Z_]\w*' t.type = reserved.get(t.value, 'IDENT') return t t_ignore = " \t" def t_error(t): print "Illegal character '%s'" % t.value[0] t.lexer.skip(1) lex.lex() import ply.yacc as yacc precedence = ( ('left', '+', '-'), ('left', '*', '/'), ('right', 'UMINUS'), ) def p_program_stmt(p): "program : stmt_list" p[0] = RootNode(p[1]) def p_stmt_list_empty(p): "stmt_list : " p[0] = [] def p_stmt_list_stmt(p): "stmt_list : stmt_list stmt EOL" global lineno lineno += 1 p[1].append(p[2]) p[0] = p[1] def p_stmt_list(p): "stmt_list : stmt_list EOL" global lineno lineno += 1 p[0] = p[1] def p_stmt(p): """stmt : expr | assign | if_stmt | while_stmt | defun""" p[0] = p[1] def p_stmt_IDENT(p): "stmt : IDENT realprim" global ftab p[0] = FuncallNode(ftab, p[1], [p[2]]) def p_if_stmt(p): "if_stmt : IF stmt THEN EOL stmt_list else_stmt END" p[0] = IfNode(p[2], p[5], p[6]) def p_else_stmt(p): "else_stmt : ELSE EOL stmt_list" p[0] = p[3] def p_else_stmt_empty(p): "else_stmt : " p[0] = [] def p_while_stmt(p): "while_stmt : WHILE stmt DO EOL stmt_list END" p[0] = WhileNode(p[2], p[5]) def p_defun(p): "defun : DEF IDENT param EOL stmt_list END" global ftab global vtable p[0] = DefNode(ftab, p[2], Function(vtable, p[2], p[3], p[5])) def p_param_name_list(p): "param : '(' name_list ')'" p[0] = p[2] def p_param(p): """param : '(' ')' | """ p[0] = [] def p_name_list(p): "name_list : IDENT" p[0] = [ p[1] ] def p_name_list2(p): "name_list : name_list ',' IDENT" p[1].append(p[3]) p[0] = p[1] def p_assign(p): "assign : IDENT '=' expr" global vtable p[0] = AssignNode(vtable, p[1], p[3]) def p_expr_plus(p): "expr : expr '+' expr" global ftab p[0] = FuncallNode(ftab, '+', [p[1], p[3]]) def p_expr_minus(p): "expr : expr '-' expr" global ftab p[0] = FuncallNode(ftab, '-', [p[1], p[3]]) def p_expr_mul(p): "expr : expr '*' expr" global ftab p[0] = FuncallNode(ftab, '*', [p[1], p[3]]) def p_expr_div(p): "expr : expr '/' expr" global ftab p[0] = FuncallNode(ftab, '/', [p[1], p[3]]) def p_expr_primary(p): "expr : primary" p[0] = p[1] def p_primary(p): "primary : realprim" p[0] = p[1] def p_primary_paren(p): "primary : '(' expr ')'" p[0] = p[2] def p_primary_uminus(p): "primary : '-' primary %prec UMINUS" global ftab p[0] = FuncallNode(ftab, '-', [LiteralNode(0), p[2]]) def p_realprim_ident(p): "realprim : IDENT" global vtable global ftab p[0] = VarRefNode(vtable, ftab, p[1]) def p_realprim_number(p): "realprim : NUMBER" p[0] = LiteralNode(p[1]) def p_realprim_string(p): "realprim : STRING" p[0] = StringNode(p[1]) def p_realprim_funcall(p): "realprim : funcall" p[0] = p[1] def p_funcall_args(p): "funcall : IDENT '(' args ')'" global ftab p[0] = FuncallNode(ftab, p[1], p[3]) def p_funcall_no_args(p): "funcall : IDENT '(' ')'" global ftab p[0] = FuncallNode(ftab, p[1], []) def p_args1(p): "args : expr" p[0] = [p[1]] def p_args2(p): "args : args ',' expr" p[1].append(p[3]) p[0] = p[1] def p_error(p): global lineno try: print "%s:%d Syntax error at '%s'" % ("-", lineno, p.value) except AttributeError: print "%s:%d Syntax error" % ("-", lineno) yacc.yacc() vtable = {} ftab = {} lineno = 1 def do_funcall(func, args): import string cmd_args = string.join([repr(x) for x in args], ",") cmd = func + "(" + cmd_args + ")" cmd_no_paren = func + " " + cmd_args try: return eval(cmd) except SyntaxError: exec(cmd_no_paren) def do_assign(vname, val): global vtable vtable[vname] = val def do_varref(vname): global vtable if vtable.has_key(vname): return vtable[vname] else: return eval(vname) tree = yacc.parse(sys.stdin.read()) #print tree.tree tree.evaluate()
で、
message = "funcall ok" print(message) print "\n" print message print "\n" print('operator', ' ok', "\n") print('1 + 1 = ', 1+1, "\n") print('1 + 2 * -3 = ', 1 + 2 * -3, "\n") if 1 then print "if ok\n" else print "if not ok\n" end i = 1 while i do print "while ok\n" i = 0 end def func print "defun ok\n" end func() func def argfunc(arg) print 'defun with arg: ' print(arg, "\n") end argfunc('ok') argfunc 'ok' def argfunc2(arg1, arg2) print 'defun with arg: ' print(arg1, " ", arg2, "\n") end argfunc2(1, 2)
を入力すると、
funcall ok funcall ok operator ok 1 + 1 = 2 1 + 2 * -3 = -5 if ok while ok defun ok defun ok defun with arg: ok defun with arg: ok defun with arg: 1 2