/** @file DProgressSsm.cpp * * @author Roland Conybeare, Jan 2026 **/ #include "ProgressSsm.hpp" #include "DExpectExprSsm.hpp" #include "ssm/ISyntaxStateMachine_DExpectExprSsm.hpp" #include "ApplySsm.hpp" #include "ParenSsm.hpp" #include #include #include #include #include #include // for xo::scm::Primitives #include #include #include #include #include #include #ifdef NOT_YET #include "expect_expr_xs.hpp" #include "pretty_exprstatestack.hpp" #include "xo/expression/AssignExpr.hpp" #include "xo/expression/Apply.hpp" #include "xo/expression/pretty_expression.hpp" #endif namespace xo { #ifdef NOT_YET using xo::scm::Expression; using xo::scm::AssignExpr; using xo::scm::Variable; using xo::scm::Apply; #endif using xo::mm::AAllocator; using xo::mm::AGCObject; using xo::print::APrintable; using xo::facet::FacetRegistry; using xo::facet::with_facet; using xo::reflect::typeseq; namespace scm { const char * optype_descr(optype x) { switch (x) { case optype::invalid: return "?optype"; case optype::op_assign: return "op:="; case optype::op_less: return "op<"; case optype::op_less_equal: return "op<="; case optype::op_equal: return "op=="; case optype::op_not_equal: return "op!="; case optype::op_great: return "op>"; case optype::op_great_equal: return "op>="; 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 "???"; } /** higher-precedence operators bind before lower-preference operators **/ int precedence(optype x) { switch (x) { case optype::invalid: case optype::n_optype: return 0; case optype::op_assign: return 1; case optype::op_less: case optype::op_less_equal: case optype::op_equal: case optype::op_not_equal: case optype::op_great: case optype::op_great_equal: return 2; case optype::op_add: case optype::op_subtract: return 3; case optype::op_multiply: case optype::op_divide: return 4; } return 0; } namespace { optype tk2op(const tokentype & tktype) { switch (tktype) { case tokentype::tk_assign: return optype::op_assign; 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; case tokentype::tk_cmpeq: // [==] return optype::op_equal; case tokentype::tk_cmpne: // [!=] return optype::op_not_equal; case tokentype::tk_leftangle: // [<] return optype::op_less; case tokentype::tk_cmple: // [<=] return optype::op_less_equal; case tokentype::tk_rightangle: // [>] return optype::op_great; case tokentype::tk_cmpge: // [>=] return optype::op_great_equal; default: assert(false); return optype::invalid; } return optype::invalid; } } DProgressSsm * DProgressSsm::_make(DArena & mm, obj lhs, optype op) { void * mem = mm.alloc(typeseq::id(), sizeof(DProgressSsm)); return new (mem) DProgressSsm(lhs, op); } obj DProgressSsm::make(DArena & mm, obj lhs, optype op) { return obj(_make(mm, lhs, op)); } void DProgressSsm::start(DArena & parser_mm, obj lhs, optype op, ParserStateMachine * p_psm) { DArena::Checkpoint ckp = parser_mm.checkpoint(); auto ssm = DProgressSsm::make(parser_mm, lhs, op); p_psm->push_ssm(ckp, ssm); } void DProgressSsm::start(DArena & parser_mm, obj lhs, ParserStateMachine * p_psm) { start(parser_mm, lhs, optype::invalid, p_psm); } void DProgressSsm::start(DArena & parser_mm, ParserStateMachine * p_psm) { start(parser_mm, obj(), p_psm); } DProgressSsm::DProgressSsm(obj valex, optype op) : lhs_{valex}, op_type_{op} {} syntaxstatetype DProgressSsm::ssm_type() const noexcept { return syntaxstatetype::progress; } #ifdef NOT_YET bool progress_xs::admits_f64() const { return false; } #endif std::string_view DProgressSsm::get_expect_str() const noexcept { if (!lhs_) { return "expr1|leftparen"; } else if (op_type_ == optype::invalid) { return "oper|semicolon|leftparen|rightparen|rightbrace"; } else { return "expr2|leftparen"; } } void DProgressSsm::on_token(const Token & tk, ParserStateMachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag()), xtag("tk", tk)); switch (tk.tk_type()) { case tokentype::tk_then: case tokentype::tk_else: case tokentype::tk_comma: this->on_completing_token(tk, p_psm); return; case tokentype::tk_symbol: this->on_symbol_token(tk, p_psm); return; case tokentype::tk_colon: this->on_colon_token(tk, p_psm); return; case tokentype::tk_singleassign: this->on_singleassign_token(tk, p_psm); return; case tokentype::tk_string: this->on_string_token(tk, p_psm); return; case tokentype::tk_f64: this->on_f64_token(tk, p_psm); return; case tokentype::tk_i64: this->on_i64_token(tk, p_psm); return; case tokentype::tk_bool: this->on_bool_token(tk, p_psm); return; case tokentype::tk_semicolon: this->on_semicolon_token(tk, p_psm); return; case tokentype::tk_rightbrace: this->on_rightbrace_token(tk, p_psm); return; case tokentype::tk_leftparen: this->on_leftparen_token(tk, p_psm); return; case tokentype::tk_rightparen: this->on_rightparen_token(tk, p_psm); return; // all the not-yet handled cases case tokentype::tk_invalid: case tokentype::tk_def: case tokentype::tk_deftype: case tokentype::tk_if: case tokentype::tk_quote: case tokentype::tk_leftbracket: case tokentype::tk_rightbracket: case tokentype::tk_leftbrace: case tokentype::tk_dot: case tokentype::tk_doublecolon: case tokentype::tk_assign: case tokentype::tk_yields: break; case tokentype::tk_star: case tokentype::tk_slash: case tokentype::tk_plus: case tokentype::tk_minus: case tokentype::tk_cmpeq: case tokentype::tk_cmpne: case tokentype::tk_leftangle: case tokentype::tk_rightangle: case tokentype::tk_cmple: case tokentype::tk_cmpge: this->on_operator_token(tk, p_psm); return; case tokentype::tk_nil: case tokentype::tk_type: case tokentype::tk_lambda: break; case tokentype::tk_let: case tokentype::tk_in: case tokentype::tk_end: case tokentype::N: break; } Super::on_token(tk, p_psm); } void DProgressSsm::on_symbol_token(const Token & tk, ParserStateMachine * p_psm) { Super::on_token(tk, p_psm); } void DProgressSsm::on_completing_token(const Token & tk, ParserStateMachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); obj expr = this->assemble_expr(p_psm); p_psm->pop_ssm(); // completes self p_psm->on_parsed_expression_with_token(expr, tk); } void DProgressSsm::on_colon_token(const Token & tk, ParserStateMachine * p_psm) { p_psm->illegal_input_on_token("DProgressSsm::on_colon_token", tk, this->get_expect_str()); } void DProgressSsm::on_singleassign_token(const Token & tk, ParserStateMachine * p_psm) { p_psm->illegal_input_on_token("DProgressSsm::on_singleassign_token", tk, this->get_expect_str()); } void DProgressSsm::on_operator_token(const Token & tk, ParserStateMachine * p_psm) { if (op_type_ == optype::invalid) { // tk is the operator this instance was waiting for. // But need to consider precedence. // possibly need to take lhs_ expression and rotate it into new lhs of // surrounding ProgressSsm this->op_type_ = tk2op(tk.tk_type()); DExpectExprSsm::start(p_psm); return; } else if (rhs_) { optype op_type2 = tk2op(tk.tk_type()); /* already have {lhs_, op_type_, rhs_}, * with incoming op_type2 operator decides whether to parse like * (lhs_, op_type_, rhs_) op_type2 ... * or * (lhs_, op_type_, (rhs_ op_type2 ...)) */ if (precedence(op_type_) >= precedence(op_type2)) { /* associate to the left * * parse like * a + b - ... (a + b) - ... * a * b - ... (a * b) - ... */ auto lhs2 = this->assemble_expr(p_psm); p_psm->pop_ssm(); DProgressSsm::start(p_psm->parser_alloc(), lhs2, op_type2, p_psm); DExpectExprSsm::start(p_psm); return; } else { /* associate to the right * * parse like * a + b * ... (a + (b * ...)) */ obj lhs1 = lhs_; optype op_type1 = op_type_; obj rhs1 = rhs_; assert(lhs1); assert(op_type1 != optype::invalid); assert(rhs1); p_psm->pop_ssm(); /* (a + ..) */ DProgressSsm::start(p_psm->parser_alloc(), lhs1, op_type1, p_psm); DExpectExprSsm::start(p_psm); /* (b * ..) */ DProgressSsm::start(p_psm->parser_alloc(), rhs1, op_type2, p_psm); DExpectExprSsm::start(p_psm); return; } } p_psm->illegal_input_on_token("DProgressSsm::on_operator_token", tk, this->get_expect_str()); } void DProgressSsm::on_string_token(const Token & tk, ParserStateMachine * p_psm) { Super::on_token(tk, p_psm); } void DProgressSsm::on_f64_token(const Token & tk, ParserStateMachine * p_psm) { Super::on_token(tk, p_psm); } void DProgressSsm::on_i64_token(const Token & tk, ParserStateMachine * p_psm) { Super::on_token(tk, p_psm); } void DProgressSsm::on_bool_token(const Token & tk, ParserStateMachine * p_psm) { Super::on_token(tk, p_psm); } void DProgressSsm::on_semicolon_token(const Token & tk, ParserStateMachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); /* note: implementation should parallel .on_rightparen_token() */ (void)tk; obj expr = this->assemble_expr(p_psm); { obj expr_pr = FacetRegistry::instance().variant(expr); assert(expr_pr); log && log(xtag("expr", expr_pr)); } p_psm->pop_ssm(); p_psm->on_parsed_expression_with_token(expr, tk); } void DProgressSsm::on_rightbrace_token(const Token & tk, ParserStateMachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); (void)tk; obj expr = this->assemble_expr(p_psm); if (expr) { obj expr_pr = FacetRegistry::instance().try_variant(expr); assert(expr_pr); log && log(xtag("expr", expr_pr)); } else { // illegal token if assemble failed Super::on_token(tk, p_psm); return; } p_psm->pop_ssm(); p_psm->on_parsed_expression_with_token(expr, tk); } void DProgressSsm::on_parsed_expression(obj expr, ParserStateMachine * p_psm) { const bool c_debug_flag = p_psm->debug_flag(); scope log(XO_DEBUG(c_debug_flag)); if (!lhs_) { log && log("accepting expr1"); this->lhs_ = expr; return; } assert (op_type_ != optype::invalid); if (!rhs_) { this->rhs_ = expr; // need next token before we know whether this DProgressSsm // is complete. Consider input like 7 + 2 * 3 vs 7 * 2 + 3 // return; } Super::on_parsed_expression(expr, p_psm); } void DProgressSsm::on_parsed_expression_with_token(obj expr, const Token & tk, ParserStateMachine * p_psm) { const bool c_debug_flag = p_psm->debug_flag(); scope log(XO_DEBUG(c_debug_flag), xtag("expr", expr), xtag("tk", tk)); #ifdef NOT_YET if (!lhs_) { log && log("DProgressSsm: accepting expr1"); this->lhs_ = expr; // now we have to handle tk! return; } #endif // here: have lhs_ expression if (op_type_ == optype::invalid) { // e.g. control here on input like // x : = 4 4 p_psm->illegal_parsed_expression ("DProgressSsm::on_parsed_expression_with_token", expr, this->get_expect_str()); return; } this->rhs_ = expr; obj expr2 = this->assemble_expr(p_psm); if (expr2) { p_psm->pop_ssm(); p_psm->on_parsed_expression_with_token(expr2, tk); } } #ifdef NOT_YET void progress_xs::apply_type_error(const char * self_name, optype op, bp expr1, bp expr2, parserstatemachine * p_psm) const { std::string errmsg = tostr("incompatible argument types T1,T2 to op", xtag("op", op), xtag("T1", expr1->valuetype()), xtag("T2", expr2->valuetype())); p_psm->on_error(self_name, std::move(errmsg)); } void progress_xs::on_expr(bp expr, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag()), xtag("expr", 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"; const char * exp = get_expect_str(); if (lhs_) { if (op_type_ == optype::invalid) { /* two consecutive expression without an operator */ this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); } #ifdef NOT_QUITE assert(result.get()); /* this expression complete.. */ std::unique_ptr self = p_psm->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(); } else { /* control here on input like * add(1,2)... * * add(1,2) needs to be handled inside a progress_xs * instance because may be followed by an operator: * add(1,2) + ... */ this->lhs_ = expr.promote(); } } void progress_xs::on_expr_with_semicolon(bp expr, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("lhs", lhs_), xtag("op", op_type_), xtag("expr", expr)); constexpr const char * c_self_name = "progress_xs::on_expr_with_semicolon"; const char * exp = get_expect_str(); if (op_type_ == optype::invalid) { this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); } this->rhs_ = expr.promote(); // FORBIDDEN, because .on_semicolon_token() destroys *this before returning // this->on_semicolon_token(token_type::semicolon(), p_psm); // INSTEAD, spell out the body rp expr2 = this->assemble_expr(p_psm); if (expr2) { std::unique_ptr self = p_psm->pop_exprstate(); p_psm->on_expr_with_semicolon(expr2); } } #endif #ifdef NOT_YET void progress_xs::on_comma_token(const token_type & tk, parserstatemachine * p_psm) { /* note: implementation parllels .on_semicolon_token(), .on_rightparen_token() */ scope log(XO_DEBUG(p_psm->debug_flag())); constexpr const char * self_name = "progress::xs::on_comma_token"; auto & xs_stack = p_psm->xs_stack_; /* stack may be something like * * applyexpr * expect_expr_xs * progress_xs * <-- comma * * 1. comma completes expression-in-progress */ /* comma confirms stack expression */ rp expr = this->assemble_expr(p_psm); std::unique_ptr self = p_psm->pop_exprstate(); if (xs_stack.empty()) { throw std::runtime_error(tostr(self_name, ": expected non-empty parsing state")); } log && log(xtag("stack", &xs_stack)); p_psm->top_exprstate().on_expr(expr, p_psm); /* now deliver comma */ p_psm->top_exprstate().on_comma_token(tk, p_psm); } void progress_xs::on_semicolon_token(const token_type & /*tk*/, parserstatemachine * p_psm) { /* note: implementation parallels .on_rightparen_token() */ scope log(XO_DEBUG(p_psm->debug_flag())); rp expr = this->assemble_expr(p_psm); log && log(xtag("assembled-expr", expr)); std::unique_ptr self = p_psm->pop_exprstate(); p_psm->on_expr_with_semicolon(expr); /* control here on input like: * (1.234; * * a. '(' sets up stack [lparen_0:expect_rhs_expression] * (see exprstate::on_leftparen()) * b. 1.234 pushes (in case operators) [lparen_0:expect_rhs_expression:expr_progress] * (see exprstate::on_f64()) * c. semicolon completes expr_progress [lparen_0:expect_rhs_expression] * deliver expresssion to expect_rhs_expression.on_expr_with_semicolon() * (see exprstate::on_expr_with_semicolon()) * d. expr_rhs_expression forwards expression to [lparen_0] * e. lparen_0 would advance to [lparen_1], but rejects semicolon */ } void progress_xs::on_assign_token(const token_type & tk, parserstatemachine * p_psm) { this->on_operator_token(tk, p_psm); } #endif void DProgressSsm::on_leftparen_token(const Token & tk, ParserStateMachine * p_psm) { if (!lhs_) { // leftparen begins possible lhs expression DParenSsm::start(p_psm); p_psm->on_token(Token::leftparen_token()); return; } if (op_type_ == optype::invalid) { // input: /// <--- F1 ---> // (..........)(.. ).. // <------ A1 -----> // <------- X1 ------> // // F1: expression evaluating to a function, // parsed as fn_expr // A1: expression parsed as a function call (i.e. apply-expression) // X1: operator expression starting with A1 // // before: // [0] ProgressSsm responsible for input beginning with F1 // .lhs = fn_expr, .op_type empty, .rhs empty // // after: // [0] ApplySsm responsible for function call A1 // .fn_expr = fn_expr // [1] ProgressSsm responsible for operator expression X1 // .lhs empty, .op_type empty, .rhs empty // // Remarks: // 1. keep ProgressSsm on the stack in case input continues like: // fn_expr(args..) + .. // i.e. to allow for infix operator following apply // obj fn_expr(this->lhs_); this->lhs_ = obj(); DApplySsm::start(fn_expr, p_psm); // + send leftparen to just-pushed apply p_psm->on_token(tk); return; } Super::on_token(tk, p_psm); } #ifdef NOT_YET /* editor bait: on_lparen */ void progress_xs::on_leftparen_token(const token_type & tk, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); /* input like: * 'foo(' -> expect function call. might continue 'foo(a,b,c)' * 'foo+(' -> expect parenthesized expression. might continue 'foo+(bar/2)' */ if (op_type_ == optype::invalid) { /* start function call */ assert(rhs_.get() == nullptr); rp fn_expr = lhs_; /* reset this progress_xs back to empty state; * apply_xs will be responsible for lhs_. */ lhs_ = nullptr; #ifdef OBSOLETE /* don't unwind! want to handle input like * f(x,y)+g(z) */ /* unwind this progress_xs + replace with function call */ std::unique_ptr self = p_psm->pop_exprstate(); #endif apply_xs::start(fn_expr, p_psm); /* control will reenter progress_xs via .on_expr() */ return; } const char * exp = get_expect_str(); constexpr const char * c_self_name = "exprstate::on_leftparen"; this->illegal_input_on_token(c_self_name, tk, exp, p_psm); } #endif void DProgressSsm::on_rightparen_token(const Token & tk, ParserStateMachine * p_psm) { /* note: implementation parallels .on_semicolon_token() */ scope log(XO_DEBUG(p_psm->debug_flag())); /* stack may be something like: * * [0] DProgressSsm * [1] DExpectExprSsm * [2] DApplySsm * * where we want rightparen to resolve in [2] DApplySsm, * after triggering completion of [0] and [1] */ auto expr = this->assemble_expr(p_psm); if (expr) { /* 1. popping self from parser stack.. */ p_psm->pop_ssm(); /* 2. report parsed subexpr to parent ssm, along with the triggering rightparen **/ p_psm->on_parsed_expression_with_token(expr, tk); return; } Super::on_token(tk, p_psm); } #ifdef NOT_YET void progress_xs::on_rightparen_token(const token_type & tk, parserstatemachine * p_psm) { /* note: implementation parallels .on_semicolon_token() */ scope log(XO_DEBUG(p_psm->debug_flag())); constexpr const char * self_name = "progress_xs::on_rightparen"; auto & xs_stack = p_psm->xs_stack_; /* stack may be something like: * * lparen_0 * expect_expr_xs * expr_progress * <-- rightparen * * 1. rightparen completes expression-in-progress * 2. rightparen must then match innermost waiting lparen_0 */ /* right paren confirms stack expression */ rp expr = this->assemble_expr(p_psm); log && log(xtag("expr", expr), xtag("do", "pop self + send {expr, rparen} -> parent")); std::unique_ptr self = p_psm->pop_exprstate(); if (xs_stack.empty()) { throw std::runtime_error(tostr(self_name, ": expected non-empty parsing stack")); } log && log(xtag("stack", &xs_stack)); p_psm->top_exprstate().on_expr(expr, p_psm); /* now deliver rightparen */ p_psm->top_exprstate().on_rightparen_token(tk, p_psm); } void progress_xs::on_else_token(const token_type & tk, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); rp expr = this->assemble_expr(p_psm); log && log(xtag("assembled-expr", expr)); std::unique_ptr self = p_psm->pop_exprstate(); p_psm->on_expr(expr); p_psm->on_else_token(tk); /* control here on input like: * * if a > b then c else.. */ } void progress_xs::on_rightbrace_token(const token_type & tk, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); rp expr = this->assemble_expr(p_psm); log && log(xtag("assembled-expr", expr)); std::unique_ptr self = p_psm->pop_exprstate(); p_psm->on_expr(expr); p_psm->on_rightbrace_token(tk); /* control here on input like: * * { n * n } */ } #endif #ifdef OBSOLETE //void progress_xs::on_operator_token(const token_type & tk, parserstatemachine * p_psm) void progress_xs::on_bool_token(const token_type & tk, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); constexpr const char * c_self_name = "progress_xs::on_bool_token"; const char * exp = get_expect_str(); if (this->op_type_ == optype::invalid) { this->illegal_input_on_token(c_self_name, tk, exp, p_psm); } else { exprstate::on_bool_token(tk, p_psm); } } void progress_xs::on_i64_token(const token_type & tk, parserstatemachine * p_psm) { constexpr bool c_debug_flag = true; scope log(XO_DEBUG(c_debug_flag)); constexpr const char * c_self_name = "progress_xs::on_i64_token"; const char * exp = get_expect_str(); if (this->op_type_ == optype::invalid) { this->illegal_input_on_token(c_self_name, tk, exp, p_psm); } else { exprstate::on_i64_token(tk, p_psm); } } #endif bool DProgressSsm::pretty(const xo::print::ppindentinfo & ppii) const { scope log(XO_DEBUG(false)); log && log(xtag("lhs_.tseq", lhs_._typeseq())); log && log(xtag("rhs_.tseq", rhs_._typeseq())); obj lhs = FacetRegistry::instance().try_variant(lhs_); obj rhs = FacetRegistry::instance().try_variant(rhs_); bool lhs_present = lhs; bool rhs_present = rhs; bool op_present = (op_type_ != optype::invalid); return ppii.pps()->pretty_struct (ppii, "DProgressSsm", refrtag("lhs", lhs, lhs_present), refrtag("op", op_type_, op_present), refrtag("rhs", rhs, rhs_present), refrtag("expect", this->get_expect_str()) ); } namespace { // make_builtin_apply_pm2 // helper function for making apply expression that invokes // a binary primitive. Use for numeric operators: // {*, /, +, -, ==} // obj assemble_numeric_expr_aux(obj expr_alloc, const TypeRef::prefix_type & prefix, obj pm_obj, obj lhs, obj rhs) { auto fn_expr = DConstant::make(expr_alloc, pm_obj); /* note: * 1. don't assume we know lhs_ / rhs_ value types yet. * perhaps have expression like * f(..) * g(..) * where f is the function that contains current ssm. * * 2. consequence: we need representation for * polymorphic multiply on unknown numeric arguments. * * 3. TypeRef::dwim(..) is a placeholder. * Plan to later provide abstract interpreter * (ie compiler pass :) to drive type inference/unification * * 4. Alternatively could supply type-annotation syntax * so human can assist inference; context here is we want * to automate the boring stuff */ TypeRef tref = TypeRef::dwim(prefix, nullptr); return DApplyExpr::make2(expr_alloc, tref, fn_expr, lhs, rhs); } #ifdef OBSOLETE obj assemble_numeric_expr_aux(obj expr_alloc, const TypeRef::prefix_type & prefix, DPrimitive_gco_2_gco_gco * p_gco_pm, obj lhs, obj rhs) { auto pm_obj = with_facet::mkobj(p_gco_pm); return assemble_numeric_expr_aux(expr_alloc, prefix, pm_obj, lhs, rhs); } #endif } obj DProgressSsm::assemble_expr(ParserStateMachine * p_psm) { /* 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 = "DProgressSsm::assemble_expr"; if ((op_type_ != optype::invalid) && !rhs_) { std::string errmsg_string = tostr("expected expression on rhs of operator op", xtag("lhs", lhs_), xtag("op", op_type_)); auto errmsg = DString::from_view(p_psm->expr_alloc(), std::string_view(errmsg_string)); p_psm->capture_error(c_self_name, errmsg); return obj(); } /* 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_assign: assert(false); break; case optype::op_equal: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_cmpeq_gco"), p_psm->cmpeq_pm(), lhs_, rhs_); case optype::op_not_equal: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_cmpne_gco"), p_psm->cmpne_pm(), lhs_, rhs_); case optype::op_less: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_cmplt_gco"), p_psm->cmplt_pm(), lhs_, rhs_); case optype::op_less_equal: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_cmple_gco"), p_psm->cmple_pm(), lhs_, rhs_); case optype::op_great: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_cmpgt_gco"), p_psm->cmpgt_pm(), lhs_, rhs_); break; case optype::op_great_equal: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_cmpge_gco"), p_psm->cmpge_pm(), lhs_, rhs_); case optype::op_multiply: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_mul_gco"), p_psm->multiply_pm(), lhs_, rhs_); case optype::op_divide: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_div_gco"), p_psm->divide_pm(), lhs_, rhs_); case optype::op_add: return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_add_gco"), p_psm->add_pm(), lhs_, rhs_); break; case optype::op_subtract: /* editor bait: op_minus */ return assemble_numeric_expr_aux (p_psm->expr_alloc(), TypeRef::prefix_type::from_chars("_sub_gco"), p_psm->subtract_pm(), lhs_, rhs_); #ifdef NOT_YET case optype::op_assign: { bp lhs = Variable::from(this->lhs_); if (!lhs) { throw std::runtime_error (tostr("progress_xs::assemble_expr", " expect variable on lhs of assignment operator :=", xtag("lhs", lhs_), xtag("rhs", rhs_))); } return AssignExpr::make(lhs.promote(), this->rhs_); } #endif case optype::n_optype: /* unreachable */ assert(false); break; } return obj(); } void DProgressSsm::visit_gco_children(VisitReason reason, obj gc) noexcept { gc.visit_poly_child(reason, &lhs_); gc.visit_poly_child(reason, &rhs_); } } /*namespace scm*/ } /*namespace xo*/ /* end DProgressSsm.cpp */