From d65eb541026a45f27540c721f22a709d740d5269 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 10 Feb 2026 15:14:40 -0500 Subject: [PATCH] xo-reader2 stack: parenthesized expressions [WIP] --- CMakeLists.txt | 26 ++ idl/IPrintable_DParenSsm.json5 | 13 + idl/ISyntaxStateMachine_DParenSsm.json5 | 13 + include/xo/reader2/DDefineSsm.hpp | 11 +- include/xo/reader2/DExpectExprSsm.hpp | 6 + include/xo/reader2/DExprSeqState.hpp | 5 + include/xo/reader2/DLambdaSsm.hpp | 6 + include/xo/reader2/DParenSsm.hpp | 127 ++++++ include/xo/reader2/LambdaSsm.hpp | 10 + include/xo/reader2/ParenSsm.hpp | 12 + .../xo/reader2/ssm/IPrintable_DParenSsm.hpp | 62 +++ .../ssm/ISyntaxStateMachine_DParenSsm.hpp | 77 ++++ include/xo/reader2/syntaxstatetype.hpp | 3 + src/reader2/CMakeLists.txt | 4 + src/reader2/DDefineSsm.cpp | 1 - src/reader2/DExpectExprSsm.cpp | 26 +- src/reader2/DExprSeqState.cpp | 30 +- src/reader2/DLambdaSsm.cpp | 40 +- src/reader2/DParenSsm.cpp | 382 ++++++++++++++++++ src/reader2/IPrintable_DParenSsm.cpp | 28 ++ src/reader2/ISyntaxStateMachine_DParenSsm.cpp | 69 ++++ src/reader2/reader2_register_facets.cpp | 6 + src/reader2/syntaxstatetype.cpp | 2 + utest/SchematikaParser.test.cpp | 171 +++++++- 24 files changed, 1095 insertions(+), 35 deletions(-) create mode 100644 idl/IPrintable_DParenSsm.json5 create mode 100644 idl/ISyntaxStateMachine_DParenSsm.json5 create mode 100644 include/xo/reader2/DParenSsm.hpp create mode 100644 include/xo/reader2/LambdaSsm.hpp create mode 100644 include/xo/reader2/ParenSsm.hpp create mode 100644 include/xo/reader2/ssm/IPrintable_DParenSsm.hpp create mode 100644 include/xo/reader2/ssm/ISyntaxStateMachine_DParenSsm.hpp create mode 100644 src/reader2/DParenSsm.cpp create mode 100644 src/reader2/IPrintable_DParenSsm.cpp create mode 100644 src/reader2/ISyntaxStateMachine_DParenSsm.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c936619f..d1e807e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,32 @@ xo_add_genfacetimpl( # ---------------------------------------------------------------- +# note: manual target; generated code committed to git +xo_add_genfacetimpl( + TARGET xo-reader2-facetimpl-syntaxstatemachine-parenssm + FACET_PKG xo_reader2 + FACET SyntaxStateMachine + REPR ParenSsm + INPUT idl/ISyntaxStateMachine_DParenSsm.json5 + OUTPUT_HPP_DIR include/xo/reader2 + OUTPUT_IMPL_SUBDIR ssm + OUTPUT_CPP_DIR src/reader2 +) + +# note: manual target; generated code committed to git +xo_add_genfacetimpl( + TARGET xo-reader2-facetimpl-printable-parenssm + FACET_PKG xo_printable2 + FACET Printable + REPR ParenSsm + INPUT idl/IPrintable_DParenSsm.json5 + OUTPUT_HPP_DIR include/xo/reader2 + OUTPUT_IMPL_SUBDIR ssm + OUTPUT_CPP_DIR src/reader2 +) + +# ---------------------------------------------------------------- + # note: manual target; generated code committed to git xo_add_genfacetimpl( TARGET xo-reader2-facetimpl-syntaxstatemachine-expectformalarglistssm diff --git a/idl/IPrintable_DParenSsm.json5 b/idl/IPrintable_DParenSsm.json5 new file mode 100644 index 00000000..495729b5 --- /dev/null +++ b/idl/IPrintable_DParenSsm.json5 @@ -0,0 +1,13 @@ +{ + mode: "implementation", + includes: [ "", + "" ], + local_types: [], + namespace1: "xo", + namespace2: "scm", + facet_idl: "idl/Printable.json5", + brief: "provide APrintable interface for DParenSsm", + using_doxygen: true, + repr: "DParenSsm", + doc: [ "implement APrintable for DParenSsm" ], +} diff --git a/idl/ISyntaxStateMachine_DParenSsm.json5 b/idl/ISyntaxStateMachine_DParenSsm.json5 new file mode 100644 index 00000000..521e7ba8 --- /dev/null +++ b/idl/ISyntaxStateMachine_DParenSsm.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 DParenSsm", + using_doxygen: true, + repr: "DParenSsm", + doc: [ "implement ASyntaxStateMachine for DParenSsm" ], +} diff --git a/include/xo/reader2/DDefineSsm.hpp b/include/xo/reader2/DDefineSsm.hpp index d675090e..f71477dd 100644 --- a/include/xo/reader2/DDefineSsm.hpp +++ b/include/xo/reader2/DDefineSsm.hpp @@ -6,7 +6,6 @@ #pragma once #include "DSyntaxStateMachine.hpp" -//#include "SyntaxStateMachine.hpp" #include "syntaxstatetype.hpp" #include #include @@ -36,7 +35,7 @@ namespace xo { * --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_5 --on_parsed_expression()--> def_6 * def_6 --on_semicolon_token()--> (done) * * def_1:expect_symbol: got 'def' keyword, symbol to follow @@ -104,9 +103,10 @@ namespace xo { /** @defgroup scm-definessm-access-methods **/ ///@{ + /** identify this nested state machine **/ static const char * ssm_classname() { return "DDefineSsm"; } - /** identify this nested state machine **/ + /** internal state **/ defexprstatetype defstate() const noexcept { return defstate_; } ///@} @@ -193,6 +193,9 @@ namespace xo { ///@} private: + /** @defgroup scm-definessm-member-vars **/ + ///@{ + /** identify define-expression state **/ defexprstatetype defstate_; @@ -200,6 +203,8 @@ namespace xo { * This will eventually be the output of this ssm **/ obj def_expr_; + + ///@} }; } /*namespace scm*/ } /*namespace xo*/ diff --git a/include/xo/reader2/DExpectExprSsm.hpp b/include/xo/reader2/DExpectExprSsm.hpp index 9b4bbd98..d6558c1e 100644 --- a/include/xo/reader2/DExpectExprSsm.hpp +++ b/include/xo/reader2/DExpectExprSsm.hpp @@ -76,6 +76,12 @@ namespace xo { void on_string_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 + **/ + void on_lambda_token(const Token & tk, + ParserStateMachine * p_psm); + ///@} /** @defgroup scm-expectexpr-ssm-facet syntaxstatemachine facet methods **/ ///@{ diff --git a/include/xo/reader2/DExprSeqState.hpp b/include/xo/reader2/DExprSeqState.hpp index 72e26203..83448b5e 100644 --- a/include/xo/reader2/DExprSeqState.hpp +++ b/include/xo/reader2/DExprSeqState.hpp @@ -113,6 +113,11 @@ namespace xo { **/ void on_bool_token(const Token & tk, ParserStateMachine * p_psm); + /** update state for this syntax on incoming leftparen token @p tk, + * overall parser state in @p p_psm + **/ + void on_leftparen_token(const Token & tk, ParserStateMachine * p_psm); + /** update state for this syntax on parsed expression @p expr * from nested ssm. * overall parser state in @p p_psm diff --git a/include/xo/reader2/DLambdaSsm.hpp b/include/xo/reader2/DLambdaSsm.hpp index d494639c..5ecbecd2 100644 --- a/include/xo/reader2/DLambdaSsm.hpp +++ b/include/xo/reader2/DLambdaSsm.hpp @@ -96,6 +96,12 @@ namespace xo { void on_yields_token(const Token & tk, ParserStateMachine * p_psm); + /** update ssm on leftbrace token @p tk, + * with overall parser state in @p p_psm + **/ + void on_leftbrace_token(const Token & tk, + ParserStateMachine * p_psm); + ///@} /** @defgroup scm-lambdassm-syntaxstatemachine-facet **/ ///@{ diff --git a/include/xo/reader2/DParenSsm.hpp b/include/xo/reader2/DParenSsm.hpp new file mode 100644 index 00000000..2ed7f58c --- /dev/null +++ b/include/xo/reader2/DParenSsm.hpp @@ -0,0 +1,127 @@ +/** @file DParenSsm.hpp + * + * @author Roland Conybeare, Feb 2026 + **/ + +#pragma once + +#include "DSyntaxStateMachine.hpp" +#include "syntaxstatetype.hpp" +#include +#include + +namespace xo { + namespace scm { + + /** + * @pre + * + * ( x * x ) + * ^ ^ ^ ^ + * | | | (done) + * | | | + * | | lparen_2 + * | lparen_1:expect expr + * lparen_0 + * + * lparen_0 --on_leftparen_token()--> lparen_1 + * lparen_1 --on_parsed_expression()--> lparen_2 + * lparen_2 --on_rightparen_token()--> (done) + * + * @endpre + **/ + + enum class parenexprstatetype { + invalid = -1, + + lparen_0, + lparen_1, + lparen_2, + + N, + }; + + extern const char * parenexprstatetype_descr(parenexprstatetype x); + + std::ostream & + operator<<(std::ostream & os, parenexprstatetype x); + + class DParenSsm : public DSyntaxStateMachine { + public: + using Super = DSyntaxStateMachine; + using TypeDescr = xo::reflect::TypeDescr; + using AAllocator = xo::mm::AAllocator; + using DArena = xo::mm::DArena; + using ppindentinfo = xo::print::ppindentinfo; + + public: + /** @defgroup scm-parenssm-ctors **/ + ///@{ + + DParenSsm(); + + /** create instance using memory from @p parser_mm + **/ + static DParenSsm * make(DArena & parser_mm); + + /** push DParenSsm instance onto @p p_psm stack **/ + static void start(ParserStateMachine * p_psm); + + ///@} + /** @defgroup scm-parenssm-access-methods **/ + ///@{ + + /** identify this nested state machine **/ + static const char * ssm_classname() { return "DParenSsm"; } + + /** internal state **/ + parenexprstatetype parenstate() const noexcept { return parenstate_; } + + ///@} + /** @defgroup scm-parenssm-admin-methods admin methods **/ + ///@{ + + /** update ssm state for incoming leftparen token @p tk, + * with overall parser state in @p p_psm + **/ + void on_leftparen_token(const Token & tk, + ParserStateMachine * p_psm); + + ///@} + /** @defgroup scm-parenssm-ssm-facet syntaxstatemachine facet methods **/ + ///@{ + + /** identifies this ssm **/ + syntaxstatetype ssm_type() const noexcept; + + /** text describing expected/allowed input to this ssm in current state. **/ + std::string_view get_expect_str() const noexcept; + + /** update ssm for token @p tk, with overall state in @p p_psm **/ + void on_token(const Token & tk, + ParserStateMachine * p_psm); + + ///@} + /** @defgroup scm-parenssm-printable-facet printable facet methods **/ + ///@{ + + bool pretty(const ppindentinfo & ppii) const; + + ///@} + + private: + /** @defgroup scm-parenssm-member-vars **/ + ///@{ + + /** identify paren-expression state **/ + parenexprstatetype parenstate_; + /** scaffold expression (representing parenthesized value) here **/ + obj expr_; + + ///@} + }; + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end DParenSsm.hpp */ diff --git a/include/xo/reader2/LambdaSsm.hpp b/include/xo/reader2/LambdaSsm.hpp new file mode 100644 index 00000000..49ac731b --- /dev/null +++ b/include/xo/reader2/LambdaSsm.hpp @@ -0,0 +1,10 @@ +/** @file LambdaSsm.hpp + * + * @author Roland Conybeare, Feb 2026 + **/ + +#include "DLambdaSsm.hpp" +#include "ssm/ISyntaxStateMachine_DLambdaSsm.hpp" +#include "ssm/IPrintable_DLambdaSsm.hpp" + +/* end LambdaSsm.hpp */ diff --git a/include/xo/reader2/ParenSsm.hpp b/include/xo/reader2/ParenSsm.hpp new file mode 100644 index 00000000..a9a786fd --- /dev/null +++ b/include/xo/reader2/ParenSsm.hpp @@ -0,0 +1,12 @@ +/** @file ParenSsm.hpp + * + * @author Roland Conybeare, Feb 2026 + **/ + +#pragma once + +#include "DParenSsm.hpp" +#include "ssm/ISyntaxStateMachine_DParenSsm.hpp" +#include "ssm/IPrintable_DParenSsm.hpp" + +/* end ParenSsm.hpp */ diff --git a/include/xo/reader2/ssm/IPrintable_DParenSsm.hpp b/include/xo/reader2/ssm/IPrintable_DParenSsm.hpp new file mode 100644 index 00000000..2b4b49c2 --- /dev/null +++ b/include/xo/reader2/ssm/IPrintable_DParenSsm.hpp @@ -0,0 +1,62 @@ +/** @file IPrintable_DParenSsm.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/IPrintable_DParenSsm.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_repr.hpp.j2] + * 3. idl for facet methods + * [idl/IPrintable_DParenSsm.json5] + **/ + +#pragma once + +#include "Printable.hpp" +#include +#include +#include "DParenSsm.hpp" + +namespace xo { namespace scm { class IPrintable_DParenSsm; } } + +namespace xo { + namespace facet { + template <> + struct FacetImplementation + { + using ImplType = xo::print::IPrintable_Xfer + ; + }; + } +} + +namespace xo { + namespace scm { + /** @class IPrintable_DParenSsm + **/ + class IPrintable_DParenSsm { + public: + /** @defgroup scm-printable-dparenssm-type-traits **/ + ///@{ + using ppindentinfo = xo::print::APrintable::ppindentinfo; + using Copaque = xo::print::APrintable::Copaque; + using Opaque = xo::print::APrintable::Opaque; + ///@} + /** @defgroup scm-printable-dparenssm-methods **/ + ///@{ + // const methods + /** Pretty-printing support for this object. +See [xo-indentlog/xo/indentlog/pretty.hpp] **/ + static bool pretty(const DParenSsm & self, const ppindentinfo & ppii); + + // non-const methods + ///@} + }; + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end */ \ No newline at end of file diff --git a/include/xo/reader2/ssm/ISyntaxStateMachine_DParenSsm.hpp b/include/xo/reader2/ssm/ISyntaxStateMachine_DParenSsm.hpp new file mode 100644 index 00000000..33be5e16 --- /dev/null +++ b/include/xo/reader2/ssm/ISyntaxStateMachine_DParenSsm.hpp @@ -0,0 +1,77 @@ +/** @file ISyntaxStateMachine_DParenSsm.hpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/ISyntaxStateMachine_DParenSsm.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_repr.hpp.j2] + * 3. idl for facet methods + * [idl/ISyntaxStateMachine_DParenSsm.json5] + **/ + +#pragma once + +#include "SyntaxStateMachine.hpp" +#include "SyntaxStateMachine.hpp" +#include "ssm/ISyntaxStateMachine_Xfer.hpp" +#include "DParenSsm.hpp" + +namespace xo { namespace scm { class ISyntaxStateMachine_DParenSsm; } } + +namespace xo { + namespace facet { + template <> + struct FacetImplementation + { + using ImplType = xo::scm::ISyntaxStateMachine_Xfer + ; + }; + } +} + +namespace xo { + namespace scm { + /** @class ISyntaxStateMachine_DParenSsm + **/ + class ISyntaxStateMachine_DParenSsm { + public: + /** @defgroup scm-syntaxstatemachine-dparenssm-type-traits **/ + ///@{ + using TypeDescr = xo::scm::ASyntaxStateMachine::TypeDescr; + using Copaque = xo::scm::ASyntaxStateMachine::Copaque; + using Opaque = xo::scm::ASyntaxStateMachine::Opaque; + ///@} + /** @defgroup scm-syntaxstatemachine-dparenssm-methods **/ + ///@{ + // const methods + /** identify a type of syntax state machine **/ + static syntaxstatetype ssm_type(const DParenSsm & self) noexcept; + /** text describing expected/allowed input to this ssm in current state **/ + static std::string_view get_expect_str(const DParenSsm & self) noexcept; + + // non-const methods + /** operate state machine for incoming token @p tk **/ + static void on_token(DParenSsm & self, const Token & tk, ParserStateMachine * p_psm); + /** update stat machine for incoming parsed symbol @p sym **/ + static void on_parsed_symbol(DParenSsm & self, std::string_view sym, ParserStateMachine * p_psm); + /** operate state machine for incoming type description @p td **/ + static void on_parsed_typedescr(DParenSsm & self, TypeDescr td, ParserStateMachine * p_psm); + /** operate state machine for formal emitted by nested ssm **/ + static void on_parsed_formal(DParenSsm & self, const DUniqueString * param_name, TypeDescr param_type, ParserStateMachine * p_psm); + /** consume formal arglist emitted by nested ssm **/ + static void on_parsed_formal_arglist(DParenSsm & self, DArray * arglist, ParserStateMachine * p_psm); + /** update state machine for incoming parsed expression @p expr **/ + static void on_parsed_expression(DParenSsm & self, obj expr, ParserStateMachine * p_psm); + /** update state machine @p p_psm for incoming parsed expression @p expr followed by token @p tk **/ + static void on_parsed_expression_with_token(DParenSsm & self, obj expr, const Token & tk, ParserStateMachine * p_psm); + ///@} + }; + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end */ \ No newline at end of file diff --git a/include/xo/reader2/syntaxstatetype.hpp b/include/xo/reader2/syntaxstatetype.hpp index a5b4b563..3031a444 100644 --- a/include/xo/reader2/syntaxstatetype.hpp +++ b/include/xo/reader2/syntaxstatetype.hpp @@ -33,6 +33,9 @@ namespace xo { /** rhs expression. state exists to achieve 1-token lookahead **/ progress, + /** expression enclosed in parentheses. See @ref DParenSsm **/ + paren, + /** toplevel of some translation unit. See @ref DExprSeqState **/ expect_toplevel_expression_sequence, diff --git a/src/reader2/CMakeLists.txt b/src/reader2/CMakeLists.txt index d883fa82..4a813900 100644 --- a/src/reader2/CMakeLists.txt +++ b/src/reader2/CMakeLists.txt @@ -37,6 +37,10 @@ set(SELF_SRCS ISyntaxStateMachine_DLambdaSsm.cpp IPrintable_DLambdaSsm.cpp + DParenSsm.cpp + ISyntaxStateMachine_DParenSsm.cpp + IPrintable_DParenSsm.cpp + DExpectFormalArglistSsm.cpp ISyntaxStateMachine_DExpectFormalArglistSsm.cpp IPrintable_DExpectFormalArglistSsm.cpp diff --git a/src/reader2/DDefineSsm.cpp b/src/reader2/DDefineSsm.cpp index 26a68224..2d2ecb0b 100644 --- a/src/reader2/DDefineSsm.cpp +++ b/src/reader2/DDefineSsm.cpp @@ -555,7 +555,6 @@ namespace xo { case tokentype::tk_end: case tokentype::N: break; - return; } Super::on_token(tk, p_psm); diff --git a/src/reader2/DExpectExprSsm.cpp b/src/reader2/DExpectExprSsm.cpp index c3cb4b31..c763e2a4 100644 --- a/src/reader2/DExpectExprSsm.cpp +++ b/src/reader2/DExpectExprSsm.cpp @@ -8,6 +8,7 @@ #include "SyntaxStateMachine.hpp" #include "ssm/ISyntaxStateMachine_DProgressSsm.hpp" #include "DSequenceSsm.hpp" +#include "LambdaSsm.hpp" #include "syntaxstatetype.hpp" #include #include @@ -138,6 +139,10 @@ namespace xo { this->on_bool_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: @@ -165,7 +170,6 @@ namespace xo { case tokentype::tk_cmpeq: case tokentype::tk_cmpne: case tokentype::tk_type: - case tokentype::tk_lambda: case tokentype::tk_then: case tokentype::tk_else: case tokentype::tk_let: @@ -379,6 +383,15 @@ namespace xo { p_psm); } + 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) @@ -412,17 +425,6 @@ namespace xo { } #ifdef NOT_YET - void - expect_expr_xs::on_lambda_token(const token_type & /*tk*/, - parserstatemachine * p_psm) - { - scope log(XO_DEBUG(p_psm->debug_flag())); - - //constexpr const char * self_name = "exprstate::on_leftparen"; - - lambda_xs::start(p_psm); - } - void expect_expr_xs::on_if_token(const token_type & /*tk*/, parserstatemachine * p_psm) diff --git a/src/reader2/DExprSeqState.cpp b/src/reader2/DExprSeqState.cpp index 08094655..82eb2f44 100644 --- a/src/reader2/DExprSeqState.cpp +++ b/src/reader2/DExprSeqState.cpp @@ -1,5 +1,5 @@ /** @file DExprSeqState.cpp -* + * * @author Roland Conybeare, Jan 2026 **/ @@ -9,6 +9,8 @@ #include "DLambdaSsm.hpp" #include "DProgressSsm.hpp" #include "DIfElseSsm.hpp" +#include "ParenSsm.hpp" +#include "ExpectExprSsm.hpp" #include #include @@ -152,9 +154,12 @@ namespace xo { this->on_bool_token(tk, p_psm); return; + case tokentype::tk_leftparen: + this->on_leftparen_token(tk, p_psm); + return; + // all the not-yet handled cases case tokentype::tk_invalid: - case tokentype::tk_leftparen: case tokentype::tk_rightparen: case tokentype::tk_leftbracket: case tokentype::tk_rightbracket: @@ -389,6 +394,27 @@ namespace xo { Super::on_token(tk, p_psm); } + void + DExprSeqState::on_leftparen_token(const Token & tk, + ParserStateMachine * p_psm) + { + switch (seqtype_) { + case exprseqtype::toplevel_interactive: { + DParenSsm::start(p_psm); + p_psm->on_token(Token::leftparen_token()); + + return; + } + case exprseqtype::toplevel_batch: + break; + case exprseqtype::N: + assert(false); // unreachable + break; + } + + Super::on_token(tk, p_psm); + } + void DExprSeqState::on_parsed_expression(obj expr, ParserStateMachine * p_psm) diff --git a/src/reader2/DLambdaSsm.cpp b/src/reader2/DLambdaSsm.cpp index 286fcfa5..a2c2dfbd 100644 --- a/src/reader2/DLambdaSsm.cpp +++ b/src/reader2/DLambdaSsm.cpp @@ -136,6 +136,10 @@ namespace xo { this->on_yields_token(tk, p_psm); return; + case tokentype::tk_leftbrace: + this->on_leftbrace_token(tk, p_psm); + return; + // all the not-yet-handled cases case tokentype::tk_def: case tokentype::tk_if: @@ -152,7 +156,6 @@ namespace xo { case tokentype::tk_rightparen: case tokentype::tk_leftbracket: case tokentype::tk_rightbracket: - case tokentype::tk_leftbrace: case tokentype::tk_rightbrace: case tokentype::tk_leftangle: case tokentype::tk_rightangle: @@ -179,11 +182,6 @@ namespace xo { } Super::on_token(tk, p_psm); -#ifdef OBSOLETE - p_psm->illegal_input_on_token("DLambdaSsm::on_token", - tk, - this->get_expect_str()); -#endif } void @@ -199,11 +197,6 @@ namespace xo { } Super::on_token(tk, p_psm); -#ifdef OBSOLETE - p_psm->illegal_input_on_token("DLambdaSsm::on_lambda_token", - tk, - this->get_expect_str()); -#endif } void @@ -220,13 +213,28 @@ namespace xo { } Super::on_token(tk, p_psm); -#ifdef OBSOLETE - p_psm->illegal_input_on_token("DLambdaSsm::on_yields_token", - tk, - this->get_expect_str()); -#endif } + void + DLambdaSsm::on_leftbrace_token(const Token & tk, + ParserStateMachine * p_psm) + { + if (lmstate_ == lambdastatetype::lm_2) { + // control here when leftbrace immediately follows + // formal param list. + // Otherwise leftbrace arrives in DExpectExprSsm + // pushed via on_parsed_typedescr() + + this->lmstate_ = lambdastatetype::lm_4; + + DExpectExprSsm::start(p_psm); + // precharge ssm for body with leftbrace + p_psm->on_token(Token::leftbrace_token()); + return; + } + + Super::on_token(tk, p_psm); + } void DLambdaSsm::on_parsed_typedescr(TypeDescr td, diff --git a/src/reader2/DParenSsm.cpp b/src/reader2/DParenSsm.cpp new file mode 100644 index 00000000..166e96bf --- /dev/null +++ b/src/reader2/DParenSsm.cpp @@ -0,0 +1,382 @@ +/** @file DParenSsm.cpp + * + * @author Roland Conybeare, Feb 2026 + **/ + +#include "ParenSsm.hpp" +#include "syntaxstatetype.hpp" +#include + +namespace xo { + using xo::facet::with_facet; + using xo::facet::typeseq; + + namespace scm { + + extern const char * + parenexprstatetype_descr(parenexprstatetype x) + { + switch(x) { + case parenexprstatetype::invalid: return "invalid"; + case parenexprstatetype::lparen_0: return "lparen_0"; + case parenexprstatetype::lparen_1: return "lparen_1"; + case parenexprstatetype::lparen_2: return "lparen_2"; + case parenexprstatetype::N: break; + } + + return "???parenexprstatetype"; + } + + std::ostream & + operator<<(std::ostream & os, parenexprstatetype x) { + os << parenexprstatetype_descr(x); + return os; + } + + DParenSsm::DParenSsm() + : parenstate_(parenexprstatetype::lparen_0), + expr_{} + {} + + DParenSsm * + DParenSsm::make(DArena & mm) + { + void * mem = mm.alloc(typeseq::id(), + sizeof(DParenSsm)); + + return new (mem) DParenSsm(); + } + + void + DParenSsm::start(ParserStateMachine * p_psm) + { + DParenSsm * paren_ssm = DParenSsm::make(p_psm->parser_alloc()); + + auto ssm = with_facet::mkobj(paren_ssm); + + p_psm->push_ssm(ssm); + } + + syntaxstatetype + DParenSsm::ssm_type() const noexcept + { + return syntaxstatetype::paren; + } + + std::string_view + DParenSsm::get_expect_str() const noexcept + { + switch (this->parenstate_) { + case parenexprstatetype::invalid: + case parenexprstatetype::N: + break; + case parenexprstatetype::lparen_0: return "lparen_0"; + case parenexprstatetype::lparen_1: return "lparen_1"; + case parenexprstatetype::lparen_2: return "lparen_2"; + } + + return "???parenexprstatetype"; + } + + void + DParenSsm::on_token(const Token & tk, + ParserStateMachine * p_psm) + { + switch (tk.tk_type()) { + case tokentype::tk_leftparen: + this->on_leftparen_token(tk, p_psm); + return; + // all the not-yet handled cases + case tokentype::tk_symbol: + case tokentype::tk_def: + case tokentype::tk_colon: + case tokentype::tk_singleassign: + case tokentype::tk_semicolon: + case tokentype::tk_invalid: + case tokentype::tk_string: + case tokentype::tk_f64: + case tokentype::tk_i64: + case tokentype::tk_bool: + case tokentype::tk_if: + case tokentype::tk_rightparen: + case tokentype::tk_leftbracket: + case tokentype::tk_rightbracket: + case tokentype::tk_leftbrace: + case tokentype::tk_rightbrace: + case tokentype::tk_leftangle: + case tokentype::tk_rightangle: + case tokentype::tk_lessequal: + case tokentype::tk_greatequal: + 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_lambda: + 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 + DParenSsm::on_leftparen_token(const Token & tk, + ParserStateMachine * p_psm) + { + Super::on_token(tk, p_psm); + } + +#ifdef OBSOLETE + void + paren_xs::start(parserstatemachine * p_psm) + { + p_psm->push_exprstate(paren_xs::make()); + expect_expr_xs::start(p_psm); + } + + bool + paren_xs::admits_rightparen() const { + switch (parenxs_type_) { + case parenexprstatetype::lparen_0: + /* unreachable */ + assert(false); + return false; + + case parenexprstatetype::lparen_1: + return true; + + case parenexprstatetype::invalid: + case parenexprstatetype::n_parenexprstatetype: + /* unreachable */ + assert(false); + return false; + } + + return false; + } + + bool + paren_xs::admits_f64() const { + switch (parenxs_type_) { + case parenexprstatetype::lparen_0: + return true; + + case parenexprstatetype::lparen_1: + return false; + + case parenexprstatetype::invalid: + case parenexprstatetype::n_parenexprstatetype: + /* unreachable */ + assert(false); + return false; + } + + return false; + } + + void + paren_xs::on_def_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr const char * c_self_name = "paren_xs::on_def"; + + this->illegal_input_error(c_self_name, tk); + } + + void + paren_xs::on_symbol_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("exstype", p_psm->top_exprstate().exs_type())); + + //constexpr const char * self_name = "paren_xs::on_symbol"; + + /* TODO: lparen_0: treat as variable reference */ + + assert(false); + } + + void + paren_xs::on_typedescr(TypeDescr /*td*/, + parserstatemachine * /*p_psm*/) + { + assert(false); + return; + } + + void + paren_xs::on_colon_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr const char * c_self_name = "paren_xs::on_colon"; + + this->illegal_input_error(c_self_name, tk); + } + + void + paren_xs::on_semicolon_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr const char * c_self_name = "paren_xs::on_semicolon"; + + this->illegal_input_error(c_self_name, tk); + } + + void + paren_xs::on_singleassign_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr const char * c_self_name = "paren_xs::on_singleassign"; + + this->illegal_input_error(c_self_name, tk); + } + + void + paren_xs::on_leftparen_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr const char * c_self_name = "paren_xs::on_leftparen"; + + this->illegal_input_error(c_self_name, tk); + } + + void + paren_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 = "paren_xs::on_rightparen"; + + if (!this->admits_rightparen()) + { + this->illegal_input_error(c_self_name, tk); + } + + if (this->parenxs_type_ == parenexprstatetype::lparen_1) { + rp expr = this->gen_expr_; + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->top_exprstate().on_expr(expr, p_psm); + } + } + + void + paren_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 = "paren_xs::on_i64"; + + this->illegal_input_error(c_self_name, tk); + } + + void + paren_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 = "paren_xs::on_f64"; + + this->illegal_input_error(c_self_name, tk); + } + + void + paren_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("exstype", this->exs_type_), + xtag("expr", expr)); + + switch (this->parenxs_type_) { + case parenexprstatetype::lparen_0: { + this->parenxs_type_ = parenexprstatetype::lparen_1; /* wants on_rightparen */ + progress_xs::start(expr.promote(), p_psm); + + return; + } + + case parenexprstatetype::lparen_1: { + this->gen_expr_ = expr.promote(); + + /* expect immediate incoming call, this time to on_rightparen() */ + return; + } + + default: + /* unreachable */ + assert(false); + return; + } + } /*on_expr*/ + + void + paren_xs::on_symbol(const std::string & /*symbol_name*/, + parserstatemachine * /*p_psm*/) + { + switch(this->parenxs_type_) { + case parenexprstatetype::lparen_0: + case parenexprstatetype::lparen_1: + /* NOT IMPLEMENTED */ + assert(false); + return; + + default: + /* unreachable */ + assert(false); + return; + } + } + + void + paren_xs::print(std::ostream & os) const { + os << ""; + } +#endif + + bool + DParenSsm::pretty(const ppindentinfo & ppii) const + { + return ppii.pps()->pretty_struct(ppii, + "DParenSsm", + refrtag("parenstate", parenstate_), + refrtag("expect", this->get_expect_str())); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end DParenSsm.cpp */ diff --git a/src/reader2/IPrintable_DParenSsm.cpp b/src/reader2/IPrintable_DParenSsm.cpp new file mode 100644 index 00000000..b2228f7f --- /dev/null +++ b/src/reader2/IPrintable_DParenSsm.cpp @@ -0,0 +1,28 @@ +/** @file IPrintable_DParenSsm.cpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/IPrintable_DParenSsm.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/IPrintable_DParenSsm.json5] +**/ + +#include "ssm/IPrintable_DParenSsm.hpp" + +namespace xo { + namespace scm { + auto + IPrintable_DParenSsm::pretty(const DParenSsm & self, const ppindentinfo & ppii) -> bool + { + return self.pretty(ppii); + } + + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end IPrintable_DParenSsm.cpp */ diff --git a/src/reader2/ISyntaxStateMachine_DParenSsm.cpp b/src/reader2/ISyntaxStateMachine_DParenSsm.cpp new file mode 100644 index 00000000..1bb2ea83 --- /dev/null +++ b/src/reader2/ISyntaxStateMachine_DParenSsm.cpp @@ -0,0 +1,69 @@ +/** @file ISyntaxStateMachine_DParenSsm.cpp + * + * Generated automagically from ingredients: + * 1. code generator: + * [xo-facet/codegen/genfacet] + * arguments: + * --input [idl/ISyntaxStateMachine_DParenSsm.json5] + * 2. jinja2 template for abstract facet .hpp file: + * [iface_facet_any.hpp.j2] + * 3. idl for facet methods + * [idl/ISyntaxStateMachine_DParenSsm.json5] +**/ + +#include "ssm/ISyntaxStateMachine_DParenSsm.hpp" + +namespace xo { + namespace scm { + auto + ISyntaxStateMachine_DParenSsm::ssm_type(const DParenSsm & self) noexcept -> syntaxstatetype + { + return self.ssm_type(); + } + + auto + ISyntaxStateMachine_DParenSsm::get_expect_str(const DParenSsm & self) noexcept -> std::string_view + { + return self.get_expect_str(); + } + + auto + ISyntaxStateMachine_DParenSsm::on_token(DParenSsm & self, const Token & tk, ParserStateMachine * p_psm) -> void + { + self.on_token(tk, p_psm); + } + auto + ISyntaxStateMachine_DParenSsm::on_parsed_symbol(DParenSsm & self, std::string_view sym, ParserStateMachine * p_psm) -> void + { + self.on_parsed_symbol(sym, p_psm); + } + auto + ISyntaxStateMachine_DParenSsm::on_parsed_typedescr(DParenSsm & self, TypeDescr td, ParserStateMachine * p_psm) -> void + { + self.on_parsed_typedescr(td, p_psm); + } + auto + ISyntaxStateMachine_DParenSsm::on_parsed_formal(DParenSsm & self, const DUniqueString * param_name, TypeDescr param_type, ParserStateMachine * p_psm) -> void + { + self.on_parsed_formal(param_name, param_type, p_psm); + } + auto + ISyntaxStateMachine_DParenSsm::on_parsed_formal_arglist(DParenSsm & self, DArray * arglist, ParserStateMachine * p_psm) -> void + { + self.on_parsed_formal_arglist(arglist, p_psm); + } + auto + ISyntaxStateMachine_DParenSsm::on_parsed_expression(DParenSsm & self, obj expr, ParserStateMachine * p_psm) -> void + { + self.on_parsed_expression(expr, p_psm); + } + auto + ISyntaxStateMachine_DParenSsm::on_parsed_expression_with_token(DParenSsm & self, obj expr, const Token & tk, ParserStateMachine * p_psm) -> void + { + self.on_parsed_expression_with_token(expr, tk, p_psm); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end ISyntaxStateMachine_DParenSsm.cpp */ diff --git a/src/reader2/reader2_register_facets.cpp b/src/reader2/reader2_register_facets.cpp index ef584c44..3955fae3 100644 --- a/src/reader2/reader2_register_facets.cpp +++ b/src/reader2/reader2_register_facets.cpp @@ -19,6 +19,8 @@ #include +#include "ParenSsm.hpp" + #include #include @@ -85,6 +87,9 @@ namespace xo { FacetRegistry::register_impl(); FacetRegistry::register_impl(); + FacetRegistry::register_impl(); + FacetRegistry::register_impl(); + log && log(xtag("DExprSeqState.tseq", typeseq::id())); log && log(xtag("DDefineSsm.tseq", typeseq::id())); log && log(xtag("DLambdaSsm.tseq", typeseq::id())); @@ -95,6 +100,7 @@ namespace xo { log && log(xtag("DExpectTypeSsm.tseq", typeseq::id())); log && log(xtag("DExpectExprSsm.tseq", typeseq::id())); log && log(xtag("DProgressSsm.tseq", typeseq::id())); + log && log(xtag("DParenSsm.tseq", typeseq::id())); log && log(xtag("ASyntaxStateMachine.tseq", typeseq::id())); return true; diff --git a/src/reader2/syntaxstatetype.cpp b/src/reader2/syntaxstatetype.cpp index aff0a87c..18acb000 100644 --- a/src/reader2/syntaxstatetype.cpp +++ b/src/reader2/syntaxstatetype.cpp @@ -23,6 +23,8 @@ namespace xo { return "sequence"; case syntaxstatetype::progress: return "progress"; + case syntaxstatetype::paren: + return "paren"; case syntaxstatetype::expect_toplevel_expression_sequence: return "expect-toplevel-expression-sequence"; case syntaxstatetype::expect_formal_arglist: diff --git a/utest/SchematikaParser.test.cpp b/utest/SchematikaParser.test.cpp index 740d71f4..870cabd1 100644 --- a/utest/SchematikaParser.test.cpp +++ b/utest/SchematikaParser.test.cpp @@ -836,7 +836,7 @@ namespace xo { TEST_CASE("SchematikaParser-interactive-lambda2", "[reader2][SchematikaParser]") { - constexpr bool c_debug_flag = true; + constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag)); ArenaConfig config; @@ -1014,6 +1014,175 @@ namespace xo { } } + + TEST_CASE("SchematikaParser-interactive-apply", "[reader2][SchematikaParser]") + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + 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: + * + * (lambda (x : i64) { x * x })(13); + * + **/ + + { + auto & result = parser.on_token(Token::leftparen_token()); + + log && log("after lparen 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::lambda_token()); + + log && log("after lambda 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::leftparen_token()); + + log && log("after lparen 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::symbol_token("x")); + + log && log("after symbol(x) 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::colon_token()); + + log && log("after colon 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::symbol_token("i64")); + + log && log("after symbol(i64) 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::rightparen_token()); + + log && log("after rightparen 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::leftbrace_token()); + + log && log("after leftbrace 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::symbol_token("x")); + + log && log("after symbol(x) 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::symbol_token("x")); + + log && log("after symbol(x) 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::rightbrace_token()); + + log && log("after rightbrace(}) 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()); + } + + } } /*namespace ut*/ } /*namespace xo*/