From 29596a7c1dc3f91bd8719fbbb9d16c6e7e8ac11e Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 14 Aug 2024 23:44:17 -0400 Subject: [PATCH 1/4] xo-reader: feat: parse infix exprs for +,-,*,/ operators --- include/xo/reader/exprstate.hpp | 4 ++ include/xo/reader/progress_xs.hpp | 21 +++++++ src/reader/exprstate.cpp | 20 ++++++ src/reader/progress_xs.cpp | 101 +++++++++++++++++++++++++++--- utest/reader.test.cpp | 3 +- 5 files changed, 141 insertions(+), 8 deletions(-) diff --git a/include/xo/reader/exprstate.hpp b/include/xo/reader/exprstate.hpp index ef65e8f5..d109f280 100644 --- a/include/xo/reader/exprstate.hpp +++ b/include/xo/reader/exprstate.hpp @@ -121,6 +121,10 @@ namespace xo { virtual void on_rightparen_token(const token_type & tk, exprstatestack * p_stack, rp * p_emit_expr); + /** handle incoming operator token **/ + virtual void on_operator_token(const token_type & tk, + exprstatestack * p_stack, + rp * p_emit_expr); /** handle incoming floating-point-literal token **/ virtual void on_f64_token(const token_type & tk, exprstatestack * p_stack, diff --git a/include/xo/reader/progress_xs.hpp b/include/xo/reader/progress_xs.hpp index 361e6d3d..9a7381c7 100644 --- a/include/xo/reader/progress_xs.hpp +++ b/include/xo/reader/progress_xs.hpp @@ -10,6 +10,18 @@ namespace xo { namespace scm { + /* represent an infix operator */ + enum class optype { + invalid = -1, + + op_add, + op_subtract, + op_multiply, + op_divide, + + n_optype + }; + /** @class progress_xs * @brief state machine for parsing a schematica runtime-value-expression **/ @@ -49,6 +61,12 @@ namespace xo { virtual void on_rightparen_token(const token_type & tk, exprstatestack * p_stack, rp * /*p_emit_expr*/) override; + + /* entry point for an infix operator token */ + virtual void on_operator_token(const token_type & tk, + exprstatestack * p_stack, + rp * p_emit_expr) override; + virtual void on_f64_token(const token_type & tk, exprstatestack * p_stack, rp * /*p_emit_expr*/) override; @@ -58,6 +76,9 @@ namespace xo { private: /** populate an expression here **/ rp gen_expr_; + + /** infix operator, if supplied **/ + optype op_type_ = optype::invalid; }; } /*namespace scm*/ } /*namespace xo*/ diff --git a/src/reader/exprstate.cpp b/src/reader/exprstate.cpp index e7933d7c..1985aa7d 100644 --- a/src/reader/exprstate.cpp +++ b/src/reader/exprstate.cpp @@ -136,6 +136,19 @@ namespace xo { this->illegal_input_error(self_name, tk); } + void + exprstate::on_operator_token(const token_type & tk, + exprstatestack * /*p_stack*/, + rp * /*p_emit_expr*/) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + constexpr const char * self_name = "exprstate::on_operator_token"; + + this->illegal_input_error(self_name, tk); + } + void exprstate::on_f64_token(const token_type & tk, exprstatestack * /*p_stack*/, @@ -220,6 +233,13 @@ namespace xo { case tokentype::tk_assign: case tokentype::tk_yields: + case tokentype::tk_plus: + case tokentype::tk_minus: + case tokentype::tk_star: + case tokentype::tk_slash: + this->on_operator_token(tk, p_stack, p_emit_expr); + return; + case tokentype::tk_type: case tokentype::tk_lambda: case tokentype::tk_if: diff --git a/src/reader/progress_xs.cpp b/src/reader/progress_xs.cpp index 4d78fb8a..241c5da4 100644 --- a/src/reader/progress_xs.cpp +++ b/src/reader/progress_xs.cpp @@ -1,8 +1,12 @@ /* @file progress_xs.cpp */ #include "progress_xs.hpp" +#include "expect_expr_xs.hpp" +#include "xo/expression/Apply.hpp" namespace xo { + using xo::ast::Apply; + namespace scm { std::unique_ptr progress_xs::make(rp valex) { @@ -28,12 +32,54 @@ namespace xo { } void - progress_xs::on_expr(ref::brw /*expr*/, - exprstatestack * /*p_stack*/, + progress_xs::on_expr(ref::brw expr, + exprstatestack * p_stack, rp * /*p_emit_expr*/) { + constexpr const char * c_self_name = "progress_xs::on_expr"; + + rp result; + /* consecutive expressions isn't legal */ - assert(false); + switch (op_type_) { + case optype::invalid: + throw std::runtime_error(tostr(c_self_name, + ": consecutive unseparated exprs not legal")); + + break; + + case optype::op_add: + result = Apply::make_add2_f64(this->gen_expr_ /*lhs*/, + expr.promote() /*rhs*/); + break; + + case optype::op_subtract: + result = Apply::make_sub2_f64(this->gen_expr_ /*lhs*/, + expr.promote() /*rhs*/); + break; + + case optype::op_multiply: + result = Apply::make_mul2_f64(this->gen_expr_ /*lhs*/, + expr.promote() /*rhs*/); + break; + + case optype::op_divide: + result = Apply::make_div2_f64(this->gen_expr_ /*lhs*/, + expr.promote() /*rhs*/); + break; + + case optype::n_optype: + /* unreachable */ + assert(false); + } + + assert(result.get()); + + /* this expression complete.. */ + std::unique_ptr self = p_stack->pop_exprstate(); + + /* ..but more operators could follow, so don't commit yet */ + p_stack->push_exprstate(progress_xs::make(result)); } void @@ -75,7 +121,7 @@ namespace xo { rp expr = this->gen_expr_; - std::unique_ptr self = p_stack->pop_exprstate(); /* NOT KOSHER. invalidates *this */ + std::unique_ptr self = p_stack->pop_exprstate(); p_stack->top_exprstate().on_expr(expr, p_stack, @@ -160,17 +206,58 @@ namespace xo { } + void + progress_xs::on_operator_token(const token_type & tk, + exprstatestack * p_stack, + rp * p_emit_expr) + { + constexpr const char * c_self_name = "progress_xs::on_operator_token"; + + if (op_type_ == optype::invalid) { + switch(tk.tk_type()) { + case tokentype::tk_plus: + this->op_type_ = optype::op_add; + break; + case tokentype::tk_minus: + this->op_type_ = optype::op_subtract; + break; + case tokentype::tk_star: + this->op_type_ = optype::op_multiply; + break; + case tokentype::tk_slash: + this->op_type_ = optype::op_divide; + break; + default: + /* unreachable */ + assert(false); + exprstate::on_operator_token(tk, p_stack, p_emit_expr); + } + + /* infix operator must be followed by non-empty expression */ + p_stack->push_exprstate(expect_expr_xs::expect_rhs_expression()); + } else { + throw std::runtime_error(tostr(c_self_name, + ": expected expression following operator", + xtag("tk", tk))); + } + } + void progress_xs::on_f64_token(const token_type & tk, - exprstatestack * /*p_stack*/, - rp * /*p_emit_expr*/) + exprstatestack * p_stack, + rp * p_emit_expr) { constexpr bool c_debug_flag = true; scope log(XO_DEBUG(c_debug_flag)); constexpr const char * self_name = "progress_xs::on_f64"; - this->illegal_input_error(self_name, tk); + if (this->op_type_ == optype::invalid) { + this->illegal_input_error(self_name, tk); + } else { + assert(false); + exprstate::on_f64_token(tk, p_stack, p_emit_expr); + } } void diff --git a/utest/reader.test.cpp b/utest/reader.test.cpp index 8aff0011..fb4a170d 100644 --- a/utest/reader.test.cpp +++ b/utest/reader.test.cpp @@ -14,7 +14,8 @@ namespace xo { std::vector s_testcase_v = { {"def foo : f64 = 3.14159265;"}, - {"def foo : f64 = (3.14159265);"} + {"def foo : f64 = (3.14159265);"}, + {"def foo : f64 = 2.0 * 3.14159265;"} }; } From bba6898ee15e7dcc3a8db683046ec99b7f48e5d3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 15 Aug 2024 00:14:55 -0400 Subject: [PATCH 2/4] xo-reader: fix+prep: setup for associative behaviore --- include/xo/reader/progress_xs.hpp | 28 +++++++- src/reader/progress_xs.cpp | 110 ++++++++++++++++++++++-------- 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/include/xo/reader/progress_xs.hpp b/include/xo/reader/progress_xs.hpp index 9a7381c7..fb56c141 100644 --- a/include/xo/reader/progress_xs.hpp +++ b/include/xo/reader/progress_xs.hpp @@ -6,6 +6,7 @@ #pragma once #include "exprstate.hpp" +#include //#include namespace xo { @@ -22,6 +23,15 @@ namespace xo { n_optype }; + extern const char * + optype_descr(optype x); + + inline std::ostream & + operator<< (std::ostream & os, optype x) { + os << optype_descr(x); + return os; + } + /** @class progress_xs * @brief state machine for parsing a schematica runtime-value-expression **/ @@ -74,11 +84,25 @@ namespace xo { virtual void print(std::ostream & os) const override; private: - /** populate an expression here **/ - rp gen_expr_; + /** assemble expression representing + * value of + * @code + * f(lhs_, rhs_) + * @endcode + * + * where f determined by @ref op_type_ + **/ + rp assemble_expr(); + + private: + /** populate an expression here, may be followed by an operator **/ + rp lhs_; /** infix operator, if supplied **/ optype op_type_ = optype::invalid; + + /** populate an expression here, following infix operator */ + rp rhs_; }; } /*namespace scm*/ } /*namespace xo*/ diff --git a/src/reader/progress_xs.cpp b/src/reader/progress_xs.cpp index 241c5da4..b7a1775b 100644 --- a/src/reader/progress_xs.cpp +++ b/src/reader/progress_xs.cpp @@ -5,9 +5,29 @@ #include "xo/expression/Apply.hpp" namespace xo { + using xo::ast::Expression; using xo::ast::Apply; namespace scm { + const char * + optype_descr(optype x) { + switch (x) { + case optype::invalid: + return "?optype"; + case optype::op_add: + return "op+"; + case optype::op_subtract: + return "op-"; + case optype::op_multiply: + return "op*"; + case optype::op_divide: + return "op/"; + case optype::n_optype: + break; + } + return "???"; + } + std::unique_ptr progress_xs::make(rp valex) { return std::make_unique(progress_xs(std::move(valex))); @@ -15,7 +35,7 @@ namespace xo { progress_xs::progress_xs(rp valex) : exprstate(exprstatetype::expr_progress), - gen_expr_{std::move(valex)} + lhs_{std::move(valex)} {} bool @@ -31,48 +51,66 @@ namespace xo { this->illegal_input_error(self_name, tk) ; } - void - progress_xs::on_expr(ref::brw expr, - exprstatestack * p_stack, - rp * /*p_emit_expr*/) - { - constexpr const char * c_self_name = "progress_xs::on_expr"; + rp + progress_xs::assemble_expr() { + /* need to defer building Apply incase expr followed by higher-precedence operator: + * consider input like + * 3.14 + 2.0 * ... + */ - rp result; - - /* consecutive expressions isn't legal */ + /* consecutive expressions not legal, e.g: + * 3.14 6.28 + * but expressions surrounding an infix operators is: + * 3.14 / 6.28 + */ switch (op_type_) { case optype::invalid: - throw std::runtime_error(tostr(c_self_name, - ": consecutive unseparated exprs not legal")); - - break; + return this->lhs_; case optype::op_add: - result = Apply::make_add2_f64(this->gen_expr_ /*lhs*/, - expr.promote() /*rhs*/); - break; + return Apply::make_add2_f64(this->lhs_, + this->rhs_); case optype::op_subtract: - result = Apply::make_sub2_f64(this->gen_expr_ /*lhs*/, - expr.promote() /*rhs*/); - break; + return Apply::make_sub2_f64(this->lhs_, + this->rhs_); case optype::op_multiply: - result = Apply::make_mul2_f64(this->gen_expr_ /*lhs*/, - expr.promote() /*rhs*/); - break; + return Apply::make_mul2_f64(this->lhs_, + this->rhs_); case optype::op_divide: - result = Apply::make_div2_f64(this->gen_expr_ /*lhs*/, - expr.promote() /*rhs*/); - break; + return Apply::make_div2_f64(this->lhs_, + this->rhs_); case optype::n_optype: /* unreachable */ assert(false); + return nullptr; } + return nullptr; + } + + void + progress_xs::on_expr(ref::brw expr, + exprstatestack * /*p_stack*/, + rp * /*p_emit_expr*/) + { + /* note: previous token probably an operator, + * handled from progress_xs::on_operator_token(), + * which pushes expect_expr_xs::expect_rhs_expression() + */ + + constexpr const char * c_self_name = "progress_xs::on_expr"; + + + if (op_type_ == optype::invalid) { + throw std::runtime_error(tostr(c_self_name, + ": consecutive unseparated exprs not legal")); + } + +#ifdef NOT_QUITE assert(result.get()); /* this expression complete.. */ @@ -80,6 +118,9 @@ namespace xo { /* ..but more operators could follow, so don't commit yet */ p_stack->push_exprstate(progress_xs::make(result)); +#endif + + this->rhs_ = expr.promote(); } void @@ -116,10 +157,12 @@ namespace xo { exprstatestack * p_stack, rp * p_emit_expr) { + /* note: implementation parllels .on_rightparen_token() */ + constexpr bool c_debug_flag = true; scope log(XO_DEBUG(c_debug_flag)); - rp expr = this->gen_expr_; + rp expr = this->assemble_expr(); std::unique_ptr self = p_stack->pop_exprstate(); @@ -171,6 +214,9 @@ namespace xo { exprstatestack * p_stack, rp * p_emit_expr) { + /* note: implementation parallels .on_semicolon_token() */ + + constexpr bool c_debug_flag = true; scope log(XO_DEBUG(c_debug_flag)); @@ -188,7 +234,7 @@ namespace xo { */ /* right paren confirms stack expression */ - rp expr = this->gen_expr_; + rp expr = this->assemble_expr(); std::unique_ptr self = p_stack->pop_exprstate(); @@ -264,8 +310,12 @@ namespace xo { progress_xs::print(std::ostream & os) const { os << ""; } From c36e8cae40edc0a011df1cd4594d7aa7c6962ec3 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 15 Aug 2024 00:36:02 -0400 Subject: [PATCH 3/4] xo-reader: feat: support operator precedence for *,/ over +,- --- include/xo/reader/progress_xs.hpp | 8 +- src/reader/progress_xs.cpp | 132 ++++++++++++++++++++++++------ 2 files changed, 114 insertions(+), 26 deletions(-) diff --git a/include/xo/reader/progress_xs.hpp b/include/xo/reader/progress_xs.hpp index fb56c141..aca1977e 100644 --- a/include/xo/reader/progress_xs.hpp +++ b/include/xo/reader/progress_xs.hpp @@ -26,6 +26,9 @@ namespace xo { extern const char * optype_descr(optype x); + extern int + precedence(optype x); + inline std::ostream & operator<< (std::ostream & os, optype x) { os << optype_descr(x); @@ -37,12 +40,13 @@ namespace xo { **/ class progress_xs : public exprstate { public: - progress_xs(rp valex); + progress_xs(rp valex, optype op); virtual ~progress_xs() = default; static const progress_xs * from(const exprstate * x) { return dynamic_cast(x); } - static std::unique_ptr make(rp valex); + static std::unique_ptr make(rp valex, + optype optype = optype::invalid); bool admits_f64() const; diff --git a/src/reader/progress_xs.cpp b/src/reader/progress_xs.cpp index b7a1775b..421524a2 100644 --- a/src/reader/progress_xs.cpp +++ b/src/reader/progress_xs.cpp @@ -28,14 +28,34 @@ namespace xo { return "???"; } - std::unique_ptr - progress_xs::make(rp valex) { - return std::make_unique(progress_xs(std::move(valex))); + int + precedence(optype x) { + switch (x) { + case optype::invalid: + case optype::n_optype: + return 0; + + case optype::op_add: + case optype::op_subtract: + return 1; + + case optype::op_multiply: + case optype::op_divide: + return 2; + } + + return 0; } - progress_xs::progress_xs(rp valex) + std::unique_ptr + progress_xs::make(rp valex, optype op) { + return std::make_unique(progress_xs(std::move(valex), op)); + } + + progress_xs::progress_xs(rp valex, optype op) : exprstate(exprstatetype::expr_progress), - lhs_{std::move(valex)} + lhs_{std::move(valex)}, + op_type_{op} {} bool @@ -58,6 +78,15 @@ namespace xo { * 3.14 + 2.0 * ... */ + constexpr const char * c_self_name = "progress_xs::assemble_expr"; + + if ((op_type_ != optype::invalid) && (rhs_.get() == nullptr)) { + throw std::runtime_error(tostr(c_self_name, + ": expected expr on rhs of operator", + xtag("lhs", lhs_), + xtag("op", op_type_))); + } + /* consecutive expressions not legal, e.g: * 3.14 6.28 * but expressions surrounding an infix operators is: @@ -252,35 +281,90 @@ namespace xo { } + namespace { + optype + tk2op(const tokentype & tktype) { + switch (tktype) { + case tokentype::tk_plus: + return optype::op_add; + case tokentype::tk_minus: + return optype::op_subtract; + case tokentype::tk_star: + return optype::op_multiply; + case tokentype::tk_slash: + return optype::op_divide; + default: + assert(false); + return optype::invalid; + } + return optype::invalid; + } + } + void progress_xs::on_operator_token(const token_type & tk, exprstatestack * p_stack, - rp * p_emit_expr) + rp * /*p_emit_expr*/) { constexpr const char * c_self_name = "progress_xs::on_operator_token"; if (op_type_ == optype::invalid) { - switch(tk.tk_type()) { - case tokentype::tk_plus: - this->op_type_ = optype::op_add; - break; - case tokentype::tk_minus: - this->op_type_ = optype::op_subtract; - break; - case tokentype::tk_star: - this->op_type_ = optype::op_multiply; - break; - case tokentype::tk_slash: - this->op_type_ = optype::op_divide; - break; - default: - /* unreachable */ - assert(false); - exprstate::on_operator_token(tk, p_stack, p_emit_expr); - } + this->op_type_ = tk2op(tk.tk_type()); /* infix operator must be followed by non-empty expression */ p_stack->push_exprstate(expect_expr_xs::expect_rhs_expression()); + } else if (rhs_) { + /* already have complete expression stashed. + * behavior depends on operator precedence for tk with stored operator + * this->op_type_ + */ + optype op2 = tk2op(tk.tk_type()); + + if (precedence(op2) <= precedence(this->op_type_)) { + /* e.g. + * 6.2 * 4.9 + ... + * + * in stack: + * 1. progress_xs lhs=6.2, op=*, rhs=4.9 + * + * out stack + * 1. progress_xs lhs=apply(*,6.2,4.9), op=+ + */ + + /* 1. instantiate expression for *this */ + auto expr = this->assemble_expr(); + + /* 2. remove from stack */ + std::unique_ptr self = p_stack->pop_exprstate(); + + /* 3. replace with new progress_xs: */ + p_stack->push_exprstate(progress_xs::make(expr, op2)); + + /* infix operator must be followed by non-empty expression */ + p_stack->push_exprstate(expect_expr_xs::expect_rhs_expression()); + } else { + /* e.g. + * 6.2 + 4.9 * ... + * + * in stack: + * 1. progress_xs lhs=6.2, op=+, rhs=4.9 + * + * out stack: + * 1. progress_xs lhs=6.2, op=+ + * 2. expect_rhs_expression + * 3. progress_xs lhs=4.9, op=* + * 4. expect_rhs_expression + */ + + std::unique_ptr self = p_stack->pop_exprstate(); + + /* 1. replace with nested incomplete infix exprs */ + p_stack->push_exprstate(progress_xs::make(lhs_, op_type_)); + p_stack->push_exprstate(expect_expr_xs::expect_rhs_expression()); + p_stack->push_exprstate(progress_xs::make(rhs_, op2)); + p_stack->push_exprstate(expect_expr_xs::expect_rhs_expression()); + } + } else { throw std::runtime_error(tostr(c_self_name, ": expected expression following operator", From 5b53dbeac7ba5d1d2a743c7539d14f5605395c02 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 15 Aug 2024 14:00:35 -0400 Subject: [PATCH 4/4] xo-reader: wip: stub for lambda expression --- include/xo/reader/lambda_xs.hpp | 26 ++++++++++++++++++++++++++ src/reader/CMakeLists.txt | 3 ++- src/reader/lambda_xs.cpp | 13 +++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 include/xo/reader/lambda_xs.hpp create mode 100644 src/reader/lambda_xs.cpp diff --git a/include/xo/reader/lambda_xs.hpp b/include/xo/reader/lambda_xs.hpp new file mode 100644 index 00000000..4c72efe9 --- /dev/null +++ b/include/xo/reader/lambda_xs.hpp @@ -0,0 +1,26 @@ +/** @file lambda_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +//#include + +namespace xo { + namespace scm { + /** @class lambda_xs + * @brief parsing state-machine for a lambda-expression + **/ + class lambda_xs : public exprstate { + public: + lambda_xs(); + + private: + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/** end lambda_xs.hpp **/ diff --git a/src/reader/CMakeLists.txt b/src/reader/CMakeLists.txt index 6907dff2..faf190f3 100644 --- a/src/reader/CMakeLists.txt +++ b/src/reader/CMakeLists.txt @@ -11,7 +11,8 @@ set(SELF_SRCS exprseq_xs.cpp expect_expr_xs.cpp expect_symbol_xs.cpp - expect_type_xs.cpp) + expect_type_xs.cpp + lambda_xs.cpp) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) xo_dependency(${SELF_LIB} xo_expression) diff --git a/src/reader/lambda_xs.cpp b/src/reader/lambda_xs.cpp new file mode 100644 index 00000000..603f3585 --- /dev/null +++ b/src/reader/lambda_xs.cpp @@ -0,0 +1,13 @@ +/* @file lambda_xs.cpp */ + +#include "lambda_xs.hpp" + +namespace xo { + namespace scm { + lambda_xs::lambda_xs() {} + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end lambda_xs.cpp */