diff options
| -rw-r--r-- | interpreter.py | 162 | ||||
| -rw-r--r-- | neb.py | 42 | ||||
| -rw-r--r-- | repl.py | 3 |
3 files changed, 169 insertions, 38 deletions
diff --git a/interpreter.py b/interpreter.py new file mode 100644 index 0000000..fd327f9 --- /dev/null +++ b/interpreter.py @@ -0,0 +1,162 @@ +from parser import Expr + + +def interpret(exprs): + ret = None + for expr in exprs: + ret = evaluate(expr) + return ret + +def evaluate(expr): + if isinstance(expr, Expr.Literal): + return expr.value + elif expr.symbol.name == "not": + return interpretNot(expr) + elif expr.symbol.name in ("*", "/"): + return interpretFactor(expr) + elif expr.symbol.name in ("-", "+"): + return interpretTerm(expr) + elif expr.symbol.name in (">", ">=", "<", "<="): + return interpretComparison(expr) + elif expr.symbol.name == "eq?": + return interpretEq(expr) + elif expr.symbol.name == "and": + return interpretAnd(expr) + elif expr.symbol.name == "or": + return interpretOr(expr) + + # flow control + elif expr.symbol.name == "if": + return interpretIf(expr) + + # io + elif expr.symbol.name == "print": + return interpretPrint(expr) + + else: + raise Exception(f"unable to evaluate: {expr}") + + +def interpretOr(expr): + # or returns true for the first expression that returns true + if len(expr.args) < 2: + raise Exception("'or' has at least two operands") + for arg in expr.args: + ev = evaluate(arg) + if ev not in (True, False): + raise Exception("'or' needs boolean arguments") + if ev == True: + return True + return False + +def interpretAnd(expr): + # and returns false for the first expression that returns false + if len(expr.args) < 2: + raise Exception("'and' has at least two operands") + for arg in expr.args: + ev = evaluate(arg) + if ev not in (True, False): + raise Exception("'and' needs boolean arguments") + if ev == False: + return False + return True + +def interpretEq(expr): + # equal + if len(expr.args) != 2: + raise Exception("'eq?' needs two arguments") + first = evaluate(expr.args[0]) + second = evaluate(expr.args[1]) + return first == second + +def interpretComparison(expr): + if len(expr.args) != 2: + raise Exception("comparisons have two operands") + left = evaluate(expr.args[0]) + if type(left) not in (int, float): + raise Exception("'left' must be a number") + right = evaluate(expr.args[1]) + if type(right) not in (int, float): + raise Exception("'right' must be a number") + + if expr.symbol.name == ">": + return left > right + elif expr.symbol.name == ">=": + return left >= right + elif expr.symbol.name == "<": + return left < right + elif expr.symbol.name == "<=": + return left <= right + +def interpretTerm(expr): + if len(expr.args) < 1: + raise Exception("term has at least one operand") + res = 0 + for arg in expr.args: + ev = evaluate(arg) + if type(ev) not in (int, float): + raise Exception("term must be a number") + if expr.symbol.name == "+": + res += ev + elif expr.symbol.name == "-": + res -= ev + return res + +def interpretFactor(expr): + if expr.symbol.name == "/": + if len(expr.args) != 2: + raise Exception("'/' requires two operands") + num = evaluate(expr.args[0]) + if type(num) not in (int, float): + raise Exception("numerator must be a number") + denom = evaluate(expr.args[1]) + if type(denom) not in (int, float): + raise Exception("denominator must be a number") + return num / denom # TODO floats and ints + else: + if len(expr.args) < 2: + raise Exception("'*' requires at least two operands") + first = evaluate(expr.args[0]) + if type(first) not in (int, float): + raise Exception("'*' operand must be a number") + res = first + for arg in expr.args[1:]: + tmp = evaluate(arg) + if type(tmp) not in (int, float): + raise Exception("'*' operand must be a number") + res = res * tmp + return res + +def interpretNot(expr): + if len(expr.args) != 1: + raise Exception("'not' takes one operand") + res = evaluate(expr.args[0]) + if res not in (True, False): + raise Exception("'not' only works on booleans") + return not res + +def interpretIf(expr): + # if cond t-branch [f-branch] + if len(expr.args) not in (2, 3): + raise Exception("too many or too few operands") + cond = evaluate(expr.args[0]) + if cond not in (True, False): + raise Exception("'if' condition must be boolean") + if cond: + return evaluate(expr.args[1]) + elif len(expr.args) == 3: + return evaluate(expr.args[2]) + return None # this shouldn't be reached + +def interpretPrint(expr): + if len(expr.args) == 0: + print() + elif len(expr.args) == 1: + ev = evaluate(expr.args[0]) + if not isinstance(ev, str): + raise Exception("can only 'print' strings") + print(ev) + else: + raise Exception("'print' takes zero or one argument") + + return None # print returns nothing @@ -1,7 +1,6 @@ from lexer import lex from parser import parse -from runner import evaluate -from std import _get_debug +from interpreter import interpret import sys @@ -15,44 +14,11 @@ def main(): data = fil.read() try: - lexed = lex(data, []) - ''' - if _get_debug(): - acc = " ".join([f"{l}" for l in lexed]) - print(f" - LEX: {acc}") - ''' - parsed = parse(lexed, []) - ''' - if _get_debug(): - acc = " ".join([f"{p}" for p in parsed]) - print(f" - PARSE: {acc}") - ''' - ev = evaluate(parsed, []) + lexed = lex(data) + parsed = parse(lexed) + ev = interpret(parsed) except Exception as e: print(f"panic! {e}") - ''' - print("### neb :)(:") - print("version: < 0") - idx = 1 - while True: - inp = input(f"#{idx}> ") - if len(inp.strip()) == 0: - continue - try: - lexed = lex(inp, []) - if _get_debug(): - acc = " ".join([f"{l}" for l in lexed]) - print(f" - LEX: {acc}") - parsed = parse(lexed, []) - if _get_debug(): - acc = " ".join([f"{p}" for p in parsed]) - print(f" - PARSE: {acc}") - ev = evaluate(parsed, []) - print(f"=> {ev}") - idx += 1 - except Exception as e: - print(f"panic! {e}") - ''' if __name__ == "__main__": @@ -1,5 +1,6 @@ from lexer import lex from parser import parse +from interpreter import interpret def _get_debug(): return True @@ -21,6 +22,8 @@ def main(): if _get_debug(): acc = " ".join([f"{p}" for p in parsed]) print(f" - PARSE: {acc}") + inter = interpret(parsed) + print(f"=> {inter}") idx += 1 except Exception as e: print(f"panic! {e}") |
