Implement defining top-level variables including in REPL

This commit is contained in:
Konstantin Nazarov 2024-09-01 23:45:20 +01:00
parent 6a5aad3ae9
commit 908b896d43
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
5 changed files with 44 additions and 0 deletions

View file

@ -256,6 +256,9 @@ Result<Value> Pair::first() const {
} }
Result<Value> Pair::second() const { return TRY(TRY(rest()).first()); } Result<Value> Pair::second() const { return TRY(TRY(rest()).first()); }
Result<Value> Pair::third() const {
return TRY(TRY(TRY(rest()).rest()).first());
}
Result<Value> Pair::rest() const { Result<Value> Pair::rest() const {
auto val = _value->rest.get(); auto val = _value->rest.get();
@ -660,5 +663,6 @@ Result<Value> Object::get(const Value& key) const {
Result<Value> Object::first() const { return ERROR(TypeMismatch); } Result<Value> Object::first() const { return ERROR(TypeMismatch); }
Result<Value> Object::second() const { return ERROR(TypeMismatch); } Result<Value> Object::second() const { return ERROR(TypeMismatch); }
Result<Value> Object::third() const { return ERROR(TypeMismatch); }
Result<Value> Object::rest() const { return ERROR(TypeMismatch); } Result<Value> Object::rest() const { return ERROR(TypeMismatch); }

View file

@ -116,6 +116,7 @@ class Object {
virtual Result<Value> get(const Value& key) const; virtual Result<Value> get(const Value& key) const;
virtual Result<Value> first() const; virtual Result<Value> first() const;
virtual Result<Value> second() const; virtual Result<Value> second() const;
virtual Result<Value> third() const;
virtual Result<Value> rest() const; virtual Result<Value> rest() const;
Object() = default; Object() = default;
@ -685,6 +686,7 @@ class Pair : public Object {
Result<Value> first() const final; Result<Value> first() const final;
Result<Value> second() const final; Result<Value> second() const final;
Result<Value> third() const final;
Result<Value> rest() const final; Result<Value> rest() const final;
virtual Result<Value> copy_value() const final; virtual Result<Value> copy_value() const final;
@ -1149,6 +1151,7 @@ class Value {
} }
Result<Value> first() const { return ((Object*)buf)->first(); } Result<Value> first() const { return ((Object*)buf)->first(); }
Result<Value> second() const { return ((Object*)buf)->second(); } Result<Value> second() const { return ((Object*)buf)->second(); }
Result<Value> third() const { return ((Object*)buf)->third(); }
Result<Value> rest() const { return ((Object*)buf)->rest(); } Result<Value> rest() const { return ((Object*)buf)->rest(); }
// TODO: cmp() probably doesn't need arena parameter // TODO: cmp() probably doesn't need arena parameter

View file

@ -557,6 +557,29 @@ Result<Expression> Compiler::compile_not(Context& context, Symbol& op,
return std::move(ex); return std::move(ex);
} }
Result<Expression> Compiler::compile_def(Context& context, Symbol& op,
const Value& expr) {
auto varname = TRY(expr.second());
if (!varname.is<Symbol>()) 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<Expression> Compiler::compile_fn(Context& context, Symbol& op, Result<Expression> Compiler::compile_fn(Context& context, Symbol& op,
const Value& expr) { const Value& expr) {
Context ctx = TRY(Context::create(context)); Context ctx = TRY(Context::create(context));
@ -841,6 +864,8 @@ Result<Expression> Compiler::compile_list(Context& context, const Value& expr) {
return compile_or(context, sym, expr); return compile_or(context, sym, expr);
} else if (TRY(sym.cmp("not")) == 0) { } else if (TRY(sym.cmp("not")) == 0) {
return compile_not(context, sym, expr); 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) { } else if (TRY(sym.cmp("fn")) == 0) {
return compile_fn(context, sym, expr); return compile_fn(context, sym, expr);
} else if (TRY(sym.cmp("let")) == 0) { } else if (TRY(sym.cmp("let")) == 0) {

View file

@ -45,6 +45,8 @@ class Compiler {
const Value& expr); const Value& expr);
Result<Expression> compile_not(Context& context, Symbol& op, Result<Expression> compile_not(Context& context, Symbol& op,
const Value& expr); const Value& expr);
Result<Expression> compile_def(Context& context, Symbol& op,
const Value& expr);
Result<Expression> compile_fn(Context& context, Symbol& op, Result<Expression> compile_fn(Context& context, Symbol& op,
const Value& expr); const Value& expr);
Result<Expression> compile_let(Context& context, Symbol& op, Result<Expression> compile_let(Context& context, Symbol& op,

View file

@ -28,8 +28,18 @@
) )
;; Top-level functions should be able to capture the "let" scope
;; to a closure
(let ((x 42)) (let ((x 42))
(fn foo() x) (fn foo() x)
) )
(assert (= (foo) 42)) (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))