diff --git a/xo-alloc/include/xo/alloc/Object.hpp b/xo-alloc/include/xo/alloc/Object.hpp index 73a60edd..c8772db9 100644 --- a/xo-alloc/include/xo/alloc/Object.hpp +++ b/xo-alloc/include/xo/alloc/Object.hpp @@ -39,6 +39,10 @@ namespace xo { template gc_ptr(const gc_ptr & x) : ptr_{x.ptr()} {} + /** runtime downcast. shorthand for dynamic_cast **/ + template + static gc_ptr from(const gc_ptr & x) { return gc_ptr{dynamic_cast(x.ptr())}; } + /** convenience for static asserts **/ static constexpr bool is_gc_ptr = true; /** see also: xo/refcnt/Refcounted.hpp **/ diff --git a/xo-expression/src/expression/GlobalSymtab.cpp b/xo-expression/src/expression/GlobalSymtab.cpp index 93a27624..b932152e 100644 --- a/xo-expression/src/expression/GlobalSymtab.cpp +++ b/xo-expression/src/expression/GlobalSymtab.cpp @@ -13,7 +13,7 @@ namespace xo { bp GlobalSymtab::require_global(const std::string & vname, - bp expr) + bp expr) { this->global_map_[vname] = expr.get(); diff --git a/xo-interpreter/include/xo/interpreter/Env.hpp b/xo-interpreter/include/xo/interpreter/Env.hpp index d4eaa513..bce51319 100644 --- a/xo-interpreter/include/xo/interpreter/Env.hpp +++ b/xo-interpreter/include/xo/interpreter/Env.hpp @@ -24,6 +24,11 @@ namespace xo { /** true iff @p vname is present in Symtab for innermost environment **/ virtual bool local_contains_var(const std::string & vname) const = 0; + /** Fetch storage location for innermost binding of variable with name @p vname. + * nullptr if not found + **/ + virtual gp * lookup_slot(const std::string & vname) = 0; + /** require storage for variable @p v. * will also establish binding path. * @@ -31,8 +36,10 @@ namespace xo { * replacing any previous variable with the same name. * * Beware of invalidating type correctness + * + * @return slot address for runtime value of @p v **/ - virtual void establish_var(bp v) = 0; + virtual gp * establish_var(bp v) = 0; //gp lookup_symbol(const std::string & name) const; }; diff --git a/xo-interpreter/include/xo/interpreter/ExpressionBoxed.hpp b/xo-interpreter/include/xo/interpreter/ExpressionBoxed.hpp index ebc8b1ec..5668a08a 100644 --- a/xo-interpreter/include/xo/interpreter/ExpressionBoxed.hpp +++ b/xo-interpreter/include/xo/interpreter/ExpressionBoxed.hpp @@ -21,6 +21,11 @@ namespace xo { static gp make(gc::IAlloc * mm, bp c); + /** runtime downcast **/ + static gp from(gp x) { + return gp::from(x); + } + const rp & contents() const { return contents_; } // inherited from Object diff --git a/xo-interpreter/include/xo/interpreter/GlobalEnv.hpp b/xo-interpreter/include/xo/interpreter/GlobalEnv.hpp index 39c7e66e..28d15e81 100644 --- a/xo-interpreter/include/xo/interpreter/GlobalEnv.hpp +++ b/xo-interpreter/include/xo/interpreter/GlobalEnv.hpp @@ -28,7 +28,8 @@ namespace xo { // inherited from Env.. virtual bool local_contains_var(const std::string & vname) const final override; - virtual void establish_var(bp var) final override; + virtual gp * lookup_slot(const std::string & vname) final override; + virtual gp * establish_var(bp var) final override; // inherited from Object.. virtual TaggedPtr self_tp() const final override; diff --git a/xo-interpreter/include/xo/interpreter/LocalEnv.hpp b/xo-interpreter/include/xo/interpreter/LocalEnv.hpp index 41ea2310..c2a90ee0 100644 --- a/xo-interpreter/include/xo/interpreter/LocalEnv.hpp +++ b/xo-interpreter/include/xo/interpreter/LocalEnv.hpp @@ -64,10 +64,12 @@ namespace xo { virtual bool local_contains_var(const std::string & vname) const final override; + virtual gp * lookup_slot(const std::string & vname) final override; + /** LocalEnv policy is that variable can be established once only. - * For example function arguments must all have distinct names + * For example function arguments must all have distinct names. **/ - virtual void establish_var(bp v) final override; + virtual gp * establish_var(bp v) final override; // inherited from Object.. virtual TaggedPtr self_tp() const final override; diff --git a/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp b/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp index 9518fd47..31a7082b 100644 --- a/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp +++ b/xo-interpreter/include/xo/interpreter/VirtualSchematikaMachine.hpp @@ -83,10 +83,12 @@ namespace xo { /** interpret define expression **/ void eval_define_op(); - /** continue after establishing value fo rhs of define exprsssion **/ void do_defexpr_assign_op(); + /** interpret variable expression **/ + void eval_variable_op(); + /** goto error state with message @p err **/ void report_error(const std::string & err); diff --git a/xo-interpreter/src/interpreter/GlobalEnv.cpp b/xo-interpreter/src/interpreter/GlobalEnv.cpp index 9bb9c32a..736af083 100644 --- a/xo-interpreter/src/interpreter/GlobalEnv.cpp +++ b/xo-interpreter/src/interpreter/GlobalEnv.cpp @@ -35,9 +35,28 @@ namespace xo { return symtab_->lookup_local(vname).get(); } - void + gp * + GlobalEnv::lookup_slot(const std::string & vname) + { + scope log(XO_DEBUG(true), xtag("name", vname)); + + assert(slot_map_.get()); + + auto ix = slot_map_->find(vname); + + if (ix == slot_map_->end()) { + return nullptr; + } else { + log && log("binding found", xtag("vname", vname)); + return &(ix->second); + } + } + + gp * GlobalEnv::establish_var(bp var) { + scope log(XO_DEBUG(true), xtag("name", var->name()), xtag("type", var->valuetype())); + // Warning: altering declared type for an already-existing variable // invalidates any type checking that relied on that variable. // @@ -60,7 +79,12 @@ namespace xo { this->symtab_->require_global(var->name(), var); - (*this->slot_map_)[var->name()] = gp(); + gp &slot = (*this->slot_map_)[var->name()]; + + /* discard any pre-existing value, we're redefining a variable */ + slot = gp(); + + return &slot; } TaggedPtr diff --git a/xo-interpreter/src/interpreter/LocalEnv.cpp b/xo-interpreter/src/interpreter/LocalEnv.cpp index ab43ffb4..413f6a69 100644 --- a/xo-interpreter/src/interpreter/LocalEnv.cpp +++ b/xo-interpreter/src/interpreter/LocalEnv.cpp @@ -52,7 +52,25 @@ namespace xo { return symtab_->lookup_local(vname); } - void + gp * + LocalEnv::lookup_slot(const std::string & vname) + { + binding_path b = symtab_->lookup_local_binding(vname); + + if (b.i_link_ == 0) { + assert((b.j_slot_ >= 0) && (static_cast(b.j_slot_) < slot_v_.size())); + + return &(slot_v_[b.j_slot_]); + } + + if (parent_.get()) { + return parent_->lookup_slot(vname); + } + + return nullptr; + } + + gp * LocalEnv::establish_var(bp v) { assert(v); diff --git a/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp b/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp index 59d202e1..b005bf12 100644 --- a/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp +++ b/xo-interpreter/src/interpreter/VirtualSchematikaMachine.cpp @@ -149,27 +149,32 @@ namespace xo { case Opcode::eval: { - log && log("eval"); + log && log("Opcode::eval"); /* generally speaking: opcode will be 1:1 with extypes */ switch (expr_->extype()) { case exprtype::constant: + log && log("eval -> constant"); this->eval_constant_op(); break; - case exprtype::define: + log && log("eval -> define"); this->eval_define_op(); break; + case exprtype::variable: + log && log("eval -> variable"); + this->eval_variable_op(); + break; + case exprtype::invalid: case exprtype::primitive: case exprtype::assign: case exprtype::apply: case exprtype::lambda: - case exprtype::variable: case exprtype::ifexpr: case exprtype::sequence: case exprtype::convert: @@ -266,29 +271,88 @@ namespace xo { /* when control arrives at .cont_, will have: * .value_ -> result of evaluating expr->rhs() */ - this->stack_ = VsmStackFrame::push1(mm, this->stack_, lhs_0, cont_); + + /* .stack_: + * frame + * [0] = lhs_0 (boxed lhs Variable) + */ + this->cont_ = &VsmOps::defexpr_assign_op; } void VirtualSchematikaMachine::do_defexpr_assign_op() { + scope log(XO_DEBUG(true)); + /* * - value: contains result of evaluating rhs of define * - stack: top frame has 1 slot, holds variable to receive assignment */ assert(value_.get()); assert(stack_.get()); + assert(env_.get()); gp sp0 = this->stack_; + bp var = Variable::from(ExpressionBoxed::from((*sp0)[0])->contents()); + + assert(var.get()); + + gp * slot = this->env_->establish_var(var); + assert(slot); + + *slot = this->value_; + //this->value_ = this->value_; // preserve value from rhs of defexpr this->stack_ = sp0->parent(); this->pc_ = this->cont_ = sp0->continuation(); } + void + VirtualSchematikaMachine::eval_variable_op() + { + using xo::scm::Variable; + + scope log(XO_DEBUG(true)); + + bp var = Variable::from(expr_); + + assert(var.get()); + assert(env_.get()); + + const gp * slot = env_->lookup_slot(var->name()); + + if (slot) { + this->value_ = *slot; + this->pc_ = cont_; + } else { + /* Unknown variable error will often be recognized in expression parser, + * in such cases this path won't be used. + * + * In interactive environment will need some kind of support for modifying + * code (e.g. replacing top-level functions/variables), and in particular, + * replacements may have different type signature. + * It's possible that allowing for such replacements winds up giving up + * typesafety guarantees. In that case this path may get activated after + * all. + */ + + std::string err = tostr("no binding for variable", xtag("name", var->name())); + + this->value_ = nullptr; + this->error_ = SchematikaError(err); + + /* note: poor man's exception */ + this->pc_ = nullptr; + this->cont_ = nullptr; + + this->pc_ = cont_; + } + } + } /*namespace scm*/ } /*namespace xo*/