xo-interpreter: implement variable lookup
This commit is contained in:
parent
fb5c43dc85
commit
d91a2ae08e
10 changed files with 140 additions and 13 deletions
|
|
@ -39,6 +39,10 @@ namespace xo {
|
|||
template <typename S>
|
||||
gc_ptr(const gc_ptr<S> & x) : ptr_{x.ptr()} {}
|
||||
|
||||
/** runtime downcast. shorthand for dynamic_cast<T*> **/
|
||||
template <typename S>
|
||||
static gc_ptr<T> from(const gc_ptr<S> & x) { return gc_ptr<T>{dynamic_cast<T*>(x.ptr())}; }
|
||||
|
||||
/** convenience for static asserts **/
|
||||
static constexpr bool is_gc_ptr = true;
|
||||
/** see also: xo/refcnt/Refcounted.hpp **/
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace xo {
|
|||
|
||||
bp<Expression>
|
||||
GlobalSymtab::require_global(const std::string & vname,
|
||||
bp<Expression> expr)
|
||||
bp<Expression> expr)
|
||||
{
|
||||
this->global_map_[vname] = expr.get();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Object> * 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<Variable> v) = 0;
|
||||
virtual gp<Object> * establish_var(bp<Variable> v) = 0;
|
||||
|
||||
//gp<Object> lookup_symbol(const std::string & name) const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,6 +21,11 @@ namespace xo {
|
|||
static gp<ExpressionBoxed> make(gc::IAlloc * mm,
|
||||
bp<Expression> c);
|
||||
|
||||
/** runtime downcast **/
|
||||
static gp<ExpressionBoxed> from(gp<Object> x) {
|
||||
return gp<ExpressionBoxed>::from(x);
|
||||
}
|
||||
|
||||
const rp<Expression> & contents() const { return contents_; }
|
||||
|
||||
// inherited from Object
|
||||
|
|
|
|||
|
|
@ -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<Variable> var) final override;
|
||||
virtual gp<Object> * lookup_slot(const std::string & vname) final override;
|
||||
virtual gp<Object> * establish_var(bp<Variable> var) final override;
|
||||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
|
|
|
|||
|
|
@ -64,10 +64,12 @@ namespace xo {
|
|||
|
||||
virtual bool local_contains_var(const std::string & vname) const final override;
|
||||
|
||||
virtual gp<Object> * 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<Variable> v) final override;
|
||||
virtual gp<Object> * establish_var(bp<Variable> v) final override;
|
||||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,28 @@ namespace xo {
|
|||
return symtab_->lookup_local(vname).get();
|
||||
}
|
||||
|
||||
void
|
||||
gp<Object> *
|
||||
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<Object> *
|
||||
GlobalEnv::establish_var(bp<Variable> 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<Object>();
|
||||
gp<Object> &slot = (*this->slot_map_)[var->name()];
|
||||
|
||||
/* discard any pre-existing value, we're redefining a variable */
|
||||
slot = gp<Object>();
|
||||
|
||||
return &slot;
|
||||
}
|
||||
|
||||
TaggedPtr
|
||||
|
|
|
|||
|
|
@ -52,7 +52,25 @@ namespace xo {
|
|||
return symtab_->lookup_local(vname);
|
||||
}
|
||||
|
||||
void
|
||||
gp<Object> *
|
||||
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<size_t>(b.j_slot_) < slot_v_.size()));
|
||||
|
||||
return &(slot_v_[b.j_slot_]);
|
||||
}
|
||||
|
||||
if (parent_.get()) {
|
||||
return parent_->lookup_slot(vname);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gp<Object> *
|
||||
LocalEnv::establish_var(bp<Variable> v)
|
||||
{
|
||||
assert(v);
|
||||
|
|
|
|||
|
|
@ -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<VsmStackFrame> sp0 = this->stack_;
|
||||
|
||||
bp<Variable> var = Variable::from(ExpressionBoxed::from((*sp0)[0])->contents());
|
||||
|
||||
assert(var.get());
|
||||
|
||||
gp<Object> * 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<Variable> var = Variable::from(expr_);
|
||||
|
||||
assert(var.get());
|
||||
assert(env_.get());
|
||||
|
||||
const gp<Object> * 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*/
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue