diff --git a/include/xo/reader/exprstate.hpp b/include/xo/reader/exprstate.hpp index fbdc8b50..87eaf29d 100644 --- a/include/xo/reader/exprstate.hpp +++ b/include/xo/reader/exprstate.hpp @@ -125,6 +125,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/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/include/xo/reader/progress_xs.hpp b/include/xo/reader/progress_xs.hpp index bc1ada8d..eeaee588 100644 --- a/include/xo/reader/progress_xs.hpp +++ b/include/xo/reader/progress_xs.hpp @@ -6,23 +6,49 @@ #pragma once #include "exprstate.hpp" +#include //#include namespace xo { namespace scm { + /* represent an infix operator */ + enum class optype { + invalid = -1, + + op_add, + op_subtract, + op_multiply, + op_divide, + + n_optype + }; + + 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); + return os; + } + /** @class progress_xs * @brief state machine for parsing a schematica runtime-value-expression **/ 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; @@ -51,6 +77,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,8 +90,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/CMakeLists.txt b/src/reader/CMakeLists.txt index ae233e22..5be9eb8a 100644 --- a/src/reader/CMakeLists.txt +++ b/src/reader/CMakeLists.txt @@ -12,7 +12,8 @@ set(SELF_SRCS expect_expr_xs.cpp expect_symbol_xs.cpp expect_formal_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/exprstate.cpp b/src/reader/exprstate.cpp index d36fba94..43504622 100644 --- a/src/reader/exprstate.cpp +++ b/src/reader/exprstate.cpp @@ -168,6 +168,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*/, @@ -252,6 +265,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/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 */ diff --git a/src/reader/progress_xs.cpp b/src/reader/progress_xs.cpp index 4d78fb8a..421524a2 100644 --- a/src/reader/progress_xs.cpp +++ b/src/reader/progress_xs.cpp @@ -1,17 +1,61 @@ /* @file progress_xs.cpp */ #include "progress_xs.hpp" +#include "expect_expr_xs.hpp" +#include "xo/expression/Apply.hpp" namespace xo { + using xo::ast::Expression; + using xo::ast::Apply; + namespace scm { - std::unique_ptr - progress_xs::make(rp valex) { - return std::make_unique(progress_xs(std::move(valex))); + 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 "???"; } - progress_xs::progress_xs(rp 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; + } + + 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), - gen_expr_{std::move(valex)} + lhs_{std::move(valex)}, + op_type_{op} {} bool @@ -27,13 +71,85 @@ namespace xo { this->illegal_input_error(self_name, tk) ; } + rp + progress_xs::assemble_expr() { + /* need to defer building Apply incase expr followed by higher-precedence operator: + * consider input like + * 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: + * 3.14 / 6.28 + */ + switch (op_type_) { + case optype::invalid: + return this->lhs_; + + case optype::op_add: + return Apply::make_add2_f64(this->lhs_, + this->rhs_); + + case optype::op_subtract: + return Apply::make_sub2_f64(this->lhs_, + this->rhs_); + + case optype::op_multiply: + return Apply::make_mul2_f64(this->lhs_, + this->rhs_); + + case optype::op_divide: + 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*/, + progress_xs::on_expr(ref::brw expr, exprstatestack * /*p_stack*/, rp * /*p_emit_expr*/) { - /* consecutive expressions isn't legal */ - assert(false); + /* 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.. */ + 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)); +#endif + + this->rhs_ = expr.promote(); } void @@ -70,12 +186,14 @@ 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(); /* NOT KOSHER. invalidates *this */ + std::unique_ptr self = p_stack->pop_exprstate(); p_stack->top_exprstate().on_expr(expr, p_stack, @@ -125,6 +243,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)); @@ -142,7 +263,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(); @@ -160,25 +281,125 @@ 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*/) + { + constexpr const char * c_self_name = "progress_xs::on_operator_token"; + + if (op_type_ == optype::invalid) { + 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", + 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 progress_xs::print(std::ostream & os) const { os << ""; } 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;"} }; }