From cf9930a54aa5c22f738b52d5f32574affb5d4798 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 13 Feb 2026 17:24:23 -0500 Subject: [PATCH] xo-reader2 stack: handle comparison expression (x == y) --- .../utest/VirtualSchematikaMachine.test.cpp | 3 +- .../include/xo/procedure2/init_primitives.hpp | 6 + .../src/procedure2/init_primitives.cpp | 74 ++++++++- .../include/xo/reader2/SchematikaParser.hpp | 3 + xo-reader2/src/reader2/DProgressSsm.cpp | 29 +++- xo-reader2/src/reader2/SchematikaParser.cpp | 6 + xo-reader2/utest/SchematikaParser.test.cpp | 157 ++++++++++++------ xo-tokenizer2/include/xo/tokenizer2/Token.hpp | 3 + 8 files changed, 221 insertions(+), 60 deletions(-) diff --git a/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp b/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp index bd456b9d..6b4f9cf8 100644 --- a/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp +++ b/xo-interpreter2/utest/VirtualSchematikaMachine.test.cpp @@ -227,7 +227,7 @@ namespace xo { TEST_CASE("VirtualSchematikaMachine-apply2", "[interpreter2][VSM]") { - scope log(XO_DEBUG(true)); + scope log(XO_DEBUG(false)); VsmConfig cfg; VirtualSchematikaMachine vsm(cfg); @@ -269,6 +269,7 @@ namespace xo { FacetRegistry::instance().visit_pools(visitor); vsm.visit_pools(visitor); } + } /*namespace ut*/ } /*namespace xo*/ diff --git a/xo-procedure2/include/xo/procedure2/init_primitives.hpp b/xo-procedure2/include/xo/procedure2/init_primitives.hpp index 48b733df..d2d51a59 100644 --- a/xo-procedure2/include/xo/procedure2/init_primitives.hpp +++ b/xo-procedure2/include/xo/procedure2/init_primitives.hpp @@ -22,6 +22,12 @@ namespace xo { **/ static DPrimitive_gco_2_gco_gco s_mul_gco_gco_pm; + /** polymorphic equality comparison + * + * TODO: this will want to move to x-numeric/ + **/ + static DPrimitive_gco_2_gco_gco s_equal_gco_gco_pm; + #ifdef NOT_YET static Primitive_f64_1_f64 s_neg_f64_pm; diff --git a/xo-procedure2/src/procedure2/init_primitives.cpp b/xo-procedure2/src/procedure2/init_primitives.cpp index dbcb3b35..c1eceada 100644 --- a/xo-procedure2/src/procedure2/init_primitives.cpp +++ b/xo-procedure2/src/procedure2/init_primitives.cpp @@ -5,10 +5,11 @@ #include "init_primitives.hpp" #include "DPrimitive.hpp" -#include -#include -#include -#include +#include +//#include +#include +//#include +#include #include #include #include @@ -54,9 +55,6 @@ namespace xo { // 2. at that point will require polymorphic dispatch // on argument representations, analogous to dispatch // in FacetRegistry - // 3. Need concept of a 'runtime context'. - // This will need to be part of the AProcedure api - // e.g. passed to apply_nocheck typeseq x_tseq = x_gco._typeseq(); typeseq y_tseq = y_gco._typeseq(); @@ -101,6 +99,65 @@ namespace xo { return obj(); } + obj + equal_gco_gco(obj rcx, + obj x_gco, + obj y_gco) + { + using xo::reflect::typeseq; + + obj mm = rcx.allocator(); + + // PLACEHOLDER + + // TODO + // 1. move this to xo-numeric2/ when available + // 2. at that point will require polymorphic dispatch on argument representations. + // + + typeseq x_tseq = x_gco._typeseq(); + typeseq y_tseq = y_gco._typeseq(); + + // FOR NOW: just test runtime values + // + if (x_tseq == typeseq::id()) { + // i64 * .. + long x = GCObjectConversion::from_gco(mm, x_gco); + + if (y_tseq == typeseq::id()) { + // i64 == i64 + long y = GCObjectConversion::from_gco(mm, y_gco); + + return DBoolean::box(mm, x == y); + } else if (y_tseq == typeseq::id()) { + // i64 == f64 + double y = GCObjectConversion::from_gco(mm, y_gco); + + return DFloat::box(mm, static_cast(x) == y); + } + } else if (x_tseq == typeseq::id()) { + if (y_tseq == typeseq::id()) { + // f64 == i64. + double x = GCObjectConversion::from_gco(mm, x_gco); + long y = GCObjectConversion::from_gco(mm, y_gco); + + return DFloat::box(mm, x == static_cast(y)); + } else if (y_tseq == typeseq::id()) { + // f64 * f64. + double x = GCObjectConversion::from_gco(mm, x_gco); + double y = GCObjectConversion::from_gco(mm, y_gco); + + return DFloat::box(mm, x == y); + } + } + + // here: error + throw std::runtime_error(tostr("mul_gco_gco: unexpected argument types xt,yt", + xtag("x.tseq", x_tseq), + xtag("y.tseq", y_tseq))); + return obj(); + } + #ifdef NOT_YET double mul_f64_f64(double x, double y) { @@ -141,6 +198,9 @@ namespace xo { DPrimitive_gco_2_gco_gco Primitives::s_mul_gco_gco_pm("_mul", &mul_gco_gco); + DPrimitive_gco_2_gco_gco + Primitives::s_equal_gco_gco_pm("_equal", &equal_gco_gco); + #ifdef NOT_YET Primitive_f64_1_f64 Primitives::s_neg_f64_pm("_neg_d", diff --git a/xo-reader2/include/xo/reader2/SchematikaParser.hpp b/xo-reader2/include/xo/reader2/SchematikaParser.hpp index 8c3f51b1..4be59f04 100644 --- a/xo-reader2/include/xo/reader2/SchematikaParser.hpp +++ b/xo-reader2/include/xo/reader2/SchematikaParser.hpp @@ -193,6 +193,9 @@ namespace xo { /** top of parser stack **/ obj top_ssm() const; + /** current parser result. Value of last return from @ref on_token **/ + const ParserResult & result() const; + /** visit parser-owned memory pools; invoke visitor(info) for each **/ void visit_pools(const MemorySizeVisitor & visitor) const; diff --git a/xo-reader2/src/reader2/DProgressSsm.cpp b/xo-reader2/src/reader2/DProgressSsm.cpp index 20bfb998..acffc851 100644 --- a/xo-reader2/src/reader2/DProgressSsm.cpp +++ b/xo-reader2/src/reader2/DProgressSsm.cpp @@ -292,11 +292,11 @@ namespace xo { break; case tokentype::tk_star: + case tokentype::tk_cmpeq: this->on_operator_token(tk, p_psm); return; case tokentype::tk_slash: - case tokentype::tk_cmpeq: case tokentype::tk_cmpne: case tokentype::tk_type: case tokentype::tk_lambda: @@ -1177,6 +1177,10 @@ namespace xo { ); } + namespace { + // make_builtin_apply_pm2 + } + obj DProgressSsm::assemble_expr(ParserStateMachine * p_psm) { @@ -1209,7 +1213,28 @@ namespace xo { return this->lhs_; case optype::op_assign: + assert(false); + break; + case optype::op_equal: + { + auto pm_obj = (with_facet::mkobj + (&Primitives::s_equal_gco_gco_pm)); + auto fn_expr = (DConstant::make + (p_psm->expr_alloc(), pm_obj)); + + // see note on op_multiply + + TypeRef tref = TypeRef::dwim + (TypeRef::prefix_type::from_chars("_equal_gco"), + nullptr); + + return DApplyExpr::make2(p_psm->expr_alloc(), + tref, + fn_expr, lhs_, rhs_); + } + break; + case optype::op_not_equal: case optype::op_less: case optype::op_less_equal: @@ -1217,6 +1242,7 @@ namespace xo { case optype::op_great_equal: case optype::op_add: case optype::op_subtract: + assert(false); break; case optype::op_multiply: @@ -1255,6 +1281,7 @@ namespace xo { break; case optype::op_divide: // TODO: implement binary operator expression assembly + assert(false); break; #ifdef NOT_YET diff --git a/xo-reader2/src/reader2/SchematikaParser.cpp b/xo-reader2/src/reader2/SchematikaParser.cpp index c53acc16..1973bbe7 100644 --- a/xo-reader2/src/reader2/SchematikaParser.cpp +++ b/xo-reader2/src/reader2/SchematikaParser.cpp @@ -47,6 +47,12 @@ namespace xo { return psm_.top_ssm(); } + const ParserResult & + SchematikaParser::result() const + { + return psm_.result(); + } + void SchematikaParser::visit_pools(const MemorySizeVisitor & visitor) const { diff --git a/xo-reader2/utest/SchematikaParser.test.cpp b/xo-reader2/utest/SchematikaParser.test.cpp index 3bdf94cc..2c36ee96 100644 --- a/xo-reader2/utest/SchematikaParser.test.cpp +++ b/xo-reader2/utest/SchematikaParser.test.cpp @@ -411,6 +411,39 @@ namespace xo { //REQUIRE(result.error_description()); } + void + utest_tokenizer_loop(SchematikaParser * parser, std::vector & tk_v, bool debug_flag) + { + scope log(XO_DEBUG(debug_flag)); + + size_t i_tk = 0; + size_t n_tk = tk_v.size(); + for (const auto & tk : tk_v) { + INFO(tostr(xtag("i_tk", i_tk), xtag("tk", tk))); + + auto & result = parser->on_token(tk); + + log && log("after token", xtag("i_tk", i_tk), xtag("tk", tk)); + log && log(xtag("parser", parser)); + log && log(xtag("result", result)); + + if (i_tk + 1 < n_tk) { + REQUIRE(parser->has_incomplete_expr() == true); + REQUIRE(!result.is_error()); + REQUIRE(result.is_incomplete()); + } else { + /* last token in tk_v[] */ + + REQUIRE(parser->has_incomplete_expr() == false); + REQUIRE(!result.is_error()); + REQUIRE(result.is_expression()); + REQUIRE(result.result_expr()); + } + + ++i_tk; + } + } + TEST_CASE("SchematikaParser-interactive-arith", "[reader2][SchematikaParser]") { const auto & testname = Catch::getResultCapture().getCurrentTestName(); @@ -435,54 +468,17 @@ namespace xo { * **/ + std::vector tk_v{ + Token::f64_token("3.14159265"), + Token::star_token(), + Token::f64_token("0.5"), + Token::semicolon_token(), + }; + + utest_tokenizer_loop(&parser, tk_v, c_debug_flag); + + const auto & result = parser.result(); { - auto & result = parser.on_token(Token::f64_token("3.14159265")); - - log && log("after float(3.14159265) token:"); - log && log(xtag("parser", &parser)); - log && log(xtag("result", result)); - - REQUIRE(parser.has_incomplete_expr() == true); - REQUIRE(!result.is_error()); - REQUIRE(result.is_incomplete()); - } - - { - auto & result = parser.on_token(Token::star_token()); - - log && log("after star token:"); - log && log(xtag("parser", &parser)); - log && log(xtag("result", result)); - - REQUIRE(parser.has_incomplete_expr() == true); - REQUIRE(!result.is_error()); - REQUIRE(result.is_incomplete()); - } - - { - auto & result = parser.on_token(Token::f64_token("0.5")); - - log && log("after float(0.5) token:"); - log && log(xtag("parser", &parser)); - log && log(xtag("result", result)); - - REQUIRE(parser.has_incomplete_expr() == true); - REQUIRE(!result.is_error()); - REQUIRE(result.is_incomplete()); - } - - { - auto & result = parser.on_token(Token::semicolon_token()); - - log && log("after semicolon token:"); - log && log(xtag("parser", &parser)); - log && log(xtag("result", result)); - - REQUIRE(parser.has_incomplete_expr() == false); - REQUIRE(!result.is_error()); - REQUIRE(result.is_expression()); - REQUIRE(result.result_expr()); - auto expr = obj::from(result.result_expr()); REQUIRE(expr); @@ -509,10 +505,69 @@ namespace xo { REQUIRE(rhs_f64); REQUIRE(rhs_f64->value() == 0.5); } + } - //REQUIRE(result.is_error()); - //// illegal input on token - //REQUIRE(result.error_description()); + TEST_CASE("SchematikaParser-interactive-cmp", "[reader2][SchematikaParser]") + { + const auto & testname = Catch::getResultCapture().getCurrentTestName(); + + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag), xtag("test", testname)); + + ArenaConfig config; + config.name_ = "test-arena"; + config.size_ = 16 * 1024; + + DArena expr_arena = DArena::map(config); + obj expr_alloc = with_facet::mkobj(&expr_arena); + + SchematikaParser parser(config, 4096, expr_alloc, false /*debug_flag*/); + + parser.begin_interactive_session(); + + /** Walkthrough parsing input equivalent to: + * + * 312 == 312 ; + * + **/ + + std::vector tk_v{ + Token::i64_token("312"), + Token::cmpeq_token(), + Token::i64_token("312"), + Token::semicolon_token(), + }; + + utest_tokenizer_loop(&parser, tk_v, c_debug_flag); + + const auto & result = parser.result(); + { + auto expr = obj::from(result.result_expr()); + REQUIRE(expr); + + REQUIRE(expr->n_args() == 2); + + auto fn = obj::from(expr->fn()); + REQUIRE(fn); + + auto pm = obj::from(fn->value()); + REQUIRE(pm); + REQUIRE(pm->name() == "_equal"); + + auto lhs = obj::from(expr->arg(0)); + REQUIRE(lhs); + + auto lhs_i64 = obj::from(lhs->value()); + REQUIRE(lhs_i64); + REQUIRE(lhs_i64->value() == 312); + + auto rhs = obj::from(expr->arg(1)); + REQUIRE(rhs); + + auto rhs_i64 = obj::from(rhs->value()); + REQUIRE(rhs_i64); + REQUIRE(rhs_i64->value() == 312); + } } TEST_CASE("SchematikaParser-interactive-lambda", "[reader2][SchematikaParser]") @@ -1017,7 +1072,7 @@ namespace xo { TEST_CASE("SchematikaParser-interactive-apply", "[reader2][SchematikaParser]") { - constexpr bool c_debug_flag = true; + constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag)); ArenaConfig config; diff --git a/xo-tokenizer2/include/xo/tokenizer2/Token.hpp b/xo-tokenizer2/include/xo/tokenizer2/Token.hpp index ab3f0cb7..b211f967 100644 --- a/xo-tokenizer2/include/xo/tokenizer2/Token.hpp +++ b/xo-tokenizer2/include/xo/tokenizer2/Token.hpp @@ -129,6 +129,9 @@ namespace xo { /** token for @c "/" **/ static Token slash_token() { return Token(tokentype::tk_slash); } + /** token for @c "==" **/ + static Token cmpeq_token() { return Token(tokentype::tk_cmpeq); } + /** token representing keyword @c type **/ static Token type() { return Token(tokentype::tk_type); } /** token representing keyword @c def **/