def let(se: SExpression, stg: LexicalVarStorage) -> SExpression: """ The ``let`` macro binds variables to a local scope: the expressions inside the macro. Like a function, a ``let`` returns the last expression in its body. Once the ``let`` returns, the variables are unbound, and cannot be accessed anymore. For example:: (let ((a (+ f g)) (b 11) (c 12)) (+ a (- b c))) In the example above, ``a`` was bound to whatever ``(+ f g)`` evaluates to, b to ``11``, and ``c`` to ``12``. Notice how the above was equivalent to another expression:: ((lambda (a b c) (+ a (- b c))) (+ f g) 11 12) You should do a syntax translation from the input to something like that. >>> from slyther.types import * >>> from slyther.parser import lisp >>> from slyther.evaluator import lisp_eval >>> stg = LexicalVarStorage( ... {'let': Variable(let), ... 'lambda': Variable(lambda_func), ... 'print': Variable(print_), ... 'x': Variable(10)}) >>> lisp_eval(lisp('(let ((x 20) (y 30)) (print x) (print y))'), stg) 20 30 NIL >>> lisp_eval(Symbol('x'), stg) 10 """ new_environ = {**stg.environ} new_local = {**stg.local} new_lex_var = LexicalVarStorage(new_environ) new_lex_var.local = new_local for item in se.car: new_lex_var.put(item.car, lisp_eval(item.cdr.car, stg)) return_val = NIL for item in se.cdr: return_val = lisp_eval(item, new_lex_var) return return_val
def curry(se: SExpression, stg: LexicalVarStorage = LexicalVarStorage({})): """ curry returns a user function with given arguments, It will return the user function if it is a user function """ if isinstance(se, UserFunction): return se
def test_fork(environ, local): stg = LexicalVarStorage(environ) stg.local = local save_local = dict(stg.local) save_environ = dict(stg.environ) fork = stg.fork() assert isinstance(fork, dict) # should not modify locals or environ during fork assert stg.local == save_local assert stg.environ == save_environ for k, v in stg.environ.items(): if k not in stg.local.keys(): assert fork[k] is v for k, v in stg.local.items(): assert fork[k] is v
def define(se: SExpression, stg: LexicalVarStorage): """ Define a variable or a function to a value, calling ``put`` on the storage:: ; variable (define var-name evaluate-me) ; lambda function (define func-name (lambda (args...) (body1) ... (bodyN))) ; SE-named function (define (func-name args...) (body1) ... (bodyN)) .. note:: If defining a function (either by ``lambda`` or by SE-named syntax), the definition **must** be made visible from within that function's ``environ``, or recursion will not work! >>> from slyther.types import * >>> from slyther.parser import lisp >>> se = lisp('((twirl alpha beta) (print alpha) (print beta))') >>> name = Symbol('twirl') >>> args = lisp('(alpha beta)') >>> body = lisp('((print alpha) (print beta))') >>> stg = LexicalVarStorage({}) >>> stg.put('NIL', NIL) >>> stg.put('define', define) >>> stg.put('lambda', lambda_func) >>> from slyther.evaluator import lisp_eval >>> lisp_eval(define(cons(cons(name, args), body), stg), stg) NIL >>> lisp_eval(define(lisp('(x 10)'), stg), stg) NIL >>> stg[name].value (lambda (alpha beta) (print alpha) (print beta)) >>> stg[name].value.environ['twirl'].value (lambda (alpha beta) (print alpha) (print beta)) >>> stg['x'].value 10 >>> stg[name].value.environ['x'].value Traceback (most recent call last): ... KeyError: 'x' """ key = se.car value = se.cdr if isinstance(key, SExpression): function = UserFunction(params=key.cdr, body=value, environ=stg.fork()) key = key.car function.environ[key] = Variable(function) stg.put(key, function) elif isinstance(key, Symbol): stg.put(key, lisp_eval(value.car, stg)) else: stg.put(key, value)
def let(se: SExpression, stg: LexicalVarStorage) -> SExpression: """ The ``let`` macro binds variables to a local scope: the expressions inside the macro. Like a function, a ``let`` returns the last expression in its body. Once the ``let`` returns, the variables are unbound, and cannot be accessed anymore. For example:: (let ((a (+ f g)) (b 11) (c 12)) (+ a (- b c))) In the example above, ``a`` was bound to whatever ``(+ f g)`` evaluates to, b to ``11``, and ``c`` to ``12``. Notice how the above was equivalent to another expression:: ((lambda (a b c) (+ a (- b c))) (+ f g) 11 12) You should do a syntax translation from the input to something like that. >>> from slyther.types import * >>> from slyther.parser import lisp >>> from slyther.evaluator import lisp_eval >>> stg = LexicalVarStorage( ... {'let': Variable(let), ... 'lambda': Variable(lambda_func), ... 'print': Variable(print_), ... 'x': Variable(10)}) >>> lisp_eval(lisp('(let ((x 20) (y 30)) (print x) (print y))'), stg) 20 30 NIL >>> lisp_eval(Symbol('x'), stg) 10 """ vals = [] ptrs = [] pred = se.car con = se.cdr for item in pred: vals.append(item.car) ptrs.append(item.cdr.car) param = SExpression.from_iterable(vals) ptrs = SExpression.from_iterable(ptrs) function = UserFunction(params=param, body=con, environ=stg.fork()) res = SExpression(function, ptrs) return res
def __init__(self): # load builtins out of slyther.bulitins builtins = { x.__name__: Variable(x) for x in map( partial(getattr, slyther.builtins), slyther.builtins.__dir__()) if isinstance(x, BuiltinCallable)} # put in the default variables builtins.update({ 'NIL': Variable(NIL), 'nil': Variable(NIL), '#t': Variable(Boolean(True)), '#f': Variable(Boolean(False)), }) self.stg = LexicalVarStorage(builtins)
def lambda_func(se: SExpression, stg: LexicalVarStorage) -> UserFunction: """ Define an anonymous function and return the ``UserFunction`` object. >>> from slyther.types import * >>> stg = LexicalVarStorage({}) >>> stg.put('x', 20) >>> f = lambda_func( ... SExpression.from_iterable([ ... SExpression.from_iterable(map(Symbol, ['a', 'b', '.', 'c'])), ... SExpression.from_iterable(map(Symbol, ['print', 'a'])), ... SExpression.from_iterable(map(Symbol, ['print', 'b'])), ... SExpression.from_iterable(map(Symbol, ['print', 'c']))]), ... stg) >>> f (lambda (a b . c) (print a) (print b) (print c)) >>> f.environ['x'].value 20 """ return UserFunction(se.car, se.cdr, stg.fork())
from slyther.parser import lisp from slyther.evaluator import lisp_eval collected = {} @BuiltinFunction def collect(name, value): collected[name] = value return value stg = LexicalVarStorage({ 'collect': Variable(collect), 'add': Variable(BuiltinFunction(operator.add)), '#t': Variable(Boolean(True)), '#f': Variable(Boolean(False)), 'NIL': Variable(NIL) }) def test_function_args(): environ = stg.fork() func = UserFunction(params=lisp('(x y z)'), body=lisp('''((collect 'z z) (collect 'x x) (collect 'y y) ;; return NIL so that TCO can be implemented ;; without breaking this test NIL)'''), environ=environ.copy())
def define(se: SExpression, stg: LexicalVarStorage): """ Define a variable or a function to a value, calling ``put`` on the storage:: ; variable (define var-name evaluate-me) ; lambda function (define func-name (lambda (args...) (body1) ... (bodyN))) ; SE-named function (define (func-name args...) (body1) ... (bodyN)) .. note:: If defining a function (either by ``lambda`` or by SE-named syntax), the definition **must** be made visible from within that function's ``environ``, or recursion will not work! >>> from slyther.types import * >>> from slyther.parser import lisp >>> se = lisp('((twirl alpha beta) (print alpha) (print beta))') >>> name = Symbol('twirl') >>> args = lisp('(alpha beta)') >>> body = lisp('((print alpha) (print beta))') >>> stg = LexicalVarStorage({}) >>> stg.put('NIL', NIL) >>> stg.put('define', define) # so that you can return a define if you want >>> stg.put('lambda', lambda_func) >>> from slyther.evaluator import lisp_eval >>> lisp_eval(define(cons(cons(name, args), body), stg), stg) NIL >>> lisp_eval(define(lisp('(x 10)'), stg), stg) NIL >>> stg[name].value (lambda (alpha beta) (print alpha) (print beta)) >>> stg[name].value.environ['twirl'].value (lambda (alpha beta) (print alpha) (print beta)) >>> stg['x'].value 10 >>> stg[name].value.environ['x'].value Traceback (most recent call last): ... KeyError: 'x' """ ''' if isinstance(se.car, Symbol): if isinstance(se.cdr, Variable): stg.put(str(se.car), se.cdr) else: f= lambda_func(se.cdr, stg) f.environ.put(str(se.car), f) else: f= UserFunction(se.cdr, se.cdr.cdr, stg) f.environ.put(str(se.car), f) ''' ''' if isinstance(se.car, Symbol): stg.put(str(se.car), se.cdr) elif callable(se.car): f= lambda_func(se.cdr, stg) f.environ.put(str(se.car), f) stg.put(str(se.car.car), f) elif callable(se.car.car): f= UserFunction(se.cdr, se.cdr.cdr, stg) f.environ.put(str(se.car), f) stg.put(str(se.car.car), f) ''' if isinstance(se.car, SExpression): f = UserFunction(se.car.cdr, se.cdr, stg.fork()) f.environ[str(se.car.car)] = Variable(f) stg.put(str(se.car.car), f) else: f = lisp_eval(se.cdr.car, stg) if isinstance(f, UserFunction): f.environ[str(se.car)] = Variable(f) stg.put(str(se.car), f) else: stg.put(str(se.car), f) """