["add", 1, 2] # 1 + 2
["abs", -3.5] # abs(-3.5)
["add", ["abs", -5], 9] # abs(-5) + 9
1+2 for historical reasonsdef do_add(args):
assert len(args) == 2
left = do(args[0])
right = do(args[1])
return left + right
args is everything except the name of the operationdo to evaluate the operandsdef do_abs(args):
assert len(args) == 1
val = do(args[0])
return abs(val)
do_ functions can be called interchangeablydef do(expr):
# Integers evaluate to themselves.
if isinstance(expr, int):
return expr
# Lists trigger function calls.
assert isinstance(expr, list)
if expr[0] == "abs":
return do_abs(expr[1:])
if expr[0] == "add":
return do_add(expr[1:])
assert False, f"Unknown operation {expr[0]}"
["add", ["abs", -3], 2]
python expr.py expr.tll
=> 5
do_ functionglobals functiondef do_abs(env, args):
assert len(args) == 1
val = do(env, args[0])
return abs(val)
def do_get(env, args):
assert len(args) == 1
assert isinstance(args[0], str)
assert args[0] in env, f"Unknown variable {args[0]}"
return env[args[0]]
def do_set(env, args):
assert len(args) == 2
assert isinstance(args[0], str)
value = do(env, args[1])
env[args[0]] = value
return value
["seq", ["set", "a", 1], ["add", ["get", "a"], 2]]def do_seq(env, args):
assert len(args) > 0
for item in args:
result = do(env, item)
return result
# not actually legal Python
result =
if a > 0:
1
elif a == 0:
0
else:
-1
[
"seq",
["set", "a", 1],
["print", "initial", ["get", "a"]],
[
"repeat", 4,
[
"seq",
["set", "a", ["add", ["get", "a"], ["get", "a"]]],
["if",
["leq", ["get", "a"], 10],
["print", "small", ["get", "a"]],
["print", "large", ["get", "a"]]
]
]
]
]
initial 1
small 2
small 4
small 8
large 16
=> None
def do(env, expr):
if isinstance(expr, int):
return expr
assert isinstance(expr, list)
if expr[0] == "abs":
return do_abs(env, expr[1:])
if expr[0] == "add":
return do_add(env, expr[1:])
if expr[0] == "get":
return do_get(env, expr[1:])
if expr[0] == "seq":
return do_seq(env, expr[1:])
if expr[0] == "set":
return do_set(env, expr[1:])
assert False, f"Unknown operation {expr[0]}"
OPS = {
name.replace("do_", ""): func
for (name, func) in globals().items()
if name.startswith("do_")
}