diff --git a/factorial.py b/factorial.py new file mode 100644 index 0000000..1c8cb5f --- /dev/null +++ b/factorial.py @@ -0,0 +1,13 @@ +from lysssp import _eval, globs + +_eval(['setq', 'factorial', ['lambda', ['x'], + ['cond', [ + [ ['equal?', 'x', 0], 1 ], + [ True, [ '*', 'x', ['factorial', ['-', 'x', 1]]]]]]]], globs) + +def test(): + print _eval(['factorial', 10], globs) + +import timeit + +print timeit.Timer(test).timeit(1) diff --git a/lambda.py b/lambda.py new file mode 100644 index 0000000..391bf83 --- /dev/null +++ b/lambda.py @@ -0,0 +1,3 @@ +from lysssp import _eval, globs + +print _eval(["apply", ["quote", ["lambda", [], 10]], ["quote", [20]]], globs) diff --git a/lysssp.py b/lysssp.py new file mode 100755 index 0000000..e6d9a36 --- /dev/null +++ b/lysssp.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +import inspect +import re + +globs = {} + +def isprim(name): + return inspect.isfunction(globs.get(name, None)) + +def islazy(name): + if isprim(name): return name in ['cond', 'quote', 'setq'] + return globs.get(name, [None])[0] == 'macro' + +def isatom(name): + return not (type(name) == list or type(name) == dict) + +def setq(sexpr, context): + globs[sexpr[0]] = sexpr[1] + return sexpr[1] + +def _apply(fn, args, context): + if isprim(fn): return globs[fn](args, context) + context = dict(zip(globs[fn][1], args)) + return _eval(globs[fn][2], context) + +def _eval(sexpr, context): + if isatom(sexpr): + if sexpr in context: + return context[sexpr] + return sexpr + + fn = sexpr[0] + args = sexpr[1:] + + if not islazy(fn): + args = map(lambda n: _eval(n, context), args) + return _apply(fn, args, context) + +def _cond(sexpr, context): + for elem in sexpr[0]: + if _eval(elem[0], context): return _eval(elem[1], context) + return False + +def _read(sexpr): + grammar = r"(\()|(\))|([^()\s]+)|(\s+)" + + def sequenceBuilder(match): + leftbracket, rightbracket, atom, whitespace = match.groups() + if(leftbracket): return '[' + elif(rightbracket): return ']' + elif(atom): return '"' + atom + '"' + elif(whitespace): return ',' + return eval(re.sub(grammar, sequenceBuilder, sexpr), None, None) + +globs['setq'] = setq +globs['cond'] = _cond +globs['car'] = lambda sexpr, context: sexpr[0][0] +globs['cdr'] = lambda sexpr, context: sexpr[0][1:] +globs['quote'] = lambda sexpr, context: sexpr[0] +globs["apply"] = lambda sexpr, context: _apply(sexpr[0], sexpr[1], context) +globs['+'] = lambda sexpr, context: sexpr[0] + sexpr[1] +globs['-'] = lambda sexpr, context: sexpr[0] - sexpr[1] +globs['*'] = lambda sexpr, context: sexpr[0] * sexpr[1] +globs['/'] = lambda sexpr, context: sexpr[0] / sexpr[1] +globs['equal?'] = lambda sexpr, context: sexpr[0] == sexpr[1] + +# FIXME: apply, eval the first arg +# TODO: define lambda func +# FIXME: sexpr=>lstruct +# TODO: _read, sexpr=>lstruct +# TODO: REPL while 1: print _eval(_read()) + diff --git a/parser.py b/parser.py new file mode 100644 index 0000000..293bf77 --- /dev/null +++ b/parser.py @@ -0,0 +1,80 @@ +from __future__ import print_function +from pprint import pprint +from rply import ParserGenerator, LexerGenerator +from rply.token import BaseBox + +lg = LexerGenerator() +lg.add("LPAREN", r"\(") +lg.add("RPAREN", r"\)") +lg.add("ATOM", r"[^\s()]+") +lg.ignore(r"\s+") + +pg = ParserGenerator(["LPAREN", "RPAREN", "ATOM"]) + + +@pg.error +def error_handler(token): + raise ValueError("unexpected %s" % token.gettokentype()) + + +@pg.production("main : sexpr") +def main(p): + return p[0] + + +@pg.production("sexpr : LPAREN expr RPAREN") +def sexpr(p): + return BoxSexpr(p[1]) + + +@pg.production("expr : ATOM") +def atom(p): + return BoxAtom(p[0].getstr()) + + +@pg.production("expr : ATOM sexpr") +@pg.production("expr : ATOM expr") +def sexpr(p): + return BoxExpr(BoxAtom(p[0].getstr()), p[1]) + +lexer = lg.build() +parser = pg.build() + + +class BoxSexpr(BaseBox): + def __init__(self, expr): + self.expr = expr + + def expr(self): + return self.expr + + def __str__(self): + return "( %s )" % (self.expr) + + +class BoxExpr(BaseBox): + def __init__(self, atom, expr): + self.atom = atom + self.expr = expr + + def atom(self): + return self.atom + + def expr(self): + return self.expr + + def __str__(self): + return "%s . ( %s )" % (self.atom, self.expr) + + +class BoxAtom(BaseBox): + def __init__(self, atom): + self.atom = atom + + def atom(self): + return self.atom + + def __str__(self): + return "%s" % (self.atom) + +print(parser.parse(lexer.lex("(+ 1 (* 3 2))")))