Blog 2019/8/28
<- previous | index | next ->
Here's a trivial eval
implementation which only knows how to do addition.
Note that the input is already in the form of an AST, as there isn't a lexer / parser (reader) implemented.
Blog 2019/8/28
<- previous | index | next ->
Here's a trivial eval
implementation which only knows how to do addition.
Note that the input is already in the form of an AST, as there isn't a lexer / parser (reader) implemented.
#!/usr/bin/env python | |
# today's coding practice: a trivial lisp-like interprer which only knows how to evaluate addition. | |
def first(l): # a.k.a. car | |
return l[0] | |
def rest(l): # a.k.a. cdr | |
return l[1:] | |
def is_atom(expr): | |
return not isinstance(expr, list) | |
def is_list(expr): | |
return isinstance(expr, list) | |
def is_symbol(expr): | |
return is_atom(expr) and expr["tag"] == "symbol" | |
def is_number(expr): | |
return is_atom(expr) and expr["tag"] == "number" | |
def is_string(expr): | |
return is_atom(expr) and expr["tag"] == "string" | |
def is_literal(expr): | |
return is_number(expr) or is_string(expr) | |
def lookup(expr, env): | |
return env[expr] | |
def add(a, b): | |
return a + b | |
def log(msg, indent=0): | |
print (" " * indent) + msg | |
def eval2(expr, env, indent=0): | |
log("eval2 %s" % expr, indent) | |
if is_literal(expr): | |
log("eval2: found literal", indent) | |
result = expr["content"] | |
elif is_symbol(expr): | |
log("eval2: found symbol", indent) | |
result = lookup(expr["content"], env) | |
elif is_list(expr): | |
log("eval2: found list", indent) | |
operator = eval2(first(expr), env, indent+1) | |
operands = [eval2(e, env, indent+1) for e in rest(expr)] | |
result = operator(*operands) | |
else: | |
raise Exception("Don't know how to evaluate '%s'" % expr) | |
log("eval2: returning %s" % result), indent | |
return result | |
def test0(): | |
print "\ntest0:" | |
env = {} | |
# ast: 42 | |
ast = { | |
"tag": "number", | |
"content": "42" | |
} | |
print eval2(ast, env) | |
def test1(): | |
print "\ntest1:" | |
env = { | |
"+": add | |
} | |
# ast: [+ 1 2] | |
ast = [ | |
{ | |
"tag": "symbol", | |
"content": "+" | |
}, | |
{ | |
"tag": "number", | |
"content": 1 | |
}, | |
{ | |
"tag": "number", | |
"content": 2 | |
} | |
] | |
print eval2(ast, env) | |
def test2(): | |
print "\ntest2:" | |
env = { | |
"+": add | |
} | |
# ast: [+ 1 [+ 2 3]] | |
ast = [ | |
{ | |
"tag": "symbol", | |
"content": "+" | |
}, | |
{ | |
"tag": "number", | |
"content": 1 | |
}, | |
[ | |
{ | |
"tag": "symbol", | |
"content": "+" | |
}, | |
{ | |
"tag": "number", | |
"content": 2 | |
}, | |
{ | |
"tag": "number", | |
"content": 3 | |
} | |
] | |
] | |
print eval2(ast, env) | |
if __name__ == "__main__": | |
test2() | |
test1() | |
test2() |
$ ./eval2.py | |
test0: | |
eval2 {'content': '42', 'tag': 'number'} | |
eval2: found literal | |
eval2: returning 42 | |
42 | |
test1: | |
eval2 [{'content': '+', 'tag': 'symbol'}, {'content': 1, 'tag': 'number'}, {'content': 2, 'tag': 'number'}] | |
eval2: found list | |
eval2 {'content': '+', 'tag': 'symbol'} | |
eval2: found symbol | |
eval2: returning <function add at 0x1099508c0> | |
eval2 {'content': 1, 'tag': 'number'} | |
eval2: found literal | |
eval2: returning 1 | |
eval2 {'content': 2, 'tag': 'number'} | |
eval2: found literal | |
eval2: returning 2 | |
eval2: returning 3 | |
3 | |
test2: | |
eval2 [{'content': '+', 'tag': 'symbol'}, {'content': 1, 'tag': 'number'}, [{'content': '+', 'tag': 'symbol'}, {'content': 2, 'tag': 'number'}, {'content': 3, 'tag': 'number'}]] | |
eval2: found list | |
eval2 {'content': '+', 'tag': 'symbol'} | |
eval2: found symbol | |
eval2: returning <function add at 0x1099508c0> | |
eval2 {'content': 1, 'tag': 'number'} | |
eval2: found literal | |
eval2: returning 1 | |
eval2 [{'content': '+', 'tag': 'symbol'}, {'content': 2, 'tag': 'number'}, {'content': 3, 'tag': 'number'}] | |
eval2: found list | |
eval2 {'content': '+', 'tag': 'symbol'} | |
eval2: found symbol | |
eval2: returning <function add at 0x1099508c0> | |
eval2 {'content': 2, 'tag': 'number'} | |
eval2: found literal | |
eval2: returning 2 | |
eval2 {'content': 3, 'tag': 'number'} | |
eval2: found literal | |
eval2: returning 3 | |
eval2: returning 5 | |
eval2: returning 6 | |
6 |