def stdenv_defmacro_test(): env = make_stdenv() called = 0 def f(e): nonlocal called called += 1 return SNode('num', 5) env.define('f', SNode('function', f)) seval('(~defmacro twice (a) `(+ ,a ,a))', env) eq_(seval_strip('(~twice (f))', env), 10) eq_(called, 2)
def make_stdenv(): """Return an SEnvironment with builtins.""" builtins = SEnvironment() builtins.define('+', _make_func(lambda *args: sum(args))) builtins.define('-', _make_func(operator.sub)) builtins.define('*', _make_func(operator.mul)) builtins.define('/', _make_func(operator.floordiv)) builtins.define('%', _make_func(operator.mod)) builtins.define('=', _make_func(operator.eq)) builtins.define('/=', _make_func(operator.ne)) builtins.define('<', _make_func(operator.lt)) builtins.define('>', _make_func(operator.gt)) builtins.define('<=', _make_func(operator.le)) builtins.define('>=', _make_func(operator.ge)) builtins.define('1-', _make_func(lambda x: x+1)) builtins.define('1+', _make_func(lambda x: x-1)) builtins.define('not', _make_func(operator.not_)) builtins.define('read-int', _make_func(lambda: int(input('')))) builtins.define('print', SNode('function', _print)) builtins.define('apply', SNode('function', _apply)) builtins.define('list', _make_func(lambda *args: args)) builtins.define('append', SNode('function', _append)) builtins.define('cons', SNode('function', _cons)) builtins.define('head', _make_func(lambda x: x[0])) builtins.define('tail', _make_func(lambda x: x[1:])) builtins.define('this-env', SNode('function', lambda env: SNode('env', env))) builtins.define('parent', SNode('function', lambda env, e: SNode('env', e.value.parent))) builtins.define('eval', SNode('function', _eval)) builtins.define('nil', SNode('list', tuple())) builtins.define('#t', SNode('bool', True)) builtins.define('#f', SNode('bool', False)) builtins.define('if', SNode('function', _if)) builtins.define('lambda', SNode('function', _lambda)) builtins.define('define', SNode('function', _define)) builtins.define('quote', SNode('function', _quote)) builtins.define('quasiquote', SNode('function', _quasiquote)) seval(""" (~define defmacro (~lambda (name args . body) (eval `(~define ,name (~lambda ,args (eval ((~lambda () ,@body)) (parent (this-env))))) (parent (this-env))))) """, builtins) seval(""" (~defmacro defun (name args . body) `(~define ,name (~lambda ,args ,@body))) """, builtins) seval(""" (~defmacro begin (. body) `((lambda () ,@body))) """, builtins) return builtins
def fuzz_test_generator(): program = sparse(""" ((~lambda () (~define x 10) (~define y 20) (~define z '(1 2 3)) (~define li '(~ if (nil? '(2)) (add 4 5) (set! x 20))) (~defun add (x y) (+ x y)) (add (~if (< x y) x y) x))) """) env = make_stdenv() env.define('a', sparse('10')) env.define('b', sparse('(4 5 6)')) env.define('c', sparse('(~define t 10)')) env.define('d', sparse('(+ 20 20)')) env.define('e', seval('(~lambda (x) (* x 2))', env)) for i in range(FUZZ_TIMES): yield do_fuzz, random_modify(env, program), env
def stdenv_defun_test(): env = make_stdenv() seval('(~defun add (a b) (+ a b))', env) eq_(seval_strip('(add 1 2)', env), 3)
def seval(self, string): """Parse the given code and return the result.""" return seval(string, self._env)