def test_set(): """Ensure set mutates bindings.""" env = prelude.env() # ensure we can't set non-existent bindings bad_call = wtypes.List( [wtypes.Integer(1), wtypes.Symbol('x'), wtypes.Symbol('set!')]) with pytest.raises(exceptions.WispException): assert bad_call.eval(env) # ensure we can set previously-existent bindings define_call = wtypes.List( [wtypes.Integer(1), wtypes.Symbol('x'), wtypes.Symbol('define')]) define_call.eval(env) assert wtypes.Symbol('x').eval(env) == wtypes.Integer(1) set_call = wtypes.List( [wtypes.Integer(2), wtypes.Symbol('x'), wtypes.Symbol('set!')]) set_call.eval(env) assert wtypes.Symbol('x').eval(env) == wtypes.Integer(2)
def test_begin(): """Ensure begin return the last expression.""" env = prelude.env() call = wtypes.List([ wtypes.List([ wtypes.Integer(1), wtypes.Integer(2), wtypes.Symbol('+'), ]), wtypes.List([ wtypes.Integer(1), wtypes.Integer(1), wtypes.Symbol('+'), ]), wtypes.Symbol('begin') ]) assert call.eval(env) == wtypes.Integer(3)
def cons(args: typing.List[wtypes.Expression], env: wisp.env.Environment) -> wtypes.List: """Attach the first argument to the head of the list in the second.""" head, rest = args if isinstance(rest, wtypes.List): return wtypes.List([head] + rest.items) else: raise exceptions.type_error(wtypes.List, rest)
def test_cond(): """Ensure we evaluate cond properly.""" env = prelude.env() call = wtypes.List([ wtypes.List([ wtypes.String('two'), wtypes.List( [wtypes.Integer(2), wtypes.Integer(2), wtypes.Symbol('eq?')]) ]), wtypes.List([ wtypes.String('one'), wtypes.List( [wtypes.Integer(1), wtypes.Integer(1), wtypes.Symbol('eq?')]) ]), wtypes.List([ wtypes.String('not-one'), wtypes.List( [wtypes.Integer(2), wtypes.Integer(1), wtypes.Symbol('eq?')]) ]), wtypes.Symbol('cond') ]) result = call.eval(env) assert result == wtypes.String('one')
def test_eval_list(): """Ensure lists are evaluated as post-fix function calls.""" env = wisp.env.Environment({'+': wtypes.Function( lambda xs, _: wtypes.Integer(xs[0].val + xs[1].val) )}) res = wtypes.List([ wtypes.Integer(1), wtypes.Integer(2), wtypes.Symbol('+') ]).eval(env) assert res == wtypes.Integer(3)
def test_cdr(): """Ensure cdr takes the rest.""" env = prelude.env() res = env[wtypes.Symbol('cdr')].call([ quoted_list([wtypes.Integer(1), wtypes.Integer(2), wtypes.Integer(3)]) ], env) assert res == wtypes.List([wtypes.Integer(2), wtypes.Integer(3)])
def test_bad_lambda(): """Ensure we handle lambdas which raise exceptions.""" env = prelude.env() # define a lambda calling a non-existent symbol bad_lambda = env[wtypes.Symbol('lambda')].call([ wtypes.List([wtypes.Symbol('x')]), wtypes.List([wtypes.Symbol('snakes')]) ], env) # raise an exception when we call it call = wtypes.List([wtypes.Integer(2), bad_lambda]) with pytest.raises(exceptions.WispException): call.eval(env) # ensure we didn't pollute even though the call raised an exception with pytest.raises(exceptions.WispException): env[wtypes.Symbol('x')]
def test_define(): """Ensure define binds values to symbols.""" env = prelude.env() res = env[wtypes.Symbol('define')].call( [wtypes.Symbol('a'), quoted_list([wtypes.String('apple')])], env) assert res == wtypes.Symbol('a') assert env[wtypes.Symbol('a')] == wtypes.List([wtypes.String('apple')])
def test_parse_list(): """Ensure we can parse lists.""" assert parser.parse_expr.parse('(1 abc "abc" #t #f +)') == wtypes.List([ wtypes.Integer(1), wtypes.Symbol('abc'), wtypes.String('abc'), wtypes.Bool(True), wtypes.Bool(False), wtypes.Symbol('+'), ])
def test_cons(): """Ensure cons sticks things together.""" env = prelude.env() res = env[wtypes.Symbol('cons')].call([ wtypes.Integer(1), quoted_list([wtypes.Integer(2), wtypes.Integer(3)]) ], env) assert res == wtypes.List( [wtypes.Integer(1), wtypes.Integer(2), wtypes.Integer(3)])
def test_lambda(): """Ensure lambdas can be defined and called.""" env = prelude.env() # define a lambda performing x - y sub_lambda = env[wtypes.Symbol('lambda')].call([ wtypes.List([wtypes.Symbol('x'), wtypes.Symbol('y')]), wtypes.List( [wtypes.Symbol('y'), wtypes.Symbol('x'), wtypes.Symbol('-')]), ], env) # ensure we can call the above lambda call = wtypes.List([wtypes.Integer(1), wtypes.Integer(3), sub_lambda]) res = call.eval(env) assert res == wtypes.Integer(2) # ensure we didn't pollute the environment with bindings from the call. with pytest.raises(exceptions.WispException): env[wtypes.Symbol('x')]
def test_cond_with_else(): """Ensure we handle else expressions.""" env = prelude.env() call = wtypes.List([ wtypes.List([wtypes.String('else-case'), wtypes.Symbol('else')]), wtypes.List([ wtypes.String('not-two'), wtypes.List( [wtypes.Integer(3), wtypes.Integer(2), wtypes.Symbol('eq?')]) ]), wtypes.List([ wtypes.String('not-one'), wtypes.List( [wtypes.Integer(2), wtypes.Integer(1), wtypes.Symbol('eq?')]) ]), wtypes.Symbol('cond') ]) result = call.eval(env) assert result == wtypes.String('else-case')
def quoted_list(elems): """Build a quoted list consisting of the given elements, safe from eval.""" return wtypes.List([wtypes.List(elems), wtypes.Symbol('quote')])
def test_closures(): """Ensure we handle closures properly.""" env = prelude.env() counter_def = wtypes.List([ wtypes.List([ wtypes.List([ wtypes.List([ wtypes.Symbol('x'), wtypes.List([ wtypes.List([ wtypes.Integer(1), wtypes.Symbol('x'), wtypes.Symbol('+') ]), wtypes.Symbol('x'), wtypes.Symbol('set!') ]), wtypes.Symbol('begin') ]), wtypes.List([]), wtypes.Symbol('lambda') ]), wtypes.List([wtypes.Symbol('x')]), wtypes.Symbol('lambda') ]), wtypes.Symbol('make-counter'), wtypes.Symbol('define'), ]) counter_def.eval(env) counter = wtypes.List([ wtypes.List([wtypes.Integer(0), wtypes.Symbol('make-counter')]), wtypes.Symbol('c'), wtypes.Symbol('define'), ]) counter.eval(env) call = wtypes.List([wtypes.Symbol('c')]) assert call.eval(env) == wtypes.Integer(1) assert call.eval(env) == wtypes.Integer(2) assert call.eval(env) == wtypes.Integer(3) # ensure 'x' from the closure hasn't leaked into the global scope. with pytest.raises(exceptions.WispException): env[wtypes.Symbol('x')]
def test_recursion(): """Ensure we can handle simple recursion.""" env = prelude.env() fib_def = wtypes.List([ wtypes.List([ wtypes.List([ wtypes.List([ wtypes.List([ wtypes.List([ wtypes.List([ wtypes.Integer(2), wtypes.Symbol('n'), wtypes.Symbol('-') ]), wtypes.Symbol('fib') ]), wtypes.List([ wtypes.List([ wtypes.Integer(1), wtypes.Symbol('n'), wtypes.Symbol('-') ]), wtypes.Symbol('fib') ]), wtypes.Symbol('+') ]), wtypes.Symbol('else') ]), wtypes.List([ wtypes.Integer(1), wtypes.List([ wtypes.Integer(1), wtypes.Symbol('n'), wtypes.Symbol('eq?') ]) ]), wtypes.List([ wtypes.Integer(0), wtypes.List([ wtypes.Integer(0), wtypes.Symbol('n'), wtypes.Symbol('eq?') ]) ]), wtypes.Symbol('cond') ]), wtypes.List([wtypes.Symbol('n')]), wtypes.Symbol('lambda') ]), wtypes.Symbol('fib'), wtypes.Symbol('define'), ]) fib_def.eval(env) call = wtypes.List([wtypes.Integer(0), wtypes.Symbol('fib')]) assert call.eval(env) == wtypes.Integer(0) call = wtypes.List([wtypes.Integer(1), wtypes.Symbol('fib')]) assert call.eval(env) == wtypes.Integer(1) call = wtypes.List([wtypes.Integer(5), wtypes.Symbol('fib')]) assert call.eval(env) == wtypes.Integer(5) call = wtypes.List([wtypes.Integer(6), wtypes.Symbol('fib')]) assert call.eval(env) == wtypes.Integer(8) call = wtypes.List([wtypes.Integer(7), wtypes.Symbol('fib')]) assert call.eval(env) == wtypes.Integer(13)
def test_eval_empty_list(): """Ensure empty lists are evaluated to empty lists.""" assert wtypes.List([]).eval({}) == wtypes.List([])
def cdr(args: typing.List[wtypes.Expression], env: wisp.env.Environment) -> wtypes.List: """Return all but the first element of the given list.""" return __list_op(lambda wlst: wtypes.List(wlst.items[1:]), args)
def test_eval_list_without_a_func(): """Ensure an error is raised when applying non-functions.""" with pytest.raises(exceptions.WispException): wtypes.List([wtypes.Integer(1)]).eval({})
def parse_list(): """Parse a list as a ()-enclosed sequence of expressions.""" yield parsec.string('(') vals = yield parsec.sepBy(parse_expr, parsec.many1(parsec.space())) yield parsec.string(')') return wtypes.List(vals)