From cb25bf30a3b228de8592bbf93534cbb51b9fe817 Mon Sep 17 00:00:00 2001 From: Loic Nageleisen Date: Mon, 27 Jan 2014 22:49:20 +0100 Subject: [PATCH] Ten Primitives (and more) achieved --- repl.py | 13 +++- wasp/__init__.py | 34 +++++++++-- wasp/ast.py | 101 ++++++++++++++++++++++--------- wasp/lib.py | 146 ++++++++++++++++++++++++++++++++++++--------- wasp/parser/box.py | 10 +--- 5 files changed, 232 insertions(+), 72 deletions(-) diff --git a/repl.py b/repl.py index 6182f1a..df5207b 100644 --- a/repl.py +++ b/repl.py @@ -1,4 +1,5 @@ import wasp +import wasp.ast import readline @@ -35,13 +36,19 @@ if __name__ == "__main__": continue try: - ast = context['read'](line) + ast = context['read'](wasp.ast.String(line), context) except wasp.lib.ReadError, e: print "Parse error:", e.message continue try: - print context['eval'](ast, context) + result = context['eval'](ast, context) except wasp.SymbolError, e: - print "Undefined symbol:", e.message + print "Eval error:", e.message + continue + + try: + context['print'](result, context) + except Exception, e: + print "Print error:", e.message continue diff --git a/wasp/__init__.py b/wasp/__init__.py index 367f7b8..98d2173 100644 --- a/wasp/__init__.py +++ b/wasp/__init__.py @@ -1,6 +1,6 @@ from __future__ import print_function - +from copy import copy import wasp.lib as lib @@ -13,19 +13,43 @@ class SymbolError(Exception): class Context(object): symbols = { - '+': lib.plus, - '-': lib.minus, - '*': lib.multiply, - '/': lib.div, + 'nil': lib.nil, + 'true': lib.true, + 'false': lib.false, 'read': lib.read, 'eval': lib.eval, 'print': lib.print, 'apply': lib.apply, 'quote': lib.quote, + 'car': lib.car, + 'cdr': lib.cdr, + 'list': lib.list, + 'cons': lib.cons, + 'cond': lib.cond, + 'eq': lib.eq, + 'append': lib.append, + 'def': lib.label, + 'lambda': lib.lambda_, + '+': lib.plus, + '-': lib.minus, + '*': lib.multiply, + '/': lib.div, } + def __init__(self, symbols={}): + self.symbols = dict(self.symbols.items() + symbols.items()) + def __getitem__(self, symbol): try: return self.symbols[symbol] except KeyError: raise SymbolError(symbol) + + def __setitem__(self, symbol, value): + self.symbols[symbol] = value + + def __add__(self, context): + return Context(dict(self.symbols.items() + context.symbols.items())) + + def copy(self): + return self.__class__(copy(self.symbols)) diff --git a/wasp/ast.py b/wasp/ast.py index 6986edd..376ae29 100644 --- a/wasp/ast.py +++ b/wasp/ast.py @@ -2,36 +2,71 @@ class Node(object): pass -class List(Node): - def __init__(self, car, cdr=None): - self.car = car - self.cdr = cdr - - def eval(self, context): - return context['apply'](self.car, self.cdr, context) - - def __repr__(self): - return "" % (self.car, self.cdr) - - class Atom(Node): pass -class Operator(Node): - pass +class Nil(Atom): + def __init__(self): + self.car = self + self.cdr = self + + def iter(self): + return [] + + def __str__(self): + return "nil" + + def __repr__(self): + return "" + + +class Truth(Atom): + def __init__(self, true=True): + self.true = true + + def __str__(self): + return ("%s" % self.true).lower() + + def __repr__(self): + return "<%s>" % self.true + + +class List(Node): + def __init__(self, car, cdr=Nil()): + self.car = car + self.cdr = cdr + + def iter(self): + car = self.car + cdr = self.cdr + while not type(cdr) is Nil: + yield car + car = cdr.car + cdr = cdr.cdr + else: + yield car + + def __str__(self): + return "(%s)" % (' '.join(str(i) for i in self.iter())) + + def __repr__(self): + return "" % (self.car, self.cdr) class Number(Atom): def __init__(self, value): self.value = value - def eval(self, context): - return self.value + def __str__(self): + return "%s" % self.value def __repr__(self): return "<%s %s>" % (type(self).__name__, self.value) + def __eq__(self, other): + return self.value == other.value + class Integer(Number): pass @@ -42,37 +77,47 @@ class Float(Number): class Symbol(Atom): - def __init__(self, value): - self.value = value + def __init__(self, name): + self.name = name - def eval(self, context): - return context[self.value] + def __str__(self): + return "%s" % self.name def __repr__(self): - return "" % self.value + return "" % self.name class String(Atom): def __init__(self, value): self.value = value - def eval(self, context): - return self.value + def __str__(self): + return '"%s"' % self.value def __repr__(self): return "" % self.value -class Quote(Node): +class Quote(List): def __init__(self, sexpr): - self.sexpr = sexpr + self.car = Symbol('quote') + self.cdr = sexpr + self.sexpr = self.cdr - def eval(self, context): - return context['quote'](self.sexpr) + def __str__(self): + return "'%s" % self.sexpr def __repr__(self): return "" % self.sexpr class Lambda(Node): - pass + def __init__(self, args, body): + self.args = args + self.body = body + + def __str__(self): + return "lambda %s %s" % (self.args, self.body) + + def __repr__(self): + return "" % (self.args, self.body) diff --git a/wasp/lib.py b/wasp/lib.py index 9017530..bf57d73 100644 --- a/wasp/lib.py +++ b/wasp/lib.py @@ -1,64 +1,156 @@ -from __future__ import print_function +from __future__ import print_function, division -import wasp.parser +import wasp.parser as parser +from wasp.ast import List, Atom, Lambda, Symbol, Nil, Truth +import wasp py_print = print py_eval = eval +py_list = list class ReadError(Exception): pass -def plus(a, b): - return a + b +nil = Nil() +true = Truth() +false = Truth(False) -def minus(a, b): - return a - b +def atom(expr): + return isinstance(expr, Atom) -def multiply(a, b): - return a * b +def car(expr, context): + return expr.car.car -def div(a, b): - return a / b +def cdr(expr, context): + return expr.car.cdr -def read(string): +def label(expr, context): + symbol = expr.car + value = expr.cdr.car + context[symbol.name] = value + return value + + +def read(string, context): try: - ptree = wasp.parser.parse(string) + ptree = parser.parse(string.value) except ValueError, e: raise ReadError(e.message) - py_print(" ^ %s" % ptree) + #py_print(" ^ %s" % ptree) try: ast = ptree.ast() except ValueError, e: raise ReadError(e.message) - py_print(" ‡ %r" % ast) + #py_print(" ‡ %r" % ast) return ast -def eval(ast, context): - # eval value unless list - # eval args unless lazy - # eval call - return ast.eval(context) +def quote(expr, context): + return expr -def apply(a, b, context): - # move to eval - args = [arg.eval(context) for arg in b] - return a.eval(context)(*args) +def eval(expr, context): + if atom(expr): + if type(expr) is Symbol: + return context[expr.name] + else: + return expr + + # else it's a list + symbol = expr.car + args = expr.cdr + + x_args = Nil() + if symbol.name in ['quote', 'lambda', 'cond']: + x_args = args + elif symbol.name in ['def']: + for arg in reversed(py_list(args.cdr.iter())): + x_args = List(eval(arg, context), x_args) + x_args = List(args.car, x_args) + else: + for arg in reversed(py_list(args.iter())): + x_args = List(eval(arg, context), x_args) + + expr = List(symbol, x_args) + return apply(expr, context) -def quote(a): - return a +def print(string, context): + py_print(str(string)) -def print(string): - py_print(string) +def apply(expr, context): + symbol = expr.car + args = expr.cdr + l = eval(symbol, context) + if type(l).__name__ == 'function': + return l(args, context) + else: + symbol_names = (symbol.name for symbol in l.args.iter()) + args_dict = dict(zip(symbol_names, args.iter())) + arg_context = wasp.Context(args_dict) + context = context + arg_context + return eval(l.body, context) + + +def lambda_(expr, context): + args = expr.car + body = expr.cdr.car + return Lambda(args, body) + + +def list(expr, context): + return expr + + +def cons(expr, context): + return List(expr.car, expr.cdr.car) + + +def cond(expr, context): + for clause in expr.iter(): + condition = eval(clause.car, context) + if condition != false and type(condition) != Nil: + return eval(clause.cdr.car, context) + return Nil() + + +def eq(expr, context): + if expr.car == expr.cdr.car: + return true + else: + return false + + +def append(expr, context): + result = Nil() + for item in reversed(py_list(expr.cdr.car.iter())): + result = List(item, result) + for item in reversed(py_list(expr.car.iter())): + result = List(item, result) + return result + + +def plus(args, context): + return args.car.value + args.cdr.car.value + + +def minus(args, context): + return args.car.value - args.cdr.car.value + + +def multiply(args, context): + return args.car.value * args.cdr.car.value + + +def div(args, context): + return args.car.value / args.cdr.car.value diff --git a/wasp/parser/box.py b/wasp/parser/box.py index eba27a8..2c1af30 100644 --- a/wasp/parser/box.py +++ b/wasp/parser/box.py @@ -29,19 +29,11 @@ class Pair(BaseBox): y = self.y return "(%s . %s)" % (self.x, y) - def _r_cdr(self, cdr): - if self.y is None: - return (self.x.ast(), ) - else: - return cdr + (self.x.ast(), ) + self.y._r_cdr(cdr) - def ast(self): car = self.x.ast() if self.y is None: - cdr = None - elif type(self.y) is Pair: - cdr = list(self.y._r_cdr(())) + cdr = ast.Nil() else: cdr = self.y.ast()