Background

Representing Expressions

i
["add", 1, 2]            # 1 + 2
["abs", -3.5]            # abs(-3.5)
["add", ["abs", -5], 9]  # abs(-5) + 9

Evaluating Expressions

i
def do_add(args):
    assert len(args) == 2
    left = do(args[0])
    right = do(args[1])
    return left + right

Evaluating Expressions

i
def do_abs(args):
    assert len(args) == 1
    val = do(args[0])
    return abs(val)

Dispatching Operations

i
def 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]}"

Dispatching Operations

Recursive evaluation of an expression tree
Figure 1: Recursively evaluating an expression tree

An Example

i
["add", ["abs", -3], 2]
i
python expr.py expr.tll
i
=> 5

Environments

i
def do_abs(env, args):
    assert len(args) == 1
    val = do(env, args[0])
    return abs(val)

Getting Variables' Values

i
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]]
i
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

Sequencing

i
def do_seq(env, args):
    assert len(args) > 0
    for item in args:
        result = do(env, item)
    return result

Everything Is An Expression

i
# not actually legal Python
result =
    if a > 0:
        1
    elif a == 0:
        0
    else:
        -1

Doubling

i
[
    "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"]]
	    ]
        ]
    ]
]

Doubling

i
initial 1
small 2
small 4
small 8
large 16
=> None

This Is Tedious

i
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]}"

Introspection

i
OPS = {
    name.replace("do_", ""): func
    for (name, func) in globals().items()
    if name.startswith("do_")
}

How Good Is Our Design?

Summary

Concept map
Figure 2: Concept map.