From 510fbdc578a7d17e66c33368f9c3497f5bffe6fd Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 6 Jun 2026 22:23:57 -0400 Subject: [PATCH] git subrepo clone (merge) git@github.com:Rconybea/xo-reader.git xo-reader subrepo: subdir: "xo-reader" merged: "cb652195" upstream: origin: "git@github.com:Rconybea/xo-reader.git" branch: "main" commit: "cb652195" git-subrepo: version: "0.4.9" origin: "???" commit: "???" --- xo-reader/.gitignore | 8 + xo-reader/.gitrepo | 12 + xo-reader/CMakeLists.txt | 39 + xo-reader/README | 53 ++ xo-reader/cmake/xo-bootstrap-macros.cmake | 35 + xo-reader/cmake/xo_readerConfig.cmake.in | 9 + xo-reader/docs/CMakeLists.txt | 7 + xo-reader/docs/README | 41 + xo-reader/docs/_static/README | 1 + xo-reader/docs/_static/img/favicon.ico | Bin 0 -> 309803 bytes xo-reader/docs/conf.py | 39 + xo-reader/docs/example.rst | 45 + xo-reader/docs/implementation.rst | 137 +++ xo-reader/docs/index.rst | 14 + xo-reader/docs/install.rst | 146 +++ xo-reader/examples/CMakeLists.txt | 2 + xo-reader/examples/exprrepl/CMakeLists.txt | 11 + xo-reader/examples/exprrepl/expreplxx.cpp | 0 xo-reader/examples/exprrepl/exprrepl.cpp | 91 ++ xo-reader/examples/exprreplxx/CMakeLists.txt | 16 + xo-reader/examples/exprreplxx/exprreplxx.cpp | 143 +++ xo-reader/include/xo/reader/apply_xs.hpp | 119 +++ xo-reader/include/xo/reader/define_xs.hpp | 135 +++ xo-reader/include/xo/reader/envframe.hpp | 60 ++ xo-reader/include/xo/reader/envframestack.hpp | 87 ++ .../include/xo/reader/expect_expr_xs.hpp | 106 +++ .../xo/reader/expect_formal_arglist_xs.hpp | 85 ++ .../include/xo/reader/expect_formal_xs.hpp | 85 ++ .../include/xo/reader/expect_symbol_xs.hpp | 32 + .../include/xo/reader/expect_type_xs.hpp | 33 + xo-reader/include/xo/reader/exprseq_xs.hpp | 84 ++ xo-reader/include/xo/reader/exprstate.hpp | 317 +++++++ .../include/xo/reader/exprstatestack.hpp | 78 ++ xo-reader/include/xo/reader/formal_arg.hpp | 58 ++ xo-reader/include/xo/reader/if_else_xs.hpp | 109 +++ xo-reader/include/xo/reader/lambda_xs.hpp | 123 +++ xo-reader/include/xo/reader/let1_xs.hpp | 66 ++ xo-reader/include/xo/reader/paren_xs.hpp | 92 ++ xo-reader/include/xo/reader/parser.hpp | 262 ++++++ xo-reader/include/xo/reader/parser_result.hpp | 65 ++ .../include/xo/reader/parserstatemachine.hpp | 129 +++ .../xo/reader/pretty_envframestack.hpp | 28 + .../xo/reader/pretty_exprstatestack.hpp | 34 + .../xo/reader/pretty_parserstatemachine.hpp | 23 + xo-reader/include/xo/reader/progress_xs.hpp | 189 ++++ xo-reader/include/xo/reader/reader.hpp | 139 +++ xo-reader/include/xo/reader/reader_error.hpp | 55 ++ xo-reader/include/xo/reader/sequence_xs.hpp | 52 ++ xo-reader/src/reader/CMakeLists.txt | 33 + xo-reader/src/reader/apply_xs.cpp | 189 ++++ xo-reader/src/reader/define_xs.cpp | 368 ++++++++ xo-reader/src/reader/envframe.cpp | 41 + xo-reader/src/reader/envframestack.cpp | 139 +++ xo-reader/src/reader/expect_expr_xs.cpp | 294 ++++++ .../src/reader/expect_formal_arglist_xs.cpp | 131 +++ xo-reader/src/reader/expect_formal_xs.cpp | 109 +++ xo-reader/src/reader/expect_symbol_xs.cpp | 49 + xo-reader/src/reader/expect_type_xs.cpp | 79 ++ xo-reader/src/reader/exprseq_xs.cpp | 276 ++++++ xo-reader/src/reader/exprstate.cpp | 675 ++++++++++++++ xo-reader/src/reader/exprstatestack.cpp | 120 +++ xo-reader/src/reader/if_else_xs.cpp | 288 ++++++ xo-reader/src/reader/lambda_xs.cpp | 315 +++++++ xo-reader/src/reader/let1_xs.cpp | 162 ++++ xo-reader/src/reader/paren_xs.cpp | 265 ++++++ xo-reader/src/reader/parser.cpp | 102 +++ xo-reader/src/reader/parser_result.cpp | 44 + xo-reader/src/reader/parserstatemachine.cpp | 203 +++++ .../src/reader/pretty_parserstatemachine.cpp | 54 ++ xo-reader/src/reader/progress_xs.cpp | 845 ++++++++++++++++++ xo-reader/src/reader/reader.cpp | 166 ++++ xo-reader/src/reader/sequence_xs.cpp | 133 +++ xo-reader/utest/CMakeLists.txt | 17 + xo-reader/utest/parser.test.cpp | 305 +++++++ xo-reader/utest/reader.test.cpp | 73 ++ xo-reader/utest/reader_utest_main.cpp | 6 + 76 files changed, 8975 insertions(+) create mode 100644 xo-reader/.gitignore create mode 100644 xo-reader/.gitrepo create mode 100644 xo-reader/CMakeLists.txt create mode 100644 xo-reader/README create mode 100644 xo-reader/cmake/xo-bootstrap-macros.cmake create mode 100644 xo-reader/cmake/xo_readerConfig.cmake.in create mode 100644 xo-reader/docs/CMakeLists.txt create mode 100644 xo-reader/docs/README create mode 100644 xo-reader/docs/_static/README create mode 100644 xo-reader/docs/_static/img/favicon.ico create mode 100644 xo-reader/docs/conf.py create mode 100644 xo-reader/docs/example.rst create mode 100644 xo-reader/docs/implementation.rst create mode 100644 xo-reader/docs/index.rst create mode 100644 xo-reader/docs/install.rst create mode 100644 xo-reader/examples/CMakeLists.txt create mode 100644 xo-reader/examples/exprrepl/CMakeLists.txt create mode 100644 xo-reader/examples/exprrepl/expreplxx.cpp create mode 100644 xo-reader/examples/exprrepl/exprrepl.cpp create mode 100644 xo-reader/examples/exprreplxx/CMakeLists.txt create mode 100644 xo-reader/examples/exprreplxx/exprreplxx.cpp create mode 100644 xo-reader/include/xo/reader/apply_xs.hpp create mode 100644 xo-reader/include/xo/reader/define_xs.hpp create mode 100644 xo-reader/include/xo/reader/envframe.hpp create mode 100644 xo-reader/include/xo/reader/envframestack.hpp create mode 100644 xo-reader/include/xo/reader/expect_expr_xs.hpp create mode 100644 xo-reader/include/xo/reader/expect_formal_arglist_xs.hpp create mode 100644 xo-reader/include/xo/reader/expect_formal_xs.hpp create mode 100644 xo-reader/include/xo/reader/expect_symbol_xs.hpp create mode 100644 xo-reader/include/xo/reader/expect_type_xs.hpp create mode 100644 xo-reader/include/xo/reader/exprseq_xs.hpp create mode 100644 xo-reader/include/xo/reader/exprstate.hpp create mode 100644 xo-reader/include/xo/reader/exprstatestack.hpp create mode 100644 xo-reader/include/xo/reader/formal_arg.hpp create mode 100644 xo-reader/include/xo/reader/if_else_xs.hpp create mode 100644 xo-reader/include/xo/reader/lambda_xs.hpp create mode 100644 xo-reader/include/xo/reader/let1_xs.hpp create mode 100644 xo-reader/include/xo/reader/paren_xs.hpp create mode 100644 xo-reader/include/xo/reader/parser.hpp create mode 100644 xo-reader/include/xo/reader/parser_result.hpp create mode 100644 xo-reader/include/xo/reader/parserstatemachine.hpp create mode 100644 xo-reader/include/xo/reader/pretty_envframestack.hpp create mode 100644 xo-reader/include/xo/reader/pretty_exprstatestack.hpp create mode 100644 xo-reader/include/xo/reader/pretty_parserstatemachine.hpp create mode 100644 xo-reader/include/xo/reader/progress_xs.hpp create mode 100644 xo-reader/include/xo/reader/reader.hpp create mode 100644 xo-reader/include/xo/reader/reader_error.hpp create mode 100644 xo-reader/include/xo/reader/sequence_xs.hpp create mode 100644 xo-reader/src/reader/CMakeLists.txt create mode 100644 xo-reader/src/reader/apply_xs.cpp create mode 100644 xo-reader/src/reader/define_xs.cpp create mode 100644 xo-reader/src/reader/envframe.cpp create mode 100644 xo-reader/src/reader/envframestack.cpp create mode 100644 xo-reader/src/reader/expect_expr_xs.cpp create mode 100644 xo-reader/src/reader/expect_formal_arglist_xs.cpp create mode 100644 xo-reader/src/reader/expect_formal_xs.cpp create mode 100644 xo-reader/src/reader/expect_symbol_xs.cpp create mode 100644 xo-reader/src/reader/expect_type_xs.cpp create mode 100644 xo-reader/src/reader/exprseq_xs.cpp create mode 100644 xo-reader/src/reader/exprstate.cpp create mode 100644 xo-reader/src/reader/exprstatestack.cpp create mode 100644 xo-reader/src/reader/if_else_xs.cpp create mode 100644 xo-reader/src/reader/lambda_xs.cpp create mode 100644 xo-reader/src/reader/let1_xs.cpp create mode 100644 xo-reader/src/reader/paren_xs.cpp create mode 100644 xo-reader/src/reader/parser.cpp create mode 100644 xo-reader/src/reader/parser_result.cpp create mode 100644 xo-reader/src/reader/parserstatemachine.cpp create mode 100644 xo-reader/src/reader/pretty_parserstatemachine.cpp create mode 100644 xo-reader/src/reader/progress_xs.cpp create mode 100644 xo-reader/src/reader/reader.cpp create mode 100644 xo-reader/src/reader/sequence_xs.cpp create mode 100644 xo-reader/utest/CMakeLists.txt create mode 100644 xo-reader/utest/parser.test.cpp create mode 100644 xo-reader/utest/reader.test.cpp create mode 100644 xo-reader/utest/reader_utest_main.cpp diff --git a/xo-reader/.gitignore b/xo-reader/.gitignore new file mode 100644 index 00000000..3d3a7826 --- /dev/null +++ b/xo-reader/.gitignore @@ -0,0 +1,8 @@ +# emacs workspace config +.projectile +# clangd working space (see emacs+lsp) +.cache +# typical cmake build directory (source-tree-nephew) +.build* +# symlink to builddir/compile_commands.json; should be set manually in dev sandbox +compile_commands.json diff --git a/xo-reader/.gitrepo b/xo-reader/.gitrepo new file mode 100644 index 00000000..da04c6dc --- /dev/null +++ b/xo-reader/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/ingydotnet/git-subrepo#readme +; +[subrepo] + remote = git@github.com:Rconybea/xo-reader.git + branch = main + commit = cb652195d3fa3774651c746a621e16f30481c22e + parent = f5828f616346f52e282cf86ef8dbc5901b8f5aad + method = merge + cmdver = 0.4.9 diff --git a/xo-reader/CMakeLists.txt b/xo-reader/CMakeLists.txt new file mode 100644 index 00000000..5c9c7885 --- /dev/null +++ b/xo-reader/CMakeLists.txt @@ -0,0 +1,39 @@ +# xo-reader/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_reader VERSION 0.1) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings + +set(PROJECT_CXX_FLAGS "-ftemplate-backtrace-limit=0") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- + +add_subdirectory(src/reader) +add_subdirectory(examples) +#add_subdirectory(utest) # test failing, temporarily removing +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# ---------------------------------------------------------------- + +if (XO_ENABLE_EXAMPLES) + install(TARGETS xo_expression_repl DESTINATION bin/xo/example/reader) + install(TARGETS xo_expression_replxx DESTINATION bin/xo/example/reader) +endif() + +# ---------------------------------------------------------------- +# docs targets depends on other library/utest/exec targets, +# must come after them +# +add_subdirectory(docs) + +# end CMakeLists.txt diff --git a/xo-reader/README b/xo-reader/README new file mode 100644 index 00000000..2c4a55b7 --- /dev/null +++ b/xo-reader/README @@ -0,0 +1,53 @@ +1. in parser we need.. type unification? +2. or perhaps just need to introduce type variables. + +3. we can't resolve type for a recursive pair of functions until we've seen both of them.. + tho we could require letrec + +def foo = lambda (n : i64) { let (n == 0) then 1 else n * foo(n - 1); }; + +strategy: +-------- + +while parsing def, instead of creating DefineExpr with nullptr TypeDescr: +a. create DefineExpr with TypeVariable. +(We have to do this because can't tell nullptr's apart,..) + +- generalize xo::reflect::TypeDescrBase + +// compose these into Expressions. +// +struct type_ref { + bool is_concrete() const { return td_ && td_->is_concrete(); } + + // generated name, so we can map between types. Don't want to create TypeDescr + // every time we have a typed location. there'd be a lot of them, and hard to collect + flatstring<11> id_; + // if TypeDescr is concrete (fully described), this describes it + TypeDescr td_; +}; + +// what we know about a type +// +struct TypeBlueprint : public Refcounted { + static bool equal(bp lhs, bp rhs); + + type_ref ref_; + + // additional descriptive info... +}; + +// effectively these are constraints? +// +using TypeSubstitutionMap = map>; + +will have typeunifier in parserstatemachine + +struct TypeUnifier { + // extend as unification proceeds + // + TypeSubstitutionMap substitutions_; +}; + +alt strategy +------------- diff --git a/xo-reader/cmake/xo-bootstrap-macros.cmake b/xo-reader/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..aba31169 --- /dev/null +++ b/xo-reader/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/xo-reader/cmake/xo_readerConfig.cmake.in b/xo-reader/cmake/xo_readerConfig.cmake.in new file mode 100644 index 00000000..94b1811e --- /dev/null +++ b/xo-reader/cmake/xo_readerConfig.cmake.in @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(xo_expression) +find_dependency(xo_tokenizer) +#find_dependency(subsys) +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Share.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/xo-reader/docs/CMakeLists.txt b/xo-reader/docs/CMakeLists.txt new file mode 100644 index 00000000..05b7681c --- /dev/null +++ b/xo-reader/docs/CMakeLists.txt @@ -0,0 +1,7 @@ +# xo-reader/docs/CMakeLists.txt + +xo_doxygen_collect_deps() +xo_docdir_doxygen_config() +xo_docdir_sphinx_config( + index.rst example.rst install.rst implementation.rst +) diff --git a/xo-reader/docs/README b/xo-reader/docs/README new file mode 100644 index 00000000..6aff5d41 --- /dev/null +++ b/xo-reader/docs/README @@ -0,0 +1,41 @@ +standalone build + + +-----------------------------------------------+ + | cmake | + | CMakeLists.txt | + | $PREFIX/share/cmake/xo_macros/xo_cxx.cmake | + +-----------------------------------------------+ + | + | +----------------------+ + +------------------------------------------------->| .build/docs/Doxyfile | + | +----------------------+ + | ^ + | (cmake) | + | /------------/ + | | + | +---------------------------------------+ +-----------------+ + +---->| doxygen |--------->| .build/docs/dox | + | | $PREFIX/share/xo-macros/Doxyfile.in | (doxygen)| +- html/ | + | +---------------------------------------+ | +- xml/ | + | +-----------------+ + | | + | |(sphinx) + | | + | v + | +---------------------------------------+ +--------------------+ + \---->| sphinx |------->| .build/docs/sphinx | + | +- conf.py | | +- html/ | + | +- _static/ | +--------------------+ + | +- *.rst | + +---------------------------------------+ + +umbrella build relies on top-level cmake macros + +files + + README this file + CMakeLists.txt build entry point + conf.py sphinx config + _static static files for sphinx + + index.rst toplevel sphinx document; entry point diff --git a/xo-reader/docs/_static/README b/xo-reader/docs/_static/README new file mode 100644 index 00000000..8230095c --- /dev/null +++ b/xo-reader/docs/_static/README @@ -0,0 +1 @@ +add any static {.html, .js, ..} files for sphinx to pickup here \ No newline at end of file diff --git a/xo-reader/docs/_static/img/favicon.ico b/xo-reader/docs/_static/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..15da2145f93eb26e6995c7868ab7883fb2361288 GIT binary patch literal 309803 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 uml diagrams + ] + +# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in +# match project name in Doxyfile.in +breathe_default_project = "xodoxxml" + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +pygments_style = 'sphinx' + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +#html_theme = 'alabaster' +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] +html_favicon = '_static/img/favicon.ico' diff --git a/xo-reader/docs/example.rst b/xo-reader/docs/example.rst new file mode 100644 index 00000000..12a3b1c2 --- /dev/null +++ b/xo-reader/docs/example.rst @@ -0,0 +1,45 @@ +.. _examples: + +.. toctree:: + :maxdepth: 2 + +Examples +======== + +See ``xo-reader/examples`` for built examples + +.. code-block:: cpp + :linenos: + + #include "xo/reader/reader.h" + + int + main() { + using namespace xo::scm; + using namespace std; + + reader rdr; + rdr.begin_translation_unit(); + + bool eof = false; + while (!eof) { + auto input = ins.read_some(); + + eof = ins.eof(); + + for (auto rem = input; !rem.empty();) { + // res: (parsed-expr, used) + auto [expr, rem2, _] = rdr.read_expr(rem, eof); + + if (expr) { + cout << expr << endl; + } + + rem = rem.suffix_after(rem2); + } + } + + if (rdr.has_prefix()) { + cout << "error: unparsed input after expression" << endl; + } + } diff --git a/xo-reader/docs/implementation.rst b/xo-reader/docs/implementation.rst new file mode 100644 index 00000000..d148c6a9 --- /dev/null +++ b/xo-reader/docs/implementation.rst @@ -0,0 +1,137 @@ +.. _implementation: + +.. toctree:: + :maxdepth: 2 + +Components +========== + +Library dependency tower for *xo-reader*: + +.. ditaa:: + + +------------------------------------------+ + | xo_reader | + +-------------------------+----------------+ + | xo_expression | | + +-------------------------+ | + | xo_reflect | xo_tokenizer | + +-----------+-------------+ | + | | xo_refcnt | | + | xo_subsys +-------------+----------------+ + | | xo_indentlog | + +-----------+------------------------------+ + +Install instructions :doc:`here` + +Abstraction tower for *xo-reader* components: + +.. ditaa:: + :--scale: 0.85 + + +--------------------------------+ + | reader | + +--------------------------------+ + | parser | + +----------------+---------------+ + | exprstatestack | envframestack | + +----------------+---------------+ + | exprstate | envframe | + +----------------+---------------+ + +``exprstate`` provides an abstract api. +We use runtime polymorphism to represent concrete parsing states. +Different expression types inherit from ``exprstate`` to encapsulate +parsing for each expression type. + +.. uml:: + :caption: exprstate + :scale: 99% + :align: center + + class exprstate { + } + + class define_xs { + } + + exprstate <|-- define_xs + + class lambda_xs { + } + + exprstate <|-- lambda_xs + + class exprseq_xs { + } + + exprstate <|-- exprseq_xs + + class let1_xs { + } + + exprstate <|-- let1_xs + + class paren_xs { + } + + exprstate <|-- paren_xs + + class sequence_xs { + } + + exprstate <|-- sequence_xs + +There are also classes for nested state machines: + +.. uml:: + :caption: exprstate + :scale: 99% + :align: center + + class exprstate { + } + + class progress_xs { + } + + exprstate <|-- progress_xs + + class expect_symbol_xs { + } + + exprstate <|-- expect_symbol_xs + + class expect_type_xs { + } + + exprstate <|-- expect_type_xs + + class expect_expr_xs { + } + + exprstate <|-- expect_expr_xs + + class expect_formal_xs { + } + + exprstate <|-- expect_formal_xs + + class expect_formal_arglist_xs { + } + + exprstate <|-- expect_formal_arglist_xs + +Putting these in context: + +.. list-table:: Schematika Parsing States + :widths: 15 30 + :header-rows: 1 + + * - exprstate class + - target syntax + * - define_xs + - ``def foo : f64 = 1;``, ``def sq = lambda (x : i64) { x * x; }`` + * - progress_xs + - possibly-incomplete arithmetic expressions + ``(a + b) * 7``.. diff --git a/xo-reader/docs/index.rst b/xo-reader/docs/index.rst new file mode 100644 index 00000000..04446576 --- /dev/null +++ b/xo-reader/docs/index.rst @@ -0,0 +1,14 @@ +.. xo-reader documentation master file. + +xo-reader documentation +======================= + +xo-reader provides a parser for the Schematika language. + +.. toctree:: + :maxdepth: 2 + :caption: xo-reader contents + + install + example + implementation diff --git a/xo-reader/docs/install.rst b/xo-reader/docs/install.rst new file mode 100644 index 00000000..0f008815 --- /dev/null +++ b/xo-reader/docs/install.rst @@ -0,0 +1,146 @@ +.. _install: + +.. toctree:: + :maxdepth: 2 + +Source +====== + +Source code lives on github `here`_ + +.. _here: https://github.com/rconybea/xo-reader + +To clone from git: + +.. code-block:: bash + + git clone https://github.com/rconybea/xo-reader + +Tested with gcc 13.3 + +Install +======= + +One-step Install +---------------- + +Install along with the rest of *XO* from `xo-umbrella2 source`_: +see install instructions for xo-umrbella2 + +.. _xo-umbrella2 source: https://github.com/rconybea/xo-umbrella2 + +Essential Xo Dependencies +------------------------- + +``xo-reader`` uses several supporting libraries from the *XO* project: + +- `xo-expression source`_ (Schematika AST representation) +- `xo-tokenizer source`_ (Schematika lexer) +- `xo-reflect source`_ (reflection library) +- `xo-refcnt source`_ (reference-counting library) +- `xo-indentlog source`_ (structured logging) +- `xo-subsys source`_ (utility library) +- `xo-cmake source`_ (shared cmake macros) + +.. _xo-expression source: https://github.com/rconybea/xo-expression +.. _xo-tokenizer source: https://github.com/rconybea/xo-tokenizer +.. _xo-reflect source: https://github.com/rconybea/xo-reflect +.. _xo-refcnt source: https://github.com/rconybea/refcnt +.. _xo-indentlog source: https://github.com/rconybea/indentlog +.. _xo-subsys source: https://github.com/rconybea/subsys +.. _xo-cmake source: https://github.com/rconybea/xo-cmake + +Building from source +-------------------- + +Install scripts for XO libraries depend on helper scripts installed from `xo-cmake`. + +Preamble: + +.. code-block:: bash + + mkdir -p ~/proj/xo + cd ~/proj/xo + + git clone https://github.com/rconybea/xo-cmake + + PREFIX=/usr/local # ..or desired installation prefix + + # want PREFIX/bin in PATH to use xo-cmake helpers + PATH=$PREFIX/bin:$PATH + +Install `xo-cmake`: + +.. code-block:: bash + + cmake -B xo-cmake/.build -S xo-cmake + cmake --install xo-cmake/.build + +Install dependencies in topological order: + +.. code-block:: bash + + xo-build --clone --configure --build --install xo-indentlog + xo-build --clone --configure --build --install xo-subsys + xo-build --clone --configure --build --install xo-refcnt + xo-build --clone --configure --build --install xo-reflect + xo-build --clone --configure --build --install xo-expression + xo-build --clone --configure --build --install xo-tokenizer + xo-build --clone --configure --build --install xo-reader + +Directories under ``PREFIX`` will then contain: + +.. code-block:: + + PREFIX + +- bin + | +- xo-build + | +- xo-cmake-config + | \- xo-cmake-lcov-harness + +- include + | \- xo + | +- cxxutil/ + | +- expression/ + | +- indentlog/ + | +- reader/ + | +- refcnt/ + | +- reflect/ + | +- subsys/ + | \- tokenizer/ + +- lib + | +- cmake + | | +- indentlog/ + | | +- refcnt/ + | | +- reflect/ + | | +- subsys/ + | | +- xo_expression/ + | | +- xo_reader/ + | | \- xo_tokenizer/ + | +- lib*.so + +- share + +- cmake + | \- xo_macros + | +- code-coverage.cmake + | +- xo-project-macros.cmake + | \- xo_cxx.cmake + +- etc + | \- xo + | \- subsystem-list + \- xo-macros + +- Doxyfile.in + +- gen-ccov.in + \- xo-bootstrap-macros.cmake + +CMake Support +------------- + +To use built-in cmake support, when using ``xo-reader`` from another project: + +Make sure ``PREFIX/lib/cmake`` is searched by cmake (for example include it in ``CMAKE_PREFIX_PATH``) + +Add to your ``CMakeLists.txt``: + +.. code-block:: cmake + + FindPackage(xo_reader CONFIG REQUIRED) + target_link_libraries(mytarget INTERFACE xo_reader) diff --git a/xo-reader/examples/CMakeLists.txt b/xo-reader/examples/CMakeLists.txt new file mode 100644 index 00000000..faab007d --- /dev/null +++ b/xo-reader/examples/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(exprrepl) +add_subdirectory(exprreplxx) diff --git a/xo-reader/examples/exprrepl/CMakeLists.txt b/xo-reader/examples/exprrepl/CMakeLists.txt new file mode 100644 index 00000000..003c9cb0 --- /dev/null +++ b/xo-reader/examples/exprrepl/CMakeLists.txt @@ -0,0 +1,11 @@ +# xo-reader/example/exprrepl/CMakeLists.txt + +set(SELF_EXE xo_expression_repl) +set(SELF_SRCS exprrepl.cpp) + +if (XO_ENABLE_EXAMPLES) + xo_add_executable(${SELF_EXE} ${SELF_SRCS}) + xo_self_dependency(${SELF_EXE} xo_reader) +endif() + +# end CMakeLists.txt diff --git a/xo-reader/examples/exprrepl/expreplxx.cpp b/xo-reader/examples/exprrepl/expreplxx.cpp new file mode 100644 index 00000000..e69de29b diff --git a/xo-reader/examples/exprrepl/exprrepl.cpp b/xo-reader/examples/exprrepl/exprrepl.cpp new file mode 100644 index 00000000..64e1c977 --- /dev/null +++ b/xo-reader/examples/exprrepl/exprrepl.cpp @@ -0,0 +1,91 @@ +/** @file exprrepl.cpp **/ + +#include "xo/reader/reader.hpp" +#include +#include // for isatty + +bool repl_getline(bool interactive, std::size_t parser_stack_size, std::istream& in, std::ostream& out, std::string& input) +{ + using namespace std; + + if (interactive) { + if (parser_stack_size <= 1) + out << "> "; + else + out << ". "; + std::flush(out); + } + + bool retval = static_cast(getline(in, input)); + + if (retval) { + cerr << "got reval->true" << endl; + + // interactive only: treat ^@ (C-RET) as ;RET + if ((input.size() > 0) && ((*input.rbegin()) == '\0')) + { + cerr << "got ^@" << endl; + + *input.rbegin() = ';'; + } + + // want reader to see newline, it's syntax + input.push_back('\n'); + } else { + cerr << "got retval->false" << endl; + } + + return retval; +} + +int +main() { + using namespace xo::scm; + using namespace std; + + using span_type = xo::scm::span; + + bool interactive = isatty(STDIN_FILENO); + bool c_debug_flag = false; + + auto toplevel_symtab = GlobalSymtab::make_empty(); + + reader rdr(toplevel_symtab, c_debug_flag); + rdr.begin_interactive_session(); + + string input_str; + + bool eof = false; + + span_type input; + std::size_t parser_stack_size = 0; + + while (repl_getline(interactive, parser_stack_size, cin, cout, input_str)) { + input = span_type::from_string(input_str); + + while (!input.empty()) { + auto [expr, consumed, psz, error] = rdr.read_expr(input, eof); + + if (expr) { + cout << expr << endl; + } else if (error.is_error()) { + cout << "parsing error: " << endl; + error.report(cout); + + break; + } + + input = input.after_prefix(consumed); + parser_stack_size = psz; + } + } + + auto [expr, _1, _2, error] = rdr.read_expr(input, true /*eof*/); + + if (expr) { + cout << expr << endl; + } else if (error.is_error()) { + cout << "parsing error: " << endl; + error.report(cout); + } +} diff --git a/xo-reader/examples/exprreplxx/CMakeLists.txt b/xo-reader/examples/exprreplxx/CMakeLists.txt new file mode 100644 index 00000000..c7365632 --- /dev/null +++ b/xo-reader/examples/exprreplxx/CMakeLists.txt @@ -0,0 +1,16 @@ +# xo-reader/example/exprreplxx/CMakeLists.txt + +set(SELF_EXE xo_expression_replxx) +set(SELF_SRCS exprreplxx.cpp) + +if (XO_ENABLE_EXAMPLES) + xo_add_executable(${SELF_EXE} ${SELF_SRCS}) + xo_self_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) + #xo_external_target_dependency(${SELF_EXE} Threads Threads::Threads) +endif() + +# end CMakeLists.txt diff --git a/xo-reader/examples/exprreplxx/exprreplxx.cpp b/xo-reader/examples/exprreplxx/exprreplxx.cpp new file mode 100644 index 00000000..e9d8079c --- /dev/null +++ b/xo-reader/examples/exprreplxx/exprreplxx.cpp @@ -0,0 +1,143 @@ +/** @file exprreplxx.cpp **/ + +#include "xo/reader/reader.hpp" +#include +#include +#include // for isatty + +// presumeably replxx assumes input is a tty +// +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 = ". "; + } + + const char * input_cstr = rx.input(prompt); + + bool retval = (input_cstr != nullptr); + + if (retval) { + //cerr << "got reval->true" << endl; + + input = input_cstr; + + } else { + //cerr << "got retval->false" << endl; + } + + 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; + + rp toplevel_symtab = GlobalSymtab::make_empty(); + + reader rdr(toplevel_symtab, 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"); +} diff --git a/xo-reader/include/xo/reader/apply_xs.hpp b/xo-reader/include/xo/reader/apply_xs.hpp new file mode 100644 index 00000000..14cf234c --- /dev/null +++ b/xo-reader/include/xo/reader/apply_xs.hpp @@ -0,0 +1,119 @@ +/* @file apply_xs.hpp */ + +#pragma once + +#include "exprstate.hpp" +#include "xo/expression/Apply.hpp" + + +namespace xo { + namespace scm { + /** + * fn ( arg1 , arg2 , .. , argn ) + * ^ ^ ^ ^ ^ ^ ^ ^ ^ + * | | | | | | | | (done) + * | | | | | | | apply_3 + * | | | | | | apply_2:expect_rhs_expression + * | | | | | apply_3 + * | | | | apply_2:expect_rhs_expression + * | | | apply_3 + * | | apply_2:expect_rhs_expression + * | apply_1 + * apply_0:expect_rhs_expression + * + * apply_0 --on_expr()--> apply_1 + * apply_1 --on_leftparen()--> apply_2 + * apply_2 --on_expr()--> apply_3 + * apply_3 --on_comma()--> apply_2 + * --on_rightparen()-> (done) + * + * apply_0: start + * apply_1: leftparen following expr allows parser to recognize apply + * apply_2: expect next argument + * apply_3: got argument, expect comma or rightparen to continue + * (done): apply complete, pop exprstate from stack + * + * In practice will start in state apply_1 + **/ + enum class applyexprstatetype { + invalid = -1, + + apply_0, + apply_1, + apply_2, + apply_3, + + n_applyexprstatetype + }; + + extern const char * applyexprstatetype_descr(applyexprstatetype x); + + std::ostream & + operator<<(std::ostream & os, applyexprstatetype x); + + /** @class apply_xs + * @brief state machine for parsing a schematic function-call-expression + * + */ + class apply_xs : public exprstate { + public: + using Apply = xo::scm::Apply; + + public: + explicit apply_xs(); + virtual ~apply_xs() = default; + + /** downcast from parent type **/ + static const apply_xs * from(const exprstate * x) { + return dynamic_cast(x); + } + + /** + * Start apply. Will trigger this after input like + * "fn(" + * + * apply_xs remains on expr stack until closing right paren + * fn(arg1-expr, arg2-expr, ...) + * + * @p fnex expression in function position + * @p p_psm parser state machine + **/ + static void start(rp fnex, + parserstatemachine * p_psm); + + virtual const char * get_expect_str() const override; + + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + + virtual void on_comma_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + virtual bool pretty_print(const print::ppindentinfo & ppii) const final override; + + private: + static std::unique_ptr make(); + + private: + /** current state of parser for this apply expression **/ + applyexprstatetype applyxs_type_ = applyexprstatetype::apply_0; + /** evaluates to function to be invoked **/ + rp fn_expr_; + /** evaluates to the arguments to pass to @ref fn_ **/ + std::vector> args_expr_v_; + }; + } /*namespace scm */ + + namespace print { +#ifndef ppdetail_atomic + PPDETAIL_ATOMIC(xo::scm::applyexprstatetype); +#endif + } +} /*namespace xo*/ + +/* end apply_xs.hpp */ diff --git a/xo-reader/include/xo/reader/define_xs.hpp b/xo-reader/include/xo/reader/define_xs.hpp new file mode 100644 index 00000000..d10a080f --- /dev/null +++ b/xo-reader/include/xo/reader/define_xs.hpp @@ -0,0 +1,135 @@ +/** @file define_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "xo/expression/DefineExpr.hpp" +#include "xo/expression/ConvertExpr.hpp" +#include "exprstate.hpp" +#include "xo/indentlog/print/ppdetail_atomic.hpp" +//#include + +namespace xo { + namespace scm { + /** + * 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 + * + **/ + 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 define_xs + * @brief state to provide parsing of a define-expression + **/ + class define_xs : public exprstate { + public: + using DefineExprAccess = xo::scm::DefineExprAccess; + using ConvertExprAccess = xo::scm::ConvertExprAccess; + + public: + define_xs(rp def_expr); + virtual ~define_xs() = default; + + static const define_xs * from(const exprstate * x) { return dynamic_cast(x); } + + static void start(parserstatemachine * p_psm); + + defexprstatetype defxs_type() const { return defxs_type_; } + bp lhs_variable() const { return def_expr_->lhs_variable(); } + + /** @return expected input in current state **/ + virtual const char * get_expect_str() const override; + + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + virtual void on_symbol(const std::string & symbol_name, + parserstatemachine * p_psm) override; + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm) override; + virtual void on_def_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_colon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_singleassign_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_i64_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_f64_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + virtual bool pretty_print(const print::ppindentinfo & ppii) const override; + + private: + static std::unique_ptr make(); + + private: + defexprstatetype defxs_type_; + /** scaffold a define-expression here **/ + rp def_expr_; + /** scafford a convert-expression here. + * May be nested within a def_expr + **/ + rp cvt_expr_; + }; + } /*namespace scm*/ + +#ifndef ppdetail_atomic + namespace print { + PPDETAIL_ATOMIC(xo::scm::defexprstatetype); + } +#endif +} /*namespace xo*/ + + +/** end define_xs.hpp **/ diff --git a/xo-reader/include/xo/reader/envframe.hpp b/xo-reader/include/xo/reader/envframe.hpp new file mode 100644 index 00000000..0dbd96e5 --- /dev/null +++ b/xo-reader/include/xo/reader/envframe.hpp @@ -0,0 +1,60 @@ +/* file envframe.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +xxx; + +#include "xo/expression/Variable.hpp" +#include "xo/expression/LocalSymtab.hpp" +#include + +namespace xo { + namespace scm { +#ifdef OBSOLETE + /** @class envframe + * @brief names/types of formal paremeters introduced by a function + * + * + **/ + class envframe { + public: + using Environment = xo::scm::Environment; + using LocalEnv = xo::scm::LocalEnv; + using Variable = xo::scm::Variable; + + public: + envframe() = default; + envframe(const std::vector> & argl, const rp & parent_env); + + const std::vector> & argl() const { return env_->argv(); } + const rp & local_env() const { return env_; } + + /** lookup variable by @p name. If found, return it. + * Otherwise return nullptr + **/ + bp lookup(const std::string & name) const; + + /** establish (replacing if already exists) binding for variable @p var. + * Replacement intended for use in interactive sessions + **/ + void upsert(bp var); + + void print (std::ostream & os) const; + + private: + rp env_; + }; + + inline std::ostream & + operator<< (std::ostream & os, const envframe & x) { + x.print(os); + return os; + } +#endif + } /*namespace scm*/ +} /*namespace xo*/ + +/* end envframe.hpp */ diff --git a/xo-reader/include/xo/reader/envframestack.hpp b/xo-reader/include/xo/reader/envframestack.hpp new file mode 100644 index 00000000..02194113 --- /dev/null +++ b/xo-reader/include/xo/reader/envframestack.hpp @@ -0,0 +1,87 @@ +/* file envframestack.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "xo/expression/LocalSymtab.hpp" + +namespace xo { + namespace scm { + /** @class envframestack + * @brief A stack of envframe objects + **/ + class envframestack { + public: + using ppstate = xo::print::ppstate; + using ppindentinfo = xo::print::ppindentinfo; + + public: + envframestack() {} + + bool empty() const { return stack_.empty(); } + std::size_t size() const { return stack_.size(); } + + /** lookup variable in environment stack. + * Visit frames in fifo order, report first match; + * nullptr if no matches. + **/ + bp lookup(const std::string & x) const; + + /** update/replace binding for variable @p target. + * New binding may have a different type. + **/ + void upsert(bp target); + + bp top_envframe() const; + void push_envframe(const rp & x); + rp pop_envframe(); + + void reset_to_toplevel() { stack_.resize(1); } + + /** relative to top-of-stack. + * 0 -> top (last in), z-1 -> bottom (first in) + **/ + bp operator[](std::size_t i) { + std::size_t z = stack_.size(); + + assert(i < z); + + return stack_[z - i - 1].get(); + } + + bp operator[](std::size_t i) const { + std::size_t z = stack_.size(); + + assert(i < z); + + return stack_[z - i - 1].get(); + } + + void print (std::ostream & os) const; + bool pretty_print(const ppindentinfo & ppii) const; + + private: + std::vector> stack_; + }; + + inline std::ostream & + operator<< (std::ostream & os, const envframestack & x) { + x.print(os); + return os; + } + + inline std::ostream & + operator<< (std::ostream & os, const envframestack * x) { + if (x) + x->print(os); + else + os << "nullptr"; + return os; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end envframestack.hpp */ diff --git a/xo-reader/include/xo/reader/expect_expr_xs.hpp b/xo-reader/include/xo/reader/expect_expr_xs.hpp new file mode 100644 index 00000000..6f6513e3 --- /dev/null +++ b/xo-reader/include/xo/reader/expect_expr_xs.hpp @@ -0,0 +1,106 @@ +/* file expect_expr_xs.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" + +namespace xo { + namespace scm { + + /** @class expect_expr_xs + * @brief state machine to expect + capture an expression + * + * Examples: + * @text + * add (1, 2) ; + * def x : i64 = 5 ; // with allow_defs + * lambda (f : x64) { ... } ; + * if (prime(x)) { ... } ; + * 5 + x ; + * @endtext + * + * top exprstate when expect_expr_xs::start() invoked + * will receive parsed expression via + * exprstate::on_expr() or exprstate::on_expr_with_semicolon(). + **/ + class expect_expr_xs : public exprstate { + public: + explicit expect_expr_xs(bool allow_defs, + bool cxl_on_rightbrace); + + static void start(parserstatemachine * p_psm); + static void start(bool allow_defs, + bool cxl_on_rightbrace, + parserstatemachine * p_psm); + + virtual const char * get_expect_str() const override; + + virtual void on_lambda_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_if_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_def_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_leftbrace_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_bool_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_i64_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_f64_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_string_token(const token_type & tk, + parserstatemachine * p_psm) final override; + + /** update exprstate in response to a successfully-parsed subexpression **/ + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + /** update exprstate in response to a successfully-parsed subexpression + * that's terminated by semicolon ';' + **/ + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + virtual bool pretty_print(const xo::print::ppindentinfo & ppii) const override; + + private: + static std::unique_ptr make(bool allow_defs, + bool cxl_on_rightbrace); + + private: + /** if true: allow a define-expression here **/ + bool allow_defs_ = false; + /** if true: expecting either: + * - expression + * - right brace '}', in which case no expression + * if false: expecting + * - expression + **/ + bool cxl_on_rightbrace_ = false; + }; + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end expect_expr_xs.hpp */ diff --git a/xo-reader/include/xo/reader/expect_formal_arglist_xs.hpp b/xo-reader/include/xo/reader/expect_formal_arglist_xs.hpp new file mode 100644 index 00000000..509fe194 --- /dev/null +++ b/xo-reader/include/xo/reader/expect_formal_arglist_xs.hpp @@ -0,0 +1,85 @@ +/* file expect_formal_arglist_xs.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" +#include "formal_arg.hpp" +#include + +namespace xo { + namespace scm { + /** + * ( name(1) : type(1) , ..., ) + * ^ ^ ^ ^ ^ + * | | | | | + * | | | | argl_1b + * | argl_1a | argl_1a + * argl_0 argl_1b + * + * argl_0 --on_leftparen_token()--> argl_1a + * argl_1a --on_formal()--> argl_1b + * argl_1b -+-on_comma_token()--> argl_1a + * \-on_rightparen_token()--> (done) + **/ + enum class formalarglstatetype { + invalid = -1, + + argl_0, + argl_1a, + argl_1b, + + n_formalarglstatetype, + }; + + extern const char * + formalarglstatetype_descr(formalarglstatetype x); + + inline std::ostream & + operator<< (std::ostream & os, formalarglstatetype x) { + os << formalarglstatetype_descr(x); + return os; + } + + /** @class expect_formal_arglist + * @brief parser state-machine for a formal parameter list + **/ + class expect_formal_arglist_xs : public exprstate { + public: + using Variable = xo::scm::Variable; + + public: + expect_formal_arglist_xs(); + + static void start(parserstatemachine * p_psm); + + virtual const char * get_expect_str() const override; + + virtual void on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_formal(const rp & formal, + parserstatemachine * p_psm) override; + virtual void on_comma_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void print(std::ostream & os) const override; + + private: + static std::unique_ptr make(); + + private: + /** parsing state-machine state **/ + formalarglstatetype farglxs_type_ = formalarglstatetype::argl_0; + /** populate with (parmaeter-name, parameter-type) list + * as they're encountered + **/ + std::vector> argl_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end expect_formal_arglist_xs.hpp */ diff --git a/xo-reader/include/xo/reader/expect_formal_xs.hpp b/xo-reader/include/xo/reader/expect_formal_xs.hpp new file mode 100644 index 00000000..97d781ee --- /dev/null +++ b/xo-reader/include/xo/reader/expect_formal_xs.hpp @@ -0,0 +1,85 @@ +/* file expect_formal_xs.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" +#include "formal_arg.hpp" + +namespace xo { + namespace scm { + /** + * name : type + * ^ ^ ^ + * | | formal_2 + * | formal_1 + * formal_0 + * + * formal_0 --on_symbol()--> formal_1 + * formal_1 --on_colon_token()--> formal_2 + * formal_2 --on_typedescr()--> (done) + **/ + enum class formalstatetype { + invalid = -1, + + formal_0, + formal_1, + formal_2, + + n_formalstatetype, + }; + + extern const char * + formalstatetype_descr(formalstatetype x); + + inline std::ostream & + operator<< (std::ostream & os, formalstatetype x) { + os << formalstatetype_descr(x); + return os; + } + + /** @class expect_formal_xs + * @brief parser state-machine for a typed formal parameter + **/ + class expect_formal_xs : public exprstate { + public: + expect_formal_xs(); + + static void start(parserstatemachine * p_psm); + + virtual void on_symbol(const std::string & symbol_name, + parserstatemachine * p_psm) override; + + virtual void on_colon_token(const token_type & tk, + parserstatemachine * p_psm) override; + + // virtual void on_comma_token(...) override; + +#ifdef PROBABLY_NOT + virtual void on_rightparen_token(const token_type & tk, + exprstatestack * p_stack, + rp * p_emit_expr) override; +#endif + + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + + private: + static std::unique_ptr make(); + + private: + /** parsing state-machine state **/ + formalstatetype formalxs_type_ = formalstatetype::formal_0; + /** populate with {parameter-name, parameter-type} + * as they're encountered + **/ + formal_arg result_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + +/* end expect_formal_xs.hpp */ diff --git a/xo-reader/include/xo/reader/expect_symbol_xs.hpp b/xo-reader/include/xo/reader/expect_symbol_xs.hpp new file mode 100644 index 00000000..be4df5e4 --- /dev/null +++ b/xo-reader/include/xo/reader/expect_symbol_xs.hpp @@ -0,0 +1,32 @@ +/* file expect_symbol_xs.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" + +namespace xo { + namespace scm { + /** @class expect_symbol_xs + * @brief state machine to expect + capture a symbol + * + * For example, lhs in a define-expression + **/ + class expect_symbol_xs : public exprstate { + public: + expect_symbol_xs(); + + static std::unique_ptr make(); + + static void start(parserstatemachine * p_psm); + + virtual void on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) override; + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end expect_symbol_xs.hpp */ diff --git a/xo-reader/include/xo/reader/expect_type_xs.hpp b/xo-reader/include/xo/reader/expect_type_xs.hpp new file mode 100644 index 00000000..28fea322 --- /dev/null +++ b/xo-reader/include/xo/reader/expect_type_xs.hpp @@ -0,0 +1,33 @@ +/* file expect_type_xs.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" + +namespace xo { + namespace scm { + /** @class expect_type_xs + * @brief state-machine for accepting a typename-expression + **/ + class expect_type_xs : public exprstate { + public: + expect_type_xs(); + + static void start(parserstatemachine * p_stack); + + virtual const char * get_expect_str() const override; + + virtual void on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) override; + + private: + static std::unique_ptr make(); + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end expect_type_xs.hpp */ diff --git a/xo-reader/include/xo/reader/exprseq_xs.hpp b/xo-reader/include/xo/reader/exprseq_xs.hpp new file mode 100644 index 00000000..60b483ad --- /dev/null +++ b/xo-reader/include/xo/reader/exprseq_xs.hpp @@ -0,0 +1,84 @@ +/** @file exprseq_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +//#include + +namespace xo { + namespace scm { + enum class exprseqtype { + /** toplevel interactive sequence. Most permissive **/ + toplevel_interactive, + /** toplevel non-interactive sequence. + * This used for top-level expressions in a translation unit. + **/ + toplevel_batch, + }; + + /** @class exprseq_xs + * @brief parsing state-machine for top-level expression sequence + * + * expression sequences come in several types: + * 1. top-level interactive + * 2. top-level batch + * + * @text + * 1 2 + * +----- + * def | y y + * if | y n 1: eval as expression + * symbol | y n 1: evaluate as variable + * i64 | y n 1: evaluate as constant + * + * @endtext + **/ + class exprseq_xs : public exprstate { + public: + explicit exprseq_xs(exprseqtype seqtype); + + static void start(exprseqtype seqtype, parserstatemachine * p_psm); + + public: + virtual const char * get_expect_str() const override; + + // ----- token input methods ----- + + virtual void on_def_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_if_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_bool_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_i64_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_f64_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_string_token(const token_type & tk, + parserstatemachine * p_psm) final override; + + // ----- victory methods ----- + + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm) override; + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + + private: + static std::unique_ptr make(exprseqtype seqtype); + + /** context for this expression sequence **/ + exprseqtype xseqtype_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/** end exprseq_xs.hpp **/ diff --git a/xo-reader/include/xo/reader/exprstate.hpp b/xo-reader/include/xo/reader/exprstate.hpp new file mode 100644 index 00000000..ed3187c3 --- /dev/null +++ b/xo-reader/include/xo/reader/exprstate.hpp @@ -0,0 +1,317 @@ +/** @file exprstate.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "xo/expression/Expression.hpp" +#include "xo/tokenizer/token.hpp" +#include "xo/indentlog/print/ppdetail_atomic.hpp" +#include +//#include + +namespace xo { + namespace scm { + /** Identify current state in an expression state machine + **/ + enum class exprstatetype { + invalid = -1, + + /** toplevel of some translation unit **/ + expect_toplevel_expression_sequence, + + /** handle define-expression + * see @ref define_xs + **/ + defexpr, + + /** handle lambda-expression + * see @ref lambda_xs + **/ + lambdaexpr, + + /** handle apply expression (aka function call) + * see @ref apply_xs + **/ + applyexpr, + + /** handle parenthesized expression + * see @ref paren_xs + **/ + parenexpr, + + /** handle sequence expression (aka block) + * see @ref sequence_xs + **/ + sequenceexpr, + + /** handle let1 (single local variable) + * see @ref let1_xs + **/ + let1expr, + + /** handle if-else expression + * see @ref if_else_xs + **/ + ifexpr, + + expect_rhs_expression, + expect_symbol, + expect_type, + /** handle formal argument list + * see @ref expect_formal_arglist_xs + **/ + expect_formal_arglist, + /** handle formal argument + * see @ref expec_formal_xs + **/ + expect_formal, + + /** handle expression-in-progress, + * in case infix operators to follow + * see @ref progress_xs + **/ + expr_progress, + + n_exprstatetype + }; + + extern const char * + exprstatetype_descr(exprstatetype x); + + inline std::ostream & + operator<< (std::ostream & os, exprstatetype x) { + os << exprstatetype_descr(x); + return os; + } + + class parserstatemachine; /* see parserstatemachine.hpp */ + class exprstatestack; /* see exprstatestack.hpp */ + + class formal_arg; + + /** state associated with a partially-parsed expression. + **/ + class exprstate { + public: + using Expression = xo::scm::Expression; + using Variable = xo::scm::Variable; + using exprtype = xo::scm::exprtype; + using token_type = token; + using TypeDescr = xo::reflect::TypeDescr; + + public: + exprstate() = default; + exprstate(exprstatetype exs_type) + : exs_type_{exs_type} + {} + virtual ~exprstate() = default; + + exprstatetype exs_type() const { return exs_type_; } + + /** update exprstate in response to incoming token @p tk, + * forward instructions to parent parser + **/ + void on_input(const token_type & tk, + parserstatemachine * p_psm); + + /** @return string describing expected/allowed input in current state **/ + virtual const char * get_expect_str() const; + + /** update exprstate in response to a successfully-parsed subexpression **/ + virtual void on_expr(bp expr, + parserstatemachine * p_psm); + + /** update exprstate in response to a successfully-parsed subexpression, that ends with semicolon **/ + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm); + + /** update exprstate when expecting a symbol **/ + virtual void on_symbol(const std::string & symbol, + parserstatemachine * p_psm); + + /** update exprstate when expeccting a typedescr **/ + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm); + + /** update exprstate when expecting a formal parameter **/ + virtual void on_formal(const rp & formal, + parserstatemachine * p_psm); + + /** update expression when epecting a formal parameter list **/ + virtual void on_formal_arglist(const std::vector> & argl, + parserstatemachine * p_psm); + + /** print human-readable representation on @p os **/ + virtual void print(std::ostream & os) const; + + /** pretty-print using @p ppii **/ + virtual bool pretty_print(const print::ppindentinfo & ppii) const; + + // ----- input methods ----- + + /** handle incoming 'def' token **/ + virtual void on_def_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming 'lambda' token **/ + virtual void on_lambda_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming symbol token **/ + virtual void on_symbol_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming ',' token **/ + virtual void on_comma_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming ':' token **/ + virtual void on_colon_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming ';' token **/ + virtual void on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming '=' token **/ + virtual void on_singleassign_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming ':=' token **/ + virtual void on_assign_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming '(' token **/ + virtual void on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm); + /** handle incoming ')' token **/ + virtual void on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming '{' token **/ + virtual void on_leftbrace_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming '}' token **/ + virtual void on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming operator token **/ + virtual void on_operator_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming if-keyword token **/ + virtual void on_if_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming then-keyword token **/ + virtual void on_then_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming then-keyword token **/ + virtual void on_else_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming bool-literal token **/ + virtual void on_bool_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming integer-literal token **/ + virtual void on_i64_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming floating-point-literal token **/ + virtual void on_f64_token(const token_type & tk, + parserstatemachine * p_psm); + + /** handle incoming string-literal token **/ + virtual void on_string_token(const token_type & tk, + parserstatemachine * p_psm); + + protected: + /** throw exception when next token is inconsistent with + * parsing state + **/ + void illegal_input_error(const char * self_name, + const token_type & tk) const; + + /** throw exception when next token is inconsistent with + * parsing state + * + * @p self_name error detected in this (c++) function + * @p expr offending input expression + * @p expect_str indicate expected input in this state + * @p p_psm parser state machine + **/ + void illegal_input_on_expr(const char * self_name, + bp expr, + const char * expect_str, + parserstatemachine * p_psm) const; + + /** throw exception when next token is inconsistent with + * parsing state + * + * @p self_name error detected in this (c++) function + * @p tk offending input token + * @p expect_str indicate expected input in this state + * @p p_psm parser state machine + **/ + void illegal_input_on_token(const char * self_name, + const token_type & tk, + const char * expect_str, + parserstatemachine * p_psm) const; + + /** throw exception when next token is inconsistent with + * parsing state + * + * @p self_name error detected in this (c++) function + * @p symbol_name offending symbol name + * @p expect_str indicate expected input in this state + * @p p_psm parser state machine + **/ + void illegal_input_on_symbol(const char * self_name, + const std::string & symbol_name, + const char * expect_str, + parserstatemachine * p_psm) const; + + /** error when typename not expected in current parsing state + * + * @p self_name error detected in this (c++) function + * @p symbol_name offending symbol name + * @p expect_str indicate expected input in this state + * @p p_psm parser state machine + **/ + void illegal_input_on_type(const char * self_name, + TypeDescr td, + const char * expect_str, + parserstatemachine * p_psm) const; + + /** capture error in @p *p_psm when unable to locate definition for a variable **/ + void unknown_variable_error(const char * self_name, + const token_type & tk, + parserstatemachine * p_psm) const; + + protected: + /** explicit subtype: identifies derived class **/ + exprstatetype exs_type_; + }; /*exprstate*/ + + inline std::ostream & + operator<< (std::ostream & os, const exprstate & x) { + x.print(os); + return os; + } + + inline std::ostream & + operator<< (std::ostream & os, const exprstate * x) { + if (x) + x->print(os); + else + os << "nullptr"; + return os; + }; + } /*namespace scm*/ + +#ifndef ppdetail_atomic + namespace print { + PPDETAIL_ATOMIC(xo::scm::exprstatetype); + PPDETAIL_ATOMIC(xo::scm::exprstate); + } /*namespace print*/ +#endif +} /*namespace xo*/ + +/** end exprstate.hpp **/ diff --git a/xo-reader/include/xo/reader/exprstatestack.hpp b/xo-reader/include/xo/reader/exprstatestack.hpp new file mode 100644 index 00000000..1186c552 --- /dev/null +++ b/xo-reader/include/xo/reader/exprstatestack.hpp @@ -0,0 +1,78 @@ +/* file exprstatestack.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" +#include "xo/indentlog/print/vector.hpp" +#include "xo/indentlog/print/pretty.hpp" + +namespace xo { + namespace scm { + /** @class exprstatestack + * @brief A stack of exprstate objects + **/ + class exprstatestack { + public: + using ppstate = xo::print::ppstate; + using ppindentinfo = xo::print::ppindentinfo; + + public: + exprstatestack() {} + + bool empty() const { return stack_.empty(); } + std::size_t size() const { return stack_.size(); } + + exprstate & top_exprstate(); + void push_exprstate(std::unique_ptr exs); + std::unique_ptr pop_exprstate(); + + void reset_to_toplevel(); + + /** relative to top-of-stack. + * 0 -> top (last in), z-1 -> bottom (first in) + **/ + std::unique_ptr & operator[](std::size_t i) { + std::size_t z = stack_.size(); + + assert(i < z); + + return stack_[z - i - 1]; + } + + const std::unique_ptr & operator[](std::size_t i) const { + std::size_t z = stack_.size(); + + assert(i < z); + + return stack_[z - i - 1]; + } + + void print (std::ostream & os) const; + bool pretty_print(const ppindentinfo & ppii) const; + + private: + std::vector> stack_; + }; + + inline std::ostream & + operator<< (std::ostream & os, const exprstatestack & x) { + x.print(os); + return os; + } + + inline std::ostream & + operator<< (std::ostream & os, const exprstatestack * x) { + if (x) + x->print(os); + else + os << "nullptr"; + return os; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end exprstatestack.hpp */ diff --git a/xo-reader/include/xo/reader/formal_arg.hpp b/xo-reader/include/xo/reader/formal_arg.hpp new file mode 100644 index 00000000..a94f45eb --- /dev/null +++ b/xo-reader/include/xo/reader/formal_arg.hpp @@ -0,0 +1,58 @@ +/* file formal_arg.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "TypeDescr.hpp" +#include "xo/indentlog/print/tag.hpp" + +namespace xo { + namespace scm { + /** @class formal_arg + * @brief description of formal parameter in an argument list + * + * Terminated by an argument separator ',' or rightparen ')' + **/ + class formal_arg { + public: + using TypeDescr = xo::reflect::TypeDescr; + + public: + formal_arg() = default; + formal_arg(const std::string & n, TypeDescr td) : name_{n}, td_{td} {} + + const std::string & name() const { return name_; } + TypeDescr td() const { return td_; } + + void assign_name(const std::string & x) { name_ = x; } + void assign_td(TypeDescr x) { td_ = x; } + + void print(std::ostream & os) const { + os << ""; + } + + private: + /** formal parameter name **/ + std::string name_; + /** type description for variable @p name **/ + TypeDescr td_ = nullptr; + }; + + inline std::ostream & + operator<< (std::ostream & os, + const formal_arg & x) { + x.print(os); + return os; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end formal_arg.hpp */ diff --git a/xo-reader/include/xo/reader/if_else_xs.hpp b/xo-reader/include/xo/reader/if_else_xs.hpp new file mode 100644 index 00000000..05ab0c60 --- /dev/null +++ b/xo-reader/include/xo/reader/if_else_xs.hpp @@ -0,0 +1,109 @@ +/** @file if_else_xs.hpp + * + * author: Roland Conybeare, Jul 2025 + **/ + +#pragma once + +#include "xo/expression/IfExpr.hpp" +#include "exprstate.hpp" +#include "xo/indentlog/print/ppdetail_atomic.hpp" + +namespace xo { + namespace scm { + /** + * if test-expr then then-expr else else-expr ; + * ^ ^ ^ ^ ^ ^ ^ + * | | | | | | | + * | if_1 if_2 if_3 if_4 if_5 if_6 + * if_0 + * + * if_0 --on_if_token()--> if_1 + * if_1 --on_expr()--> if_2 + * if_2 --on_then_token()--> if_3 + * if_3 --on_expr()--> if_4 + * if_4 --on_else_token()--> if_5 + * --on_semicolon_token()--> (done) + * if_5 --on_expr()-->if_6 + * if_6 --on_semicolon_token()--> (done) + **/ + enum class ifexprstatetype { + invalid = -1, + + if_0, + if_1, + if_2, + if_3, + if_4, + if_5, + if_6, + + n_ifexprstatetype, + }; + + extern const char * ifexprstatetype_descr(ifexprstatetype x); + + std::ostream & + operator<<(std::ostream & os, ifexprstatetype x); + + /** @class if_else_xs + * @brief state to provide parsing of a conditional expression + **/ + class if_else_xs : public exprstate { + public: + using IfExprAccess = xo::scm::IfExprAccess; + + public: + if_else_xs(rp if_expr); + virtual ~if_else_xs() = default; + + static const if_else_xs * from(const exprstate * x) { return dynamic_cast(x); } + + static void start(parserstatemachine * p_psm); + + // ----- inherited from exprstate ----- + + virtual const char * get_expect_str() const override; + + virtual void on_if_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_then_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_else_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + + private: + static std::unique_ptr make(); + + /** exit this exprstate, + * and deliver @ref if_expr_ to parent exprstate + **/ + void finish_and_continue(parserstatemachine * p_psm); + + private: + ifexprstatetype ifxs_type_; + /** scaffold output expression here **/ + rp if_expr_; + + }; + } /*namespace scm*/ + + namespace print { +#ifndef ppdetail_atomic + PPDETAIL_ATOMIC(xo::scm::ifexprstatetype); +#endif + } +} /*namespace xo*/ + +/* end if_else_xs.hpp */ diff --git a/xo-reader/include/xo/reader/lambda_xs.hpp b/xo-reader/include/xo/reader/lambda_xs.hpp new file mode 100644 index 00000000..0d4d89cb --- /dev/null +++ b/xo-reader/include/xo/reader/lambda_xs.hpp @@ -0,0 +1,123 @@ +/** @file lambda_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +#include "xo/expression/LocalSymtab.hpp" +//#include + +namespace xo { + namespace scm { + /** + * @text + * + * lambda ( name(1) : type(1), ..., ) : type body-expr ; + * ^ ^ ^ ^ ^ ^ + * | | | | lm_4 lm_5 + * | | | lm_3 + * lm_0 lm_1 lm_2 + * + * lm_0 --on_lambda_token()--> lm_1 + * lm_1 --on_formal_arglist()--> lm_2 + * lm_2 --on_expr()--> lm_3 + * lm_5 --on_semicolon_token()--> (done) + * + * @endtext + **/ + enum class lambdastatetype { + invalid = -1, + + lm_0, + lm_1, + lm_2, + lm_3, + lm_4, + lm_5, + + n_lambdastatetype + }; + + extern const char * + lambdastatetype_descr(lambdastatetype x); + + inline std::ostream & + operator<< (std::ostream & os, lambdastatetype x) { + os << lambdastatetype_descr(x); + return os; + } + + /** @class lambda_xs + * @brief parsing state-machine for a lambda-expression + * + **/ + class lambda_xs : public exprstate { + public: + using SymbolTable = xo::scm::SymbolTable; + using LocalSymtab = xo::scm::LocalSymtab; + + public: + lambda_xs(); + + static void start(parserstatemachine * p_psm); + + virtual const char * get_expect_str() const override; + + virtual void on_lambda_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm) override; + virtual void on_formal_arglist(const std::vector> & argl, + parserstatemachine * p_psm) override; + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + virtual void on_leftbrace_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_colon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_f64_token(const token_type & tk, + parserstatemachine * p_psm) final override; + + virtual void print(std::ostream & os) const override; + virtual bool pretty_print(const print::ppindentinfo & ppii) const override; + + private: + static std::unique_ptr make(); + + private: + /** parsing state-machine state **/ + lambdastatetype lmxs_type_ = lambdastatetype::lm_0; + + /** lambda environment (for formal parameters) **/ + rp local_env_; + + /** explicit return type (if supplied) **/ + TypeDescr explicit_return_td_ = nullptr; + + /** lambda signature (when known) **/ + TypeDescr lambda_td_ = nullptr; + + /** body expression **/ + rp body_; + + /** parent environment **/ + rp parent_env_; + + }; + } /*namespace scm*/ + + namespace print { +#ifndef ppdetail_atomic + PPDETAIL_ATOMIC(xo::scm::lambdastatetype); +#endif + } +} /*namespace xo*/ + + +/** end lambda_xs.hpp **/ diff --git a/xo-reader/include/xo/reader/let1_xs.hpp b/xo-reader/include/xo/reader/let1_xs.hpp new file mode 100644 index 00000000..cdd00df4 --- /dev/null +++ b/xo-reader/include/xo/reader/let1_xs.hpp @@ -0,0 +1,66 @@ +/* file let1_xs.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" +#include "xo/expression/LocalSymtab.hpp" + +namespace xo { + namespace scm { + class let1_xs : public exprstate { + public: + using LocalEnv = xo::scm::LocalSymtab; + + public: + /** given local definition equivalent to + * def lhs_name = rhs; + * rest... + * parse sequence of incoming expressions rest... (until '}') + * + * Result expression creates and inits @p lhs_name, + * then evaluates expressions that follow definition + * up to same-level '}' + **/ + static void start(const std::string & lhs_name, + const rp & rhs, + parserstatemachine * p_psm); + + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + + virtual void on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) override; + + private: + let1_xs(std::string lhs_name, + rp local_env, + rp rhs); + + /** named ctor idiom **/ + static std::unique_ptr make(std::string lhs_name, + rp local_env, + rp rhs); + + private: + /** name for new local variable **/ + std::string lhs_name_; + /** environment. contains just @ref lhs_name_ **/ + rp local_env_; + /** set initial value for @ref lhs_name_ from value of this expression **/ + rp rhs_; + + /** evaluate expressions in this sequence, in order, in environment + * with variable @ref lhs_name_ defined + **/ + std::vector> expr_v_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end let1_xs.hpp */ diff --git a/xo-reader/include/xo/reader/paren_xs.hpp b/xo-reader/include/xo/reader/paren_xs.hpp new file mode 100644 index 00000000..ab7da4fc --- /dev/null +++ b/xo-reader/include/xo/reader/paren_xs.hpp @@ -0,0 +1,92 @@ +/** @file paren_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +//#include + +namespace xo { + namespace scm { + enum class parenexprstatetype { + invalid = -1, + + lparen_0, + lparen_1, + + n_parenexprstatetype, + }; + + extern const char * + parenexprstatetype_descr(parenexprstatetype x); + + inline std::ostream & + operator<< (std::ostream & os, parenexprstatetype x) { + os << parenexprstatetype_descr(x); + return os; + } + + /** @class paren_xs + * @brief state machine for handling parentheses in expressions + **/ + class paren_xs : public exprstate { + public: + paren_xs(); + virtual ~paren_xs() = default; + + static const paren_xs * from(const exprstate * x) { return dynamic_cast(x); } + + static void start(parserstatemachine * p_psm); + + bool admits_f64() const; + bool admits_rightparen() const; + + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_symbol(const std::string & symbol, + parserstatemachine * p_psm) override; + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm) override; + + virtual void on_def_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_colon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_singleassign_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_i64_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_f64_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + + private: + static std::unique_ptr make(); + + private: + /** + * ( foo ... ) + * ^ + * | + * lparen_0 + **/ + parenexprstatetype parenxs_type_; + /** populate expression (representing parenthesized value) here **/ + rp gen_expr_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/** end paren_xs.hpp **/ diff --git a/xo-reader/include/xo/reader/parser.hpp b/xo-reader/include/xo/reader/parser.hpp new file mode 100644 index 00000000..9db626ee --- /dev/null +++ b/xo-reader/include/xo/reader/parser.hpp @@ -0,0 +1,262 @@ +/* file parser.hpp + * + * author: Roland Conybeare, Jul 2024 + */ + +#pragma once + +#include "exprstatestack.hpp" +#include "envframestack.hpp" +#include "parser_result.hpp" +#include "parserstatemachine.hpp" +#include "xo/expression/GlobalSymtab.hpp" +#include + +namespace xo { + namespace scm { + /** schematica parser + * + * Examples: + * + * decltype point + * + * // forward declarations + * decl pi : f64; + * decl fib(n : i32) -> i32; + * + * def pi = 3.14159265; // constant. = is single assignment + * + * def fib(n : i32) -> i32 { + * // nested defs ok + * def aux(n : i32, s1 : i32, s2 : i32) -> i32 { + * // or: + * // (n == 0) ? s1 : aux(n - 1, s1 + s2, s1) + * // + * if (n == 0) { + * s1; + * } else { + * aux(n - 1, s1 + s2, s1); + * } + * + * // or: + * // if (n == 0) ? s1 : aux(n - 1, s1 + s2, s1) + * } + * + * aux(n=n, s1=1, s2=0); + * } + * + * def x := "fu"; // non-constant + * x += "bar"; + * + * def anotherfib = lambda(n : i32) { fib(n) }; + * + * def any : object; + * def l : list = '(); + * + * deftype point :: {x : f64, y : f64}; + * deftype polar :: {arg : f64, mag : f64}; + * deftype converter :: (point -> polar); + * + * def polar2rect(pt : polar) -> point { + * point(x = pt.mag * cos(arg), + * y = pt.mag * sin(arg)); + * } + * + * Grammar: + * toplevel-program = $toplevel-expression(1); ..; $toplevel-expression(n) + * + * if interactive: + * toplevel-expression = expression + * else + * toplevel-expression = type-decl | define-expr + * + * type-decl = decltype $typename [<$tp1 .. $tpn>] + * expression = type-decl + * | define-expr + * | literal-expr + * | variable-expr + * | apply-expr + * | if-expr + * | lambda-expr + * | arithmetic-expr + * | block + * + * define-expr = type-decl + * | type-def + * | variable-def + * | function-decl + * | function-def + * + * type-def = deftype $typename [<$tp1 .. $tpn>] :: type-def-rhs + * type-def-rhs = object + * | bool + * | i128 | i64 | i32 | i16 | i8 + * | f128 | f64 | f32 | f16 + * | struct $typename { ($membername(i) : $typename(i))* } + * [end $typename] + * | tuple $typename { $typename(1), .., $typename(n) } + * [end $typename] + * | copytype $typename + * | subtype $typename { ($member(i) : $typename(i))* } + * + * variable-def = decl $varname [: $typename] [= expression] + * function-decl = decl $functionname($varname(1) : $typename(1), + * .., + * $varname(n) : $typename(n)) -> $typename[ret] + * function-def = def $functionname($varname(1) : $typename(1), + * .., + * $varname(n) : $typename(n)) [-> $typename[ret]] + * body-expr + * [ end $functionname ] + * literal-expr = boolean-literal + * | integer-literal + * | fp-literal + * | string-literal + * | symbol-literal + * | struct-literal + * + * + * boolean-literal = true | false + * + * variable-expr = $varname + * apply-expr = fn-expr(arg-expr(1), .., arg-expr(n)) + * fn-expr = expression + * arg-expr(i) = expression + * + * if-expr = if (test-expr) then-block else else-block + * | ((test-expr) ? then-expr : else-expr) + * test-expr = expression + * then-block = block + * else-block = block + * + * block = { (definition | expression)* } + * + * lambda-expr = lambda ($paramname(1) : $type(1), + * .., + * $paramname(n) : $type(n)) body-expr + * body-expr = expression + * + * arithmetic-expr = expression binop expression + * + * binop = + + * | - + * | * + * | / + * | | + * | & + * | ^ + * | == + * | != + * | < + * | <= + * | => + * | > + * + **/ + class parser { + public: + using Expression = xo::scm::Expression; + using token_type = exprstate::token_type; // token; + + public: + /** create parser in initial state; + * parser is ready to receive tokens via @ref include_token + * + * At least for xo-interpreter will have non-empty symbol table. + * + * @p toplevel_symtab symbol table. + * @p debug_flag true to enable debug logging + **/ + parser(const rp & toplevel_symtab, bool debug_flag); + + 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 **/ + std::size_t stack_size() const { return psm_.xs_stack_.size(); } + /** for diagnostics: exprstatetype at level @p i + * (taken relative to top of stack) + * + * @pre 0 <= i < stack_size + **/ + exprstatetype i_exstype(std::size_t i) const { + std::size_t z = psm_.xs_stack_.size(); + + if (i < z) { + return psm_.xs_stack_[i]->exs_type(); + } + + /* out of bounds */ + return exprstatetype::invalid; + } + + exprstate const * i_exstate(std::size_t i) const { + std::size_t z = psm_.xs_stack_.size(); + + if (i < z) { + return psm_.xs_stack_[i].get(); + } + + /* out of bounds */ + return nullptr; + } + + /** true iff parser contains state for an incomplete expression. + * For this to be true, parser must have consumed at least one token + * since end of last toplevel expression + **/ + bool has_incomplete_expr() const; + + /** put parser into state for beginning an interactive session. + **/ + void begin_interactive_session(); + + /** put parser into state for beginning of a translation unit + * (i.e. input stream) + **/ + void begin_translation_unit(); + + /** include next token @p tk and increment parser state. + * + * @param tk next input token + * @return parsed expression, if @p tk completes an expression. + * otherwise nullptr + **/ + const parser_result & include_token(const token_type & tk); + + /** reset parsed result expression; use using return value from + * @ref include_token. Complicating api here to avoid copying parser_result + * on each token + **/ + void reset_result(); + + /** reset to starting parsing state. + * use this after encountering an error, to avoid cascade of + * spurious secondary errors.. particularly important when + * invoked asa part of a REPL. + **/ + void reset_to_idle_toplevel(); + + /** print human-readable representation on stream @p os **/ + void print(std::ostream & os) const; + + private: + /** state machine **/ + parserstatemachine psm_; + }; /*parser*/ + + inline std::ostream & + operator<< (std::ostream & os, + const parser & x) { + x.print(os); + return os; + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end parser.hpp */ diff --git a/xo-reader/include/xo/reader/parser_result.hpp b/xo-reader/include/xo/reader/parser_result.hpp new file mode 100644 index 00000000..8ccee992 --- /dev/null +++ b/xo-reader/include/xo/reader/parser_result.hpp @@ -0,0 +1,65 @@ +/* file parser_result.hpp + * + * author: Roland Conybeare, Jul 2025 + */ + +#pragma once + +#include "xo/expression/Expression.hpp" + +namespace xo { + namespace scm { + enum parser_result_type { + /** no result yet (incomplete expression) **/ + none, + /** emit expression **/ + expression, + /** emit error **/ + error + }; + + struct parser_result { + using Expression = xo::scm::Expression; + + public: + parser_result() = default; + parser_result(parser_result_type type, rp expr, const char * src_fn, std::string errmsg); + + static parser_result none(); + static parser_result error(const char * error_src_function, + std::string errmsg); + static parser_result expression(rp expr); + + bool is_none() const { return result_type_ == parser_result_type::none; } + bool is_expression() const { return result_type_ == parser_result_type::expression; } + bool is_error() const { return result_type_ == parser_result_type::error; } + + parser_result_type result_type() const { return result_type_; } + const rp & result_expr() const { return result_expr_; } + const char * error_src_function() const { return error_src_function_; } + const std::string & error_description() const { return error_description_; } + + public: + /** none|expression|error_description + * + * @text + * result_type | error_src_function | error_description + * -------------+--------------------+------------------- + * none | nullptr | empty + * expression | nullptr | empty + * error | non-null | non-empty + * @endtext + **/ + parser_result_type result_type_ = parser_result_type::none; + /** if @ref result_state is parser_result_state::expression -> non-null result expression **/ + rp result_expr_; + /** if @ref result_state is parser_result_state::error -> non-null source function **/ + const char * error_src_function_ = nullptr; + /** if @ref result_state is parser_result_state::error -> non-empty error description **/ + std::string error_description_; + }; + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end parser_result.hpp */ diff --git a/xo-reader/include/xo/reader/parserstatemachine.hpp b/xo-reader/include/xo/reader/parserstatemachine.hpp new file mode 100644 index 00000000..9cec6ee3 --- /dev/null +++ b/xo-reader/include/xo/reader/parserstatemachine.hpp @@ -0,0 +1,129 @@ +/* file parserstatemachine.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" +#include "exprstatestack.hpp" +#include "envframestack.hpp" +#include "parser_result.hpp" +#include "xo/expression/typeinf/type_unifier.hpp" + +namespace xo { + namespace scm { + /** @class parserstatemachine + * @brief public parser state. + * + * Schematica parser state; sent to subsidiary single-feature state machines. + * For example entry points for the lambda feature (@ref lambda_xs) + * will accept a non-const parserstatemachine pointer argument + **/ + class parserstatemachine { + public: + using Expression = xo::scm::Expression; + using Variable = xo::scm::Variable; + using LocalEnv = xo::scm::LocalSymtab; + using token_type = token; + + public: + explicit parserstatemachine(bool debug_flag) + : debug_flag_{debug_flag} + {} + + //const parser_result & result() const { return result_; } + //parser_result_state result_state() const { return result_state_; } + //const rp & result_expr() const { return result_expr_; } + //const char * error_src_function() const { return error_src_function_; } + //const std::string & error_description() const { return error_description_; } + + std::unique_ptr pop_exprstate(); + exprstate & top_exprstate(); + /** get exprstate @p i levels from the top **/ + const exprstate & lookup_exprstate(size_t i) const; + void push_exprstate(std::unique_ptr x); + /** @return number of stacked expression states **/ + size_t exprstate_stack_size() const { return xs_stack_.size(); } + + bool debug_flag() const { return debug_flag_; } + + /** lookup variable name in lexical context represented by + * this psm. nullptr if not found + **/ + bp lookup_var(const std::string & x) const; + + /** update/replace binding for variable @p x in lexical context + * represented by this psm. Always acts on innermost frame. + **/ + void upsert_var(bp x); + + /** @return available variable bindings in current parsing state **/ + bp top_envframe() const; + /** @return frame @p i levels from the top **/ + bp lookup_envframe(std::size_t i) const; + /** push frame @p x (with new variable bindings) onto environment stack **/ + void push_envframe(const rp & x); + /** @return pop innermost environment frame and return it **/ + rp pop_envframe(); + /** @return number of stacked environment frames **/ + size_t env_stack_size() const { return env_stack_.size(); } + + // ----- parsing outputs ----- + + void on_expr(bp expr); + void on_expr_with_semicolon(bp expr); + void on_symbol(const std::string & symbol); + + // ----- parsing inputs ----- + + void on_semicolon_token(const token_type & tk); + void on_operator_token(const token_type & tk); + void on_leftbrace_token(const token_type & tk); + void on_rightbrace_token(const token_type & tk); + void on_then_token(const token_type & tk); + void on_else_token(const token_type & tk); + void on_f64_token(const token_type & tk); + + // ----- parsing error ----- + + /** @p self_name location (implementation function) where error detected + **/ + void on_error(const char * self_name, std::string error_description); + + /** write human-readable representation on @p os **/ + void print(std::ostream & os) const; + + public: + /** state recording state associated with enclosing expressions. + * + * Note: at least asof c++23, the std::stack api doesn't support access + * to members other than the top. + * + * for stack with N elements (N = stack_.size()): + * - bottom of stack is stack_[0] + * - top of stack is stack_[N-1] + **/ + exprstatestack xs_stack_; + /** environment frames for lexical context. + * push a frame on each nested lambda; + * pop when lambda body goes out of scope + **/ + envframestack env_stack_; + /** type inference/unification. + * apply to unresolved types associated with @ref Expression instances. + **/ + type_unifier unifier_; + /** parser result state (as of last call to @ref exprstate::on_input) **/ + parser_result result_; + /** enable/disable debug logging **/ + bool debug_flag_ = false; + }; + + inline std::ostream & + operator<<(std::ostream & os, const parserstatemachine & x) { + x.print(os); + return os; + } + } /*namespace scm*/ +} /*namespace xo*/ diff --git a/xo-reader/include/xo/reader/pretty_envframestack.hpp b/xo-reader/include/xo/reader/pretty_envframestack.hpp new file mode 100644 index 00000000..842368fa --- /dev/null +++ b/xo-reader/include/xo/reader/pretty_envframestack.hpp @@ -0,0 +1,28 @@ +/* file pretty_envframestack.hpp + * + * author: Roland Conybeare, Jul 2025 + */ + +#pragma once + +#include "envframestack.hpp" +#include "xo/indentlog/print/pretty.hpp" + +namespace xo { + namespace print { + template <> + struct ppdetail { + static inline bool print_pretty(const ppindentinfo & ppii, const xo::scm::envframestack * p) { + return p->pretty_print(ppii); + } + }; + + template <> + struct ppdetail { + static inline bool print_pretty(const ppindentinfo & ppii, const xo::scm::envframestack * p) { + return p->pretty_print(ppii); + } + }; + + } /*namespace print*/ +} /*namespace xo*/ diff --git a/xo-reader/include/xo/reader/pretty_exprstatestack.hpp b/xo-reader/include/xo/reader/pretty_exprstatestack.hpp new file mode 100644 index 00000000..bc8f6606 --- /dev/null +++ b/xo-reader/include/xo/reader/pretty_exprstatestack.hpp @@ -0,0 +1,34 @@ +/* file pretty_exprstatestack.hpp + * + * author: Roland Conybeare, Jul 2025 + */ + +#pragma once + +#include "exprstatestack.hpp" +#include "xo/indentlog/print/pretty.hpp" + +namespace xo { + namespace print { + template <> + struct ppdetail { + static inline bool print_pretty(const ppindentinfo & ppii, const xo::scm::exprstatestack * p) { + return p->pretty_print(ppii); + } + }; + + template <> + struct ppdetail { + static inline bool print_pretty(const ppindentinfo & ppii, const xo::scm::exprstatestack * p) { + return p->pretty_print(ppii); + } + }; + + template <> + struct ppdetail { + static inline bool print_pretty(const ppindentinfo & ppii, const xo::scm::exprstate * p) { + return p->pretty_print(ppii); + } + }; + } /*namespace print*/ +} /*namespace xo*/ diff --git a/xo-reader/include/xo/reader/pretty_parserstatemachine.hpp b/xo-reader/include/xo/reader/pretty_parserstatemachine.hpp new file mode 100644 index 00000000..50e6737f --- /dev/null +++ b/xo-reader/include/xo/reader/pretty_parserstatemachine.hpp @@ -0,0 +1,23 @@ +/* file pretty_parserstatemachine.hpp + * + * author: Roland Conybeare, Jul 2025 + */ + +#pragma once + +#include "parserstatemachine.hpp" +#include "xo/indentlog/print/pretty.hpp" + +namespace xo { + namespace print { + template<> + struct ppdetail { + static bool print_pretty(const ppindentinfo & ppii, const xo::scm::parserstatemachine & x); + }; + + template<> + struct ppdetail { + static bool print_pretty(const ppindentinfo & ppii, const xo::scm::parserstatemachine * x); + }; + } +} /*namespace xo*/ diff --git a/xo-reader/include/xo/reader/progress_xs.hpp b/xo-reader/include/xo/reader/progress_xs.hpp new file mode 100644 index 00000000..1884623a --- /dev/null +++ b/xo-reader/include/xo/reader/progress_xs.hpp @@ -0,0 +1,189 @@ +/** @file progress_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +#include "xo/reflect/TypeDescr.hpp" +#include +//#include + +namespace xo { + namespace scm { + /** represent an infix operator. + * + * See @ref progress_xs::assemble_expr() for translation + * to Expression + **/ + enum class optype { + invalid = -1, + + /** op:= **/ + op_assign, + + /** op< **/ + op_less, + /** op<= **/ + op_less_equal, + /** op== **/ + op_equal, + /** op!= **/ + op_not_equal, + /** op> **/ + op_great, + /** op>= **/ + op_great_equal, + + /** op+ **/ + op_add, + /** op- **/ + op_subtract, + + /** op* **/ + op_multiply, + /** op/ **/ + op_divide, + + n_optype + }; + + extern const char * + optype_descr(optype x); + + /** report operator precedence. + * lowest operator precedence is 1 + **/ + extern int + precedence(optype x); + + inline std::ostream & + operator<< (std::ostream & os, optype x) { + os << optype_descr(x); + return os; + } + + /** @class progress_xs + * @brief state machine for parsing a schematica runtime-value-expression + * + * Handles an expression that produces a value, for example appearing on the + * right-hand side of a definition. + * + * Deals with infix operators, handles operator precedence. + * Also generates argument-type-specific arithmetic expressions, + * for example using ``Apply::make_add2_f64()`` when adding floating-point numbers + * + * One reason for this to exist is to simulate one-token lookahead. + * To look at but not consume a token T, can push a progress_xs instance P, + * then send T to P. + **/ + class progress_xs : public exprstate { + public: + progress_xs(rp valex, optype op); + virtual ~progress_xs() = default; + + static const progress_xs * from(const exprstate * x) { + return dynamic_cast(x); + } + + static void start(rp valex, + optype optype, + parserstatemachine * p_psm); + static void start(rp valex, + parserstatemachine * p_psm); + + bool admits_f64() const; + + void apply_type_error(const char * self_name, + optype op, + bp expr1, + bp expr2, + parserstatemachine * p_psm) const; + + virtual const char * get_expect_str() const final override; + + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + virtual void on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_comma_token(const token_type & tk, + parserstatemachine * p_psm) final override; + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm) override; + + virtual void on_def_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_colon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_singleassign_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_assign_token(const token_type & tk, + parserstatemachine * p_psm) final override; + virtual void on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_then_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_else_token(const token_type & tk, + parserstatemachine * p_psm) override; + + /* entry point for an infix operator token */ + virtual void on_operator_token(const token_type & tk, + parserstatemachine * p_psm) final override; + + virtual void on_bool_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_i64_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void on_f64_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + virtual bool pretty_print(const print::ppindentinfo & ppii) const override; + + private: + static std::unique_ptr make(rp valex, + optype optype = optype::invalid); + + private: + /** assemble expression representing + * value of + * @code + * f(lhs_, rhs_) + * @endcode + * + * where f determined by @ref op_type_ + **/ + rp assemble_expr(parserstatemachine * p_psm); + + private: + /** populate an expression here, may be followed by an operator **/ + rp lhs_; + + /** infix operator, if supplied **/ + optype op_type_ = optype::invalid; + + /** populate an expression here, following infix operator */ + rp rhs_; + }; + } /*namespace scm*/ + +#ifndef ppdetail_atomic + namespace print { + PPDETAIL_ATOMIC(xo::scm::optype); + } +#endif +} /*namespace xo*/ + + +/** end progress_xs.hpp **/ diff --git a/xo-reader/include/xo/reader/reader.hpp b/xo-reader/include/xo/reader/reader.hpp new file mode 100644 index 00000000..5d0ec503 --- /dev/null +++ b/xo-reader/include/xo/reader/reader.hpp @@ -0,0 +1,139 @@ +/* file reader.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "parser.hpp" +#include "reader_error.hpp" +#include "xo/expression/Expression.hpp" +#include "xo/expression/GlobalSymtab.hpp" +#include "xo/expression/pretty_expression.hpp" +#include "xo/tokenizer/tokenizer.hpp" + +namespace xo { + namespace scm { + /** @class parse_result + * @brief Result object returned from reader::read_expr + **/ + struct reader_result { + using Expression = xo::scm::Expression; + using span_type = span; + + reader_result(rp expr, span_type rem, std::size_t psz, const reader_error & error) + : expr_{std::move(expr)}, rem_{rem}, parser_stack_size_{psz}, error_{error} {} + + /** true if reader parsed a complete expression **/ + bool expr_complete() const { return expr_.get(); } + + /** parsed schematica expression **/ + rp expr_; + /** span giving text input consumed to construct expr, + * including any leading whitespace. + * This is the span returned in result of tokenizer::scan() + **/ + span_type rem_; + + /** parser nesting level when this result delivered + * will be zero whenever @ref expr_ is non-null + **/ + std::size_t parser_stack_size_ = 0; + + /** error description, whenever .error_.is_error() is true **/ + reader_error error_; + }; + + /** + * Use: + * @code + * reader rdr; + * rdr.begin_translation_unit() + * + * bool eof = false; + * while (!eof) { + * auto input = ins.read_some(); + * // eof: true if no more input will be forthcoming from this stream + * eof = ins.eof(); + * + * for (auto rem = input; !rem.empty();) { + * // res: (parsed-expr, used) + * auto [expres = rdr.read_expr(rem, eof); + * + * if (res.first) { + * // do something with res.first (parsed expr) + * ... + * } + * + * rem = rem.suffix_after(res.second); + * } + * } + * + * // expect !rdr.has_prefix() + * + * @endcode + **/ + class reader { + public: + using tokenizer_type = tokenizer; + using span_type = tokenizer_type::span_type; + + public: + reader(const rp & toplevel_symtab, bool debug_flag); + + bool debug_flag() const { return parser_.debug_flag(); } + + /** call once before calling .read_expr() + * for a new interactive session + **/ + void begin_interactive_session(); + + /** counterpart to .begin_interactive_session() + **/ + reader_result end_interactive_session(); + + /** call once before calling .read_expr(): + * 1. with new reader + * 2. if last read_expr() call had eof=true + **/ + void begin_translation_unit(); + + /** counterpart to .begin_translation_unit(), + * provided for symmetry's sake + * + * Equivalent to: + * @code + * read_expr(span_type(nullptr, nullptr), true); + * @endcode + **/ + reader_result end_translation_unit(); + + /** Try to read one expression from @p input. + * Return struct containing parsed expression + * and span of characters comprising that expression + * + * @param input Supply this input span of chars + * @param eof. True if input stream supplying @p input + * reports end-of-file immediately after the last char + * in @p input. + **/ + reader_result read_expr(const span_type & input, 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: text -> tokens **/ + tokenizer_type tokenizer_; + + /** parser: tokens -> expressions **/ + parser parser_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + +/* end reader.hpp */ diff --git a/xo-reader/include/xo/reader/reader_error.hpp b/xo-reader/include/xo/reader/reader_error.hpp new file mode 100644 index 00000000..d61db03e --- /dev/null +++ b/xo-reader/include/xo/reader/reader_error.hpp @@ -0,0 +1,55 @@ +/* reader_error.hpp + * + * Author: Roland Conybeare, Jul 2025 + */ + +#pragma once + +#include "xo/tokenizer/tokenizer_error.hpp" + +namespace xo { + namespace scm { + class reader_error { + public: + using input_state_type = typename tokenizer_error::input_state_type; + + public: + /** default ctor represents a not-an-error sentinel object **/ + reader_error() = default; + /** construct to capture parsing error context + * @ + **/ + reader_error(const char * src_function, + std::string error_description, + const input_state_type & input_state, + size_t error_pos) + : tk_error_{src_function, error_description, input_state, error_pos} + {} + + const tokenizer_error & tk_error() const { return tk_error_; } + + /** true, except for sentinel not-an-error object **/ + bool is_error() const { return tk_error_.is_error(); } + /** false, except for object in sentinel state **/ + bool is_not_an_error() const { return tk_error_.is_not_an_error(); } + + const char * src_function() const { return tk_error_.src_function(); } + + /** print error representation to stream @p os. Intended for parser/tokenizer + * diagnostics. For Schematika errors prefer @ref report + **/ + void print(std::ostream & os) const { tk_error_.print(os); } + + /** print human-oriented error report on @p os. **/ + void report(std::ostream & os) const { tk_error_.report(os); } + + private: + /** for parser-level errors, will still use this for + * {src function, error description, input state and error pos} + **/ + tokenizer_error tk_error_; + }; + } +} + +/* end reader_error.hpp */ diff --git a/xo-reader/include/xo/reader/sequence_xs.hpp b/xo-reader/include/xo/reader/sequence_xs.hpp new file mode 100644 index 00000000..cad81cf2 --- /dev/null +++ b/xo-reader/include/xo/reader/sequence_xs.hpp @@ -0,0 +1,52 @@ +/** @file sequence_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +#include + +namespace xo { + namespace scm { class Sequence; } + namespace scm { class Lambda; } + + namespace scm { + class sequence_xs : public exprstate { + public: + using Sequence = xo::scm::Sequence; + using Lambda = xo::scm::Lambda; + + public: + /** start parsing a sequence-expr. + * input begins with first expression in the sequence. + **/ + static void start(parserstatemachine * p_psm); + + virtual void on_expr(bp expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) override; + + virtual void on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) override; + + virtual void print(std::ostream & os) const override; + virtual bool pretty_print(const xo::print::ppindentinfo & ppii) const override; + + private: + sequence_xs(); + + /** named ctor idiom **/ + static std::unique_ptr make(); + + private: + /** will build SequenceExpr from in-order contents of this vector **/ + std::vector> expr_v_; + }; + } /*namespace scm*/ +} /*namespace xo*/ + + +/** end sequence_xs.hpp **/ diff --git a/xo-reader/src/reader/CMakeLists.txt b/xo-reader/src/reader/CMakeLists.txt new file mode 100644 index 00000000..e8e8bf02 --- /dev/null +++ b/xo-reader/src/reader/CMakeLists.txt @@ -0,0 +1,33 @@ +# reader/CMakeLists.txt + +set(SELF_LIB xo_reader) +set(SELF_SRCS + parser_result.cpp + parser.cpp + parserstatemachine.cpp + reader.cpp + exprstate.cpp + exprstatestack.cpp + define_xs.cpp + if_else_xs.cpp + progress_xs.cpp + apply_xs.cpp + paren_xs.cpp + sequence_xs.cpp + exprseq_xs.cpp + expect_expr_xs.cpp + expect_symbol_xs.cpp + expect_formal_xs.cpp + expect_formal_arglist_xs.cpp + expect_type_xs.cpp + lambda_xs.cpp + let1_xs.cpp + envframestack.cpp + pretty_parserstatemachine.cpp +) + +xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_dependency(${SELF_LIB} xo_expression) +xo_dependency(${SELF_LIB} xo_tokenizer) + +# end CMakeLists.txt diff --git a/xo-reader/src/reader/apply_xs.cpp b/xo-reader/src/reader/apply_xs.cpp new file mode 100644 index 00000000..8f5b557a --- /dev/null +++ b/xo-reader/src/reader/apply_xs.cpp @@ -0,0 +1,189 @@ +/* @file apply_xs.cpp */ + +#include "apply_xs.hpp" +#include "parserstatemachine.hpp" +#include "expect_expr_xs.hpp" + +namespace xo { + namespace scm { + // ----- applyexprstatetype ----- + + const char * + applyexprstatetype_descr(applyexprstatetype x) { + switch (x) { + case applyexprstatetype::invalid: return "invalid"; + case applyexprstatetype::apply_0: return "apply_0"; + case applyexprstatetype::apply_1: return "apply_1"; + case applyexprstatetype::apply_2: return "apply_2"; + case applyexprstatetype::apply_3: return "apply_3"; + case applyexprstatetype::n_applyexprstatetype: break; + } + + return "???applyexprstatetype"; + } + + std::ostream & + operator<<(std::ostream & os, applyexprstatetype x) { + os << applyexprstatetype_descr(x); + return os; + } + + // ----- apply_xs ----- + + std::unique_ptr + apply_xs::make() { + return std::make_unique(apply_xs()); + } + + void + apply_xs::start(rp fn_expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + p_psm->push_exprstate(apply_xs::make()); + p_psm->top_exprstate().on_expr(fn_expr.get(), p_psm); + p_psm->top_exprstate().on_leftparen_token(token_type::leftparen(), p_psm); + } + + apply_xs::apply_xs() + : exprstate(exprstatetype::applyexpr) + {} + + const char * + apply_xs::get_expect_str() const { + switch(applyxs_type_) { + case applyexprstatetype::invalid: return "invalid"; + case applyexprstatetype::apply_0: return "expr"; + case applyexprstatetype::apply_1: return "lparen"; + case applyexprstatetype::apply_2: return "expr"; + case applyexprstatetype::apply_3: return "comma|rparen"; + case applyexprstatetype::n_applyexprstatetype: break; + } + + return "?expect"; + } + + void + apply_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + switch (applyxs_type_) { + case applyexprstatetype::invalid: + case applyexprstatetype::n_applyexprstatetype: + // unreachable + break; + case applyexprstatetype::apply_0: + log && log("stash fn -> new state apply_1"); + this->fn_expr_ = expr.promote(); + this->applyxs_type_ = applyexprstatetype::apply_1; + return; + case applyexprstatetype::apply_1: + log && log("error: was expecting lparen"); + // error, expecting lparen + break; + case applyexprstatetype::apply_2: + log && log(xtag("expr", expr), xtag("do", "stash expr -> new state apply_3")); + this->args_expr_v_.push_back(expr.promote()); + this->applyxs_type_ = applyexprstatetype::apply_3; + return; + case applyexprstatetype::apply_3: + // error, expecting comma|rparen + break; + } + + /* control here --implies-> error state */ + + constexpr const char * c_self_name = "apply_xs::on_expr"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); + } + + void + apply_xs::on_comma_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + if (this->applyxs_type_ == applyexprstatetype::apply_3) { + this->applyxs_type_ = applyexprstatetype::apply_2; + expect_expr_xs::start(p_psm); + } else { + constexpr const char * c_self_name = "apply_xs::on_comma_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + } + + void + apply_xs::on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("applyxs_type", applyxs_type_); + + if (this->applyxs_type_ == applyexprstatetype::apply_1) { + this->applyxs_type_ = applyexprstatetype::apply_2; + expect_expr_xs::start(p_psm); + } else { + constexpr const char * c_self_name = "apply_xs::on_leftparen_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + } + + void + apply_xs::on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("applyxs_type", applyxs_type_); + + if (this->applyxs_type_ == applyexprstatetype::apply_3) { + /* (done) state */ + log("apply complete -> pop + send expr"); + + rp apply_expr = Apply::make(this->fn_expr_, this->args_expr_v_); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->top_exprstate().on_expr(apply_expr, p_psm); + return; + } + + constexpr const char * c_self_name = "apply_xs::on_rightparen_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + apply_xs::print(std::ostream & os) const + { + os << ""; + } + + bool + apply_xs::pretty_print(const xo::print::ppindentinfo & ppii) const + { + return ppii.pps()->pretty_struct(ppii, "apply_xs", + refrtag("applyxs_type", applyxs_type_), + refrtag("fn_expr", fn_expr_), + refrtag("args_expr_v", args_expr_v_)); + } + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end apply_xs.cpp */ diff --git a/xo-reader/src/reader/define_xs.cpp b/xo-reader/src/reader/define_xs.cpp new file mode 100644 index 00000000..d5cf23df --- /dev/null +++ b/xo-reader/src/reader/define_xs.cpp @@ -0,0 +1,368 @@ +/* @file define_xs.cpp */ + +#include "define_xs.hpp" +#include "parserstatemachine.hpp" +#include "expect_symbol_xs.hpp" +#include "expect_expr_xs.hpp" +#include "expect_type_xs.hpp" +#include "pretty_expression.hpp" + +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 ----- + + 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 + { + /* + * 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->defxs_type_) { + 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 + 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_)); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end define_xs.cpp */ diff --git a/xo-reader/src/reader/envframe.cpp b/xo-reader/src/reader/envframe.cpp new file mode 100644 index 00000000..985bc1c6 --- /dev/null +++ b/xo-reader/src/reader/envframe.cpp @@ -0,0 +1,41 @@ +/* file envframe.cpp + * + * author: Roland Conybeare + */ + +#include "envframe.hpp" +#include "xo/indentlog/print/vector.hpp" + +namespace xo { + using xo::scm::Variable; + + namespace scm { +#ifdef OBSOLETE + envframe::envframe(const std::vector> & argl, + const rp& parent_env) + { + this->env_ = LocalEnv::make(argl, parent_env); + } + + bp + envframe::lookup(const std::string & target) const { + return env_->lookup_local(target); + } + + void + envframe::upsert(bp target) { + env_->upsert_local(target); + } + + void + envframe::print(std::ostream & os) const { + os << "argv()) + << ">"; + } +#endif + } /*namespace scm */ +} /*namespace xo*/ + + +/* end envframe.cpp */ diff --git a/xo-reader/src/reader/envframestack.cpp b/xo-reader/src/reader/envframestack.cpp new file mode 100644 index 00000000..19e15d27 --- /dev/null +++ b/xo-reader/src/reader/envframestack.cpp @@ -0,0 +1,139 @@ +/* file envframestack.cpp + * + * author: Roland Conybeare + */ + +#include "envframestack.hpp" +#include "pretty_envframestack.hpp" +#include "pretty_localenv.hpp" + +namespace xo { + using xo::scm::LocalSymtab; + using xo::scm::Variable; + + namespace scm { + bp + envframestack::top_envframe() const { + std::size_t z = stack_.size(); + + if (z == 0) { + throw std::runtime_error + ("parser::top_exprstate: unexpected empty stack"); + } + + return stack_[z-1].get(); + } + + void + envframestack::push_envframe(const rp & frame) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag), + xtag("frame", frame)); + + std::size_t z = stack_.size(); + + stack_.resize(z+1); + + stack_[z] = frame; + } + + rp + envframestack::pop_envframe() { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + std::size_t z = stack_.size(); + + if (z > 0) { + //std::unique_ptr top = std::move(stack_[z-1]); + + rp retval = stack_.at(z-1); + + stack_.resize(z-1); + + return retval; + } else { + return nullptr; + } + } + + bp + envframestack::lookup(const std::string & x) const { + for (std::size_t i = 0, z = this->size(); i < z; ++i) { + const auto & frame = (*this)[i]; + + auto retval = frame->lookup_local(x); + + if (retval) + return retval; + } + + return bp::from_native(nullptr); + } + + void + envframestack::upsert(bp x) { + /* upsert should always happen in the innermost lexical context. + * We are providing new variable binding (perhaps shadowing an existing binding) + */ + this->top_envframe()->upsert_local(x); + } + + void + envframestack::print(std::ostream & os) const { + os << "" << std::endl; + } + + bool + envframestack::pretty_print(const ppindentinfo & ppii) const + { + ppstate * pps = ppii.pps(); + + if (ppii.upto()) { + if (stack_.size() > 1) + return false; + + if (!pps->print_upto("print_upto_tag("size", stack_.size())) + return false; + + if ((stack_.size() > 0) + && !pps->print_upto_tag("[0]", stack_[0])) + { + return false; + } + + pps->write(">"); + + return true; + } else { + pps->write("newline_pretty_tag(ppii.ci1(), "size", stack_.size()); + + for (std::size_t i = 0, z = stack_.size(); i < z; ++i) { + std::string i_str = tostr("[", z-i-1, "]"); + pps->newline_pretty_tag(ppii.ci1(), i_str, stack_[i]); + } + pps->write(">"); + + return false; + } + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end envframestack.cpp */ diff --git a/xo-reader/src/reader/expect_expr_xs.cpp b/xo-reader/src/reader/expect_expr_xs.cpp new file mode 100644 index 00000000..10caafeb --- /dev/null +++ b/xo-reader/src/reader/expect_expr_xs.cpp @@ -0,0 +1,294 @@ +/* file expect_expr_xs.cpp + * + * author: Roland Conybeare + */ + +#include "expect_expr_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "define_xs.hpp" +#include "lambda_xs.hpp" +#include "if_else_xs.hpp" +#include "paren_xs.hpp" +#include "sequence_xs.hpp" +#include "progress_xs.hpp" +#include "xo/expression/Lambda.hpp" +#include "xo/expression/Constant.hpp" +#include "xo/expression/pretty_expression.hpp" + +namespace xo { + using xo::scm::Constant; + + namespace scm { + + std::unique_ptr + expect_expr_xs::make(bool allow_defs, + bool cxl_on_rightbrace) + { + return std::make_unique(expect_expr_xs(allow_defs, + cxl_on_rightbrace)); + + } + + void + expect_expr_xs::start(bool allow_defs, + bool cxl_on_rightbrace, + parserstatemachine * p_psm) + { + p_psm->push_exprstate(expect_expr_xs::make(allow_defs, + cxl_on_rightbrace)); + } + + void + expect_expr_xs::start(parserstatemachine * p_psm) { + start(false /*!allow_defs*/, + false /*!cxl_on_rightbrace*/, + p_psm); + } + + expect_expr_xs::expect_expr_xs(bool allow_defs, + bool cxl_on_rightbrace) + : exprstate(exprstatetype::expect_rhs_expression), + allow_defs_{allow_defs}, + cxl_on_rightbrace_{cxl_on_rightbrace} + {} + + const char * + expect_expr_xs::get_expect_str() const + { + if (allow_defs_) { + return "def|lambda|lparen|lbrace|literal|var"; + } else { + return "lambda|lparen|lbrace|literal|var"; + } + } + + void + expect_expr_xs::on_def_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + if (allow_defs_) { + define_xs::start(p_psm); + } else { + exprstate::on_def_token(tk, p_psm); + } + } + + 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) + { + if_else_xs::start(p_psm); + } + + void + expect_expr_xs::on_leftparen_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + //constexpr const char * self_name = "exprstate::on_leftparen"; + + /* push lparen_0 to remember to look for subsequent rightparen. */ + paren_xs::start(p_psm); + } + + void + expect_expr_xs::on_leftbrace_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + /* push lparen_0 to remember to look for subsequent rightparen. */ + sequence_xs::start(p_psm); + } + + void + expect_expr_xs::on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + if (cxl_on_rightbrace_) { + auto self = p_psm->pop_exprstate(); + + /* do not call .on_expr(), since '}' cancelled */ + + p_psm->on_rightbrace_token(tk); + } else { + exprstate::on_rightbrace_token(tk, p_psm); + } + } + + void + expect_expr_xs::on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("tk", tk)); + + constexpr const char * c_self_name = "expect_expr_xs::on_symbol_token"; + + /* various possibilities when looking for rhs expression: + * + * x := y // (1) + * x := f(a) // (2) + * x := f(a,b) // (3) + * + * need lookahead token following symbol to distinguish + * between (1) (symbol completes rhs expression) + * and {(2), (3)} (symbol is function call) + */ + + bp var = p_psm->lookup_var(tk.text()); + + if (!var) { + this->unknown_variable_error(c_self_name, tk, p_psm); + return; + } + + /* e.g. + * def pi = 3.14159265; + * def mypi = pi; + * ^ + * def pi2 = pi * 2; + * ^ + * def y = foo(pi2); + * ^ + */ + progress_xs::start(var.promote(), p_psm); + + #ifdef NOT_YET + p_stack->push_exprstate(exprstate(exprstatetype::expr_progress, + Variable::make(name, type))); +#endif + +#ifdef LATER + p_psm->pop_exprstate(); + p_psm->top_exprstate().on_symbol(tk.text(), + p_stack, p_emit_expr); +#endif + return; + } + + void + expect_expr_xs::on_bool_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + progress_xs::start + (Constant::make(tk.bool_value()), + p_psm); + } + + void + expect_expr_xs::on_i64_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag()), + xtag("tk", tk), + xtag("do", "push progress xs w/ tk value")); + + progress_xs::start + (Constant::make(tk.i64_value()), + p_psm); + } + + void + expect_expr_xs::on_f64_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + //constexpr const char * self_name = "exprstate::on_f64_token"; + + /* e.g. + * def pi = 3.14159265; + * \---tk---/ + */ + progress_xs::start + (Constant::make(tk.f64_value()), + p_psm); + } + + void + expect_expr_xs::on_string_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + /* e.g. + * def msg = "hello, world"; + * \----tk----/ + */ + progress_xs::start + (Constant::make(tk.text()), + p_psm); + } + + void + expect_expr_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("exstype", this->exs_type_), + xtag("expr", expr.promote())); + log && log("pop expect_expr_xs, forward to parent"); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr(expr); + } /*on_expr*/ + + void + expect_expr_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("exstype", this->exs_type_), + xtag("expr", expr.promote())); + log && log("pop expect_expr_xs, forward to parent"); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr_with_semicolon(expr); + } /*on_expr_with_semicolon*/ + + void + expect_expr_xs::print(std::ostream & os) const { + os << ""; + } + + bool + expect_expr_xs::pretty_print(const xo::print::ppindentinfo & ppii) const + { + return ppii.pps()->pretty_struct(ppii, "expect_expr_xs", + refrtag("allow_defs", allow_defs_), + refrtag("cxl_on_rightbrace", cxl_on_rightbrace_)); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end expect_expr_xs.cpp */ diff --git a/xo-reader/src/reader/expect_formal_arglist_xs.cpp b/xo-reader/src/reader/expect_formal_arglist_xs.cpp new file mode 100644 index 00000000..f05680d9 --- /dev/null +++ b/xo-reader/src/reader/expect_formal_arglist_xs.cpp @@ -0,0 +1,131 @@ +/* file expect_formal_arglist_xs.cpp + * + * author: Roland Conybeare + */ + +#include "expect_formal_arglist_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "expect_formal_xs.hpp" +#include "expect_symbol_xs.hpp" +#include "xo/expression/Variable.hpp" +#include "xo/indentlog/print/vector.hpp" + +namespace xo { + namespace scm { + const char * + formalarglstatetype_descr(formalarglstatetype x) { + switch (x) { + case formalarglstatetype::invalid: + return "invalid"; + case formalarglstatetype::argl_0: + return "argl_0"; + case formalarglstatetype::argl_1a: + return "argl_1a"; + case formalarglstatetype::argl_1b: + return "argl_1b"; + case formalarglstatetype::n_formalarglstatetype: + break; + } + + return "?formalarglstatetype"; + } + + std::unique_ptr + expect_formal_arglist_xs::make() { + return std::make_unique + (expect_formal_arglist_xs()); + } + + void + expect_formal_arglist_xs::start(parserstatemachine * p_psm) + { + p_psm->push_exprstate(expect_formal_arglist_xs::make()); + } + + const char * + expect_formal_arglist_xs::get_expect_str() const { + switch (farglxs_type_) { + case formalarglstatetype::invalid: + case formalarglstatetype::n_formalarglstatetype: + assert(false); // impossible + break; + case formalarglstatetype::argl_0: + return "leftparen"; + case formalarglstatetype::argl_1a: + return "formal-name"; + case formalarglstatetype::argl_1b: + return "comma|rightparen"; + } + + return "?expect"; + } + + expect_formal_arglist_xs::expect_formal_arglist_xs() + : exprstate(exprstatetype::expect_formal_arglist), + farglxs_type_{formalarglstatetype::argl_0} + {} + + void + expect_formal_arglist_xs::on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (farglxs_type_ == formalarglstatetype::argl_0) { + this->farglxs_type_ = formalarglstatetype::argl_1a; + /* TODO: refactor to have setup method on each exprstate */ + expect_formal_xs::start(p_psm); + } else { + exprstate::on_leftparen_token(tk, p_psm); + } + } + + void + expect_formal_arglist_xs::on_formal(const rp & formal, + parserstatemachine * p_psm) + { + if (farglxs_type_ == formalarglstatetype::argl_1a) { + this->farglxs_type_ = formalarglstatetype::argl_1b; + this->argl_.push_back(formal); + } else { + exprstate::on_formal(formal, p_psm); + } + } + + void + expect_formal_arglist_xs::on_comma_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (farglxs_type_ == formalarglstatetype::argl_1b) { + this->farglxs_type_ = formalarglstatetype::argl_1a; + expect_formal_xs::start(p_psm); + } else { + exprstate::on_comma_token(tk, p_psm); + } + } + + void + expect_formal_arglist_xs::on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (farglxs_type_ == formalarglstatetype::argl_1b) { + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->top_exprstate().on_formal_arglist(this->argl_, p_psm); + } else { + exprstate::on_rightparen_token(tk, p_psm); + } + } + + void + expect_formal_arglist_xs::print(std::ostream & os) const { + os << ""; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end expect_formal_arglist_xs.cpp */ diff --git a/xo-reader/src/reader/expect_formal_xs.cpp b/xo-reader/src/reader/expect_formal_xs.cpp new file mode 100644 index 00000000..3bb72dad --- /dev/null +++ b/xo-reader/src/reader/expect_formal_xs.cpp @@ -0,0 +1,109 @@ +/* file expect_formal_xs.cpp + * + * author: Roland Conybeare + */ + +#include "expect_formal_xs.hpp" +#include "expect_symbol_xs.hpp" +#include "expect_type_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "xo/expression/Variable.hpp" + +namespace xo { + using xo::scm::Variable; + using xo::reflect::TypeDescr; + + namespace scm{ + const char * + formalstatetype_descr(formalstatetype x) { + switch (x) { + case formalstatetype::invalid: + case formalstatetype::n_formalstatetype: + return "?formalstatetype"; + case formalstatetype::formal_0: + return "formal_0"; + case formalstatetype::formal_1: + return "formal_1"; + case formalstatetype::formal_2: + return "formal_2"; + } + + return "???formalstatetype"; + } + + std::unique_ptr + expect_formal_xs::make() { + return std::make_unique(expect_formal_xs()); + } + + void + expect_formal_xs::start(parserstatemachine * p_psm) { + p_psm->push_exprstate(expect_formal_xs::make()); + + expect_symbol_xs::start(p_psm); + } + + expect_formal_xs::expect_formal_xs() + : exprstate(exprstatetype::expect_formal) + {} + + void + expect_formal_xs::on_symbol(const std::string & symbol_name, + parserstatemachine * p_psm) + { + if (this->formalxs_type_ == formalstatetype::formal_0) { + this->formalxs_type_ = formalstatetype::formal_1; + this->result_.assign_name(symbol_name); + } else { + exprstate::on_symbol(symbol_name, p_psm); + } + } + + void + expect_formal_xs::on_colon_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (this->formalxs_type_ == formalstatetype::formal_1) { + this->formalxs_type_ = formalstatetype::formal_2; + expect_type_xs::start(p_psm); + /* control reenters via expect_formal_xs::on_typedescr() */ + } else { + exprstate::on_colon_token(tk, + p_psm); + } + } + + void + expect_formal_xs::on_typedescr(TypeDescr td, + parserstatemachine * p_psm) + { + if (this->formalxs_type_ == formalstatetype::formal_2) { + this->result_.assign_td(td); + + std::unique_ptr self = p_psm->pop_exprstate(); + + rp var = Variable::make(result_.name(), + result_.td()); + + p_psm->top_exprstate().on_formal(var, p_psm); + } else { + exprstate::on_typedescr(td, p_psm); + } + } + + void + expect_formal_xs::print(std::ostream & os) const { + os << ""; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end expect_formal_xs.cpp */ diff --git a/xo-reader/src/reader/expect_symbol_xs.cpp b/xo-reader/src/reader/expect_symbol_xs.cpp new file mode 100644 index 00000000..1baa4178 --- /dev/null +++ b/xo-reader/src/reader/expect_symbol_xs.cpp @@ -0,0 +1,49 @@ +/* file expect_symbol_xs.cpp + * + * author: Roland Conybeare + */ + +#include "expect_symbol_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" + +namespace xo { + namespace scm { + std::unique_ptr + expect_symbol_xs::make() { + return std::make_unique(expect_symbol_xs()); + } + + void + expect_symbol_xs::start(parserstatemachine * p_psm) + { + p_psm->push_exprstate(expect_symbol_xs::make()); + } + + expect_symbol_xs::expect_symbol_xs() + : exprstate(exprstatetype::expect_symbol) + {} + + void + expect_symbol_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("tk", tk)); + + assert(&p_psm->top_exprstate() == this); + + /* have to do pop first, before sending symbol to + * the o.g. symbol-requester + */ + std::unique_ptr self{p_psm->pop_exprstate()}; + + p_psm->on_symbol(tk.text()); + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end expect_symbol_xs.cpp */ diff --git a/xo-reader/src/reader/expect_type_xs.cpp b/xo-reader/src/reader/expect_type_xs.cpp new file mode 100644 index 00000000..62d797f5 --- /dev/null +++ b/xo-reader/src/reader/expect_type_xs.cpp @@ -0,0 +1,79 @@ +/* file expect_type_xs.cpp + * + * author: Roland Conybeare + */ + +#include "expect_type_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "xo/reflect/Reflect.hpp" + +namespace xo { + using xo::reflect::Reflect; + + namespace scm { + + std::unique_ptr + expect_type_xs::make() { + return std::make_unique(expect_type_xs()); + } + + void + expect_type_xs::start(parserstatemachine * p_psm) { + p_psm->push_exprstate(expect_type_xs::make()); + } + + expect_type_xs::expect_type_xs() + : exprstate(exprstatetype::expect_type) + {} + + const char * + expect_type_xs::get_expect_str() const + { + return "typename"; + } + + void + expect_type_xs::on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) + { + const char * c_self_name = "expect_type_xs::on_symbol_token"; + + TypeDescr td = nullptr; + + /* TODO: replace with typetable lookup */ + + if (tk.text() == "bool") + td = Reflect::require(); + else if (tk.text() == "str") + td = Reflect::require(); + else if (tk.text() == "f64") + td = Reflect::require(); + else if(tk.text() == "f32") + td = Reflect::require(); + else if(tk.text() == "i16") + td = Reflect::require(); + else if(tk.text() == "i32") + td = Reflect::require(); + else if(tk.text() == "i64") + td = Reflect::require(); + + if (!td) { + const char * exp = get_expect_str(); + + std::string errmsg = tostr("expect_type_xs: unexpected token for parsing state", + xtag("expecting", exp), + xtag("token", tk.tk_type()), + xtag("text", tk.text()), + xtag("state", this->exs_type())); + + p_psm->on_error(c_self_name, std::move(errmsg)); + } + + std::unique_ptr self = p_psm->pop_exprstate(); + p_psm->top_exprstate().on_typedescr(td, p_psm); + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end expect_type_xs.cpp */ diff --git a/xo-reader/src/reader/exprseq_xs.cpp b/xo-reader/src/reader/exprseq_xs.cpp new file mode 100644 index 00000000..48307d77 --- /dev/null +++ b/xo-reader/src/reader/exprseq_xs.cpp @@ -0,0 +1,276 @@ +/* @file exprseq_xs.cpp */ + +#include "exprseq_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "exprseq_xs.hpp" +//#include "expect_expr_xs.hpp" +#include "progress_xs.hpp" +#include "define_xs.hpp" +#include "if_else_xs.hpp" +#include "expect_symbol_xs.hpp" +#include "xo/expression/Constant.hpp" + +namespace xo { + namespace scm { + std::unique_ptr + exprseq_xs::make(exprseqtype seqtype) + { + return std::make_unique(exprseq_xs(seqtype)); + } + + void + exprseq_xs::start(exprseqtype seqtype, parserstatemachine * p_psm) + { + p_psm->push_exprstate(exprseq_xs::make(seqtype)); + } + + exprseq_xs::exprseq_xs(exprseqtype x) + : exprstate(exprstatetype::expect_toplevel_expression_sequence), + xseqtype_{x} + { + } + + const char * + exprseq_xs::get_expect_str() const + { + /* + * def... + * ^ + * exprseq_xs + */ + switch (this->xseqtype_) { + case exprseqtype::toplevel_interactive: + return "def|expression"; + case exprseqtype::toplevel_batch: + return "def"; + } + + return "?expect"; + } + + void + exprseq_xs::on_def_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + define_xs::start(p_psm); + + /* keyword 'def' introduces a definition: + * def pi : f64 = 3.14159265 + * def sq(x : f64) -> f64 { (x * x) } + */ + } + + void + exprseq_xs::on_if_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (xseqtype_ == exprseqtype::toplevel_interactive) + { + /* in interactive session, allow top-level if-expressions. + * Could be: + * if sometest() then do_something() else do_otherthing(); + */ + if_else_xs::start(p_psm); + } else { + constexpr const char * c_self_name = "exprseq_xs::on_if_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + } + + void + exprseq_xs::on_symbol_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "exprseq_xs::on_symbol_token"; + + if (xseqtype_ == exprseqtype::toplevel_interactive) + { + /* In interactive session, allow top-level variable reference. + * This could be: + * a; // variable references + * a = 1; // single assignment + * a == 1; // rhs expression + * a + b; // rhs expression + * a(1,2); // apply expression + * Variable must have been defined! + */ + bp var = p_psm->lookup_var(tk.text()); + + if (var.get()) { + progress_xs::start(var.promote(), p_psm); + } else { + this->unknown_variable_error(c_self_name, tk, p_psm); + } + } else { + /* policy: don't allow variable references as toplevel expressions + * unless interactive session + */ + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, + tk, + exp, + p_psm); + } + } + + void + exprseq_xs::on_bool_token(const token_type & tk, + parserstatemachine * p_psm) + { + using xo::scm::Constant; + + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + constexpr const char * c_self_name = "exprseq_xs::on_bool_token"; + + if (xseqtype_ == exprseqtype::toplevel_interactive) + { + progress_xs::start(Constant::make(tk.bool_value()), p_psm); + } else { + /* policy: don't allow literals as toplevel expressions + * unless interactive session + */ + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, + tk, + exp, + p_psm); + } + } + + void + exprseq_xs::on_i64_token(const token_type & tk, + parserstatemachine * p_psm) + { + using xo::scm::Constant; + + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprseq_xs::on_i64_token"; + + if (xseqtype_ == exprseqtype::toplevel_interactive) + { + progress_xs::start(Constant::make(tk.i64_value()), p_psm); + } else { + /* policy: don't allow literals as toplevel expressions + * unless interactive session. + */ + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, + tk, + exp, + p_psm); + } + } + + void + exprseq_xs::on_f64_token(const token_type & tk, + parserstatemachine * p_psm) + { + using xo::scm::Constant; + + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprseq_xs::on_f64_token"; + + if (xseqtype_ == exprseqtype::toplevel_interactive) + { + progress_xs::start(Constant::make(tk.f64_value()), p_psm); + } else { + /* policy: don't allow literals as toplevel expressions + * unless interactive session. + */ + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, + tk, + exp, + p_psm); + } + } + + void + exprseq_xs::on_string_token(const token_type & tk, + parserstatemachine * p_psm) + { + using xo::scm::Constant; + + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprseq_xs::on_string_token"; + + if (xseqtype_ == exprseqtype::toplevel_interactive) + { + // remark: + // 1. Constant is an expression. At present (nov 2025) these are + // reference-counted (+ leak in xo-interpreter). + // 2. Could fix leak by adding a finalization feature to GC. + // Do intend to eventually support finalization, + // but not to use it here. + // 3. Instead mean to change allocation strategy for Expression + // to use GC instead. + // 4. As intermediate step try migrating Expression hierarchy + // to support arena allocation. + // See xo/alloc/ArenaAllocT.hpp + assoc'd unit test + // + progress_xs::start(Constant::make(tk.text()), p_psm); + } else { + /* policy: don't allow literals as toplevel expressions + * unless interactive session. + */ + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, + tk, + exp, + p_psm); + } + } + + void + exprseq_xs::on_typedescr(TypeDescr /*td*/, + parserstatemachine * /*p_psm*/) + { + /* unreachable - typedescr should never get delivered to exprseq */ + assert(false); + return; + } + + void + exprseq_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + /* toplevel expression sequence accepts an + * arbitrary number of expressions. + */ + + p_psm->result_ = parser_result::expression(expr.promote()); + } + + void + exprseq_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + /* toplevel expression sequence accepts an + * arbitrary number of expressions. + * + * semicolons are sometimes mandatory to avoid ambiguity. + */ + + p_psm->result_ = parser_result::expression(expr.promote()); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end exprseq_xs.cpp */ diff --git a/xo-reader/src/reader/exprstate.cpp b/xo-reader/src/reader/exprstate.cpp new file mode 100644 index 00000000..5bbfcb86 --- /dev/null +++ b/xo-reader/src/reader/exprstate.cpp @@ -0,0 +1,675 @@ +/* @file exprstate.cpp */ + +#include "exprstate.hpp" +#include "exprstatestack.hpp" +#include "parserstatemachine.hpp" +#include "pretty_parserstatemachine.hpp" +#include "pretty_expression.hpp" +//#include "formal_arg.hpp" +#include "xo/expression/Variable.hpp" +#include "xo/indentlog/print/vector.hpp" +#include "xo/indentlog/print/pretty_vector.hpp" +#include +//#include "define_xs.hpp" +//#include "progress_xs.hpp" +//#include "paren_xs.hpp" +//#include "expect_expr_xs.hpp" +//#include "xo/expression/Constant.hpp" +//#include "xo/reflect/Reflect.hpp" + +namespace xo { + //using xo::scm::Constant; + //using xo::reflect::Reflect; + using xo::reflect::TypeDescr; + using xo::print::ppindentinfo; + + namespace scm { + const char * + exprstatetype_descr(exprstatetype x) { + switch (x) { + case exprstatetype::invalid: + return "?invalid"; + case exprstatetype::expect_toplevel_expression_sequence: + return "expect_toplevel_expression_sequence"; + case exprstatetype::defexpr: + return "defexpr"; + case exprstatetype::lambdaexpr: + return "lambdaexpr"; + case exprstatetype::applyexpr: + return "applyexpr"; + case exprstatetype::parenexpr: + return "parenexpr"; + case exprstatetype::sequenceexpr: + return "sequenceexpr"; + case exprstatetype::let1expr: + return "let1expr"; + case exprstatetype::ifexpr: + return "ifexpr"; + case exprstatetype::expect_rhs_expression: + return "expect_rhs_expression"; + case exprstatetype::expect_symbol: + return "expect_symbol"; + case exprstatetype::expect_type: + return "expect_type"; + case exprstatetype::expect_formal_arglist: + return "expect_formal_arglist"; + case exprstatetype::expect_formal: + return "expect_formal"; + case exprstatetype::expr_progress: + return "expr_progress"; + case exprstatetype::n_exprstatetype: + break; + } + + return "???"; + } + + void + exprstate::on_def_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("exstype", p_psm->top_exprstate().exs_type())); + + constexpr const char * c_self_name = "exprstate::on_def_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_lambda_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "exprstate::on_lambda_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::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 * c_self_name = "exprstate::on_symbol_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_typedescr(TypeDescr td, + parserstatemachine * p_psm) + { + /* returning type description to something that wants it */ + + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("exstype", + p_psm->top_exprstate().exs_type())); + + constexpr const char * c_self_name = "exprstate::on_typedescr"; + const char * exp = get_expect_str(); + + this->illegal_input_on_type(c_self_name, td, exp, p_psm); + } + + void + exprstate::on_formal(const rp & formal, + parserstatemachine * p_psm) + { + /* returning type description to something that wants it */ + + 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 * c_self_name = "exprstate::on_formal"; + const char * exp = this->get_expect_str(); + + std::string errmsg = tostr("unexpected formal-arg for parsing state", + xtag("expecting", exp), + xtag("formal", formal), + xtag("state", this->exs_type())); + + p_psm->on_error(c_self_name, std::move(errmsg)); + } + + void + exprstate::on_formal_arglist(const std::vector> & argl, + parserstatemachine * p_psm) + { + /* returning type description to something that wants it */ + + 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 * c_self_name = "exprstate::on_formal_arglist"; + const char * exp = get_expect_str(); + + std::string errmsg = tostr("unexpected formal-arglist for parsing state", + xtag("expecting", exp), + xtag("argl", argl), + xtag("state", this->exs_type())); + + p_psm->on_error(c_self_name, std::move(errmsg)); + } + + void + exprstate::on_colon_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_colon"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_comma_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 = "exprstate::on_comma"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_semicolon_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 = "exprstate::on_semicolon"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_singleassign_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 = "exprstate::on_singleassign_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_assign_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 = "exprstate::on_assign_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + + void + exprstate::on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_leftparen_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::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 = "exprstate::on_rightparen"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_leftbrace_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 = "exprstate::on_leftbrace_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_rightbrace_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 = "exprstate::on_rightbrace_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_operator_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 = "exprstate::on_operator_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_if_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_if_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_then_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_then_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_else_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_else_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_bool_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_bool_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_i64_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_i64_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_f64_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_f64_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_string_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "exprstate::on_string_token"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + exprstate::on_input(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + log && log(xtag("tk", tk)); + log && log(xtag("state", *this)); + log && log(xtag("psm", p_psm)); + log && log(xtag("proofoflogging", true)); + + switch (tk.tk_type()) { + + case tokentype::tk_def: + this->on_def_token(tk, p_psm); + return; + + case tokentype::tk_lambda: + this->on_lambda_token(tk, p_psm); + return; + + case tokentype::tk_bool: + this->on_bool_token(tk, p_psm); + return; + + case tokentype::tk_i64: + this->on_i64_token(tk, p_psm); + return; + + case tokentype::tk_f64: + this->on_f64_token(tk, p_psm); + return; + + case tokentype::tk_string: + this->on_string_token(tk, p_psm); + return; + + case tokentype::tk_symbol: + this->on_symbol_token(tk, p_psm); + return; + + case tokentype::tk_leftparen: + this->on_leftparen_token(tk, p_psm); + return; + + case tokentype::tk_rightparen: + this->on_rightparen_token(tk, p_psm); + return; + + case tokentype::tk_leftbracket: + case tokentype::tk_rightbracket: + assert(false); + break; + + case tokentype::tk_leftbrace: + this->on_leftbrace_token(tk, p_psm); + return; + + case tokentype::tk_rightbrace: + this->on_rightbrace_token(tk, p_psm); + return; + + case tokentype::tk_leftangle: + case tokentype::tk_rightangle: + case tokentype::tk_lessequal: + case tokentype::tk_greatequal: + this->on_operator_token(tk, p_psm); + return; + + case tokentype::tk_dot: + assert(false); + return; + + case tokentype::tk_comma: + this->on_comma_token(tk, p_psm); + return; + + case tokentype::tk_colon: + this->on_colon_token(tk, p_psm); + return; + + case tokentype::tk_doublecolon: + assert(false); + return; + + case tokentype::tk_semicolon: + this->on_semicolon_token(tk, p_psm); + return; + + case tokentype::tk_singleassign: + this->on_singleassign_token(tk, p_psm); + return; + + case tokentype::tk_assign: + this->on_assign_token(tk, p_psm); + return; + + case tokentype::tk_yields: + assert(false); + break; + + case tokentype::tk_plus: + case tokentype::tk_minus: + case tokentype::tk_star: + case tokentype::tk_slash: + case tokentype::tk_cmpeq: + case tokentype::tk_cmpne: + this->on_operator_token(tk, p_psm); + return; + + case tokentype::tk_type: + assert(false); + return; + + case tokentype::tk_if: + this->on_if_token(tk, p_psm); + return; + + case tokentype::tk_then: + this->on_then_token(tk, p_psm); + return; + + case tokentype::tk_else: + this->on_else_token(tk, p_psm); + return; + + case tokentype::tk_let: + + case tokentype::tk_in: + case tokentype::tk_end: + assert(false); + return; + + case tokentype::tk_invalid: + case tokentype::n_tokentype: + assert(false); + return; + } + + assert(false); + } + + const char * + exprstate::get_expect_str() const + { + return "?expect"; + } + + void + exprstate::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)); + + constexpr const char * c_self_name = "exprstate::on_expr"; + const char * exp = get_expect_str(); + + this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); + } /*on_expr*/ + + void + exprstate::on_expr_with_semicolon(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)); + + constexpr const char * c_self_name = "exprstate::on_expr_with_semicolon"; + const char * exp = get_expect_str(); + + this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); + } /*on_expr_with_semicolon*/ + + void + exprstate::on_symbol(const std::string & symbol_name, + parserstatemachine * p_psm) + { + /* unreachable - derived class that can receive + * will override this method + */ + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("exstype", this->exs_type_), + xtag("symbol_name", symbol_name)); + + constexpr const char * c_self_name = "exprstate::on_symbol"; + const char * exp = get_expect_str(); + + this->illegal_input_on_symbol(c_self_name, symbol_name, exp, p_psm); + } + + void + exprstate::print(std::ostream & os) const { + os << ""; + } + + bool + exprstate::pretty_print(const ppindentinfo & ppii) const + { + return ppii.pps()->pretty_struct(ppii, "exprstate", + refrtag("type", exs_type_)); + } + + void + exprstate::illegal_input_error(const char * self_name, + const token_type & tk) const + { + throw std::runtime_error + (tostr(self_name, + ": unexpected input token for parsing state", + xtag("token", tk), + xtag("state", *this))); + } + + void + exprstate::illegal_input_on_expr(const char * self_name, + bp expr, + const char * expect_str, + parserstatemachine * p_psm) const + { + std::string errmsg = tostr("unexpected expression for parsing state", + xtag("expecting", expect_str), + xtag("expr", expr), + xtag("state", this->exs_type())); + + p_psm->on_error(self_name, std::move(errmsg)); + } + + void + exprstate::illegal_input_on_token(const char * self_name, + const token_type & tk, + const char * expect_str, + parserstatemachine * p_psm) const + { + std::string errmsg = tostr("exprstate::illegal_input_on_token: unexpected token for parsing state", + xtag("expecting", expect_str), + xtag("token", tk.tk_type()), + xtag("text", tk.text()), + xtag("state", this->exs_type())); + + p_psm->on_error(self_name, std::move(errmsg)); + } + + void + exprstate::illegal_input_on_symbol(const char * self_name, + const std::string & symbol, + const char * expect_str, + parserstatemachine * p_psm) const + { + std::string errmsg = tostr("unexpected symbol", + xtag("expecting", expect_str), + xtag("symbol", symbol), + xtag("state", this->exs_type())); + + p_psm->on_error(self_name, std::move(errmsg)); + } + + void + exprstate::illegal_input_on_type(const char * self_name, + const TypeDescr td, + const char * expect_str, + parserstatemachine * p_psm) const + { + std::string errmsg = tostr("unexpected type", + xtag("expecting", expect_str), + xtag("type", td), + xtag("state", this->exs_type())); + + p_psm->on_error(self_name, std::move(errmsg)); + } + + void + exprstate::unknown_variable_error(const char * self_name, + const token_type & tk, + parserstatemachine * p_psm) const + { + std::string errmsg = tostr("unknown variable name", + xtag("var", tk.text())); + + p_psm->on_error(self_name, std::move(errmsg)); + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end exprstate.cpp */ diff --git a/xo-reader/src/reader/exprstatestack.cpp b/xo-reader/src/reader/exprstatestack.cpp new file mode 100644 index 00000000..96852b73 --- /dev/null +++ b/xo-reader/src/reader/exprstatestack.cpp @@ -0,0 +1,120 @@ +/* file exprstatestack.cpp + * + * author: Roland Conybeare + */ + +#include "exprstatestack.hpp" +#include "pretty_exprstatestack.hpp" +#include + +namespace xo { + namespace scm { + exprstate & + exprstatestack::top_exprstate() { + std::size_t z = stack_.size(); + + if (z == 0) { + throw std::runtime_error + ("parser::top_exprstate: unexpected empty stack"); + } + + return *(stack_[z-1]); + } + + void + exprstatestack::reset_to_toplevel() { + this->stack_.resize(1); + } + + void + exprstatestack::push_exprstate(std::unique_ptr exs) { + constexpr bool c_debug_flag = false; + scope log(XO_DEBUG(c_debug_flag), + xtag("exs", exs.get())); + + std::size_t z = stack_.size(); + + stack_.resize(z+1); + + stack_[z] = std::move(exs); + } + + std::unique_ptr + exprstatestack::pop_exprstate() { + constexpr bool c_debug_flag = false; + scope log(XO_DEBUG(c_debug_flag), + xtag("top.exstype", top_exprstate().exs_type())); + + std::size_t z = stack_.size(); + + if (z > 0) { + std::unique_ptr top = std::move(stack_[z-1]); + + stack_.resize(z-1); + + return top; + } else { + return nullptr; + } + } + + void + exprstatestack::print(std::ostream & os) const { + os << "" << std::endl; + } + + bool + exprstatestack::pretty_print(const ppindentinfo & ppii) const + { + ppstate * pps = ppii.pps(); + + if (ppii.upto()) { + if (stack_.size() > 1) + return false; + + if (!pps->print_upto("print_upto_tag("size", stack_.size())) + return false; + + /** always multiple lines if more than one element in stack **/ + if ((stack_.size() > 0) + && !pps->print_upto_tag("[0]", stack_[0].get())) + { + return false; + } + + pps->write(">"); + + return true; + } else { + pps->write("newline_pretty_tag(ppii.ci1(), "size", stack_.size()); + + for (std::size_t i = 0, z = stack_.size(); i < z; ++i) { + std::string i_str = tostr("[", z-i-1, "]"); + + pps->newline_pretty_tag(ppii.ci1(), i_str, stack_[i].get()); + } + + pps->write(">"); + + return false; + } + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end exprstatestack.cpp */ diff --git a/xo-reader/src/reader/if_else_xs.cpp b/xo-reader/src/reader/if_else_xs.cpp new file mode 100644 index 00000000..01d142e0 --- /dev/null +++ b/xo-reader/src/reader/if_else_xs.cpp @@ -0,0 +1,288 @@ +/** @file if_else_xs.cpp + * + * author: Roland Conybeare, Jul 2025 + **/ + +#include "if_else_xs.hpp" +//#include "exprstatestack.hpp" +#include "parserstatemachine.hpp" +#include "expect_expr_xs.hpp" +#include "xo/indentlog/print/ppdetail_atomic.hpp" + +namespace xo { + namespace scm { + // ----- ifexprstatetype ----- + + const char * + ifexprstatetype_descr(ifexprstatetype x) { + switch (x) { + case ifexprstatetype::invalid: return "invalid"; + case ifexprstatetype::if_0: return "if_0"; + case ifexprstatetype::if_1: return "if_1"; + case ifexprstatetype::if_2: return "if_2"; + case ifexprstatetype::if_3: return "if_3"; + case ifexprstatetype::if_4: return "if_4"; + case ifexprstatetype::if_5: return "if_5"; + case ifexprstatetype::if_6: return "if_6"; + case ifexprstatetype::n_ifexprstatetype: break; + } + + return "???ifexprstatetype"; + } + + std::ostream & + operator<<(std::ostream & os, ifexprstatetype x) { + os << ifexprstatetype_descr(x); + return os; + } + + // ----- if_else_xs ----- + + std::unique_ptr + if_else_xs::make() { + return std::make_unique(if_else_xs(IfExprAccess::make_empty())); + } + + void + if_else_xs::start(parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + p_psm->push_exprstate(if_else_xs::make()); + p_psm->top_exprstate().on_if_token(token_type::if_token(), p_psm); + } + + if_else_xs::if_else_xs(rp if_expr) + : exprstate(exprstatetype::ifexpr), + ifxs_type_{ifexprstatetype::if_0}, + if_expr_{std::move(if_expr)} + {} + + const char * + if_else_xs::get_expect_str() const + { + /** + * if test-expr then then-expr else else-expr ; + * ^ ^ ^ ^ ^ ^ ^ + * | | | | | | | + * | if_1 if_2 if_3 if_4 if_5 if_6 + * if_0 + * + * if_0 --on_if_token()--> if_1 + * if_1 --on_expr()--> if_2 + * if_2 --on_then_token()--> if_3 + * if_3 --on_expr()--> if_4 + * if_4 --on_else_token()--> if_5 + * --on_semicolon_token()--> (done) + * if_5 --on_expr()-->if_6 + * if_6 --on_semicolon_token()--> (done) + **/ + switch (this->ifxs_type_) { + case ifexprstatetype::invalid: + case ifexprstatetype::if_0: + case ifexprstatetype::n_ifexprstatetype: + assert(false); // unreachable + return nullptr; + case ifexprstatetype::if_1: + return "expression"; + case ifexprstatetype::if_2: + return "then"; + case ifexprstatetype::if_3: + return "expression"; + case ifexprstatetype::if_4: + return "else|semicolon"; + case ifexprstatetype::if_5: + return "expression"; + case ifexprstatetype::if_6: + return "semicolon"; + } + + return "?expect"; + } + + void + if_else_xs::on_if_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("ifxs_type", ifxs_type_); + + if (this->ifxs_type_ == ifexprstatetype::if_0) { + this->ifxs_type_ = ifexprstatetype::if_1; + + expect_expr_xs::start(p_psm); + return; + } + + constexpr const char * c_self_name = "if_else_xs::on_if_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + if_else_xs::on_then_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("ifxs_type", ifxs_type_); + + if (this->ifxs_type_ == ifexprstatetype::if_2) { + this->ifxs_type_ = ifexprstatetype::if_3; + + expect_expr_xs::start(p_psm); + return; + } + + constexpr const char * c_self_name = "if_else_xs::on_then_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + if_else_xs::on_else_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("ifxs_type", ifxs_type_); + + if (this->ifxs_type_ == ifexprstatetype::if_4) { + this->ifxs_type_ = ifexprstatetype::if_5; + + expect_expr_xs::start(p_psm); + return; + } + + constexpr const char * c_self_name = "if_else_xs::on_else_token"; + const char * exp = this->get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + if_else_xs::finish_and_continue(parserstatemachine * p_psm) + { + rp if_expr = this->if_expr_; + std::unique_ptr self = p_psm->pop_exprstate(); + + if (this->ifxs_type_ == ifexprstatetype::if_4) { + /* if no else-branch, then if-expr can't have valuetype */ + if_expr->assign_valuetype(nullptr); + } + + p_psm->top_exprstate().on_expr(if_expr, p_psm); + } + + void + if_else_xs::on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + this->finish_and_continue(p_psm); + p_psm->on_rightbrace_token(tk); + } + + void + if_else_xs::on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log("ifxs_type", ifxs_type_); + + const char * c_self_name = "if_else_xs::on_semicolon_token"; + + switch (this->ifxs_type_) { + case ifexprstatetype::invalid: + case ifexprstatetype::if_0: + case ifexprstatetype::n_ifexprstatetype: + // unreachable + assert(false); + break; + + case ifexprstatetype::if_1: + case ifexprstatetype::if_2: + case ifexprstatetype::if_3: + case ifexprstatetype::if_5: + this->illegal_input_on_token(c_self_name, tk, get_expect_str(), p_psm); + break; + case ifexprstatetype::if_4: + case ifexprstatetype::if_6: { + this->finish_and_continue(p_psm); + break; + } + } + } + + void + if_else_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("ifxs_type", ifxs_type_)); + + switch (this->ifxs_type_) { + case ifexprstatetype::invalid: + case ifexprstatetype::if_0: + case ifexprstatetype::n_ifexprstatetype: + assert(false); // unreachable + return; + case ifexprstatetype::if_1: + if_expr_->assign_test(expr.promote()); + ifxs_type_ = ifexprstatetype::if_2; + return; + case ifexprstatetype::if_2: + /** error: expecting 'then' **/ + break; + case ifexprstatetype::if_3: + if_expr_->assign_when_true(expr.promote()); + ifxs_type_ = ifexprstatetype::if_4; + return; + case ifexprstatetype::if_4: + /** error: expecting 'else' or ';' **/ + break; + case ifexprstatetype::if_5: + if_expr_->assign_when_false(expr.promote()); + ifxs_type_ = ifexprstatetype::if_6; + return; + case ifexprstatetype::if_6: + /** error: expecting ';' **/ + break; + } + + constexpr const char* c_self_name = "if_else_xs::on_expr"; + const char * exp = get_expect_str(); + + this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); + } + + void + if_else_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + + log && log(xtag("ifxs_type", ifxs_type_)); + + this->on_expr(expr, p_psm); + this->on_semicolon_token(token_type::semicolon(), p_psm); + } + + void + if_else_xs::print(std::ostream & os) const { + os << ""; + } + + } /*namespace scm*/ +} /*namespace xo*/ diff --git a/xo-reader/src/reader/lambda_xs.cpp b/xo-reader/src/reader/lambda_xs.cpp new file mode 100644 index 00000000..ca070627 --- /dev/null +++ b/xo-reader/src/reader/lambda_xs.cpp @@ -0,0 +1,315 @@ +/* @file lambda_xs.cpp */ + +#include "lambda_xs.hpp" +#include "define_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "expect_formal_arglist_xs.hpp" +#include "expect_expr_xs.hpp" +#include "expect_type_xs.hpp" +#include "pretty_expression.hpp" +#include "pretty_variable.hpp" +#include "xo/expression/Lambda.hpp" + +namespace xo { + using xo::scm::Lambda; + using xo::scm::LocalSymtab; + + namespace scm { + const char * + lambdastatetype_descr(lambdastatetype x) { + switch(x) { + case lambdastatetype::invalid: return "invalid"; + case lambdastatetype::lm_0: return "lm_0"; + case lambdastatetype::lm_1: return "lm_1"; + case lambdastatetype::lm_2: return "lm_2"; + case lambdastatetype::lm_3: return "lm_3"; + case lambdastatetype::lm_4: return "lm_4"; + case lambdastatetype::lm_5: return "lm_5"; + default: break; + } + + return "???lambdastatetype"; + } + + // ----- lambda_xs - ---- + + std::unique_ptr + lambda_xs::make() { + return std::make_unique(lambda_xs()); + } + + void + lambda_xs::start(parserstatemachine * p_psm) + { + p_psm->push_exprstate(lambda_xs::make()); + p_psm->top_exprstate() + .on_lambda_token(token_type::lambda(), p_psm); + } + + lambda_xs::lambda_xs() : exprstate(exprstatetype::lambdaexpr) {} + + const char * + lambda_xs::get_expect_str() const + { + /* + * lambda (x : f64) : f64 { ... } ; + * ^ ^ ^ ^ ^ ^ + * | | | | | lm_5 + * | | | | lm_4:expect_expression + * | | | lm_3 + * | | lm_2 + * | lm_1: + * expect_expression + */ + switch (this->lmxs_type_) { + case lambdastatetype::invalid: + case lambdastatetype::lm_0: + case lambdastatetype::n_lambdastatetype: + assert(false); // impossible + return nullptr; + case lambdastatetype::lm_1: + return "lambda-params"; + case lambdastatetype::lm_2: + return "colon|lambda-body"; + case lambdastatetype::lm_3: + return "type"; + case lambdastatetype::lm_4: + return "lambda-body"; + case lambdastatetype::lm_5: + return "semicolon"; + } + + return "?expect"; + } + + void + lambda_xs::on_lambda_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (lmxs_type_ == lambdastatetype::lm_0) { + this->lmxs_type_ = lambdastatetype::lm_1; + expect_formal_arglist_xs::start(p_psm); + } else { + exprstate::on_lambda_token(tk, p_psm); + } + } + + void + lambda_xs::on_formal_arglist(const std::vector> & argl, + parserstatemachine * p_psm) + { + if (lmxs_type_ == lambdastatetype::lm_1) { + this->lmxs_type_ = lambdastatetype::lm_2; + this->parent_env_ = p_psm->top_envframe().promote(); + this->local_env_ = LocalSymtab::make(argl, parent_env_); + + p_psm->push_envframe(local_env_); + + //expect_expr_xs::start(p_psm); + } else { + exprstate::on_formal_arglist(argl, p_psm); + } + } + + void + lambda_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + this->on_expr(expr, p_psm); + this->on_semicolon_token(token_type::semicolon(), p_psm); + } + + void + lambda_xs::on_colon_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "lambda_xs::on_colon_token"; + + if (lmxs_type_ == lambdastatetype::lm_2) { + this->lmxs_type_ = lambdastatetype::lm_3; + expect_type_xs::start(p_psm); + /* control reenters via .on_typedescr() */ + } else { + this->illegal_input_on_token(c_self_name, tk, this->get_expect_str(), p_psm); + } + } + + void + lambda_xs::on_leftbrace_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "lambda_xs::on_leftbrace_token"; + + if (lmxs_type_ == lambdastatetype::lm_2) + this->lmxs_type_ = lambdastatetype::lm_4; + + if (lmxs_type_ == lambdastatetype::lm_4) { + expect_expr_xs::start(p_psm); + /* want { to start expr sequence, that finishes on matching } */ + p_psm->on_leftbrace_token(token_type::leftbrace()); + } else { + this->illegal_input_on_token(c_self_name, tk, this->get_expect_str(), p_psm); + } + } + + void + lambda_xs::on_typedescr(TypeDescr td, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "lambda_xs::on_typedescr"; + scope log(XO_DEBUG(p_psm->debug_flag())); + + assert(td); + + if (lmxs_type_ == lambdastatetype::lm_3) { + this->lmxs_type_ = lambdastatetype::lm_4; + this->explicit_return_td_ = td; + + this->lambda_td_ = Lambda::assemble_lambda_td(local_env_->argv(), + explicit_return_td_); + + /* 1. at this point we know function signature (@ref lambda_td_) + * 2. if this lambda appears on the rhs of a define, + * propagate function signature to the define. + * 3. this makes recursive function definitions like this work + * without relying on type inference: + * def fact = lambda (n : i64) : i64 { + * if (n == 0) then + * 1 + * else + * n * fact(n - 1) + * } + * 4. while parsing the body of the lambda, we want environment + * to already associate the lambda's signature with variable 'fact', + * so that when parser encounters 'fact(n - 1)' the expression has + * known valuetype. + */ + + if ((p_psm->exprstate_stack_size() >= 3) + && (p_psm->lookup_exprstate(1).exs_type() == exprstatetype::expect_rhs_expression) + && (p_psm->lookup_exprstate(2).exs_type() == exprstatetype::defexpr) + && (p_psm->env_stack_size() >= 2) + ) + { + const define_xs * def_xs = dynamic_cast(&(p_psm->lookup_exprstate(2))); + + assert(def_xs); + + bp def_var = def_xs->lhs_variable(); + + if (def_var->valuetype() == nullptr) { + log && log("assign discovered lambda type T to enclosing define", + xtag("lhs", def_var.get()), + xtag("T", print::unq(this->lambda_td_->canonical_name()))); + + def_var->assign_valuetype(lambda_td_); + } else { + /* don't need to unify here. if def already hasa a type, + * that's because it was explicitly specified. + * will discover any conflict after reporting parsed lambda + * to define_xs + */ + } + } + + expect_expr_xs::start(p_psm); + /* control reenters via .on_expr() or .on_expr_with_semicolon() */ + } else { + this->illegal_input_on_type(c_self_name, td, this->get_expect_str(), p_psm); + } + } + + void + lambda_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "lambda_xs::on_expr"; + + if (lmxs_type_ == lambdastatetype::lm_4) { + this->lmxs_type_ = lambdastatetype::lm_5; + this->body_ = expr.promote(); + } else { + this->illegal_input_on_expr(c_self_name, expr, this->get_expect_str(), p_psm); + } + } + + void + lambda_xs::on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (lmxs_type_ == lambdastatetype::lm_5) { + /* done! */ + + std::unique_ptr self = p_psm->pop_exprstate(); + + std::string name = Variable::gensym("lambda"); + + /* top env frame recorded arguments to this lambda */ + p_psm->pop_envframe(); + + rp lm; + + /* TODO: unify explicit_return_td_ with body_ */ + + if (lambda_td_) { + lm = Lambda::make(name, lambda_td_, local_env_, body_); + } else { + lm = Lambda::make_from_env(name, local_env_, + explicit_return_td_, body_); + } + + p_psm->top_exprstate().on_expr(lm, p_psm); + p_psm->top_exprstate().on_semicolon_token(tk, p_psm); + + return; + } + + exprstate::on_semicolon_token(tk, p_psm); + } + + void + lambda_xs::on_f64_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "lambda_xs::on_f64_token"; + + /* f64 literal can begin lambda body, otherwise illegal. + * for example: + * def foo = lambda (x: bool) 3.14; + */ + if (lmxs_type_ == lambdastatetype::lm_2) { + /* omitting return type. + * omitting left brace. + */ + this->lmxs_type_ = lambdastatetype::lm_4; + + expect_expr_xs::start(p_psm); + p_psm->on_f64_token(tk); + } else { + this->illegal_input_on_token(c_self_name, tk, this->get_expect_str(), p_psm); + } + } + + // TODO: on_i64_token, on_bool token + + void + lambda_xs::print(std::ostream & os) const { + os << ""; + } + + bool + lambda_xs::pretty_print(const xo::print::ppindentinfo & ppii) const + { + return ppii.pps()->pretty_struct(ppii, "lambda_xs", + refrtag("lmxs_type", lmxs_type_), + refrtag("body", body_)); + } + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end lambda_xs.cpp */ diff --git a/xo-reader/src/reader/let1_xs.cpp b/xo-reader/src/reader/let1_xs.cpp new file mode 100644 index 00000000..2a65b2a3 --- /dev/null +++ b/xo-reader/src/reader/let1_xs.cpp @@ -0,0 +1,162 @@ +/* file let1_xs.cpp + * + * author: Roland Conybeare + */ + +#include "let1_xs.hpp" +#include "expect_expr_xs.hpp" +#include "parserstatemachine.hpp" +#include "xo/expression/Sequence.hpp" +#include "xo/expression/DefineExpr.hpp" +#include "xo/expression/Apply.hpp" +#include "xo/expression/Lambda.hpp" + +namespace xo { + using Sequence = xo::scm::Sequence; + using DefineExpr = xo::scm::DefineExpr; + using Apply = xo::scm::Apply; + using Lambda = xo::scm::Lambda; + using LambdaAccess = xo::scm::LambdaAccess; + using Environment = xo::scm::SymbolTable; + using LocalEnv = xo::scm::LocalSymtab; + using Variable = xo::scm::Variable; + + namespace scm { + std::unique_ptr + let1_xs::make(std::string lhs_name, + rp local_env, + rp rhs) + { + return std::make_unique(let1_xs(std::move(lhs_name), + std::move(local_env), + std::move(rhs))); + } + + void + let1_xs::start(const std::string & lhs_name, + const rp & rhs, + parserstatemachine * p_psm) + { + rp parent_env = p_psm->top_envframe().promote(); + rp var1 = Variable::make(lhs_name, rhs->valuetype()); + rp let_env = LocalEnv::make1(var1, parent_env); + + p_psm->push_envframe(let_env); + + // TODO: stash let_env in let1_xs, then pick up directly in .on_rightbrace_token() + // still have to push here so vars can find it + // + p_psm->push_exprstate(let1_xs::make(lhs_name, let_env, rhs)); + + expect_expr_xs::start(true /*allow_defs*/, + true /*cxl_on_rightbrace*/, + p_psm); + } + + let1_xs::let1_xs(std::string lhs_name, + rp local_env, + rp rhs) + : exprstate(exprstatetype::let1expr), + lhs_name_{std::move(lhs_name)}, + local_env_{std::move(local_env)}, + rhs_{std::move(rhs)} + {} + + void + let1_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + bp def_expr = DefineExpr::from(expr); + + if (def_expr) { + /** starting a nested let here: + * control returns via + * .on_expr(x) + * with x something like: + * Apply(Lambda(gensym(), + * [Variable(def_expr->lhs_name(), + * def_expr->valuetype())], + * body...)) + * followed immediately by + * .on_rightbrace_token() + **/ + let1_xs::start(def_expr->lhs_name(), + def_expr->rhs(), + p_psm); + } else { + this->expr_v_.push_back(expr.promote()); + + expect_expr_xs::start(true /*allow_defs*/, + true /*cxl_on_rightbrace*/, + p_psm); + } + } + + void + let1_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + /* same as on_expr(), since we only use let1_xs inside a block { .. } + * This means final ';' is unnecessary + */ + + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + bp def_expr = DefineExpr::from(expr); + + if (def_expr) { + let1_xs::start(def_expr->lhs_name(), + def_expr->rhs(), + p_psm); + } else { + this->expr_v_.push_back(expr.promote()); + + expect_expr_xs::start(true /*allow_defs*/, + true /*cxl_on_rightbvrace*/, + p_psm); + } + } + + void + let1_xs::on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) + { + auto self = p_psm->pop_exprstate(); + + auto expr = Sequence::make(this->expr_v_); + + /* top env frame was established by let1_xs::start(); + * now unwind it + */ + p_psm->pop_envframe(); + + std::string lambda_name = Variable::gensym("let1"); + + rp parent_env = p_psm->top_envframe().promote(); + + rp lambda + = Lambda::make_from_env(lambda_name, + local_env_, + nullptr /*explicit_return_td*/, + expr); + + rp result + = Apply::make(lambda, {this->rhs_}); + + p_psm->top_exprstate().on_expr(result, p_psm); + + /* caller of let1_xs expects the same rightbrace '}' + * -- remember we pushed let1_xs to handle an embedded def-expr + * in a sequence + */ + p_psm->on_rightbrace_token(tk); + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end let1_xs.cpp */ diff --git a/xo-reader/src/reader/paren_xs.cpp b/xo-reader/src/reader/paren_xs.cpp new file mode 100644 index 00000000..950a1547 --- /dev/null +++ b/xo-reader/src/reader/paren_xs.cpp @@ -0,0 +1,265 @@ +/* @file paren_xs.cpp */ + +#include "paren_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "progress_xs.hpp" +#include "expect_expr_xs.hpp" +#include "xo/expression/pretty_expression.hpp" + +namespace xo { + namespace scm { + 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::n_parenexprstatetype: break; + } + + return "???parenexprstatetype"; + } + + paren_xs::paren_xs() + : exprstate(exprstatetype::parenexpr), + parenxs_type_{parenexprstatetype::lparen_0} + {} + + std::unique_ptr + paren_xs::make() { + return std::make_unique(paren_xs()); + } + + 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 << ""; + } + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end paren_xs.cpp */ diff --git a/xo-reader/src/reader/parser.cpp b/xo-reader/src/reader/parser.cpp new file mode 100644 index 00000000..3c3f7050 --- /dev/null +++ b/xo-reader/src/reader/parser.cpp @@ -0,0 +1,102 @@ +/* file parser.cpp + * + * author: Roland Conybeare + */ + +#include "parser.hpp" +#include "parserstatemachine.hpp" +#include "define_xs.hpp" +#include "exprseq_xs.hpp" +#include "pretty_expression.hpp" +#include "xo/expression/DefineExpr.hpp" +#include "xo/expression/Constant.hpp" +#include "xo/expression/ConvertExpr.hpp" +#include "xo/expression/GlobalSymtab.hpp" +//#include "xo/expression/LocalSymtab.hpp" +//#include +#include + +namespace xo { + using xo::scm::Expression; + using xo::scm::LocalSymtab; + using xo::reflect::TypeDescr; + + namespace scm { + // ----- parser ----- + + parser::parser(const rp & toplevel_symtab, bool debug_flag) + : psm_{debug_flag} + { +#ifdef OBSOLETE + /* top-level environment. initially empty */ + rp toplevel_env = GlobalSymtab::make_empty(); +#endif + + this->psm_.env_stack_.push_envframe(toplevel_symtab); + } + + bool + parser::has_incomplete_expr() const { + /* (don't count toplevel exprseq) */ + return psm_.xs_stack_.size() > 1; + } + + void + parser::begin_interactive_session() { + exprseq_xs::start(exprseqtype::toplevel_interactive, &psm_); + } + + void + parser::begin_translation_unit() { + exprseq_xs::start(exprseqtype::toplevel_batch, &psm_); + } + + const parser_result & + parser::include_token(const token_type & tk) + { + scope log(XO_DEBUG(psm_.debug_flag()), xtag("tk", tk)); + + if (psm_.xs_stack_.empty()) { + throw std::runtime_error(tostr("parser::include_token", + ": parser not expecting input" + "(call parser.begin_translation_unit()..?)", + xtag("token", tk))); + } + + /* stack_ is non-empty */ + + log && log(xrefrtag("top", psm_.xs_stack_.top_exprstate())); + + psm_.xs_stack_.top_exprstate().on_input(tk, &psm_); + + return psm_.result_; + } /*include_token*/ + + void + parser::reset_result() + { + psm_.result_ = parser_result::none(); + } + + void + parser::reset_to_idle_toplevel() + { + psm_.xs_stack_.reset_to_toplevel(); + psm_.env_stack_.reset_to_toplevel(); + psm_.result_ = parser_result::none(); + } /*discard_current_state*/ + + void + parser::print(std::ostream & os) const { + os << "" << std::endl; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end parser.cpp */ diff --git a/xo-reader/src/reader/parser_result.cpp b/xo-reader/src/reader/parser_result.cpp new file mode 100644 index 00000000..bf47f405 --- /dev/null +++ b/xo-reader/src/reader/parser_result.cpp @@ -0,0 +1,44 @@ +/* file parser_result.cpp + * + * author: Roland Conybeare, Jul 2025 + */ + +#include "parser_result.hpp" + +namespace xo { + namespace scm { + parser_result::parser_result(parser_result_type type, rp expr, const char * src_fn, std::string errmsg) + : result_type_{type}, result_expr_{std::move(expr)}, error_src_function_{src_fn}, error_description_{std::move(errmsg)} + {} + + parser_result + parser_result::none() + { + return parser_result(parser_result_type::none, + nullptr, + nullptr, + std::string()); + } + + parser_result + parser_result::error(const char * error_src_function, + std::string errmsg) + { + return parser_result(parser_result_type::error, + nullptr, + error_src_function, + std::move(errmsg)); + } + + parser_result + parser_result::expression(rp expr) + { + return parser_result(parser_result_type::expression, + std::move(expr), + nullptr /*src_function*/, + std::string()); + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end parser_result.cpp */ diff --git a/xo-reader/src/reader/parserstatemachine.cpp b/xo-reader/src/reader/parserstatemachine.cpp new file mode 100644 index 00000000..e27801e0 --- /dev/null +++ b/xo-reader/src/reader/parserstatemachine.cpp @@ -0,0 +1,203 @@ +/* file parserstatemachine.cpp + * + * author: Roland Conybeare + */ + +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "pretty_parserstatemachine.hpp" +#include "pretty_envframestack.hpp" +#include "pretty_localenv.hpp" +#include "xo/expression/pretty_expression.hpp" + +namespace xo { + using xo::scm::LocalSymtab; + using xo::scm::Variable; + + namespace scm { + bp + parserstatemachine::lookup_var(const std::string & x) const { + return Variable::from(env_stack_.lookup(x)); + } + + void + parserstatemachine::upsert_var(bp x) { + env_stack_.upsert(x); + } + + std::unique_ptr + parserstatemachine::pop_exprstate() { + return xs_stack_.pop_exprstate(); + } + + exprstate & + parserstatemachine::top_exprstate() { + return xs_stack_.top_exprstate(); + } + + const exprstate & + parserstatemachine::lookup_exprstate(size_t i) const { + return *xs_stack_[i]; + } + + void + parserstatemachine::push_exprstate(std::unique_ptr x) { + xs_stack_.push_exprstate(std::move(x)); + } + + bp + parserstatemachine::top_envframe() const { + return env_stack_.top_envframe(); + } + + bp + parserstatemachine::lookup_envframe(std::size_t i) const { + return env_stack_[i]; + } + + void + parserstatemachine::push_envframe(const rp & x) { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("frame", x)); + + env_stack_.push_envframe(x); + } + + rp + parserstatemachine::pop_envframe() { + scope log(XO_DEBUG(debug_flag_)); + + return env_stack_.pop_envframe(); + } + + void + parserstatemachine::on_expr(bp x) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("x", x), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_expr(x, this); + } + + void + parserstatemachine::on_expr_with_semicolon(bp x) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("x", x), + xtag("psm", this)); + + assert(!this->xs_stack_.empty()); + + this->xs_stack_.top_exprstate().on_expr_with_semicolon(x, this); + } + + void + parserstatemachine::on_symbol(const std::string & x) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("x", x), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_symbol(x, this); + } + + void + parserstatemachine::on_semicolon_token(const token_type & tk) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("tk", tk), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_semicolon_token(tk, this); + } + + void + parserstatemachine::on_operator_token(const token_type & tk) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("tk", tk), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_operator_token(tk, this); + } + + void + parserstatemachine::on_leftbrace_token(const token_type & tk) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("tk", tk), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_leftbrace_token(tk, this); + } + + void + parserstatemachine::on_rightbrace_token(const token_type & tk) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("tk", tk), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_rightbrace_token(tk, this); + } + + void + parserstatemachine::on_then_token(const token_type & tk) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("tk", tk), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_then_token(tk, this); + } + + void + parserstatemachine::on_else_token(const token_type & tk) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("tk", tk), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_else_token(tk, this); + } + + void + parserstatemachine::on_f64_token(const token_type & tk) + { + scope log(XO_DEBUG(debug_flag_)); + + log && log(xtag("tk", tk), + xtag("psm", this)); + + this->xs_stack_.top_exprstate().on_f64_token(tk, this); + } + + void + parserstatemachine::on_error(const char * self_name, std::string errmsg) + { + this->result_ = parser_result::error(self_name, std::move(errmsg)); + } + + void + parserstatemachine::print(std::ostream & os) const { + os << ""; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end parserstatemachine.cpp */ diff --git a/xo-reader/src/reader/pretty_parserstatemachine.cpp b/xo-reader/src/reader/pretty_parserstatemachine.cpp new file mode 100644 index 00000000..3e03b136 --- /dev/null +++ b/xo-reader/src/reader/pretty_parserstatemachine.cpp @@ -0,0 +1,54 @@ +/* file pretty_parserstatemachine.cpp + * + * author: Roland Conybeare, Jul 2025 + */ + +#include "pretty_parserstatemachine.hpp" +#include "pretty_exprstatestack.hpp" +#include "pretty_envframestack.hpp" +#include "exprstatestack.hpp" + +namespace xo { + namespace print { + bool + ppdetail::print_pretty(const ppindentinfo & ppii, const xo::scm::parserstatemachine & x) + { + ppstate * pps = ppii.pps(); + + if (ppii.upto()) { + if (!pps->print_upto("print_upto_tag("stack", &x.xs_stack_)) + return false; + + if (!pps->print_upto_tag("env_stack", &x.env_stack_)) + return false; + + return pps->print_upto(">"); + } else { + pps->write("newline_pretty_tag(ppii.ci1(), "stack", &x.xs_stack_); + pps->newline_pretty_tag(ppii.ci1(), "env_stack", &x.env_stack_); + pps->write(">"); + + return false; + } + } + + bool + ppdetail::print_pretty(const ppindentinfo & ppii, const xo::scm::parserstatemachine * x) + { + if (x) { + return ppdetail::print_pretty(ppii, *x); + } else { + if (ppii.upto()) { + return ppii.pps()->print_upto(""); + } else { + ppii.pps()->write(""); + return false; + } + } + } + } /*namespace print*/ +} /*namespace xo*/ diff --git a/xo-reader/src/reader/progress_xs.cpp b/xo-reader/src/reader/progress_xs.cpp new file mode 100644 index 00000000..01cfff21 --- /dev/null +++ b/xo-reader/src/reader/progress_xs.cpp @@ -0,0 +1,845 @@ +/* @file progress_xs.cpp */ + +#include "progress_xs.hpp" +#include "apply_xs.hpp" +#include "exprstatestack.hpp" +#include "expect_expr_xs.hpp" +#include "parserstatemachine.hpp" +#include "pretty_exprstatestack.hpp" +#include "xo/expression/AssignExpr.hpp" +#include "xo/expression/Apply.hpp" +#include "xo/expression/pretty_expression.hpp" + +namespace xo { + using xo::scm::Expression; + using xo::scm::AssignExpr; + using xo::scm::Variable; + using xo::scm::Apply; + + namespace scm { + const char * + optype_descr(optype x) { + switch (x) { + case optype::invalid: + return "?optype"; + case optype::op_assign: + return "op:="; + case optype::op_less: + return "op<"; + case optype::op_less_equal: + return "op<="; + case optype::op_equal: + return "op=="; + case optype::op_not_equal: + return "op!="; + case optype::op_great: + return "op>"; + case optype::op_great_equal: + return "op>="; + case optype::op_add: + return "op+"; + case optype::op_subtract: + return "op-"; + case optype::op_multiply: + return "op*"; + case optype::op_divide: + return "op/"; + case optype::n_optype: + break; + } + return "???"; + } + + int + precedence(optype x) { + switch (x) { + case optype::invalid: + case optype::n_optype: + return 0; + + case optype::op_assign: + return 1; + + case optype::op_less: + case optype::op_less_equal: + case optype::op_equal: + case optype::op_not_equal: + case optype::op_great: + case optype::op_great_equal: + return 2; + + case optype::op_add: + case optype::op_subtract: + return 3; + + case optype::op_multiply: + case optype::op_divide: + return 4; + } + + return 0; + } + + std::unique_ptr + progress_xs::make(rp valex, optype op) { + return std::make_unique(progress_xs(std::move(valex), op)); + } + + void + progress_xs::start(rp valex, optype op, parserstatemachine * p_psm) { + p_psm->push_exprstate(progress_xs::make(valex, op)); + } + + void + progress_xs::start(rp valex, parserstatemachine * p_psm) { + p_psm->push_exprstate(progress_xs::make(valex)); + } + + progress_xs::progress_xs(rp valex, optype op) + : exprstate(exprstatetype::expr_progress), + lhs_{std::move(valex)}, + op_type_{op} + {} + + bool + progress_xs::admits_f64() const { return false; } + + const char * + progress_xs::get_expect_str() const { + if (op_type_ == optype::invalid) { + return "oper|semicolon|rightparen"; + } else { + return "expr|leftparen"; + } + } + + void + progress_xs::on_def_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "progress_xs::on_def"; + const char * exp = get_expect_str(); + + /* nothing here - admits_definition unconditionally false */ + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + progress_xs::apply_type_error(const char * self_name, + optype op, + bp expr1, + bp expr2, + parserstatemachine * p_psm) const + { + std::string errmsg = tostr("incompatible argument types T1,T2 to op", + xtag("op", op), + xtag("T1", expr1->valuetype()), + xtag("T2", expr2->valuetype())); + + p_psm->on_error(self_name, std::move(errmsg)); + } + + rp + progress_xs::assemble_expr(parserstatemachine * p_psm) { + /* need to defer building Apply incase expr followed by higher-precedence operator: + * consider input like + * 3.14 + 2.0 * ... + */ + + constexpr const char * c_self_name = "progress_xs::assemble_expr"; + + if ((op_type_ != optype::invalid) && (rhs_.get() == nullptr)) { + std::string errmsg = tostr("expected expression on rhs of operator op", + xtag("lhs", lhs_), + xtag("op", op_type_)); + + p_psm->on_error(c_self_name, errmsg); + } + + /* consecutive expressions not legal, e.g: + * 3.14 6.28 + * but expressions surrounding an infix operators is: + * 3.14 / 6.28 + */ + switch (op_type_) { + case optype::invalid: + return this->lhs_; + + case optype::op_assign: + { + bp lhs = Variable::from(this->lhs_); + + if (!lhs) { + throw std::runtime_error + (tostr("progress_xs::assemble_expr", + " expect variable on lhs of assignment operator :=", + xtag("lhs", lhs_), + xtag("rhs", rhs_))); + } + + return AssignExpr::make(lhs.promote(), + this->rhs_); + } + + case optype::op_equal: + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_cmp_eq_i64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + + case optype::op_not_equal: + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_cmp_ne_i64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + + case optype::op_less: + // TODO: floating-point less-than + + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_cmp_lt_i64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + + case optype::op_less_equal: + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_cmp_le_i64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + + case optype::op_great: + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_cmp_gt_i64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + + case optype::op_great_equal: + // TODO: upconvert integer->double + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_cmp_ge_i64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + + assert(false); + + case optype::op_add: + // TODO: upconvert integer->double + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_add2_i64(lhs_, rhs_); + } else if (lhs_->valuetype()->is_f64() && rhs_->valuetype()->is_f64()) { + return Apply::make_add2_f64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + case optype::op_subtract: + // TODO: upconvert integer->double + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_sub2_i64(lhs_, rhs_); + } else if (lhs_->valuetype()->is_f64() && rhs_->valuetype()->is_f64()) { + return Apply::make_sub2_f64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + + case optype::op_multiply: + // TODO: upconvert integer->double + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_mul2_i64(lhs_, rhs_); + } else if (lhs_->valuetype()->is_f64() && rhs_->valuetype()->is_f64()) { + return Apply::make_mul2_f64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + + break; + + case optype::op_divide: + // TODO: upconvert integer->double + if (lhs_->valuetype()->is_i64() && rhs_->valuetype()->is_i64()) { + return Apply::make_div2_i64(lhs_, rhs_); + } else if (lhs_->valuetype()->is_f64() && rhs_->valuetype()->is_f64()) { + return Apply::make_div2_f64(lhs_, rhs_); + } else { + this->apply_type_error(c_self_name, + op_type_, lhs_, rhs_, p_psm); + return nullptr; + } + break; + + case optype::n_optype: + /* unreachable */ + assert(false); + return nullptr; + } + + return nullptr; + } + + void + progress_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag()), xtag("expr", expr)); + + /* note: previous token probably an operator, + * handled from progress_xs::on_operator_token(), + * which pushes expect_expr_xs::expect_rhs_expression() + */ + + constexpr const char * c_self_name = "progress_xs::on_expr"; + const char * exp = get_expect_str(); + + if (lhs_) { + if (op_type_ == optype::invalid) { + /* two consecutive expression without an operator */ + this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); + } + +#ifdef NOT_QUITE + assert(result.get()); + + /* this expression complete.. */ + std::unique_ptr self = p_psm->pop_exprstate(); + + /* ..but more operators could follow, so don't commit yet */ + p_stack->push_exprstate(progress_xs::make(result)); +#endif + + this->rhs_ = expr.promote(); + } else { + /* control here on input like + * add(1,2)... + * + * add(1,2) needs to be handled inside a progress_xs + * instance because may be followed by an operator: + * add(1,2) + ... + */ + this->lhs_ = expr.promote(); + } + } + + void + progress_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("lhs", lhs_), xtag("op", op_type_), xtag("expr", expr)); + + constexpr const char * c_self_name = "progress_xs::on_expr_with_semicolon"; + const char * exp = get_expect_str(); + + if (op_type_ == optype::invalid) { + this->illegal_input_on_expr(c_self_name, expr, exp, p_psm); + } + + this->rhs_ = expr.promote(); + + // FORBIDDEN, because .on_semicolon_token() destroys *this before returning + // this->on_semicolon_token(token_type::semicolon(), p_psm); + // INSTEAD, spell out the body + + rp expr2 = this->assemble_expr(p_psm); + + if (expr2) { + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr_with_semicolon(expr2); + } + } + + void + progress_xs::on_symbol_token(const token_type & /*tk*/, + parserstatemachine * /*p_psm*/) + { + /* illegal input, e.g. + * foo bar + */ + assert(false); + } + + void + progress_xs::on_comma_token(const token_type & tk, + parserstatemachine * p_psm) + { + /* note: implementation parllels .on_semicolon_token(), .on_rightparen_token() */ + + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * self_name = "progress::xs::on_comma_token"; + + auto & xs_stack = p_psm->xs_stack_; + + /* stack may be something like + * + * applyexpr + * expect_expr_xs + * progress_xs + * <-- comma + * + * 1. comma completes expression-in-progress + */ + + /* comma confirms stack expression */ + rp expr = this->assemble_expr(p_psm); + + std::unique_ptr self = p_psm->pop_exprstate(); + + if (xs_stack.empty()) { + throw std::runtime_error(tostr(self_name, + ": expected non-empty parsing state")); + } + + log && log(xtag("stack", &xs_stack)); + + p_psm->top_exprstate().on_expr(expr, p_psm); + + /* now deliver comma */ + p_psm->top_exprstate().on_comma_token(tk, p_psm); + } + + void + progress_xs::on_typedescr(TypeDescr /*td*/, + parserstatemachine * /*p_psm*/) + { + /* unreachable */ + assert(false); + } + + void + progress_xs::on_colon_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "progress_xs::on_colon"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + progress_xs::on_semicolon_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + /* note: implementation parallels .on_rightparen_token() */ + + scope log(XO_DEBUG(p_psm->debug_flag())); + + rp expr = this->assemble_expr(p_psm); + + log && log(xtag("assembled-expr", expr)); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr_with_semicolon(expr); + + /* control here on input like: + * (1.234; + * + * a. '(' sets up stack [lparen_0:expect_rhs_expression] + * (see exprstate::on_leftparen()) + * b. 1.234 pushes (in case operators) [lparen_0:expect_rhs_expression:expr_progress] + * (see exprstate::on_f64()) + * c. semicolon completes expr_progress [lparen_0:expect_rhs_expression] + * deliver expresssion to expect_rhs_expression.on_expr_with_semicolon() + * (see exprstate::on_expr_with_semicolon()) + * d. expr_rhs_expression forwards expression to [lparen_0] + * e. lparen_0 would advance to [lparen_1], but rejects semicolon + */ + } + + void + progress_xs::on_singleassign_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr const char * c_self_name = "progress_xs::on_singleassign"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + progress_xs::on_assign_token(const token_type & tk, + parserstatemachine * p_psm) + { + this->on_operator_token(tk, p_psm); + } + + /* editor bait: on_lparen */ + void + progress_xs::on_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + /* input like: + * 'foo(' -> expect function call. might continue 'foo(a,b,c)' + * 'foo+(' -> expect parenthesized expression. might continue 'foo+(bar/2)' + */ + + if (op_type_ == optype::invalid) { + /* start function call */ + assert(rhs_.get() == nullptr); + + rp fn_expr = lhs_; + + /* reset this progress_xs back to empty state; + * apply_xs will be responsible for lhs_. + */ + lhs_ = nullptr; + +#ifdef OBSOLETE + /* don't unwind! want to handle input like + * f(x,y)+g(z) + */ + /* unwind this progress_xs + replace with function call */ + std::unique_ptr self = p_psm->pop_exprstate(); +#endif + + apply_xs::start(fn_expr, p_psm); + + /* control will reenter progress_xs via .on_expr() */ + return; + } + + constexpr const char * c_self_name = "exprstate::on_leftparen"; + const char * exp = get_expect_str(); + + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } + + void + progress_xs::on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + /* note: implementation parallels .on_semicolon_token() */ + + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * self_name = "progress_xs::on_rightparen"; + + auto & xs_stack = p_psm->xs_stack_; + + /* stack may be something like: + * + * lparen_0 + * expect_expr_xs + * expr_progress + * <-- rightparen + * + * 1. rightparen completes expression-in-progress + * 2. rightparen must then match innermost waiting lparen_0 + */ + + /* right paren confirms stack expression */ + rp expr = this->assemble_expr(p_psm); + + log && log(xtag("expr", expr), + xtag("do", "pop self + send {expr, rparen} -> parent")); + + std::unique_ptr self = p_psm->pop_exprstate(); + + if (xs_stack.empty()) { + throw std::runtime_error(tostr(self_name, + ": expected non-empty parsing stack")); + } + + log && log(xtag("stack", &xs_stack)); + + p_psm->top_exprstate().on_expr(expr, p_psm); + + /* now deliver rightparen */ + p_psm->top_exprstate().on_rightparen_token(tk, p_psm); + } + + void + progress_xs::on_then_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + rp expr = this->assemble_expr(p_psm); + + log && log(xtag("assembled-expr", expr)); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr(expr); + p_psm->on_then_token(tk); + + /* control here on input like: + * + * if a > b then.. + * + */ + } + + void + progress_xs::on_else_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + rp expr = this->assemble_expr(p_psm); + + log && log(xtag("assembled-expr", expr)); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr(expr); + p_psm->on_else_token(tk); + + /* control here on input like: + * + * if a > b then c else.. + */ + } + + void + progress_xs::on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + rp expr = this->assemble_expr(p_psm); + + log && log(xtag("assembled-expr", expr)); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr(expr); + p_psm->on_rightbrace_token(tk); + + /* control here on input like: + * + * { n * n } + */ + } + + namespace { + optype + tk2op(const tokentype & tktype) { + switch (tktype) { + case tokentype::tk_assign: + return optype::op_assign; + case tokentype::tk_plus: + return optype::op_add; + case tokentype::tk_minus: + return optype::op_subtract; + case tokentype::tk_star: + return optype::op_multiply; + case tokentype::tk_slash: + return optype::op_divide; + case tokentype::tk_cmpeq: + return optype::op_equal; + case tokentype::tk_cmpne: + return optype::op_not_equal; + case tokentype::tk_leftangle: + return optype::op_less; + case tokentype::tk_lessequal: + return optype::op_less_equal; + case tokentype::tk_rightangle: + return optype::op_great; + case tokentype::tk_greatequal: + return optype::op_great_equal; + default: + assert(false); + return optype::invalid; + } + return optype::invalid; + } + } + + void + progress_xs::on_operator_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "progress_xs::on_operator_token"; + + if (op_type_ == optype::invalid) { + this->op_type_ = tk2op(tk.tk_type()); + + /* infix operator must be followed by non-empty expression */ + expect_expr_xs::start(p_psm); + } else if (rhs_) { + /* already have complete expression stashed. + * behavior depends on operator precedence for tk with stored operator + * this->op_type_ + */ + optype op2 = tk2op(tk.tk_type()); + + if (precedence(op2) <= precedence(this->op_type_)) { + /* e.g. + * 6.2 * 4.9 + ... + * + * in stack: + * 1. progress_xs lhs=6.2, op=*, rhs=4.9 + * + * out stack + * 1. progress_xs lhs=apply(*,6.2,4.9), op=+ + */ + + /* 1. instantiate expression for *this */ + auto expr = this->assemble_expr(p_psm); + + /* 2. remove from stack */ + std::unique_ptr self = p_psm->pop_exprstate(); + + /* 3. replace with new progress_xs: */ + progress_xs::start(expr, op2, p_psm); + + /* infix operator must be followed by non-empty expression */ + expect_expr_xs::start(p_psm); + } else { + /* e.g. + * 6.2 + 4.9 * ... + * + * in stack: + * 1. progress_xs lhs=6.2, op=+, rhs=4.9 + * + * out stack: + * 1. progress_xs lhs=6.2, op=+ + * 2. expect_rhs_expression + * 3. progress_xs lhs=4.9, op=* + * 4. expect_rhs_expression + */ + + std::unique_ptr self = p_psm->pop_exprstate(); + + /* 1. replace with nested incomplete infix exprs */ + progress_xs::start(lhs_, op_type_, p_psm); + expect_expr_xs::start(p_psm); + progress_xs::start(rhs_, op2, p_psm); + expect_expr_xs::start(p_psm); + } + + } else { + throw std::runtime_error(tostr(c_self_name, + ": expected expression following operator", + xtag("tk", tk))); + } + } + + void + progress_xs::on_bool_token(const token_type & tk, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + constexpr const char * c_self_name = "progress_xs::on_bool_token"; + const char * exp = get_expect_str(); + + if (this->op_type_ == optype::invalid) { + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } else { + exprstate::on_bool_token(tk, p_psm); + } + } + + void + progress_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 = "progress_xs::on_i64_token"; + const char * exp = get_expect_str(); + + if (this->op_type_ == optype::invalid) { + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } else { + exprstate::on_i64_token(tk, p_psm); + } + } + + void + progress_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 = "progress_xs::on_f64_token"; + const char * exp = get_expect_str(); + + if (this->op_type_ == optype::invalid) { + this->illegal_input_on_token(c_self_name, tk, exp, p_psm); + } else { + exprstate::on_f64_token(tk, p_psm); + } + } + + void + progress_xs::print(std::ostream & os) const { + os << ""; + } + + bool + progress_xs::pretty_print(const xo::print::ppindentinfo & ppii) const + { + if (ppii.upto()) { + return (ppii.pps()->print_upto("print_upto(refrtag("lhs", lhs_)) : true) + && (op_type_ != optype::invalid ? ppii.pps()->print_upto(refrtag("op", op_type_)) : true) + && (rhs_ ? ppii.pps()->print_upto(refrtag("rhs", rhs_)) : true) + && ppii.pps()->print_upto(">")); + } else { + ppii.pps()->write("pretty(refrtag("lhs", lhs_)); + if (op_type_ != optype::invalid) + ppii.pps()->pretty(refrtag("op", op_type_)); + if (rhs_) + ppii.pps()->pretty(refrtag("rhs", rhs_)); + ppii.pps()->write(">"); + return false; + } + } + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end progress_xs.cpp */ diff --git a/xo-reader/src/reader/reader.cpp b/xo-reader/src/reader/reader.cpp new file mode 100644 index 00000000..7bac4435 --- /dev/null +++ b/xo-reader/src/reader/reader.cpp @@ -0,0 +1,166 @@ +/* @file reader.cpp */ + +#include "reader.hpp" + +namespace xo { + namespace scm { + reader::reader(const rp & toplevel_symtab, bool debug_flag) : + tokenizer_{debug_flag}, + parser_{toplevel_symtab, debug_flag} + {} + + void + reader::begin_interactive_session() { + parser_.begin_interactive_session(); + } + + reader_result + reader::end_interactive_session() { + return this->read_expr(span_type(nullptr, nullptr), true /*eof*/); + } + + void + reader::begin_translation_unit() { + parser_.begin_translation_unit(); + } + + reader_result + reader::end_translation_unit() { + return this->read_expr(span_type(nullptr, nullptr), true /*eof*/); + } + + reader_result + reader::read_expr(const span_type & input_arg, bool eof_flag) + { + scope log(XO_DEBUG(this->debug_flag())); + + span_type input = input_arg; + + /* input text-span consumed by this call. + * Always comprises some number (possibly 0) + * of complete tokens, along with any leading + * whitespace. + * + * expr_span may also begin and end part way through + * distinct input lines + */ + span_type expr_span = input.prefix(0ul); + + while (!input.empty()) { + /* each loop iteration reads one token */ + + /* read one token from input. + * tokenizer stashes one line at a time, but used_span only + * reports in used_span the portion representing the first token. + */ + auto [tk, used_span, error1] + = this->tokenizer_.scan(input, eof_flag); + + log && log(xtag("consumed", used_span)); + log && log(xtag("input.pre", input)); + + expr_span += used_span; + + if (tk.is_valid()) { + log && log("input_state.current_line", tokenizer_.input_state().current_line()); + + /* forward just-read token to parser */ + auto parser_result = this->parser_.include_token(tk); + + log && log("after parser.include_token"); + log && log("input_state.current_line", tokenizer_.input_state().current_line()); + + if (parser_result.is_expression()) { + log && log(xtag("outcome", "victory!"), + xtag("expr", parser_result.result_expr())); + + rp result_expr = parser_result.result_expr(); + + this->parser_.reset_result(); + + /* token completes an expression -> victory */ + return reader_result(parser_result.result_expr(), + expr_span, parser_.stack_size(), reader_error()); + } else if (parser_result.is_error()) { + /* 1. parser detected error. + * 2. tokenizer_.input_state().current_pos refers to position just after offending token + * 3. error_pos here is 0 because error detected at token boundary + */ + reader_error error2(parser_result.error_src_function(), + parser_result.error_description(), + tokenizer_.input_state().rewind(tk.text().size()), + 0 /*error_pos*/); + + std::cout << "parser error pre-report:" << std::endl; + error2.report(std::cout); + + return reader_result(nullptr, expr_span, parser_.stack_size(), error2); + } else { + /* token did not complete an expression + * (e.g. token for '[') + * + * input span may contain more tokens -> iterate + */ + } + } else /*invalid token*/ { + if (error1.is_error()) { + /* tokenizer detected an error */ + + std::cout << "tokenizer error pre-report:" << std::endl; + error1.report(std::cout); + + return reader_result(nullptr, expr_span, parser_.stack_size(), + reader_error(error1.src_function(), + error1.error_description(), + error1.input_state(), + error1.error_pos())); + } else if (used_span.size() > 0) { + /* control here on reaching the last token in input line; + * used_span will be the entire line + */ + } else { + /* control should not come here: invalid token with no error + */ + assert(input.empty()); + break; + } + } + + input = input.after_prefix(used_span); + } + + /* control here: either + * 1. input.empty (perhaps ate some whitespace, ok) + * 2. missing or incomplete token (ok unless eof) + */ + if (eof_flag) { + if (parser_.has_incomplete_expr()) { + throw std::runtime_error + ("reader::read_expr" + ": eof reached with incomplete expression"); + } + + if (tokenizer_.has_prefix()) { + throw std::runtime_error + ("reader::read_expr" + ": unintelligible input recognized at eof"); + } + } + + log && log(xtag("outcome", "noop"), + xtag("parser.stack_size", parser_.stack_size())); + + return reader_result(nullptr, expr_span, parser_.stack_size(), reader_error()); + } + + void + reader::reset_to_idle_toplevel() + { + this->tokenizer_.discard_current_line(); + this->parser_.reset_to_idle_toplevel(); + } + + } /*namespace scm*/ +} /*namespace xo*/ + +/* end reader.cpp */ diff --git a/xo-reader/src/reader/sequence_xs.cpp b/xo-reader/src/reader/sequence_xs.cpp new file mode 100644 index 00000000..a7695673 --- /dev/null +++ b/xo-reader/src/reader/sequence_xs.cpp @@ -0,0 +1,133 @@ +/* @file sequence_xs.cpp */ + +#include "sequence_xs.hpp" +#include "parserstatemachine.hpp" +#include "expect_expr_xs.hpp" +#include "let1_xs.hpp" +#include "xo/expression/DefineExpr.hpp" +#include "xo/expression/Sequence.hpp" +#include "xo/expression/pretty_expression.hpp" + +namespace xo { + using xo::scm::DefineExpr; + + namespace scm { + std::unique_ptr + sequence_xs::make() { + return std::make_unique(sequence_xs()); + } + + void + sequence_xs::start(parserstatemachine * p_psm) { + p_psm->push_exprstate(sequence_xs::make()); + /* want to accept anything that starts an expression, + * except that } ends it + */ + expect_expr_xs::start(true /*allow_defs*/, + true /*cxl_on_rightbrace*/, + p_psm); + } + + sequence_xs::sequence_xs() + : exprstate(exprstatetype::sequenceexpr) + {} + + void + sequence_xs::on_expr(bp expr, + parserstatemachine * p_psm) + { + scope log(XO_DEBUG(p_psm->debug_flag())); + + log && log(xtag("expr", expr.promote())); + + /* TODO: if expr is a DefineExpr, + * then need to rewrite... + * + * ...prefix + * DefineExpr(lhs_name, rhs) + * rest... + * + * becomes: + * + * /-- .outer_seq_expr_ + * v + * Sequence( + * ...prefix, + * + * /-- .inner_lm_expr_ + * v + * Apply(Lambda(gen999, + * [Variable(lhs_name, type(rhs))], + * /-- .expr_v_ + * v + * sequencify(rest...)), + * rhs)) + * + * so amongst other things, + * helpful to have nested seequence_xs that propagates '}' + * instead of swallowing it. + */ + bp def_expr = DefineExpr::from(expr); + + if (def_expr) { + /** nested_start: control returns via + * .on_expr(x) + * with x something like: + * Apply(Lambda(gensym(), + * [Variable(def_expr->lhs_name(), + * def_expr->valuetype())], + * body...)) + * followed immediately by + * .on_rightbrace_token() + **/ + let1_xs::start(def_expr->lhs_name(), + def_expr->rhs(), + p_psm); + } else { + this->expr_v_.push_back(expr.promote()); + + expect_expr_xs::start(true /*allow_defs*/, + true /*cxl_on_rightbrace*/, + p_psm); + } + } + + void + sequence_xs::on_expr_with_semicolon(bp expr, + parserstatemachine * p_psm) + { + /* sequence continues until right brace */ + this->on_expr(expr, p_psm); + } + + void + sequence_xs::on_rightbrace_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + auto self = p_psm->pop_exprstate(); + + /* make sequence from expressions seen at this level, + * and report it to parent + */ + auto expr = Sequence::make(this->expr_v_); + + p_psm->top_exprstate().on_expr(expr, p_psm); + } + + void + sequence_xs::print(std::ostream & os) const { + os << ""; + } + + bool + sequence_xs::pretty_print(const xo::print::ppindentinfo & ppii) const + { + return ppii.pps()->pretty_struct(ppii, "sequence_xs", + xrefrtag("expr_v.size", expr_v_.size())); + } + + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end sequence_xs.cpp */ diff --git a/xo-reader/utest/CMakeLists.txt b/xo-reader/utest/CMakeLists.txt new file mode 100644 index 00000000..1f876ca1 --- /dev/null +++ b/xo-reader/utest/CMakeLists.txt @@ -0,0 +1,17 @@ +# xo-reader/utest/CMakeLists.txt + +set(UTEST_EXE utest.reader) +set(UTEST_SRCS + reader_utest_main.cpp + parser.test.cpp + reader.test.cpp) + +if (ENABLE_TESTING) + xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS}) + xo_self_dependency(${UTEST_EXE} xo_reader) + #xo_dependency(${UTEST_EXE} xo_ratio) + #xo_dependency(${UTEST_EXE} xo_reflectutil) + xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2) +endif() + +# end CMakeLists.txt diff --git a/xo-reader/utest/parser.test.cpp b/xo-reader/utest/parser.test.cpp new file mode 100644 index 00000000..3d6f3fac --- /dev/null +++ b/xo-reader/utest/parser.test.cpp @@ -0,0 +1,305 @@ +/* file parser.test.cpp + * + * author: Roland Conybeare + */ + +#include "xo/reader/parser.hpp" +#include "xo/reader/define_xs.hpp" +#include + +namespace xo { + using parser_type = xo::scm::parser; + using token_type = parser_type::token_type; + using xo::scm::GlobalSymtab; + using xo::scm::exprstatetype; + using xo::scm::define_xs; + using xo::scm::defexprstatetype; + using std::cerr; + using std::endl; + + //using xo::scm::Expression; + + namespace ut { + TEST_CASE("parser", "[parser]") { + for (std::size_t i_tc = 0; i_tc < 2; ++i_tc) { + constexpr bool c_debug_flag = true; + + rp toplevel_symtab = GlobalSymtab::make_empty(); + + parser_type parser(toplevel_symtab, c_debug_flag); + + scope log(XO_DEBUG(c_debug_flag), xtag("i_tc", i_tc)); + + parser.begin_translation_unit(); + + REQUIRE(parser.stack_size() == 1); + REQUIRE(parser.i_exstype(0) + == exprstatetype::expect_toplevel_expression_sequence); + + /* input: + * def + */ + { + auto r1 = parser.include_token(token_type::def()); + + REQUIRE(r1.is_none()); + REQUIRE(r1.result_expr().get() == nullptr); + REQUIRE(r1.error_src_function() == nullptr); + REQUIRE(r1.error_description().empty()); + + /* stack should be: + * + * expect_toplevel_expression_sequence + * def_1 + * expect_symbol + */ + CHECK(parser.stack_size() == 3); + if (parser.stack_size() > 0) + CHECK(parser.i_exstype(0) == exprstatetype::expect_symbol); + if (parser.stack_size() > 1) { + CHECK(parser.i_exstype(1) == exprstatetype::defexpr); + REQUIRE(define_xs::from(parser.i_exstate(1)) != nullptr); + CHECK(define_xs::from(parser.i_exstate(1))->defxs_type() == defexprstatetype::def_1); + } + if (parser.stack_size() > 2) + CHECK(parser.i_exstype(2) + == exprstatetype::expect_toplevel_expression_sequence); + } + + /* input: + * def foo + * ^ ^ + * 0 1 + */ + { + auto r2 = parser.include_token(token_type::symbol_token("foo")); + + cerr << "parser state after [def foo]" << endl; + cerr << parser << endl; + + REQUIRE(r2.is_none()); + REQUIRE(r2.result_expr().get() == nullptr); + REQUIRE(r2.error_src_function() == nullptr); + REQUIRE(r2.error_description().empty()); + + /* stack should be: + * + * expect_toplevel_expression_sequence + * def_1 + */ + CHECK(parser.stack_size() == 2); + if (parser.stack_size() > 0) { + CHECK(parser.i_exstype(0) == exprstatetype::defexpr); + REQUIRE(define_xs::from(parser.i_exstate(0)) != nullptr); + CHECK(define_xs::from(parser.i_exstate(0))->defxs_type() == defexprstatetype::def_2); + } + if (parser.stack_size() > 1) + CHECK(parser.i_exstype(1) + == exprstatetype::expect_toplevel_expression_sequence); + + } + + if (i_tc == 0) { + ; + } else if (i_tc == 1) { + /* input: + * def foo : + * ^ ^ + * 0 1 + */ + { + auto r3 = parser.include_token(token_type::colon()); + + cerr << "parser state after [def foo :]" << endl; + cerr << parser << endl; + + REQUIRE(r3.is_none()); + REQUIRE(r3.result_expr().get() == nullptr); + REQUIRE(r3.error_src_function() == nullptr); + REQUIRE(r3.error_description().empty()); + + /* stack should be: + * + * expect_toplevel_expression_sequence + * def_3 + * expect_symbol + */ + CHECK(parser.stack_size() == 3); + if (parser.stack_size() > 0) + CHECK(parser.i_exstype(0) == exprstatetype::expect_type); + if (parser.stack_size() > 1) { + CHECK(parser.i_exstype(1) == exprstatetype::defexpr); + REQUIRE(define_xs::from(parser.i_exstate(1)) != nullptr); + CHECK(define_xs::from(parser.i_exstate(1))->defxs_type() == defexprstatetype::def_3); + } + if (parser.stack_size() > 2) + CHECK(parser.i_exstype(2) + == exprstatetype::expect_toplevel_expression_sequence); + } + + /* input: + * def foo : f64 + * ^ ^ + * 0 1 + */ + { + auto r4 = parser.include_token(token_type::symbol_token("f64")); + + cerr << "parser state after [def foo : f64]" << endl; + cerr << parser << endl; + + REQUIRE(r4.is_none()); + REQUIRE(r4.result_expr().get() == nullptr); + REQUIRE(r4.error_src_function() == nullptr); + REQUIRE(r4.error_description().empty()); + + CHECK(parser.stack_size() == 2); + + /* stack should be: + * + * expect_toplevel_expression_sequence + * def_4 + */ + CHECK(parser.stack_size() == 2); + if (parser.stack_size() > 0) { + CHECK(parser.i_exstype(0) == exprstatetype::defexpr); + REQUIRE(define_xs::from(parser.i_exstate(0)) != nullptr); + CHECK(define_xs::from(parser.i_exstate(0))->defxs_type() == defexprstatetype::def_4); + } + if (parser.stack_size() > 1) + CHECK(parser.i_exstype(1) + == exprstatetype::expect_toplevel_expression_sequence); + + /* expecting either: + * = rhs-expression + * new-expression + */ + } + } + + /* input: + * + * i_tc==0: + * def foo = + * ^ ^ + * 0 1 + * + * i_tc==1: + * def foo : f64 = + * ^ ^ + * 0 1 + */ + { + auto r5 = parser.include_token(token_type::singleassign()); + + cerr << "parser state after [def foo : f64 =]" << endl; + cerr << parser << endl; + + REQUIRE(r5.is_none()); + REQUIRE(r5.result_expr().get() == nullptr); + REQUIRE(r5.error_src_function() == nullptr); + REQUIRE(r5.error_description().empty()); + + CHECK(parser.stack_size() == 3); + + /* stack should be + * + * expect_toplevel_expression_sequence + * def_5 + * expect_expression + */ + CHECK(parser.stack_size() == 3); + if (parser.stack_size() > 0) + CHECK(parser.i_exstype(0) == exprstatetype::expect_rhs_expression); + if (parser.stack_size() > 1) { + CHECK(parser.i_exstype(1) == exprstatetype::defexpr); + REQUIRE(define_xs::from(parser.i_exstate(1)) != nullptr); + CHECK(define_xs::from(parser.i_exstate(1))->defxs_type() == defexprstatetype::def_5); + } + if (parser.stack_size() > 2) + CHECK(parser.i_exstype(2) + == exprstatetype::expect_toplevel_expression_sequence); + } + + /* input: + * + * i_tc==0: + * def foo = 3.14159265 + * ^ ^ + * 0 1 + * + * i_tc==1: + * def foo : f64 = 3.14159265 + * ^ ^ + * 0 1 + */ + { + auto r6 = parser.include_token(token_type::f64_token("3.14159265")); + + cerr << "parser state after [def foo : f64 = 3.14159265]" << endl; + cerr << parser << endl; + + REQUIRE(r6.is_none()); + REQUIRE(r6.result_expr().get() == nullptr); + REQUIRE(r6.error_src_function() == nullptr); + REQUIRE(r6.error_description().empty()); + + /* stack should be + * + * expect_toplevel_expression_sequence + */ + CHECK(parser.stack_size() == 4); + if (parser.stack_size() > 0) + CHECK(parser.i_exstype(0) == exprstatetype::expr_progress); + if (parser.stack_size() > 1) + CHECK(parser.i_exstype(1) == exprstatetype::expect_rhs_expression); + if (parser.stack_size() > 2) { + CHECK(parser.i_exstype(2) == exprstatetype::defexpr); + REQUIRE(define_xs::from(parser.i_exstate(2)) != nullptr); + CHECK(define_xs::from(parser.i_exstate(2))->defxs_type() == defexprstatetype::def_5); + } + if (parser.stack_size() > 3) + CHECK(parser.i_exstype(3) + == exprstatetype::expect_toplevel_expression_sequence); + } + + /* input: + * + * i_tc==0: + * def foo = 3.14159265 ; + * ^ ^ + * 0 1 + * + * i_tc==1: + * def foo : f64 = 3.14159265 ; + * ^ ^ + * 0 1 + */ + { + auto r7 = parser.include_token(token_type::semicolon()); + + cerr << "parser state after [def foo : f64 = 3.14159265;]" << endl; + cerr << parser << endl; + + REQUIRE(r7.is_expression()); + REQUIRE(r7.result_expr().get() != nullptr); + REQUIRE(r7.error_src_function() == nullptr); + REQUIRE(r7.error_description().empty()); + + CHECK(parser.stack_size() == 1); + + /* stack should be + * + * expect_toplevel_expression_sequence + */ + CHECK(parser.stack_size() == 1); + if (parser.stack_size() > 0) + CHECK(parser.i_exstype(0) + == exprstatetype::expect_toplevel_expression_sequence); + } + } + } /*TEST_CASE(parser)*/ + } /*namespace ut*/ +} /*namespace xo*/ + +/* end parser.test.cpp */ diff --git a/xo-reader/utest/reader.test.cpp b/xo-reader/utest/reader.test.cpp new file mode 100644 index 00000000..0bccf3b3 --- /dev/null +++ b/xo-reader/utest/reader.test.cpp @@ -0,0 +1,73 @@ +/* @file reader.test.cpp */ + +#include "xo/reader/reader.hpp" +#include + +namespace xo { + using xo::scm::reader; + using xo::scm::GlobalSymtab; + + namespace ut { + namespace { + struct test_case { + const char * text_; + }; + + std::vector s_testcase_v = { + {"def foo : f64 = 3.14159265;"}, + {"def foo : f64 = (3.14159265);"}, + {"def foo = 2.0 * 3.141569265;"}, + {"def foo = lambda (x : f64) 3.1415965;"}, + {"def foo = lambda (x : f64, y : f64) 3.1415965;"}, + {"def foo = lambda (x : f64) x;"}, + {"def foo = lambda (x : f64) { def y = x * x; y; };"}, + {"add(1,2);"}, + }; + } + + TEST_CASE("reader", "[reader]") { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag), xtag("utest", "reader")); + + for (std::size_t i_tc = 0; i_tc < s_testcase_v.size(); ++i_tc) { + const test_case & tc = s_testcase_v[i_tc]; + + rp toplevel_symtab = GlobalSymtab::make_empty(); + + reader rdr(toplevel_symtab, c_debug_flag); + + scope log(XO_ENTER2(always, c_debug_flag, "reader.testcase"), + xtag("i_tc", i_tc)); + + rdr.begin_translation_unit(); + + try { + auto input + = reader::span_type::from_cstr(tc.text_); + auto rr + = rdr.read_expr(input, true /*eof*/); + + REQUIRE(rr.expr_.get()); + + log && log(xtag("expr", rr.expr_)); + +#ifdef OBSOLETE // input not consumed until next line presented + input = input.after_prefix(rr.rem_); + + log && log(xtag("post.input", input)); +#endif + + //REQUIRE(input.empty()); + } catch (std::exception & ex) { + log && log(ex.what()); + + INFO(ex.what()); + + REQUIRE(false); + } + } + } + } /*namespace ut*/ +} /*namespace xo*/ + +/* end reader.test.cpp */ diff --git a/xo-reader/utest/reader_utest_main.cpp b/xo-reader/utest/reader_utest_main.cpp new file mode 100644 index 00000000..d1013151 --- /dev/null +++ b/xo-reader/utest/reader_utest_main.cpp @@ -0,0 +1,6 @@ +/* file parser_utest_main.cpp */ + +#define CATCH_CONFIG_MAIN +#include + +/* end parser_utest_main.cpp */