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 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_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 eval_expr(expr, env): typ = expr[0] if typ == "number": return ("number", float(expr[1])) elif typ == "string": return ("string", expr[1]) elif typ == "none": return ("none", ) elif typ == "operation": return _operation(expr, env) elif typ == "symbol": name = expr[1] ret = env.get(name) if ret is None: raise Exception("Unknown symbol '%s'." % name) else: return ret elif typ == "assignment": var_name = expr[1][1] if var_name in env.items: raise Exception("Not allowed to re-assign symbol '%s'." % var_name) val = eval_expr(expr[2], env) env.set(var_name, val) return val elif typ == "call": return _function_call(expr, env) elif typ == "function": return ("function", expr[1], expr[2], Env(env)) else: raise Exception("Unknown expression type: " + str(expr))
def compile_(output, filename): env = Env() with open(output, "w") as outfile: outfile.write( compile_list(parse(lex(pycell.library.as_text(env))), env)) with open(filename, encoding="ascii") as infile: outfile.write( compile_list(parse(lex(chars_in_file(infile))), 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 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 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 _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 repl(stdin, stdout, stderr): env = Env(parent=None, stdin=stdin, stdout=stdout, stderr=stderr) pycell.library.import_(env) while True: try: p = Prompt(stdout) for value in eval_iter( parse(lex(p.handle_chars(chars_in_file(stdin)))), env): p.value(value) break except Exception as e: stderr.write(str(e)) stderr.write("\n") stdout.write("\n") stdout.flush()
def runned(inp): with StringIO() as stdin, StringIO() as stdout: env = Env(stdin=stdin, stdout=stdout, stderr=stdout) pycell.library.import_(env) return eval_list(parse(lex(inp)), env)
def evald(inp, stdout=None): env = Env(stdout=stdout) pycell.library.import_(env) return eval_list(parse(lex(inp)), env)
def None_evaluates_to_None(): assert_that(eval_expr(("none", ), Env()), equals(("none", )))
def run(filename, stdin, stdout, stderr): env = Env(stdin=stdin, stdout=stdout, stderr=stdout) pycell.library.import_(env) with open(filename, encoding="ascii") as f: eval_list(parse(lex(chars_in_file(f))), env)
def compiled(inp, env=None): if env is None: env = Env() return compile_list(parse(lex(inp)), env)
def evald(inp, env=None): if env is None: env = Env() return eval_list(parse(lex(inp)), env)