『Rubyを256倍使うための本 無道編』の 12.if_while/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 FuncallNode(Node):
    def __init__(self, func, args):
        self.func = func
        self.args = args
    def evaluate(self):
        args = map(lambda x: x.evaluate(), self.args)
        
        if 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 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, vname):
        self.vtable = vtable
        self.vname  = vname
    def evaluate(self):
        if self.vtable.has_key(self.vname):
            return self.vtable[self.vname]
        else:
            try:
                return eval(self.vtable[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 m20090512 import *

import ply.lex as lex

tokens = (
  'NUMBER',
  'STRING',
  'IDENT',
  'EOL',

  'IF',
  'THEN',
  'ELSE',
  'WHILE',
  'DO',
  'END',
)

literals = "(),=+-*/"

reserved = {
  'if'    : 'IF',
  'then'  : 'THEN',
  'else'  : 'ELSE',
  'while' : 'WHILE',
  'do'    : 'DO',
  'end'   : 'END',
}

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"""
    p[0] = p[1]

def p_stmt_IDENT(p):
    "stmt : IDENT realprim"
    p[0] = FuncallNode(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_assign(p):
    "assign : IDENT '=' expr"
    global vtable
    p[0] = AssignNode(vtable, p[1], p[3])

def p_expr_plus(p):
    "expr : expr '+' expr"
    p[0] = FuncallNode('+', [p[1], p[3]])

def p_expr_minus(p):
    "expr : expr '-' expr"
    p[0] = FuncallNode('-', [p[1], p[3]])

def p_expr_mul(p):
    "expr : expr '*' expr"
    p[0] = FuncallNode('*', [p[1], p[3]])

def p_expr_div(p):
    "expr : expr '/' expr"
    p[0] = FuncallNode('/', [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"
    p[0] = FuncallNode('-', [LiteralNode(0), p[2]])

def p_realprim_ident(p):
    "realprim : IDENT"
    global vtable
    p[0] = VarRefNode(vtable, 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 ')'"
    p[0] = FuncallNode(p[1], p[3])

def p_funcall_no_args(p):
    "funcall : IDENT '(' ')'"
    p[0] = FuncallNode(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 = {}
lineno = 1

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+2*3, " ", 10/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

を入力すると、

funcall ok


funcall ok


operator  ok 

2   7   3 

if ok

while ok