Creative Commons Licence This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License

How to write a programming language

Part 3: The Evaluator

Andy Balaam
artificialworlds.net/blog

Contents

Recap

Recap - Lexing & Parsing

print( x + 2 );

becomes:

("call", ("symbol", "print"), [ ("operation", "+", ("symbol", "x"), ("number", "2") ) ] )

Evaluation

Evaluation is finding values

Scope

x = "World!"; myfn = { x = "Hello, "; print( x ); }; myfn(); print( x );

Scope

x = "World!"; myfn = { x = "Hello, "; print( x ); }; myfn(); print( x );
Hello, World!

Scope

outerfn = { x = 12; innerfn = { print(x); }; innerfn; }; thing = outerfn(); thing();

Scope

outerfn = { x = 12; innerfn = { print(x); }; innerfn; }; thing = outerfn(); thing();
12

Environments

Environments

class Env: # ... def get(self, name): if name in self.items: return self.items[name] elif self.parent is not None: return self.parent.get(name) else: return None

Environments

class Env: # ... def set(self, name, value): self.items[name] = value

Cell's evaluator

def eval_expr(expr, env): typ = expr[0] if typ == "number": ... elif typ == "string": ... elif typ == "none": ... elif typ == "operation": ... elif typ == "symbol": ... elif typ == "assignment": ... elif typ == "call": ... elif typ == "function": ... else: ...

Cell's evaluator

def eval_expr(expr, env): ... if typ == "number": return ("number", float(expr[1]))

Cell's evaluator

def eval_expr(expr, env): ... elif typ == "string": return ("string", expr[1])

Cell's evaluator

def eval_expr(expr, env): ... elif typ == "none": return ("none",)

Cell's evaluator

def eval_expr(expr, env): ... elif typ == "operation": return _operation(expr, env)

Cell's evaluator

def _operation(expr, env): arg1 = eval_expr(expr[2], env) arg2 = eval_expr(expr[3], env) if expr[1] == "+": return ("number", arg1[1] + arg2[1]) elif expr[1] == "-": return ("number", arg1[1] - arg2[1]) # ... else: raise Exception("Unknown operation: " + expr[1])

Cell's evaluator

def eval_expr(expr, env): ... elif typ == "symbol": name = expr[1] ret = env.get(name) if ret is None: raise Exception("Unknown symbol '%s'." % name) else: return ret

Cell's evaluator

def eval_expr(expr, env): ... elif typ == "assignment": var_name = expr[1][1] val = eval_expr(expr[2], env) env.set(var_name, val) return val

Cell's evaluator

def eval_expr(expr, env): ... elif typ == "call": return _function_call(expr, env)

Cell's evaluator

def _function_call(expr, env): fn = eval_expr(expr[1], env) args = list((eval_expr(a, env) for a in expr[2])) if fn[0] == "function": ... elif fn[0] == "native": ... else: ...

Cell's evaluator

args = list((eval_expr(a, env) for a in expr[2])) ... if fn[0] == "function": params = fn[1] fail_if_wrong_number_of_args(expr[1], params, args) body = fn[2] fn_env = fn[3] new_env = Env(fn_env) for p, a in zip(params, args): new_env.set(p[1], a) return eval_list(body, new_env)

Cell's evaluator

args = list((eval_expr(a, env) for a in expr[2])) ... if fn[0] == "function": ... elif fn[0] == "native": py_fn = fn[1] params = inspect.getargspec(py_fn).args fail_if_wrong_number_of_args(expr[1], params[1:], args) return fn[1](env, *args)

Cell's evaluator

args = list((eval_expr(a, env) for a in expr[2])) ... if fn[0] == "function": ... elif fn[0] == "native": else: raise Exception( "Not a function: %s" % str(fn))

Cell's evaluator

def eval_expr(expr, env): ... elif typ == "function": return ("function", expr[1], expr[2], Env(env))

Cell's evaluator

def eval_expr(expr, env): ... else: raise Exception("Unknown expression type: " + str(expr))

End result: value + side effects

Discussion

Donate

Donate! patreon.com/andybalaam

Play

Play! artificialworlds.net/rabbit-escape

More info

Videos youtube.com/user/ajbalaam
Twitter @andybalaam
Blog artificialworlds.net/blog
Projects artificialworlds.net
GitHub github.com/andybalaam