From 1d40b5da55b8bf05d5332a7d82a744eb1856dda6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 19 Nov 2025 09:38:32 -0500 Subject: [PATCH 1/3] xo-interpreter: repl example [wip] --- xo-interpreter/example/CMakeLists.txt | 1 + xo-interpreter/example/replxx/CMakeLists.txt | 17 +++ xo-interpreter/example/replxx/replxx.cpp | 140 ++++++++++++++++++ .../include/xo/interpreter/Schematika.hpp | 40 +++++ 4 files changed, 198 insertions(+) create mode 100644 xo-interpreter/example/CMakeLists.txt create mode 100644 xo-interpreter/example/replxx/CMakeLists.txt create mode 100644 xo-interpreter/example/replxx/replxx.cpp create mode 100644 xo-interpreter/include/xo/interpreter/Schematika.hpp diff --git a/xo-interpreter/example/CMakeLists.txt b/xo-interpreter/example/CMakeLists.txt new file mode 100644 index 00000000..ce7aaf20 --- /dev/null +++ b/xo-interpreter/example/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(replxx) diff --git a/xo-interpreter/example/replxx/CMakeLists.txt b/xo-interpreter/example/replxx/CMakeLists.txt new file mode 100644 index 00000000..96f11ae4 --- /dev/null +++ b/xo-interpreter/example/replxx/CMakeLists.txt @@ -0,0 +1,17 @@ +# xo-interpreter/example/replxx/CMakeLists.txt + +set(SELF_EXE xo_interpreter_replxx) +set(SELF_SRCS replxx.cpp) + +if (XO_ENABLE_EXAMPLES) + xo_add_executable(${SELF_EXE} ${SELF_SRCS}) + xo_self_dependency(${SELF_EXE} xo_interpreter) + # TODO: consider promoting to regular app + xo_dependency(${SELF_EXE} xo_reader) + xo_external_target_dependency(${SELF_EXE} replxx replxx::replxx) + + find_package(Threads REQUIRED) + target_link_libraries(${SELF_EXE} PUBLIC Threads::Threads) +endif() + +# end CMakeLists.txt diff --git a/xo-interpreter/example/replxx/replxx.cpp b/xo-interpreter/example/replxx/replxx.cpp new file mode 100644 index 00000000..ad735b1d --- /dev/null +++ b/xo-interpreter/example/replxx/replxx.cpp @@ -0,0 +1,140 @@ +/** @file replxx.cpp **/ + +#include "xo/reader/reader.hpp" +#include +#include +#include // for isatty + +// similar helper in exprreplxx.cpp +// +bool replxx_getline(bool interactive, + std::size_t parser_stack_size, + replxx::Replxx & rx, + std::string & input) +{ + using namespace std; + + char const * prompt = ""; + + if (interactive) { + if (parser_stack_size <= 1) + prompt = "> "; + else + prompt = ". "; + } + + /* input_cstr: next line of input from replxx library */ + const char * input_cstr = rx.input(prompt); + + bool retval = (input_cstr != nullptr); + + if (retval) { + /* got new input */ + input = input_cstr; + } + + rx.history_add(input); + + input.push_back('\n'); + + return retval; +} + +void +welcome(std::ostream & os) +{ + using namespace std; + + os << "read-eval-print loop for schematika expressions" << endl; + os << " ctrl-a/ctrl-e beginning/end of line" << endl; + os << " ctrl-u delete entire line" << endl; + os << " ctrl-k delete to end of line" << endl; + os << " meta- backward delete word" << endl; + os << " |meta-p previous command from history" << endl; + os << " |meta-n next command from history" << endl; + os << " / page through history faster" << endl; + os << " ctrl-s/ctrl-r forward/backward history search" << endl; + os << endl; +} + +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; + + bool interactive = isatty(STDIN_FILENO); + + Replxx rx; + rx.set_max_history_size(1000); + rx.history_load("repl_history.txt"); +// rx.bind_key_internal(Replxx::KEY::control('p'), "history_previous"); +// rx.bind_key_internal(Replxx::KEY::control('n'), "history_next"); + + constexpr bool c_debug_flag = false; + + reader rdr(c_debug_flag); + rdr.begin_interactive_session(); + + string input_str; + + bool eof = false; + + span_type input; + std::size_t parser_stack_size = 0; + + welcome(cerr); + + while (replxx_getline(interactive, parser_stack_size, rx, input_str)) { + input = span_type::from_string(input_str); + + while (!input.empty()) { + auto [expr, consumed, psz, error] = rdr.read_expr(input, eof); + + if (expr) { + ppconfig ppc; + ppstate_standalone pps(&cout, 0, &ppc); + + 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; + } + + /* here: input.empty() or 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); + } + + rx.history_save("repl_history.txt"); +} + +/* end replxx.cpp */ diff --git a/xo-interpreter/include/xo/interpreter/Schematika.hpp b/xo-interpreter/include/xo/interpreter/Schematika.hpp new file mode 100644 index 00000000..7a19869b --- /dev/null +++ b/xo-interpreter/include/xo/interpreter/Schematika.hpp @@ -0,0 +1,40 @@ +/** @file Schematika.hpp + * + * @author Roland Conybeare, Nov 2025 + **/ + +#pramga once + + + +namespace xo { + namespace scm { + + /** @class Schematika + * @brief schematika interpreter state + **/ + class Schematika { + public: + /** interactive read-eval-print loop. + * Uses replxx to read from stdin. + * If stdin is interactive, accepts line editing commands: + * - ctrl-a goto beginning of line + * - ctrl-e goto end of line + * - ctrl-k delete to end of line + * - meta- backwards delete word + * - meta-p| retrieve previous command from history + * - meta-n| retrieve next command from history + * - / page through history faster + * - ctrl-s forward history search + * - ctrl-r backward history search + **/ + void interactive_repl(); + + private: + class Impl; + std::unique_ptr p_impl_; + }; + } +} + +/* end Schematika.hpp */ From 20b105d90993c934377cdbbf630ad676077314e2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 19 Nov 2025 09:39:03 -0500 Subject: [PATCH 2/3] xo-reader: fix hardwired debug flag --- xo-reader/examples/exprreplxx/exprreplxx.cpp | 8 ++++++-- xo-reader/include/xo/reader/parser.hpp | 6 +++++- xo-reader/include/xo/reader/reader.hpp | 2 ++ xo-reader/src/reader/exprstate.cpp | 6 ++---- xo-reader/src/reader/exprstatestack.cpp | 4 ++-- xo-reader/src/reader/progress_xs.cpp | 6 ++---- xo-reader/src/reader/reader.cpp | 3 +-- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/xo-reader/examples/exprreplxx/exprreplxx.cpp b/xo-reader/examples/exprreplxx/exprreplxx.cpp index db1f3e1c..51d6e8d9 100644 --- a/xo-reader/examples/exprreplxx/exprreplxx.cpp +++ b/xo-reader/examples/exprreplxx/exprreplxx.cpp @@ -7,7 +7,10 @@ // presumeably replxx assumes input is a tty // -bool replxx_getline(bool interactive, std::size_t parser_stack_size, replxx::Replxx & rx, std::string& input) +bool replxx_getline(bool interactive, + std::size_t parser_stack_size, + replxx::Replxx & rx, + std::string& input) { using namespace std; @@ -58,7 +61,8 @@ welcome(std::ostream& os) } int -main() { +main() +{ using namespace replxx; using namespace xo::scm; using xo::scm::Expression; diff --git a/xo-reader/include/xo/reader/parser.hpp b/xo-reader/include/xo/reader/parser.hpp index 952fa9d6..9ef70f83 100644 --- a/xo-reader/include/xo/reader/parser.hpp +++ b/xo-reader/include/xo/reader/parser.hpp @@ -165,7 +165,11 @@ namespace xo { **/ explicit parser(bool debug_flag); - /** true if parser is at top-level, i.e. ready for next top-level expression **/ + bool debug_flag() const { return psm_.debug_flag(); } + + /** true if parser is at top-level, + * i.e. ready for next top-level expression + **/ bool is_at_toplevel() const { return stack_size() == 0; } /** for diagnostics: number of entries in parser stack **/ diff --git a/xo-reader/include/xo/reader/reader.hpp b/xo-reader/include/xo/reader/reader.hpp index 69c42885..546b923c 100644 --- a/xo-reader/include/xo/reader/reader.hpp +++ b/xo-reader/include/xo/reader/reader.hpp @@ -80,6 +80,8 @@ namespace xo { public: explicit reader(bool debug_flag); + bool debug_flag() const { return parser_.debug_flag(); } + /** call once before calling .read_expr() * for a new interactive session **/ diff --git a/xo-reader/src/reader/exprstate.cpp b/xo-reader/src/reader/exprstate.cpp index 476c6420..0a8b0d32 100644 --- a/xo-reader/src/reader/exprstate.cpp +++ b/xo-reader/src/reader/exprstate.cpp @@ -68,8 +68,7 @@ namespace xo { exprstate::on_def_token(const token_type & tk, parserstatemachine * p_psm) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("exstype", p_psm->top_exprstate().exs_type())); @@ -357,8 +356,7 @@ namespace xo { exprstate::on_input(const token_type & tk, parserstatemachine * p_psm) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("tk", tk)); log && log(xtag("state", *this)); log && log(xtag("psm", p_psm)); diff --git a/xo-reader/src/reader/exprstatestack.cpp b/xo-reader/src/reader/exprstatestack.cpp index 4ccad6d6..96852b73 100644 --- a/xo-reader/src/reader/exprstatestack.cpp +++ b/xo-reader/src/reader/exprstatestack.cpp @@ -28,7 +28,7 @@ namespace xo { void exprstatestack::push_exprstate(std::unique_ptr exs) { - constexpr bool c_debug_flag = true; + constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag), xtag("exs", exs.get())); @@ -41,7 +41,7 @@ namespace xo { std::unique_ptr exprstatestack::pop_exprstate() { - constexpr bool c_debug_flag = true; + constexpr bool c_debug_flag = false; scope log(XO_DEBUG(c_debug_flag), xtag("top.exstype", top_exprstate().exs_type())); diff --git a/xo-reader/src/reader/progress_xs.cpp b/xo-reader/src/reader/progress_xs.cpp index e7047945..c99aaaa6 100644 --- a/xo-reader/src/reader/progress_xs.cpp +++ b/xo-reader/src/reader/progress_xs.cpp @@ -339,8 +339,7 @@ namespace xo { progress_xs::on_expr_with_semicolon(bp expr, parserstatemachine * p_psm) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(p_psm->debug_flag())); log && log(xtag("lhs", lhs_), xtag("op", op_type_), xtag("expr", expr)); @@ -606,8 +605,7 @@ namespace xo { progress_xs::on_operator_token(const token_type & tk, parserstatemachine * p_psm) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(p_psm->debug_flag())); constexpr const char * c_self_name = "progress_xs::on_operator_token"; diff --git a/xo-reader/src/reader/reader.cpp b/xo-reader/src/reader/reader.cpp index 5889462a..2fb59d72 100644 --- a/xo-reader/src/reader/reader.cpp +++ b/xo-reader/src/reader/reader.cpp @@ -31,8 +31,7 @@ namespace xo { reader_result reader::read_expr(const span_type & input_arg, bool eof) { - constexpr bool c_debug_flag = true; - scope log(XO_DEBUG(c_debug_flag)); + scope log(XO_DEBUG(this->debug_flag())); span_type input = input_arg; From dfccc6c0ff868b077a1cb08e36fcc8f69910d7e1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 19 Nov 2025 09:39:17 -0500 Subject: [PATCH 3/3] build: nits --- CMakeLists.txt | 2 +- xo-interpreter/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5cfad97..1b554c34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ add_subdirectory(xo-expression) add_subdirectory(xo-pyexpression) add_subdirectory(xo-tokenizer) add_subdirectory(xo-reader) -add_subdirectory(xo-symboltable) +#add_subdirectory(xo-symboltable) add_subdirectory(xo-interpreter) add_subdirectory(xo-jit) add_subdirectory(xo-pyjit) diff --git a/xo-interpreter/CMakeLists.txt b/xo-interpreter/CMakeLists.txt index 370a9650..a14ee394 100644 --- a/xo-interpreter/CMakeLists.txt +++ b/xo-interpreter/CMakeLists.txt @@ -24,7 +24,7 @@ xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets # ---------------------------------------------------------------- -#add_subdirectory(example) +add_subdirectory(example) add_subdirectory(utest) # ----------------------------------------------------------------