xo-reader: prefer xo::bp spelling to xo::ref::brw

This commit is contained in:
Roland Conybeare 2025-07-05 13:54:34 -05:00
commit 53b6adeef1
26 changed files with 152 additions and 47 deletions

View file

@ -79,9 +79,9 @@ namespace xo {
defexprstatetype defxs_type() const { return defxs_type_; } defexprstatetype defxs_type() const { return defxs_type_; }
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_expr_with_semicolon(ref::brw<Expression> expr, virtual void on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_symbol(const std::string & symbol_name, virtual void on_symbol(const std::string & symbol_name,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;

View file

@ -25,11 +25,14 @@ namespace xo {
const std::vector<rp<Variable>> & argl() const { return argl_; } const std::vector<rp<Variable>> & argl() const { return argl_; }
/** lookup variable by name. If found, return it. /** lookup variable by @p name. If found, return it.
* Otherwise return nullptr * Otherwise return nullptr
**/ **/
rp<Variable> lookup(const std::string & name) const; rp<Variable> lookup(const std::string & name) const;
/** establish (replacing if already exists) binding for variable @p var **/
void upsert(bp<Variable> var);
void print (std::ostream & os) const; void print (std::ostream & os) const;
private: private:
@ -44,5 +47,4 @@ namespace xo {
} /*namespace scm*/ } /*namespace scm*/
} /*namespace xo*/ } /*namespace xo*/
/* end envframe.hpp */ /* end envframe.hpp */

View file

@ -28,6 +28,11 @@ namespace xo {
**/ **/
rp<Variable> lookup(const std::string & x) const; rp<Variable> lookup(const std::string & x) const;
/** update/replace binding for variable @p target.
* New binding may have a different type.
**/
void upsert(bp<Variable> target);
envframe & top_envframe(); envframe & top_envframe();
void push_envframe(envframe x); void push_envframe(envframe x);
void pop_envframe(); void pop_envframe();

View file

@ -48,12 +48,12 @@ namespace xo {
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
/** update exprstate in response to a successfully-parsed subexpression **/ /** update exprstate in response to a successfully-parsed subexpression **/
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
/** update exprstate in response to a successfully-parsed subexpression, /** update exprstate in response to a successfully-parsed subexpression,
* that's terminated by semicolon ';' * that's terminated by semicolon ';'
**/ **/
virtual void on_expr_with_semicolon(ref::brw<Expression> expr, virtual void on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;

View file

@ -56,9 +56,9 @@ namespace xo {
virtual void on_typedescr(TypeDescr td, virtual void on_typedescr(TypeDescr td,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_expr_with_semicolon(ref::brw<Expression> expr, virtual void on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
private: private:

View file

@ -106,11 +106,11 @@ namespace xo {
parserstatemachine * p_psm); parserstatemachine * p_psm);
/** update exprstate in response to a successfully-parsed subexpression **/ /** update exprstate in response to a successfully-parsed subexpression **/
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm); parserstatemachine * p_psm);
/** update exprstate in response to a successfully-parsed subexpression, that ends with semicolon **/ /** update exprstate in response to a successfully-parsed subexpression, that ends with semicolon **/
virtual void on_expr_with_semicolon(ref::brw<Expression> expr, virtual void on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm); parserstatemachine * p_psm);
/** update exprstate when expecting a symbol **/ /** update exprstate when expecting a symbol **/
@ -189,6 +189,11 @@ namespace xo {
void illegal_input_error(const char * self_name, void illegal_input_error(const char * self_name,
const token_type & tk) const; const token_type & tk) const;
/** throw exception when unable to locate definition for a variable
**/
void unknown_variable_error(const char * self_name,
const token_type & tk) const;
protected: protected:
/** explicit subtype: identifies derived class **/ /** explicit subtype: identifies derived class **/
exprstatetype exs_type_; exprstatetype exs_type_;

View file

@ -55,9 +55,9 @@ namespace xo {
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_formal_arglist(const std::vector<rp<Variable>> & argl, virtual void on_formal_arglist(const std::vector<rp<Variable>> & argl,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_expr_with_semicolon(ref::brw<Expression> expr, virtual void on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_semicolon_token(const token_type & tk, virtual void on_semicolon_token(const token_type & tk,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;

View file

@ -25,7 +25,7 @@ namespace xo {
const rp<Expression> & rhs, const rp<Expression> & rhs,
parserstatemachine * p_psm); parserstatemachine * p_psm);
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_rightbrace_token(const token_type & tk, virtual void on_rightbrace_token(const token_type & tk,

View file

@ -43,7 +43,7 @@ namespace xo {
bool admits_f64() const; bool admits_f64() const;
bool admits_rightparen() const; bool admits_rightparen() const;
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_symbol(const std::string & symbol, virtual void on_symbol(const std::string & symbol,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;

View file

@ -154,7 +154,7 @@ namespace xo {
/** create parser in initial state; /** create parser in initial state;
* parser is ready to receive tokens via @ref include_token * parser is ready to receive tokens via @ref include_token
**/ **/
parser() = default; parser();
/** for diagnostics: number of entries in parser stack **/ /** for diagnostics: number of entries in parser stack **/
std::size_t stack_size() const { return xs_stack_.size(); } std::size_t stack_size() const { return xs_stack_.size(); }

View file

@ -40,13 +40,18 @@ namespace xo {
**/ **/
rp<Variable> lookup_var(const std::string & x) const; rp<Variable> lookup_var(const std::string & x) const;
/** update/replace binding for variable @p x in lexical context
* represented by this psm. Always acts on innermost frame.
**/
void upsert_var(bp<Variable> x);
void push_envframe(envframe x); void push_envframe(envframe x);
void pop_envframe(); void pop_envframe();
// ----- parsing outputs ----- // ----- parsing outputs -----
void on_expr(ref::brw<Expression> expr); void on_expr(bp<Expression> expr);
void on_expr_with_semicolon(ref::brw<Expression> expr); void on_expr_with_semicolon(bp<Expression> expr);
void on_symbol(const std::string & symbol); void on_symbol(const std::string & symbol);
// ---- parsing inputs ----- // ---- parsing inputs -----

View file

@ -47,6 +47,10 @@ namespace xo {
/** @class progress_xs /** @class progress_xs
* @brief state machine for parsing a schematica runtime-value-expression * @brief state machine for parsing a schematica runtime-value-expression
*
* Handles an expression that produces a value, for example appearing on the
* right-hand side of a definition.
*
**/ **/
class progress_xs : public exprstate { class progress_xs : public exprstate {
public: public:
@ -65,7 +69,7 @@ namespace xo {
bool admits_f64() const; bool admits_f64() const;
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_symbol_token(const token_type & tk, virtual void on_symbol_token(const token_type & tk,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;

View file

@ -24,7 +24,7 @@ namespace xo {
**/ **/
static void start(parserstatemachine * p_psm); static void start(parserstatemachine * p_psm);
virtual void on_expr(ref::brw<Expression> expr, virtual void on_expr(bp<Expression> expr,
parserstatemachine * p_psm) override; parserstatemachine * p_psm) override;
virtual void on_rightbrace_token(const token_type & tk, virtual void on_rightbrace_token(const token_type & tk,

View file

@ -58,7 +58,7 @@ namespace xo {
{} {}
void void
define_xs::on_expr(ref::brw<Expression> expr, define_xs::on_expr(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
@ -90,7 +90,7 @@ namespace xo {
} }
void void
define_xs::on_expr_with_semicolon(ref::brw<Expression> expr, define_xs::on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
this->on_expr(expr, p_psm); this->on_expr(expr, p_psm);
@ -186,11 +186,16 @@ namespace xo {
log && log("defxs_type", defxs_type_); log && log("defxs_type", defxs_type_);
if (this->defxs_type_ == defexprstatetype::def_6) { if (this->defxs_type_ == defexprstatetype::def_6) {
rp<Expression> expr = this->def_expr_; rp<DefineExprAccess> def_expr = this->def_expr_;
std::unique_ptr<exprstate> self = p_psm->pop_exprstate(); std::unique_ptr<exprstate> self = p_psm->pop_exprstate();
p_psm->top_exprstate().on_expr(expr, p_psm); /* remember variable binding in lexical context,
* so we can refer to it later
*/
p_psm->upsert_var(def_expr->lhs_variable());
p_psm->top_exprstate().on_expr(def_expr, p_psm);
} else { } else {
exprstate::on_semicolon_token(tk, p_psm); exprstate::on_semicolon_token(tk, p_psm);
} }

View file

@ -20,6 +20,20 @@ namespace xo {
return nullptr; return nullptr;
} }
void
envframe::upsert(bp<Variable> target) {
for (auto & var : this->argl_) {
if (var->name() == target->name()) {
/* replace existing variable -- may change type */
var = target.promote();
return;
}
}
/* here: target not already present in this frame, append it */
this->argl_.push_back(target.promote());
}
void void
envframe::print(std::ostream & os) const { envframe::print(std::ostream & os) const {
os << "<envframe" os << "<envframe"

View file

@ -66,6 +66,14 @@ namespace xo {
return nullptr; return nullptr;
} }
void
envframestack::upsert(bp<Variable> x) {
/* upsert should always happen in the innermost lexical context.
* We are providing new variable binding (perhaps shadowing an existing binding)
*/
this->top_envframe().upsert(x);
}
void void
envframestack::print(std::ostream & os) const { envframestack::print(std::ostream & os) const {
os << "<envframestack" os << "<envframestack"

View file

@ -203,7 +203,7 @@ namespace xo {
} }
void void
expect_expr_xs::on_expr(ref::brw<Expression> expr, expect_expr_xs::on_expr(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
@ -218,7 +218,7 @@ namespace xo {
} /*on_expr*/ } /*on_expr*/
void void
expect_expr_xs::on_expr_with_semicolon(ref::brw<Expression> expr, expect_expr_xs::on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;

View file

@ -4,9 +4,11 @@
#include "parserstatemachine.hpp" #include "parserstatemachine.hpp"
#include "exprstatestack.hpp" #include "exprstatestack.hpp"
#include "exprseq_xs.hpp" #include "exprseq_xs.hpp"
#include "expect_expr_xs.hpp" //#include "expect_expr_xs.hpp"
#include "progress_xs.hpp"
#include "define_xs.hpp" #include "define_xs.hpp"
#include "expect_symbol_xs.hpp" #include "expect_symbol_xs.hpp"
#include "xo/expression/Constant.hpp"
namespace xo { namespace xo {
namespace scm { namespace scm {
@ -47,17 +49,41 @@ namespace xo {
void void
exprseq_xs::on_symbol_token(const token_type & tk, exprseq_xs::on_symbol_token(const token_type & tk,
parserstatemachine * /*p_psm*/) parserstatemachine * p_psm)
{ {
constexpr const char * c_self_name = "exprseq_xs::on_symbol_token"; constexpr const char * c_self_name = "exprseq_xs::on_symbol_token";
this->illegal_input_error(c_self_name, tk); if (xseqtype_ == exprseqtype::toplevel_interactive)
{
/* In interactive session, allow top-level variable reference.
* This could be:
* a; // variable references
* a = 1; // single assignment
* a == 1; // rhs expression
* a + b; // rhs expression
* Variable must have been defined!
*/
rp<Variable> var = p_psm->lookup_var(tk.text());
if (var.get()) {
progress_xs::start(var, p_psm);
} else {
this->unknown_variable_error(c_self_name, tk);
}
} else {
/* policy: don't allow variable references as toplevel expressions
* unless interactive session
*/
this->illegal_input_error(c_self_name, tk);
}
} }
void void
exprseq_xs::on_i64_token(const token_type & tk, exprseq_xs::on_i64_token(const token_type & tk,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
using xo::ast::Constant;
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
scope log(XO_DEBUG(c_debug_flag)); scope log(XO_DEBUG(c_debug_flag));
@ -65,9 +91,11 @@ namespace xo {
if (xseqtype_ == exprseqtype::toplevel_interactive) if (xseqtype_ == exprseqtype::toplevel_interactive)
{ {
expect_expr_xs::start(p_psm); progress_xs::start(Constant<int64_t>::make(tk.i64_value()), p_psm);
p_psm->top_exprstate().on_i64_token(tk, p_psm);
} else { } else {
/* policy: don't allow literals as toplevel expressions
* unless interactive session.
*/
this->illegal_input_error(c_self_name, tk); this->illegal_input_error(c_self_name, tk);
} }
} }
@ -82,7 +110,7 @@ namespace xo {
} }
void void
exprseq_xs::on_expr(ref::brw<Expression> expr, exprseq_xs::on_expr(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
/* toplevel expression sequence accepts an /* toplevel expression sequence accepts an
@ -95,7 +123,7 @@ namespace xo {
} /*on_expr*/ } /*on_expr*/
void void
exprseq_xs::on_expr_with_semicolon(ref::brw<Expression> expr, exprseq_xs::on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
/* toplevel expression sequence accepts an /* toplevel expression sequence accepts an

View file

@ -58,8 +58,13 @@ namespace xo {
void void
exprstate::on_def_token(const token_type & tk, exprstate::on_def_token(const token_type & tk,
parserstatemachine * /*p_psm*/) parserstatemachine * p_psm)
{ {
constexpr bool c_debug_flag = true;
scope log(XO_DEBUG(c_debug_flag));
log && log(xtag("exstype", p_psm->top_exprstate().exs_type()));
this->illegal_input_error("exprstate::on_def_token", tk); this->illegal_input_error("exprstate::on_def_token", tk);
} }
@ -387,7 +392,7 @@ namespace xo {
} }
void void
exprstate::on_expr(ref::brw<Expression> expr, exprstate::on_expr(bp<Expression> expr,
parserstatemachine * /*p_psm*/) parserstatemachine * /*p_psm*/)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
@ -400,7 +405,7 @@ namespace xo {
} /*on_expr*/ } /*on_expr*/
void void
exprstate::on_expr_with_semicolon(ref::brw<Expression> expr, exprstate::on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * /*p_psm*/) parserstatemachine * /*p_psm*/)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
@ -454,6 +459,16 @@ namespace xo {
xtag("token", tk), xtag("token", tk),
xtag("state", *this))); xtag("state", *this)));
} }
void
exprstate::unknown_variable_error(const char * self_name,
const token_type & tk) const
{
throw std::runtime_error
(tostr(self_name,
": unknown variable name",
xtag("var", tk.text())));
}
} /*namespace scm*/ } /*namespace scm*/
} /*namespace xo*/ } /*namespace xo*/

View file

@ -71,7 +71,7 @@ namespace xo {
} }
void void
lambda_xs::on_expr(ref::brw<Expression> expr, lambda_xs::on_expr(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
if (lmxs_type_ == lambdastatetype::lm_2) { if (lmxs_type_ == lambdastatetype::lm_2) {
@ -83,7 +83,7 @@ namespace xo {
} }
void void
lambda_xs::on_expr_with_semicolon(ref::brw<Expression> expr, lambda_xs::on_expr_with_semicolon(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
this->on_expr(expr, p_psm); this->on_expr(expr, p_psm);

View file

@ -54,13 +54,13 @@ namespace xo {
{} {}
void void
let1_xs::on_expr(ref::brw<Expression> expr, let1_xs::on_expr(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
scope log(XO_DEBUG(c_debug_flag)); scope log(XO_DEBUG(c_debug_flag));
ref::brw<DefineExpr> def_expr = DefineExpr::from(expr); bp<DefineExpr> def_expr = DefineExpr::from(expr);
if (def_expr) { if (def_expr) {
/** nested_start: control returns via /** nested_start: control returns via

View file

@ -195,7 +195,7 @@ namespace xo {
} }
void void
paren_xs::on_expr(ref::brw<Expression> expr, paren_xs::on_expr(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;

View file

@ -24,6 +24,15 @@ namespace xo {
namespace scm { namespace scm {
// ----- parser ----- // ----- parser -----
parser::parser()
: xs_stack_{}, env_stack_{}
{
/* top-level environment. initially empty */
envframe toplevel_env;
this->env_stack_.push_envframe(toplevel_env);
}
bool bool
parser::has_incomplete_expr() const { parser::has_incomplete_expr() const {
return !xs_stack_.empty(); return !xs_stack_.empty();

View file

@ -15,6 +15,11 @@ namespace xo {
return p_env_stack_->lookup(x); return p_env_stack_->lookup(x);
} }
void
parserstatemachine::upsert_var(bp<Variable> x) {
p_env_stack_->upsert(x);
}
std::unique_ptr<exprstate> std::unique_ptr<exprstate>
parserstatemachine::pop_exprstate() { parserstatemachine::pop_exprstate() {
return p_stack_->pop_exprstate(); return p_stack_->pop_exprstate();
@ -49,7 +54,7 @@ namespace xo {
} }
void void
parserstatemachine::on_expr(ref::brw<Expression> x) parserstatemachine::on_expr(bp<Expression> x)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
scope log(XO_DEBUG(c_debug_flag)); scope log(XO_DEBUG(c_debug_flag));
@ -62,7 +67,7 @@ namespace xo {
} }
void void
parserstatemachine::on_expr_with_semicolon(ref::brw<Expression> x) parserstatemachine::on_expr_with_semicolon(bp<Expression> x)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
scope log(XO_DEBUG(c_debug_flag)); scope log(XO_DEBUG(c_debug_flag));

View file

@ -118,7 +118,7 @@ namespace xo {
case optype::op_assign: case optype::op_assign:
{ {
ref::brw<Variable> lhs = Variable::from(this->lhs_); bp<Variable> lhs = Variable::from(this->lhs_);
if (!lhs) { if (!lhs) {
throw std::runtime_error throw std::runtime_error
@ -158,7 +158,7 @@ namespace xo {
} }
void void
progress_xs::on_expr(ref::brw<Expression> expr, progress_xs::on_expr(bp<Expression> expr,
parserstatemachine * /*p_psm*/) parserstatemachine * /*p_psm*/)
{ {
/* note: previous token probably an operator, /* note: previous token probably an operator,
@ -218,7 +218,7 @@ namespace xo {
progress_xs::on_semicolon_token(const token_type & /*tk*/, progress_xs::on_semicolon_token(const token_type & /*tk*/,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
/* note: implementation parllels .on_rightparen_token() */ /* note: implementation parallels .on_rightparen_token() */
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
scope log(XO_DEBUG(c_debug_flag)); scope log(XO_DEBUG(c_debug_flag));

View file

@ -32,7 +32,7 @@ namespace xo {
{} {}
void void
sequence_xs::on_expr(ref::brw<Expression> expr, sequence_xs::on_expr(bp<Expression> expr,
parserstatemachine * p_psm) parserstatemachine * p_psm)
{ {
constexpr bool c_debug_flag = true; constexpr bool c_debug_flag = true;
@ -67,7 +67,7 @@ namespace xo {
* helpful to have nested seequence_xs that propagates '}' * helpful to have nested seequence_xs that propagates '}'
* instead of swallowing it. * instead of swallowing it.
*/ */
ref::brw<DefineExpr> def_expr = DefineExpr::from(expr); bp<DefineExpr> def_expr = DefineExpr::from(expr);
if (def_expr) { if (def_expr) {
/** nested_start: control returns via /** nested_start: control returns via