diff --git a/src/common.cpp b/src/common.cpp index db504bf..d67824c 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -256,6 +256,9 @@ Result Pair::first() const { } Result Pair::second() const { return TRY(TRY(rest()).first()); } +Result Pair::third() const { + return TRY(TRY(TRY(rest()).rest()).first()); +} Result Pair::rest() const { auto val = _value->rest.get(); @@ -660,5 +663,6 @@ Result Object::get(const Value& key) const { Result Object::first() const { return ERROR(TypeMismatch); } Result Object::second() const { return ERROR(TypeMismatch); } +Result Object::third() const { return ERROR(TypeMismatch); } Result Object::rest() const { return ERROR(TypeMismatch); } diff --git a/src/common.hpp b/src/common.hpp index 504081e..b2d7f96 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -116,6 +116,7 @@ class Object { virtual Result get(const Value& key) const; virtual Result first() const; virtual Result second() const; + virtual Result third() const; virtual Result rest() const; Object() = default; @@ -685,6 +686,7 @@ class Pair : public Object { Result first() const final; Result second() const final; + Result third() const final; Result rest() const final; virtual Result copy_value() const final; @@ -1149,6 +1151,7 @@ class Value { } Result first() const { return ((Object*)buf)->first(); } Result second() const { return ((Object*)buf)->second(); } + Result third() const { return ((Object*)buf)->third(); } Result rest() const { return ((Object*)buf)->rest(); } // TODO: cmp() probably doesn't need arena parameter diff --git a/src/compiler.cpp b/src/compiler.cpp index a85dbe4..57b0ac1 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -557,6 +557,29 @@ Result Compiler::compile_not(Context& context, Symbol& op, return std::move(ex); } +Result Compiler::compile_def(Context& context, Symbol& op, + const Value& expr) { + auto varname = TRY(expr.second()); + if (!varname.is()) return ERROR(CompilationError); + + auto var_expr = TRY(expr.third()); + + uint64_t maxreg = context.maxreg; + Expression ex = TRY(Expression::create()); + + auto comp = TRY(compile_expr(context, var_expr)); + ex.add_code(comp.code); + + int64_t gname = TRY(context.add_const(varname)); + TRY(ex.add_opcode(Oc::GlobalStore, {1, (int64_t)gname}, + {0, (int64_t)comp.reg})); + + ex.reg = comp.reg; + context.maxreg = maxreg + 1; + + return std::move(ex); +} + Result Compiler::compile_fn(Context& context, Symbol& op, const Value& expr) { Context ctx = TRY(Context::create(context)); @@ -841,6 +864,8 @@ Result Compiler::compile_list(Context& context, const Value& expr) { return compile_or(context, sym, expr); } else if (TRY(sym.cmp("not")) == 0) { return compile_not(context, sym, expr); + } else if (TRY(sym.cmp("def")) == 0) { + return compile_def(context, sym, expr); } else if (TRY(sym.cmp("fn")) == 0) { return compile_fn(context, sym, expr); } else if (TRY(sym.cmp("let")) == 0) { diff --git a/src/compiler.hpp b/src/compiler.hpp index bf2dc42..b4b611b 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -45,6 +45,8 @@ class Compiler { const Value& expr); Result compile_not(Context& context, Symbol& op, const Value& expr); + Result compile_def(Context& context, Symbol& op, + const Value& expr); Result compile_fn(Context& context, Symbol& op, const Value& expr); Result compile_let(Context& context, Symbol& op, diff --git a/test/function.vli b/test/function.vli index fcd3e0e..527cc01 100644 --- a/test/function.vli +++ b/test/function.vli @@ -28,8 +28,18 @@ ) +;; Top-level functions should be able to capture the "let" scope +;; to a closure (let ((x 42)) (fn foo() x) ) (assert (= (foo) 42)) + + +;; Top-level functions should be able to access top-level variables + +(def four 4) + +(fn add-four (x) (+ x four)) +(assert (= (add-four 5) 9))