From d3066ef88d3d9897fa2fc72290dae93fbf37e3c6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 19 Jan 2026 00:38:43 -0500 Subject: [PATCH] xo-reader2: + DDefineSsm + utest --- xo-reader2/CMakeLists.txt | 2 +- .../idl/ISyntaxStateMachine_DDefineSsm.json5 | 13 + xo-reader2/include/xo/reader2/DDefineSsm.hpp | 97 +++++ .../include/xo/reader2/SchematikaParser.hpp | 2 +- .../ssm/ISyntaxStateMachine_DDefineSsm.hpp | 66 +++ .../include/xo/reader2/syntaxstatetype.hpp | 3 + xo-reader2/src/reader2/CMakeLists.txt | 2 + xo-reader2/src/reader2/DDefineSsm.cpp | 395 ++++++++++++++++++ .../ISyntaxStateMachine_DDefineSsm.cpp | 50 +++ xo-reader2/src/reader2/SchematikaParser.cpp | 2 +- xo-reader2/utest/CMakeLists.txt | 11 + xo-reader2/utest/SchematikaParser.test.cpp | 95 +++++ xo-reader2/utest/reader2_utest_main.cpp | 27 ++ 13 files changed, 762 insertions(+), 3 deletions(-) create mode 100644 xo-reader2/idl/ISyntaxStateMachine_DDefineSsm.json5 create mode 100644 xo-reader2/include/xo/reader2/DDefineSsm.hpp create mode 100644 xo-reader2/include/xo/reader2/ssm/ISyntaxStateMachine_DDefineSsm.hpp create mode 100644 xo-reader2/src/reader2/DDefineSsm.cpp create mode 100644 xo-reader2/src/reader2/ISyntaxStateMachine_DDefineSsm.cpp create mode 100644 xo-reader2/utest/CMakeLists.txt create mode 100644 xo-reader2/utest/SchematikaParser.test.cpp create mode 100644 xo-reader2/utest/reader2_utest_main.cpp diff --git a/xo-reader2/CMakeLists.txt b/xo-reader2/CMakeLists.txt index 30732417..1f3a4520 100644 --- a/xo-reader2/CMakeLists.txt +++ b/xo-reader2/CMakeLists.txt @@ -20,7 +20,7 @@ add_definitions(${PROJECT_CXX_FLAGS}) # ---------------------------------------------------------------- # output targets -#add_subdirectory(utest) +add_subdirectory(utest) # note: manual target; generated code committed to git xo_add_genfacet( diff --git a/xo-reader2/idl/ISyntaxStateMachine_DDefineSsm.json5 b/xo-reader2/idl/ISyntaxStateMachine_DDefineSsm.json5 new file mode 100644 index 00000000..11d2c5f7 --- /dev/null +++ b/xo-reader2/idl/ISyntaxStateMachine_DDefineSsm.json5 @@ -0,0 +1,13 @@ +{ + mode: "implementation", + includes: [ "\"SyntaxStateMachine.hpp\"", + "\"ssm/ISyntaxStateMachine_Xfer.hpp\"" ], + local_types: [ ], + namespace1: "xo", + namespace2: "scm", + facet_idl: "idl/SyntaxStateMachine.json5", + brief: "provide ASyntaxStateMachine interface for DDefineSsm", + using_doxygen: true, + repr: "DDefineSsm", + doc: [ "implement ASyntaxStateMachine for DDefineSsm" ], +} diff --git a/xo-reader2/include/xo/reader2/DDefineSsm.hpp b/xo-reader2/include/xo/reader2/DDefineSsm.hpp new file mode 100644 index 00000000..10ec3097 --- /dev/null +++ b/xo-reader2/include/xo/reader2/DDefineSsm.hpp @@ -0,0 +1,97 @@ +/** @file DDefineSsm.hpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#include "ParserStateMachine.hpp" +#include "SyntaxStateMachine.hpp" +#include "syntaxstatetype.hpp" +#include + +namespace xo { + namespace scm { + /** + * @pre + * + * def foo : f64 = 1 ; + * ^ ^ ^ ^ ^ ^ ^ ^ + * | | | | | | | (done) + * | | | | | | def_6:expect_rhs_expression:expr_progress + * | | | | | def_5:expect_rhs_expression + * | | | | def_4 + * | | | def_3:expect_type + * | | def_2 + * | def_1:expect_symbol + * def_0 + * expect_toplevel_expression_sequence + * + * def_0 --on_def_token()--> def_1 + * def_1 --on_symbol()--> def_2 + * def_2 --on_colon_token()--> def_3 + * --on_singleassign_token()--> def_5 + * def_3 --on_typedescr()--> def_4 + * def_4 --on_singleassign_token()--> def_5 + * def_5 --on_expr()--> def_6 + * def_6 --on_semicolon_token()--> (done) + * + * def_1:expect_symbol: got 'def' keyword, symbol to follow + * def_1: got symbol name + * def_3:expect_symbol got (optional) colon, type name to follow + * def_4: got symbol type + * def_6:expect_rhs_expression got (optional) equal sign, value to follow + * (done): definition complete, pop exprstate from stack + * + * @endpre + **/ + enum class defexprstatetype { + invalid = -1, + + def_0, + def_1, + def_2, + def_3, + def_4, + def_5, + def_6, + + n_defexprstatetype, + }; + + extern const char * defexprstatetype_descr(defexprstatetype x); + + std::ostream & + operator<<(std::ostream & os, defexprstatetype x); + + /** @class DDefineSsm + * @brief state machine for parsing a define expression + **/ + class DDefineSsm { + public: + + public: + /** @defgroup scm-define-ssm-facet syntaxstatemachine facet methods **/ + ///@{ + + /** identifies the ssm implemented here **/ + syntaxstatetype ssm_type() const noexcept; + + /** text describing expected/allowed input to this ssm in current state. + * Intended to drive error mesages + **/ + std::string_view get_expect_str() const noexcept; + + /** update state for this syntax on incoming token @p tk, + * overall parser state in @p p_psm + **/ + void on_if_token(const Token & tk, ParserStateMachine * p_psm); + + ///@} + + private: + /** identify define-expression state **/ + defexprstatetype defstate_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + +/* end DDefineSsm.hpp */ diff --git a/xo-reader2/include/xo/reader2/SchematikaParser.hpp b/xo-reader2/include/xo/reader2/SchematikaParser.hpp index 7d5f36d2..b0f78bff 100644 --- a/xo-reader2/include/xo/reader2/SchematikaParser.hpp +++ b/xo-reader2/include/xo/reader2/SchematikaParser.hpp @@ -198,7 +198,7 @@ namespace xo { * @return parsed expression, if @p tk completes an expression. * otherwise nullptr **/ - const ParserResult & include_token(const token_type & tk); + const ParserResult & on_token(const token_type & tk); /** reset parsed result expression; use using return value from * @ref include_token. Complicating api here to avoid copying ParserResult diff --git a/xo-reader2/include/xo/reader2/ssm/ISyntaxStateMachine_DDefineSsm.hpp b/xo-reader2/include/xo/reader2/ssm/ISyntaxStateMachine_DDefineSsm.hpp new file mode 100644 index 00000000..adb39c48 --- /dev/null +++ b/xo-reader2/include/xo/reader2/ssm/ISyntaxStateMachine_DDefineSsm.hpp @@ -0,0 +1,66 @@ +/** @file ISyntaxStateMachine_DDefineSsm.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/Users/roland/proj/xo-umbrella2/xo-facet/codegen/genfacet] + * arguments: + * --input [idl/ISyntaxStateMachine_DDefineSsm.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_repr.hpp.j2] + * 3. idl for facet methods + * [idl/ISyntaxStateMachine_DDefineSsm.json5] + **/ + +#pragma once + +#include "SyntaxStateMachine.hpp" +#include "SyntaxStateMachine.hpp" +#include "ssm/ISyntaxStateMachine_Xfer.hpp" +#include "DDefineSsm.hpp" + +namespace xo { namespace scm { class ISyntaxStateMachine_DDefineSsm; } } + +namespace xo { + namespace facet { + template <> + struct FacetImplementation + { + using ImplType = xo::scm::ISyntaxStateMachine_Xfer + ; + }; + } +} + +namespace xo { + namespace scm { + /** @class ISyntaxStateMachine_DDefineSsm + **/ + class ISyntaxStateMachine_DDefineSsm { + public: + /** @defgroup scm-syntaxstatemachine-ddefinessm-type-traits **/ + ///@{ + using Copaque = xo::scm::ASyntaxStateMachine::Copaque; + using Opaque = xo::scm::ASyntaxStateMachine::Opaque; + ///@} + /** @defgroup scm-syntaxstatemachine-ddefinessm-methods **/ + ///@{ + // const methods + /** identify a type of syntax state machine **/ + static syntaxstatetype ssm_type(const DDefineSsm & self) noexcept; + /** text describing expected/allowed input to this ssm in current state **/ + static std::string_view get_expect_str(const DDefineSsm & self) noexcept; + + // non-const methods + /** update state machine for incoming define-keyworkd-token @p tk **/ + static void on_def_token(DDefineSsm & self, const Token & tk, ParserStateMachine * ps_psm); + /** update state machine for incoming if-keyword-token @p tk **/ + static void on_if_token(DDefineSsm & self, const Token & tk, ParserStateMachine * p_psm); + ///@} + }; + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end */ \ No newline at end of file diff --git a/xo-reader2/include/xo/reader2/syntaxstatetype.hpp b/xo-reader2/include/xo/reader2/syntaxstatetype.hpp index 913cd118..ae02cad4 100644 --- a/xo-reader2/include/xo/reader2/syntaxstatetype.hpp +++ b/xo-reader2/include/xo/reader2/syntaxstatetype.hpp @@ -21,6 +21,9 @@ namespace xo { /** toplevel of some translation unit. See @ref DExprSeqState **/ expect_toplevel_expression_sequence, + /** handle define-expression. See @ref DDefineSsm **/ + defexpr, + /** comes lasts, counts number of valid enums **/ N }; diff --git a/xo-reader2/src/reader2/CMakeLists.txt b/xo-reader2/src/reader2/CMakeLists.txt index f3eaace2..ff8eb418 100644 --- a/xo-reader2/src/reader2/CMakeLists.txt +++ b/xo-reader2/src/reader2/CMakeLists.txt @@ -14,6 +14,8 @@ set(SELF_SRCS DExprSeqState.cpp ISyntaxStateMachine_DExprSeqState.cpp + DDefineSsm.cpp + reader2_register_facets.cpp reader2_register_types.cpp ) diff --git a/xo-reader2/src/reader2/DDefineSsm.cpp b/xo-reader2/src/reader2/DDefineSsm.cpp new file mode 100644 index 00000000..609ec0db --- /dev/null +++ b/xo-reader2/src/reader2/DDefineSsm.cpp @@ -0,0 +1,395 @@ +/** @file DDefineSsm.cpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#include "DDefineSsm.hpp" +#ifdef NOT_YET +#include "parserstatemachine.hpp" +#include "expect_symbol_xs.hpp" +#include "expect_expr_xs.hpp" +#include "expect_type_xs.hpp" +#include "pretty_expression.hpp" +#endif + +namespace xo { + namespace scm { + // ----- defexprstatetype ----- + + const char * + defexprstatetype_descr(defexprstatetype x) { + switch (x) { + case defexprstatetype::invalid: return "invalid"; + case defexprstatetype::def_0: return "def_0"; + case defexprstatetype::def_1: return "def_1"; + case defexprstatetype::def_2: return "def_2"; + case defexprstatetype::def_3: return "def_3"; + case defexprstatetype::def_4: return "def_4"; + case defexprstatetype::def_5: return "def_5"; + case defexprstatetype::def_6: return "def_6"; + case defexprstatetype::n_defexprstatetype: break; + } + + return "???defexprstatetype"; + } + + std::ostream & + operator<<(std::ostream & os, defexprstatetype x) { + os << defexprstatetype_descr(x); + return os; + } + + // ----- define_xs ----- + +#ifdef NOT_YET + std::unique_ptr + define_xs::make() { + return std::make_unique(define_xs(DefineExprAccess::make_empty())); + } + + void + define_xs::start(parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + p_psm->push_exprstate(define_xs::make()); + p_psm->top_exprstate().on_def_token(token_type::def(), p_psm); + } + + define_xs::define_xs(rp def_expr) + : exprstate(exprstatetype::defexpr), + defxs_type_{defexprstatetype::def_0}, + def_expr_{std::move(def_expr)} + {} + + // const char * + // define_xs::get_expect_str() const { ... } + + void + define_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("defxs_type", defxs_type_)); + + if (this->defxs_type_ == defexprstatetype::def_5) { + /* have all the ingredients to create an expression + * representing a definition + * + * 1. if ir_type is a symbol, interpret as variable name. + * Need to be able to locate variable by type + * 2. if ir_type is an expression, adopt as rhs + */ + rp rhs_value = expr.promote(); + + if (this->cvt_expr_) { + this->cvt_expr_->assign_arg(rhs_value); + } else { + /* note: establishes .def_expr_ valuetype */ + this->def_expr_->assign_rhs(rhs_value); + } + + rp def_expr = this->def_expr_; + + this->defxs_type_ = defexprstatetype::def_6; + return; + } + + constexpr const char * c_self_name = "define_xs::on_expr"; + const char * exp = get_expect_str(); + + this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); + } + + void + define_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("defxs_type", defxs_type_)); + + this->on_expr(expr, p_psm); + /* semicolon is allowed to terminate def expr */ + this->on_semicolon_token(token_type::semicolon(), p_psm); + } + + void + define_xs::on_symbol(const std::string & symbol_name, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("defxs_type", defxs_type_), xtag("env_stack_size", p_psm->env_stack_size())); + + if (this->defxs_type_ == defexprstatetype::def_1) { + this->defxs_type_ = defexprstatetype::def_2; + this->def_expr_->assign_lhs_name(symbol_name); + + // if this is a genuine top-level define (i.e. nesting level = 0), + // then we need to upsert so we can refer to rhs later. + // + // In other contexts (e.g. body-of-lambda) will be rewriting + // { + // def y = foo(x,x); + // bar(y,y); + // } + // into something like + // { + // (lambda (y123) bar(y123,y123))(foo(x,x)); + // } + // + // This works in the body of lambda, because we don't evaluate anything + // until lambda definition is complete. + // + // For interactive top-level defs we want to evaluate as we go, + // so need incremental bindings. + + if (p_psm->env_stack_size() == 1) { + /* remember variable binding in lexical context, + * so we can refer to it later + */ + p_psm->upsert_var(this->def_expr_->lhs_variable()); + } + + return; + } + + constexpr const char * c_self_name = "define_xs::on_symbol"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_symbol(c_self_name, symbol_name, exp, p_psm); + } + + void + define_xs::on_typedescr(TypeDescr td, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("defxs_type", defxs_type_); + + if (this->defxs_type_ == defexprstatetype::def_3) { + this->defxs_type_ = defexprstatetype::def_4; + this->cvt_expr_ = ConvertExprAccess::make(td /*dest_type*/, + nullptr /*source_expr*/); + /* note: establishes .def_expr_ valuetype */ + this->def_expr_->assign_rhs(this->cvt_expr_); + return; + } + + constexpr const char * c_self_name = "define_xs::on_symbol"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_type(c_self_name, td, exp, p_psm); + } + + void + define_xs::on_def_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log("defxs_type", defxs_type_); + + if (this->defxs_type_ == defexprstatetype::def_0) { + this->defxs_type_ = defexprstatetype::def_1; + + expect_symbol_xs::start(p_psm); + return; + } + + constexpr const char * c_self_name = "define_xs::on_def_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + define_xs::on_colon_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log("defxs_type", defxs_type_); + + if (this->defxs_type_ == defexprstatetype::def_2) { + this->defxs_type_ = defexprstatetype::def_3; + + expect_type_xs::start(p_psm); + return; + } + + constexpr const char * c_self_name = "define_xs::on_symbol"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + define_xs::on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) + { + /* def expr consumes semicolon */ + + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("defxs_type", defxs_type_); + + if (this->defxs_type_ == defexprstatetype::def_6) { + rp def_expr = this->def_expr_; + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->top_exprstate().on_expr(def_expr, p_psm); + return; + } + + constexpr const char * c_self_name = "define_xs::on_symbol"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + define_xs::on_singleassign_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log("defxs_type", defxs_type_); + + if ((this->defxs_type_ == defexprstatetype::def_2) + || (this->defxs_type_ == defexprstatetype::def_4)) + { + this->defxs_type_ = defexprstatetype::def_5; + expect_expr_xs::start(p_psm); + return; + } + + constexpr const char * c_self_name = "define_xs::on_singleassign_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + define_xs::on_rightparen_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 = "define_xs::on_rightparen"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + define_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 = "define_xs::on_i64"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + define_xs::on_f64_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 = "define_xs::on_f64"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + define_xs::print(std::ostream & os) const { + os << ""; + } + + bool + define_xs::pretty_print(const xo::print::ppindentinfo & ppii) const + { + return ppii.pps()->pretty_struct(ppii, "define_xs", + refrtag("defxs_type", defxs_type_)); + } +#endif + + //////////////////////////////////////////////////////////////// + + syntaxstatetype + DDefineSsm::ssm_type() const noexcept + { + return syntaxstatetype::defexpr; + } + + std::string_view + DDefineSsm::get_expect_str() const noexcept + { + /* + * def foo = 1 ; + * def foo : f64 = 1 ; + * ^ ^ ^ ^ ^ ^ ^ ^ + * | | | | | | | (done) + * | | | | | | def_6 + * | | | | | def_5:expect_rhs_expression + * | | | | def_4 + * | | | def_3:expect_type + * | | def_2 + * | def_1:expect_symbol + * expect_toplevel_expression_sequence + * + * note that we skip from def_2 -> def_5 if '=' instead of ':' + */ + switch (this->defstate_) { + case defexprstatetype::invalid: + case defexprstatetype::def_0: + case defexprstatetype::n_defexprstatetype: + assert(false); // impossible + return nullptr; + case defexprstatetype::def_1: + return "symbol"; + case defexprstatetype::def_2: + return "singleassign|colon"; + case defexprstatetype::def_4: + return "singleassign"; + case defexprstatetype::def_3: + return "type"; + case defexprstatetype::def_5: + return "expression"; + case defexprstatetype::def_6: + return "semicolon"; + } + + return "?expect"; + } + + void + DDefineSsm::on_if_token(const Token & tk, + ParserStateMachine * p_psm) + { + p_psm->illegal_input_on_token("DDefineSsm::on_if_token", + tk, + this->get_expect_str()); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end DDefineSsm.cpp */ diff --git a/xo-reader2/src/reader2/ISyntaxStateMachine_DDefineSsm.cpp b/xo-reader2/src/reader2/ISyntaxStateMachine_DDefineSsm.cpp new file mode 100644 index 00000000..bf17c9b5 --- /dev/null +++ b/xo-reader2/src/reader2/ISyntaxStateMachine_DDefineSsm.cpp @@ -0,0 +1,50 @@ +/** @file ISyntaxStateMachine_DDefineSsm.cpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [/Users/roland/proj/xo-umbrella2/xo-facet/codegen/genfacet] + * arguments: + * --input [idl/ISyntaxStateMachine_DDefineSsm.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/ISyntaxStateMachine_DDefineSsm.json5] +**/ + +#include "ssm/ISyntaxStateMachine_DDefineSsm.hpp" + +namespace xo { + namespace scm { + auto + ISyntaxStateMachine_DDefineSsm::ssm_type(const DDefineSsm & self) noexcept + -> syntaxstatetype + { + return self.ssm_type(); + } + + auto + ISyntaxStateMachine_DDefineSsm::get_expect_str(const DDefineSsm & self) noexcept + -> std::string_view + { + return self.get_expect_str(); + } + + auto + ISyntaxStateMachine_DDefineSsm::on_def_token(DDefineSsm & self, + const Token & tk, + ParserStateMachine * ps_psm) -> void + { + self.on_def_token(tk, ps_psm); + } + auto + ISyntaxStateMachine_DDefineSsm::on_if_token(DDefineSsm & self, + const Token & tk, + ParserStateMachine * p_psm) -> void + { + self.on_if_token(tk, p_psm); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end ISyntaxStateMachine_DDefineSsm.cpp */ diff --git a/xo-reader2/src/reader2/SchematikaParser.cpp b/xo-reader2/src/reader2/SchematikaParser.cpp index 67af915f..f9f0b482 100644 --- a/xo-reader2/src/reader2/SchematikaParser.cpp +++ b/xo-reader2/src/reader2/SchematikaParser.cpp @@ -48,7 +48,7 @@ namespace xo { } const ParserResult & - SchematikaParser::include_token(const token_type & tk) + SchematikaParser::on_token(const token_type & tk) { scope log(XO_DEBUG(debug_flag_), xtag("tk", tk)); diff --git a/xo-reader2/utest/CMakeLists.txt b/xo-reader2/utest/CMakeLists.txt new file mode 100644 index 00000000..f6de05ed --- /dev/null +++ b/xo-reader2/utest/CMakeLists.txt @@ -0,0 +1,11 @@ +# build unittest xo-reader2/utest + +set(UTEST_EXE utest.reader2) +set(UTEST_SRCS + reader2_utest_main.cpp + SchematikaParser.test.cpp +) + +xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS}) +xo_self_dependency(${UTEST_EXE} xo_reader2) +xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2) diff --git a/xo-reader2/utest/SchematikaParser.test.cpp b/xo-reader2/utest/SchematikaParser.test.cpp new file mode 100644 index 00000000..1f066f3e --- /dev/null +++ b/xo-reader2/utest/SchematikaParser.test.cpp @@ -0,0 +1,95 @@ +/** @file SchematikaParser.test.cpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#include +#include +#include + +namespace xo { + using xo::scm::SchematikaParser; + using xo::scm::ParserResult; + using xo::scm::parser_result_type; + using xo::scm::Token; + using xo::mm::ArenaConfig; + using xo::mm::AAllocator; + using xo::mm::DArena; + using xo::facet::with_facet; + + namespace ut { + TEST_CASE("SchematikaParser-ctor", "[reader2][SchematikaParser]") + { + 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, &expr_alloc, false /*debug_flag*/); + + REQUIRE(parser.debug_flag() == false); + REQUIRE(parser.is_at_toplevel() == true); + } + + TEST_CASE("SchematikaParser-begin-interactive", "[reader2][SchematikaParser]") + { + 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, &expr_alloc, false /*debug_flag*/); + + parser.begin_interactive_session(); + + // after begin_interactive_session, parser has toplevel exprseq + // but is still "at toplevel" in the sense of ready for input + REQUIRE(parser.has_incomplete_expr() == false); + } + + TEST_CASE("SchematikaParser-begin-batch", "[reader2][SchematikaParser]") + { + 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, &expr_alloc, false /*debug_flag*/); + + parser.begin_translation_unit(); + + // after begin_translation_unit, parser has toplevel exprseq + // but is still "at toplevel" in the sense of ready for input + REQUIRE(parser.has_incomplete_expr() == false); + } + + TEST_CASE("SchematikaParser-interactive-if", "[reader2][SchematikaParser]") + { + 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, &expr_alloc, false /*debug_flag*/); + + parser.begin_interactive_session(); + + parser.on_token(Token::if_token()); + + // after begin_interactive_session, parser has toplevel exprseq + // but is still "at toplevel" in the sense of ready for input + REQUIRE(parser.has_incomplete_expr() == false); + } + + } /*namespace ut*/ +} /*namespace xo*/ + +/* end SchematikaParser.test.cpp */ diff --git a/xo-reader2/utest/reader2_utest_main.cpp b/xo-reader2/utest/reader2_utest_main.cpp new file mode 100644 index 00000000..cccb0e64 --- /dev/null +++ b/xo-reader2/utest/reader2_utest_main.cpp @@ -0,0 +1,27 @@ +/** @file reader2_utest_main.cpp + * + * @author Roland Conybeare, Jan 2026 + **/ + +#include + +#define CATCH_CONFIG_RUNNER +#include "catch2/catch.hpp" + +int +main(int argc, char* argv[]) +{ + using xo::Subsystem; + + // initialize subsystems + Subsystem::initialize_all(); + + // Run Catch2's test session + int result = Catch::Session().run(argc, argv); + + // cleanup here, if any + + return result; +} + +/* end reader2_utest_main.cpp */