/** @file DExpectExprSsm.cpp * * @author Roland Conybeare, Jan 2026 **/ #include "ExpectExprSsm.hpp" #include "ParserStateMachine.hpp" #include "ParserStack.hpp" #include "SyntaxStateMachine.hpp" #include "ProgressSsm.hpp" #include "DSequenceSsm.hpp" #include "IfElseSsm.hpp" #include "LambdaSsm.hpp" #include "QuoteSsm.hpp" #include "syntaxstatetype.hpp" #include #include #include #include #include #include #include #include #include #ifdef NOT_YET #include "define_xs.hpp" #include "paren_xs.hpp" #endif namespace xo { using xo::scm::DFloat; using xo::mm::AGCObject; using xo::reflect::typeseq; using xo::facet::with_facet; namespace scm { DExpectExprSsm::DExpectExprSsm(bool allow_defs, bool cxl_on_rightbrace, bool cxl_on_rightparen) : allow_defs_{allow_defs}, cxl_on_rightbrace_{cxl_on_rightbrace}, cxl_on_rightparen_{cxl_on_rightparen} { } DExpectExprSsm * DExpectExprSsm::_make(DArena & mm, bool allow_defs, bool cxl_on_rightbrace, bool cxl_on_rightparen) { void * mem = mm.alloc(typeseq::id(), sizeof(DExpectExprSsm)); return new (mem) DExpectExprSsm(allow_defs, cxl_on_rightbrace, cxl_on_rightparen); } obj DExpectExprSsm::make(DArena & mm, bool allow_defs, bool cxl_on_rightbrace, bool cxl_on_rightparen) { return obj(_make(mm, allow_defs, cxl_on_rightbrace, cxl_on_rightparen)); } void DExpectExprSsm::start(bool allow_defs, bool cxl_on_rightbrace, bool cxl_on_rightparen, ParserStateMachine * p_psm) { auto & mm = p_psm->parser_alloc(); DArena::Checkpoint ckp = mm.checkpoint(); auto ssm = DExpectExprSsm::make(mm, allow_defs, cxl_on_rightbrace, cxl_on_rightparen); p_psm->push_ssm(ckp, ssm); } void DExpectExprSsm::start(ParserStateMachine * p_psm) { start(false /*!allow_defs*/, false /*!cxl_on_rightbrace*/, false /*cxl_on_rightparen*/, p_psm); } syntaxstatetype DExpectExprSsm::ssm_type() const noexcept { return syntaxstatetype::expect_rhs_expression; } std::string_view DExpectExprSsm::get_expect_str() const noexcept { if (allow_defs_) { return "def|if|lambda|lparen|lbrace|literal|var"; } else { return "if|lambda|lparen|lbrace|literal|var"; } } void DExpectExprSsm::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_leftparen: this->on_leftparen_token(tk, p_psm); return; case tokentype::tk_rightparen: this->on_rightparen_token(tk, p_psm); return; case tokentype::tk_leftbrace: this->on_leftbrace_token(tk, p_psm); return; case tokentype::tk_symbol: this->on_symbol_token(tk, p_psm); return; case tokentype::tk_def: this->on_def_token(tk, p_psm); return; case tokentype::tk_quote: this->on_quote_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_nil: this->on_nil_token(tk, p_psm); return; case tokentype::tk_if: this->on_if_token(tk, p_psm); return; case tokentype::tk_lambda: this->on_lambda_token(tk, p_psm); return; // all the not-yet handled cases case tokentype::tk_invalid: case tokentype::tk_deftype: case tokentype::tk_singleassign: case tokentype::tk_colon: case tokentype::tk_semicolon: case tokentype::tk_leftbracket: case tokentype::tk_rightbracket: case tokentype::tk_rightbrace: case tokentype::tk_leftangle: case tokentype::tk_rightangle: case tokentype::tk_cmple: case tokentype::tk_cmpge: case tokentype::tk_dot: case tokentype::tk_comma: case tokentype::tk_doublecolon: case tokentype::tk_assign: case tokentype::tk_yields: case tokentype::tk_plus: case tokentype::tk_minus: case tokentype::tk_star: case tokentype::tk_slash: case tokentype::tk_cmpeq: case tokentype::tk_cmpne: case tokentype::tk_type: case tokentype::tk_then: case tokentype::tk_else: case tokentype::tk_let: case tokentype::tk_in: case tokentype::tk_end: case tokentype::N: break; } Super::on_token(tk, p_psm); } void DExpectExprSsm::on_leftparen_token(const Token & tk, ParserStateMachine * p_psm) { // need progress ssm here because this is allowed: // // if (foo) > 5 then ... // // Start progress ssm, delegate incoming token thereto // DProgressSsm::start(p_psm->parser_alloc(), p_psm); p_psm->on_token(tk); } void DExpectExprSsm::on_rightparen_token(const Token & tk, ParserStateMachine * p_psm) { if (cxl_on_rightparen_) { /* abandon expression, delegate rightparen to parent */ p_psm->pop_ssm(); p_psm->on_token(tk); return; } Super::on_token(tk, p_psm); } void DExpectExprSsm::on_leftbrace_token(const Token & tk, ParserStateMachine * p_psm) { (void)tk; DSequenceSsm::start(p_psm); } void DExpectExprSsm::on_symbol_token(const Token & tk, ParserStateMachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("tk", tk)); DVarRef * var = p_psm->lookup_varref(tk.text()); if (!var) { p_psm->error_unbound_variable(ssm_classname(), tk.text()); } // examples of possible continuations from symbol foo // foo ; // (1) foo is entire rvalue expression // foo + ... // (2) foo begin operator expression // foo(..); // (3) foo begin apply function // // DProgressSsm::start(p_psm->parser_alloc(), obj(var), p_psm); } #ifdef NOT_YET void expect_expr_xs::on_symbol_token(const token_type & tk, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("tk", tk)); constexpr const char * c_self_name = "expect_expr_xs::on_symbol_token"; /* various possibilities when looking for rhs expression: * * x := y // (1) * x := f(a) // (2) * x := f(a,b) // (3) * * need lookahead token following symbol to distinguish * between (1) (symbol completes rhs expression) * and {(2), (3)} (symbol is function call) */ bp var = p_psm->lookup_var(tk.text()); if (!var) { this->unknown_variable_error(c_self_name, tk, p_psm); return; } /* e.g. * def pi = 3.14159265; * def mypi = pi; * ^ * def pi2 = pi * 2; * ^ * def y = foo(pi2); * ^ */ progress_xs::start(var.promote(), p_psm); #ifdef NOT_YET p_stack->push_exprstate(exprstate(exprstatetype::expr_progress, Variable::make(name, type))); #endif #ifdef LATER p_psm->pop_exprstate(); p_psm->top_exprstate().on_symbol(tk.text(), p_stack, p_emit_expr); #endif return; } #endif #ifdef NOT_YET void expect_expr_xs::on_def_token(const token_type & tk, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); if (allow_defs_) { define_xs::start(p_psm); } else { exprstate::on_def_token(tk, p_psm); } } #endif void DExpectExprSsm::on_def_token(const Token & tk, ParserStateMachine * p_psm) { Super::on_token(tk, p_psm); } void DExpectExprSsm::on_quote_token(const Token & tk, ParserStateMachine * p_psm) { (void)tk; DProgressSsm::start(p_psm->parser_alloc(), p_psm); DQuoteSsm::start(p_psm); p_psm->on_token(Token::quote_token()); } void DExpectExprSsm::on_bool_token(const Token & tk, ParserStateMachine * p_psm) { auto flag = DBoolean::box(p_psm->expr_alloc(), tk.bool_value()); auto expr = DConstant::make(p_psm->expr_alloc(), flag); // DProgressSsm responsible for resolving cases like // true; // true && false; DProgressSsm::start(p_psm->parser_alloc(), expr, p_psm); } void DExpectExprSsm::on_nil_token(const Token & tk, ParserStateMachine * p_psm) { (void)tk; auto nil = DList::nil(); auto expr = DConstant::make(p_psm->expr_alloc(), nil); // DProgressSsm responsible for resolving cases like // nil ++ nil; DProgressSsm::start(p_psm->parser_alloc(), expr, p_psm); } void DExpectExprSsm::on_f64_token(const Token & tk, ParserStateMachine * p_psm) { auto f64o = DFloat::box(p_psm->expr_alloc(), tk.f64_value()); auto expr = DConstant::make(p_psm->expr_alloc(), f64o); // DProgressSsm responsible for resolving cases like // 1.9, // 1.9; // 1.9 + 2; // 1.9 + 2 .. // could be followed by infix // 1.9 + 2 * 3; // 1.9 + 2 * 3 .. // could be followed by infix // 1.9 * (2 + 3) DProgressSsm::start(p_psm->parser_alloc(), expr, p_psm); } void DExpectExprSsm::on_i64_token(const Token & tk, ParserStateMachine * p_psm) { auto i64o = DInteger::box(p_psm->expr_alloc(), tk.i64_value()); auto expr = DConstant::make(p_psm->expr_alloc(), i64o); // Consider parser stack, with control here at b // // a * b - c // ^ // // [1] ExpectExpr <-- this // [0] ProgressSsm :lhs k(b) :op * :rhs _ // // if parent is ProgressSsm [0], need to deliver k(b) to it; // Then let that ProgressSsm [0] establish whether next token // is operator. // assert((void*)p_psm->stack()->top().data() == (void*)this); if (p_psm->stack()->parent()) { auto parent_ssm = (obj::from (p_psm->stack()->parent()->top())); if (parent_ssm) { // parent is-a DProgressSsm instance p_psm->pop_ssm(); p_psm->on_parsed_expression(expr); return; } } // DProgressSsm responsible for resolving cases like // 1, // 1; // 1 + 2; // 1 + 2 .. // could be followed by infix // 1 + 2 * 3; // 1 + 2 * 3 .. // could be followed by infix // 1 * (2 + 3) DProgressSsm::start(p_psm->parser_alloc(), expr, p_psm); } void DExpectExprSsm::on_string_token(const Token & tk, ParserStateMachine * p_psm) { auto str = DString::from_str(p_psm->expr_alloc(), tk.text()); auto str_o = obj(str); auto expr = DConstant::make(p_psm->expr_alloc(), str_o); /* e.g. * def msg = "hello, world"; * \----tk----/ * * DProgressSsm responsible for operators that apply to string * "foo"; * "foo" <= "bar" * "foo" + ", she said"; */ DProgressSsm::start(p_psm->parser_alloc(), expr, p_psm); } void DExpectExprSsm::on_if_token(const Token & tk, ParserStateMachine * p_psm) { (void)tk; DIfElseSsm::start(p_psm->parser_alloc(), p_psm->expr_alloc(), p_psm); // TODO: should send if-token here } void DExpectExprSsm::on_lambda_token(const Token & tk, ParserStateMachine * p_psm) { (void)tk; DLambdaSsm::start(p_psm); } void DExpectExprSsm::on_parsed_expression(obj expr, ParserStateMachine * p_psm) { p_psm->pop_ssm(); p_psm->on_parsed_expression(expr); } void DExpectExprSsm::on_parsed_expression_with_token(obj expr, const Token & tk, ParserStateMachine * p_psm) { // expression (reported by nested ProgressSsm) // completes this DExpectExprSsm's assignment p_psm->pop_ssm(); p_psm->on_parsed_expression_with_token(expr, tk); } #ifdef NOT_YET void DExpectExprSsm::on_quoted_literal(obj lit, ParserStateMachine * p_psm) { // note: impl here parallels .on_f64_token() .on_i64_token() etc. auto expr = DConstant::make(p_psm->expr_alloc(), lit); DProgressSsm::start(p_psm->parser_alloc(), expr, p_psm); } #endif bool DExpectExprSsm::pretty(const ppindentinfo & ppii) const { return ppii.pps()->pretty_struct (ppii, "DExpectExprSsm", refrtag("allow_defs", allow_defs_), refrtag("cxl_on_rightbrace", cxl_on_rightbrace_), refrtag("cxl_on_rightparen", cxl_on_rightparen_), refrtag("expect", this->get_expect_str())); } #ifdef NOT_YET void expect_expr_xs::on_if_token(const token_type & /*tk*/, parserstatemachine * p_psm) { if_else_xs::start(p_psm); } void expect_expr_xs::on_rightbrace_token(const token_type & tk, parserstatemachine * p_psm) { constexpr bool c_debug_flag = true; scope log(XO_DEBUG(c_debug_flag)); if (cxl_on_rightbrace_) { auto self = p_psm->pop_exprstate(); /* do not call .on_expr(), since '}' cancelled */ p_psm->on_rightbrace_token(tk); } else { exprstate::on_rightbrace_token(tk, p_psm); } } void expect_expr_xs::on_expr(bp expr, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("exstype", this->exs_type_), xtag("expr", expr.promote())); log && log("pop expect_expr_xs, forward to parent"); std::unique_ptr self = p_psm->pop_exprstate(); p_psm->on_expr(expr); } /*on_expr*/ void expect_expr_xs::on_expr_with_semicolon(bp expr, parserstatemachine * p_psm) { scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("exstype", this->exs_type_), xtag("expr", expr.promote())); log && log("pop expect_expr_xs, forward to parent"); std::unique_ptr self = p_psm->pop_exprstate(); p_psm->on_expr_with_semicolon(expr); } /*on_expr_with_semicolon*/ void expect_expr_xs::print(std::ostream & os) const { os << ""; } #endif void DExpectExprSsm::visit_gco_children(VisitReason, obj) noexcept { // all members POD, skip } } /*namespace scm*/ } /*namespace xo*/ /* end DExpectExprSsm.cpp */