From 8f64b05b711aa37794fb49f9450b24c3b282b5c5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 23 Jan 2026 11:54:32 -0500 Subject: [PATCH] xo-reader2: + example app 'readerreplxx' --- CMakeLists.txt | 5 + example/CMakeLists.txt | 1 + example/readerreplxx/CMakeLists.txt | 14 +++ example/readerreplxx/readerreplxx.cpp | 159 ++++++++++++++---------- include/xo/reader2/SchematikaParser.hpp | 1 + include/xo/reader2/SchematikaReader.hpp | 22 +++- src/reader2/CMakeLists.txt | 4 +- src/reader2/SchematikaParser.cpp | 1 + src/reader2/SchematikaReader.cpp | 77 ++++++++---- 9 files changed, 187 insertions(+), 97 deletions(-) create mode 100644 example/CMakeLists.txt create mode 100644 example/readerreplxx/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index bc35b6a9..43c747f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,11 @@ xo_add_genfacet_all(xo-reader2-genfacet-all) add_subdirectory(src/reader2) +# ---------------------------------------------------------------- +# example programs + +add_subdirectory(example) + # ---------------------------------------------------------------- # cmake helper (for external xo-reader2 users) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 00000000..fbb01ff0 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(readerreplxx) diff --git a/example/readerreplxx/CMakeLists.txt b/example/readerreplxx/CMakeLists.txt new file mode 100644 index 00000000..37ecd45e --- /dev/null +++ b/example/readerreplxx/CMakeLists.txt @@ -0,0 +1,14 @@ +# xo-reader2/example/readerreplxx/CMakeLists.txt + +set(SELF_EXE xo_reader2_readereplxx) +set(SELF_SRCS readerreplxx.cpp) + +if (XO_ENABLE_EXAMPLES) + xo_add_executable(${SELF_EXE} ${SELF_SRCS}) + xo_self_dependency(${SELF_EXE} xo_reader2) + xo_external_target_dependency(${SELF_EXE} replxx replxx::replxx) + + # replxx requires this + find_package(Threads REQUIRED) + target_link_libraries(${SELF_EXE} PUBLIC Threads::Threads) +endif() diff --git a/example/readerreplxx/readerreplxx.cpp b/example/readerreplxx/readerreplxx.cpp index e30ab047..76bd0520 100644 --- a/example/readerreplxx/readerreplxx.cpp +++ b/example/readerreplxx/readerreplxx.cpp @@ -1,6 +1,11 @@ /** @file readerreplxx.cpp **/ -#include "xo/reader/reader.hpp" +#include +#include +#include +#include +//#include +#include #include #include #include // for isatty @@ -8,7 +13,7 @@ // presumeably replxx assumes input is a tty // bool replxx_getline(bool interactive, - std::size_t parser_stack_size, + bool is_at_toplevel, replxx::Replxx & rx, const char ** p_input) { @@ -17,32 +22,23 @@ bool replxx_getline(bool interactive, char const * prompt = ""; if (interactive) { - if (parser_stack_size <= 1) - prompt = "> "; - else - prompt = ". "; + prompt = ((is_at_toplevel) ? "> " : ". "); } const char * input_cstr = rx.input(prompt); bool retval = (input_cstr != nullptr); - if (retval) { - //cerr << "got reval->true" << endl; + if (retval) + *p_input = input_cstr; - input = input_cstr; - - } else { - //cerr << "got retval->false" << endl; - } - - rx.history_add(input); + rx.history_add(input_cstr); return retval; } void -welcome(std::ostream& os) +welcome(std::ostream & os) { using namespace std; @@ -58,18 +54,68 @@ welcome(std::ostream& os) os << endl; } +namespace { + using xo::scm::SchematikaReader; + using xo::print::ppstate_standalone; + using xo::print::ppconfig; + using std::cout; + using std::endl; + + /** body of read-parse-print loop + * + * true -> no errors; + * false -> reader encountered error + **/ + bool + reader_seq(SchematikaReader * p_reader, + SchematikaReader::span_type * p_input, + bool eof) + { + auto [expr, remaining, error] = p_reader->read_expr(*p_input, eof); + + if (expr) { + ppconfig ppc; + ppstate_standalone pps(&cout, 0, &ppc); + + pps.prettyn(expr); + + *p_input = remaining; + + return true; + } else if (error.is_error()) { + cout << "parsing error (detected in " << error.src_function() << "): " << endl; + error.report(cout); + + /* discard stashed remainder of input line + * (for nicely-formatted errors) + */ + p_reader->reset_to_idle_toplevel(); + + return false; + } else { + /* partial expression or whitespace input, no error */ + return true; + } + } +} + int main() { using namespace replxx; - using namespace xo::scm; - using xo::scm::Expression; - using xo::print::ppconfig; - using xo::print::ppstate_standalone; - using xo::rp; - using namespace std; - using span_type = xo::scm::span; + using xo::scm::SchematikaReader; + using xo::scm::ReaderConfig; + using xo::mm::AAllocator; + using xo::mm::DX1Collector; + using xo::mm::CollectorConfig; + using xo::mm::DArena; + //using xo::print::ppconfig; + //using xo::print::ppstate_standalone; + using xo::facet::with_facet; + using xo::facet::obj; + using xo::scope; + using namespace std; bool interactive = isatty(STDIN_FILENO); @@ -82,63 +128,40 @@ main() constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag)); - DArena expr_arena = DArena::map(ArenaConfig{ .name_ = "expr-arena", .size_ = 2*1024*1024; }); - obj expr_alloc = with_facet::mkobj(&expr_arena); - constexpr size_t c_max_stringtable_cap = 1024*1024; - SchematikaParser parser(expr_arena.config_, c_max_stringtable_cap, expr_alloc, c_debug_flag); + CollectorConfig x1_config = (CollectorConfig() + .with_size(4*1024*1024)); + DX1Collector x1(x1_config); + obj expr_alloc = with_facet::mkobj(&x1); - parser.begin_interactive_session(); + // accepting defaults too + ReaderConfig rdr_config = ReaderConfig(); - string input_str; - - bool eof = false; - - span_type input; - std::size_t parser_stack_size = 0; + SchematikaReader rdr(rdr_config, expr_alloc); + using span_type = SchematikaReader::span_type; welcome(cerr); - while (replxx_getline(interactive, parser_stack_size, rx, input_str)) { - input = span_type::from_string(input_str); + rdr.begin_interactive_session(); - while (!input.empty()) { - auto [expr, consumed, psz, error] = rdr.read_expr(input, eof); + bool eof = false; + const char * input_str; + span_type input; - if (expr) { - ppconfig ppc; - ppstate_standalone pps(&cout, 0, &ppc); + while (replxx_getline(interactive, rdr.is_at_toplevel(), rx, &input_str)) { + input = span_type::from_cstr(input_str); - pps.prettyn(expr); - } else if (error.is_error()) { - cout << "parsing error (detected in " << error.src_function() << "): " << endl; - error.report(cout); - - /* discard stashed remainder of input line - * (for nicely-formatted errors) - */ - rdr.reset_to_idle_toplevel(); - break; - } - - input = input.after_prefix(consumed); - parser_stack_size = psz; + while (!input.empty() && reader_seq(&rdr, &input, false /*eof*/)) { + ; } - /* here: input.empty() or error encountered */ - + /* here: either: + * 1. input.empty() or + * 2. error encountered + */ } - auto [expr, _1, _2, error] = rdr.read_expr(input, true /*eof*/); - - if (expr) { - ppconfig ppc; - ppstate_standalone pps(&cout, 0, &ppc); - - pps.prettyn>(rp(expr)); - } else if (error.is_error()) { - cout << "parsing error (detected in " << error.src_function() << "): " << endl; - error.report(cout); - } + /* reminder: eof can complete at most one token */ + reader_seq(&rdr, &input, true /*eof*/); rx.history_save("repl_history.txt"); } diff --git a/include/xo/reader2/SchematikaParser.hpp b/include/xo/reader2/SchematikaParser.hpp index e2667b5f..7be74aa5 100644 --- a/include/xo/reader2/SchematikaParser.hpp +++ b/include/xo/reader2/SchematikaParser.hpp @@ -157,6 +157,7 @@ namespace xo { using ArenaConfig = xo::mm::ArenaConfig; using AAllocator = xo::mm::AAllocator; using ppindentinfo = xo::print::ppindentinfo; + using size_type = std::size_t; public: /** create parser in initial state; diff --git a/include/xo/reader2/SchematikaReader.hpp b/include/xo/reader2/SchematikaReader.hpp index c60612a2..bad6c053 100644 --- a/include/xo/reader2/SchematikaReader.hpp +++ b/include/xo/reader2/SchematikaReader.hpp @@ -18,11 +18,11 @@ namespace xo { /** schematika expression parsed from input **/ obj expr_; - /** input span up to end of expression. + /** unconsumed portion of input span * only relevant when result type is expression. - * (otherwise treat entire input as consumed) + * (otherwise input consumed) **/ - span_type consumed_; + span_type remaining_input_; /** {src_function, error_description, input_state, error_pos} **/ TokenizerError tk_error_; @@ -36,18 +36,32 @@ namespace xo { class SchematikaReader { public: using AAllocator = xo::mm::AAllocator; + using span_type = xo::mm::span; + using size_type = std::size_t; public: SchematikaReader(const ReaderConfig & config, obj expr_alloc); + /** true iff parser is at top-level. + * false iff parser is working on incomplete expression + **/ + bool is_at_toplevel() const noexcept; + /** prepare interactive session * (allows rvalue expressions at toplevel) **/ void begin_interactive_session(); /** consume input @p input_cstr **/ - const ReaderResult & read_expr(const char * input_cstr, bool eof); + const ReaderResult & read_expr(span_type input_span, bool eof); + + /** reset to known starting point after encountering an error. + * - remainder of stashed current line. + * Necesary for well-formatted error reporting. + * - current parsing state + **/ + void reset_to_idle_toplevel(); private: /** tokenizer converts a stream of chars diff --git a/src/reader2/CMakeLists.txt b/src/reader2/CMakeLists.txt index 97c46c6a..37dda298 100644 --- a/src/reader2/CMakeLists.txt +++ b/src/reader2/CMakeLists.txt @@ -3,6 +3,8 @@ set(SELF_LIB xo_reader2) set(SELF_SRCS init_reader2.cpp + reader2_register_facets.cpp + reader2_register_types.cpp SchematikaReader.cpp ReaderConfig.cpp @@ -39,8 +41,6 @@ set(SELF_SRCS ISyntaxStateMachine_DProgressSsm.cpp IPrintable_DProgressSsm.cpp - reader2_register_facets.cpp - reader2_register_types.cpp ) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) diff --git a/src/reader2/SchematikaParser.cpp b/src/reader2/SchematikaParser.cpp index fea573c7..13f9edff 100644 --- a/src/reader2/SchematikaParser.cpp +++ b/src/reader2/SchematikaParser.cpp @@ -7,6 +7,7 @@ #include "ParserStateMachine.hpp" #include "ParserStack.hpp" #include "DExprSeqState.hpp" +#include #include #include diff --git a/src/reader2/SchematikaReader.cpp b/src/reader2/SchematikaReader.cpp index f8c6a152..bfd14575 100644 --- a/src/reader2/SchematikaReader.cpp +++ b/src/reader2/SchematikaReader.cpp @@ -17,6 +17,12 @@ namespace xo { { } + bool + SchematikaReader::is_at_toplevel() const noexcept + { + return parser_.is_at_toplevel(); + } + void SchematikaReader::begin_interactive_session() { @@ -27,24 +33,28 @@ namespace xo { // Schematika::end_interactive_session() const ReaderResult & - SchematikaReader::read_expr(const char * input_cstr, bool eof) + SchematikaReader::read_expr(span_type input_ext, bool eof) { - if (input_cstr && *input_cstr) { + if (!input_ext.empty()) { auto [error, input] - = tokenizer_.buffer_input_line(input_cstr, - false /*!eof*/); + = tokenizer_.buffer_input_line(input_ext, eof); + // log && log(xtag("msg", "buffered input line")); // log && log(xtag("input", input)); - - while (!input.empty()) { - auto [tk, consumed, error] = tkz.scan(input); + auto [tk, consumed, error] = tokenizer_.scan(input); + + auto rem_input = input.after_prefix(consumed); if (!tk.is_valid() && error.is_error()) { - this->result_ = ReaderResult { .expr_ = obj(), - .tk_error_ = std::move(error), - .consumed_ = nullptr }; + this->result_ + = ReaderResult + { .expr_ = obj(), + .remaining_input_ = rem_input, + .tk_error_ = std::move(error) + }; + return result_; } @@ -58,7 +68,7 @@ namespace xo { // error_description :: const DString * // } // - const ParserResult & presult = parser_include_token(tk); + const ParserResult & presult = parser_.on_token(tk); if (presult.is_error()) { // tk_error { @@ -76,29 +86,50 @@ namespace xo { // // tk_error.report(cout); - this->result_ = ReaderResult { .expr = obj(), - .tk_error_ = std::move(error), - .consumed_ = nullptr }; + this->result_ + = ReaderResult + { .expr_ = obj(), + .remaining_input_ = rem_input, + .tk_error_ = std::move(error) }; + + assert(presult.error_description()); // carefully created error description, maybe - this->result.tk_error_.error_description_ = presult.error_description_; + this->result_.tk_error_ + = result_.tk_error_.with_error + (presult.error_src_fn_, + std::string + (std::string_view(*(presult.error_description())))); + return result_; + } else if (presult.is_expression()) { + this->result_ + = ReaderResult + { + .expr_ = presult.result_expr(), + .remaining_input_ = rem_input, + .tk_error_ = TokenizerError() + }; + + return result_; } - - xxxx; - } else if (error.is_error()) { - xxxx; - // error.report(cout); - break; } - input = input.after_prefix(consumed); + input = rem_input; } } - ++line_no; + this->result_ = ReaderResult(); + + return this->result_; } + void + SchematikaReader::reset_to_idle_toplevel() + { + this->tokenizer_.discard_current_line(); + this->parser_.reset_to_idle_toplevel(); + } } /*namespace scm*/ } /*namespace xo*/