def A_native_function_can_edit_the_environment(): def mx3(env): env.set("x", ("number", 3)) env = Env() env.set("make_x_three", ("native", mx3)) assert_that(evald("x=1;make_x_three();x;", env), equals(("number", 3)))
def A_closure_holds_updateable_values(): def dumb_set(env, sym, val): env.parent.parent.parent.set(sym[1], val) def dumb_if_equal(env, val1, val2, then_fn, else_fn): if val1 == val2: ret = then_fn else: ret = else_fn return eval_expr(("call", ret, []), env) env = Env() env.set("dumb_set", ("native", dumb_set)) env.set("dumb_if_equal", ("native", dumb_if_equal)) assert_that( evald( """ counter = { x = 0; {:(meth) dumb_if_equal(meth, "get", {x;}, {dumb_set("x", x + 1);} ); } }(); counter("inc"); counter("inc"); counter("get"); """, env), equals(("number", 2)))
def Native_function_gets_called(): def native_fn(env, x, y): return ("number", x[1] + y[1]) env = Env() env.set("native_fn", ("native", native_fn)) assert_that(evald("native_fn( 2, 8 );", env), equals(("number", 10)))
def A_closure_holds_updateable_values(): def dumb_set(env, sym, val): env.parent.parent.parent.set(sym[1], val) def dumb_if_equal(env, val1, val2, then_fn, else_fn): if val1 == val2: ret = then_fn else: ret = else_fn return eval_expr(("call", ret, []), env) env = Env() env.set("dumb_set", ("native", dumb_set)) env.set("dumb_if_equal", ("native", dumb_if_equal)) assert_that( evald( """ counter = { x = 0; {:(meth) dumb_if_equal(meth, "get", {x;}, {dumb_set("x", x + 1);} ); } }(); counter("inc"); counter("inc"); counter("get"); """, env ), equals(("number", 2)) )
def A_native_function_can_edit_the_environment(): def mx3(env): env.set("x", ("number", 3)) env = Env() env.set("make_x_three", ("native", mx3)) assert_that( evald("x=1;make_x_three();x;", env), equals(("number", 3)) )
def Wrong_number_of_arguments_to_a_native_function_is_an_error(): def native_fn0(env): return ("number", 12) def native_fn3(env, x, y, z): return ("number", 12) env = Env() env.set("native_fn0", ("native", native_fn0)) env.set("native_fn3", ("native", native_fn3)) assert_prog_fails( "native_fn0(3);", "1 arguments passed to function ('symbol', 'native_fn0'), but it requires 0 arguments.", env) assert_prog_fails( "native_fn3(3, 2);", "2 arguments passed to function ('symbol', 'native_fn3'), but it requires 3 arguments.", env)
def Wrong_number_of_arguments_to_a_native_function_is_an_error(): def native_fn0(env): return ("number", 12) def native_fn3(env, x, y, z): return ("number", 12) env = Env() env.set("native_fn0", ("native", native_fn0)) env.set("native_fn3", ("native", native_fn3)) assert_prog_fails( "native_fn0(3);", "1 arguments passed to function ('symbol', 'native_fn0'), but it requires 0 arguments.", env ) assert_prog_fails( "native_fn3(3, 2);", "2 arguments passed to function ('symbol', 'native_fn3'), but it requires 3 arguments.", env )
def _function_call(expr, env): fn = eval_expr(expr[1], env) args = list((eval_expr(a, env) for a in expr[2])) if fn[0] == "function": params = fn[1] fail_if_wrong_number_of_args(expr[1], params, args) body = fn[2] fn_env = fn[3] new_env = Env(fn_env) for p, a in zip(params, args): new_env.set(p[1], a) return eval_list(body, new_env) elif fn[0] == "native": py_fn = fn[1] params = inspect.getargspec(py_fn).args fail_if_wrong_number_of_args(expr[1], params[1:], args) return fn[1](env, *args) else: raise Exception( "Attempted to call something that is not a function: %s" % str(fn))
def _function_call(expr, env): fn = eval_expr(expr[1], env) args = list((eval_expr(a, env) for a in expr[2])) if fn[0] == "function": params = fn[1] fail_if_wrong_number_of_args(expr[1], params, args) body = fn[2] fn_env = fn[3] new_env = Env(fn_env) for p, a in zip(params, args): new_env.set(p[1], a) return eval_list(body, new_env) elif fn[0] == "native": py_fn = fn[1] params = inspect.getargspec(py_fn).args fail_if_wrong_number_of_args(expr[1], params[1:], args) return fn[1](env, *args) else: raise Exception( "Attempted to call something that is not a function: %s" % str(fn) )