diff --git a/xo-reader2/include/xo/reader2/DExpectExprSsm.hpp b/xo-reader2/include/xo/reader2/DExpectExprSsm.hpp index c963c9c1..7600948c 100644 --- a/xo-reader2/include/xo/reader2/DExpectExprSsm.hpp +++ b/xo-reader2/include/xo/reader2/DExpectExprSsm.hpp @@ -51,6 +51,12 @@ namespace xo { /** @defgroup scm-expectexpr-methods general methods **/ ///@{ + /** update state for this syntax on incoming leftparen token @p tk, + * with overall parser state in @p p_psm + **/ + void on_leftparen_token(const Token & tk, + ParserStateMachine * p_psm); + /** update state for this syntax on incoming leftbrace token @p tk, * with overall parser state in @p p_psm **/ @@ -81,8 +87,18 @@ namespace xo { void on_string_token(const Token & tk, ParserStateMachine * p_psm); + /** update state for this syntax on incoming if-token @p tk, + * overall parser state in @p p_psm. + * + * action: start nested if-else ssm + **/ + void on_if_token(const Token & tk, + ParserStateMachine * p_psm); + /** update state for this syntax on incoming lambda token @p tk, * overall parser state in @p p_psm + * + * action: start nested lambda ssm **/ void on_lambda_token(const Token & tk, ParserStateMachine * p_psm); diff --git a/xo-reader2/include/xo/reader2/DParenSsm.hpp b/xo-reader2/include/xo/reader2/DParenSsm.hpp index 818e0470..05c3926b 100644 --- a/xo-reader2/include/xo/reader2/DParenSsm.hpp +++ b/xo-reader2/include/xo/reader2/DParenSsm.hpp @@ -116,6 +116,14 @@ namespace xo { void on_parsed_expression(obj expr, ParserStateMachine * p_psm); + /** update ssm for expression @p expr (emitted by nested ssm) + * that's immediately followed by token @p tk + * with overall parser state in @p p_psm + **/ + void on_parsed_expression_with_token(obj expr, + const Token & tk, + ParserStateMachine * p_psmn); + ///@} /** @defgroup scm-parenssm-printable-facet printable facet methods **/ ///@{ diff --git a/xo-reader2/src/reader2/DExpectExprSsm.cpp b/xo-reader2/src/reader2/DExpectExprSsm.cpp index a2db5967..445ee76d 100644 --- a/xo-reader2/src/reader2/DExpectExprSsm.cpp +++ b/xo-reader2/src/reader2/DExpectExprSsm.cpp @@ -8,6 +8,7 @@ #include "SyntaxStateMachine.hpp" #include "ssm/ISyntaxStateMachine_DProgressSsm.hpp" #include "DSequenceSsm.hpp" +#include "IfElseSsm.hpp" #include "LambdaSsm.hpp" #include "syntaxstatetype.hpp" #include @@ -103,9 +104,9 @@ namespace xo { DExpectExprSsm::get_expect_str() const noexcept { if (allow_defs_) { - return "def|lambda|lparen|lbrace|literal|var"; + return "def|if|lambda|lparen|lbrace|literal|var"; } else { - return "lambda|lparen|lbrace|literal|var"; + return "if|lambda|lparen|lbrace|literal|var"; } } @@ -116,6 +117,10 @@ namespace xo { 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_leftbrace: this->on_leftbrace_token(tk, p_psm); return; @@ -144,17 +149,19 @@ namespace xo { this->on_bool_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_if: case tokentype::tk_singleassign: case tokentype::tk_colon: case tokentype::tk_semicolon: - case tokentype::tk_leftparen: case tokentype::tk_rightparen: case tokentype::tk_leftbracket: case tokentype::tk_rightbracket: @@ -187,6 +194,22 @@ namespace xo { 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_leftbrace_token(const Token & tk, ParserStateMachine * p_psm) @@ -388,6 +411,18 @@ namespace xo { 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) diff --git a/xo-reader2/src/reader2/DIfElseSsm.cpp b/xo-reader2/src/reader2/DIfElseSsm.cpp index e78d187b..b7d150af 100644 --- a/xo-reader2/src/reader2/DIfElseSsm.cpp +++ b/xo-reader2/src/reader2/DIfElseSsm.cpp @@ -423,14 +423,71 @@ namespace xo { // TODO: may consider allowing if-else to terminate on other particular tokens // e.g. ')' - if ((tk.tk_type() == tokentype::tk_then) - || (tk.tk_type() == tokentype::tk_else) - || (tk.tk_type() == tokentype::tk_semicolon)) - { + switch (ifstate_) { + case ifexprstatetype::invalid: + case ifexprstatetype::N: + // impossible + assert(false); + break; + case ifexprstatetype::if_0: + // also unreachable + break; + case ifexprstatetype::if_1: + // only ok if tk-type is tk_then. + if (tk.tk_type() == tokentype::tk_then) { + // advance to if_2 -then-> if_3 this->on_parsed_expression(expr, p_psm); this->on_token(tk, p_psm); return; } + break; + case ifexprstatetype::if_2: + // illegal, not expecting expression + break; + case ifexprstatetype::if_3: + // incoming expr argument is sufficient to complete this if-else + // but may continue on else-token + if (tk.tk_type() == tokentype::tk_else) { + // advance to if_4 -else-> if_5 + this->on_parsed_expression(expr, p_psm); + this->on_token(tk, p_psm); + return; + } else if ((tk.tk_type() == tokentype::tk_semicolon) + || (tk.tk_type() == tokentype::tk_rightparen) + || (tk.tk_type() == tokentype::tk_rightbrace)) { + + this->on_parsed_expression(expr, p_psm); + p_psm->pop_ssm(); + p_psm->on_parsed_expression_with_token(if_expr_, tk); + return; + } + + break; + case ifexprstatetype::if_4: + // illegal, not expecting expression + break; + + case ifexprstatetype::if_5: + // incoming expr argument completes this if-else + // advance to if_6 + if ((tk.tk_type() == tokentype::tk_semicolon) + || (tk.tk_type() == tokentype::tk_rightparen) + || (tk.tk_type() == tokentype::tk_rightbrace)) + { + // attaches expr as else- branch + this->on_parsed_expression(expr, p_psm); + + p_psm->pop_ssm(); + p_psm->on_parsed_expression_with_token(if_expr_, tk); + return; + } + break; + + case ifexprstatetype::if_6: + // illegal, not expecting expression + break; + + } Super::on_parsed_expression_with_token(expr, tk, p_psm); } diff --git a/xo-reader2/src/reader2/DParenSsm.cpp b/xo-reader2/src/reader2/DParenSsm.cpp index ad86ddd5..b89dec52 100644 --- a/xo-reader2/src/reader2/DParenSsm.cpp +++ b/xo-reader2/src/reader2/DParenSsm.cpp @@ -364,7 +364,23 @@ namespace xo { } Super::on_parsed_expression(expr, p_psm); + } + void + DParenSsm::on_parsed_expression_with_token(obj expr, + const Token & tk, + ParserStateMachine * p_psm) + { + if (parenstate_ == parenexprstatetype::lparen_1) { + this->parenstate_ = parenexprstatetype::lparen_2; + this->expr_ = expr; + + this->on_token(tk, p_psm); + + return; + } + + Super::on_parsed_expression(expr, p_psm); } #ifdef NOT_YET diff --git a/xo-reader2/src/reader2/DProgressSsm.cpp b/xo-reader2/src/reader2/DProgressSsm.cpp index 4af449c0..72ec3e93 100644 --- a/xo-reader2/src/reader2/DProgressSsm.cpp +++ b/xo-reader2/src/reader2/DProgressSsm.cpp @@ -41,6 +41,7 @@ namespace xo { using xo::scm::Variable; using xo::scm::Apply; #endif + using xo::mm::AGCObject; using xo::print::APrintable; using xo::facet::FacetRegistry; using xo::facet::with_facet; @@ -214,7 +215,7 @@ namespace xo { if (!lhs_) { return "expr1|leftparen"; } else if (op_type_ == optype::invalid) { - return "oper|semicolon|rightparen|righbrace"; + return "oper|semicolon|rightparen|rightbrace"; } else { return "expr2|leftparen"; } @@ -293,10 +294,10 @@ namespace xo { case tokentype::tk_assign: case tokentype::tk_yields: case tokentype::tk_plus: - case tokentype::tk_minus: break; case tokentype::tk_star: + case tokentype::tk_minus: case tokentype::tk_cmpeq: this->on_operator_token(tk, p_psm); return; @@ -1244,7 +1245,6 @@ namespace xo { case optype::op_great: case optype::op_great_equal: case optype::op_add: - case optype::op_subtract: assert(false); break; @@ -1286,6 +1286,24 @@ namespace xo { // TODO: implement binary operator expression assembly assert(false); break; + case optype::op_subtract: /* editor bait: op_minus */ + { + auto pm_obj = (with_facet::mkobj + (&Primitives::s_sub_gco_gco_pm)); + auto fn_expr = (DConstant::make + (p_psm->expr_alloc(), pm_obj)); + + // see comment on op_multiply re need for poly impl + + TypeRef tref = TypeRef::dwim + (TypeRef::prefix_type::from_chars("_sub_gco"), + nullptr); + + return DApplyExpr::make2(p_psm->expr_alloc(), + tref, fn_expr, lhs_, rhs_); + } + + break; #ifdef NOT_YET case optype::op_assign: @@ -1308,6 +1326,7 @@ case optype::op_equal: if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { return Apply::make_cmp_eq_i64(lhs_, rhs_); } else { + this->apply_type_error(c_self_name, op_type_, lhs_, rhs_, p_psm); return nullptr; diff --git a/xo-reader2/utest/SchematikaParser.test.cpp b/xo-reader2/utest/SchematikaParser.test.cpp index 6153ecb7..2b92161a 100644 --- a/xo-reader2/utest/SchematikaParser.test.cpp +++ b/xo-reader2/utest/SchematikaParser.test.cpp @@ -274,7 +274,7 @@ namespace xo { { const auto & testname = Catch::getResultCapture().getCurrentTestName(); - constexpr bool c_debug_flag = true; + constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag), xtag("test", testname)); ParserFixture fixture(testname, c_debug_flag); @@ -809,7 +809,7 @@ namespace xo { const auto & testname = Catch::getResultCapture().getCurrentTestName(); - constexpr bool c_debug_flag = true; + constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag), xtag("test", testname)); ParserFixture fixture(testname, c_debug_flag); @@ -861,6 +861,64 @@ namespace xo { log && fixture.log_memory_layout(&log); } + TEST_CASE("SchematikaParser-batch-def2", "[reader2][SchematikaParser]") + { + // top-level recursive function definition + + const auto & testname = Catch::getResultCapture().getCurrentTestName(); + + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag), xtag("test", testname)); + + ParserFixture fixture(testname, c_debug_flag); + auto & parser = *(fixture.parser_); + + parser.begin_interactive_session(); + + /** Walkthrough parsing input equivalent to: + * + * def fact = lambda (n) { if (n == 0) then 1 else n * fact(n - 1) }; + * ^ ^ ^ ^ ^^^ ^ ^ ^^ ^ ^^ ^ ^ ^ ^ ^ ^ ^^ ^ ^^ ^^ + * 0 1 2 3 4|6 7 8 9| b c| e f g h i j k| m n| p| + * 5 a d l o q + **/ + + std::vector tk_v{ + /* [ 0] */ Token::def_token(), + + /* [ 1] */ Token::symbol_token("fact"), + /* [ 2] */ Token::singleassign_token(), + /* [ 3] */ Token::lambda_token(), + /* [ 4] */ Token::leftparen_token(), + /* [ 5] */ Token::symbol_token("n"), + /* [ 6] */ Token::rightparen_token(), + /* [ 7] */ Token::leftbrace_token(), + /* [ 8] */ Token::if_token(), + /* [ 9] */ Token::leftparen_token(), + /* [ a] */ Token::symbol_token("n"), + /* [ b] */ Token::cmpeq_token(), + /* [ c] */ Token::i64_token("0"), + /* [ d] */ Token::rightparen_token(), + /* [ e] */ Token::then_token(), + /* [ f] */ Token::i64_token("1"), + /* [ g] */ Token::else_token(), + /* [ h] */ Token::symbol_token("n"), + /* [ i] */ Token::star_token(), + /* [ j] */ Token::symbol_token("fact"), + /* [ k] */ Token::leftparen_token(), + /* [ l] */ Token::symbol_token("n"), + /* [ m] */ Token::minus_token(), + /* [ n] */ Token::i64_token("1"), + /* [ o] */ Token::rightparen_token(), + /* [ p] */ Token::rightbrace_token(), + /* [ q] */ Token::semicolon_token(), + }; + + utest_tokenizer_loop(&parser, tk_v, c_debug_flag); + + log && fixture.log_memory_layout(&log); + } + } /*namespace ut*/ } /*namespace xo*/