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/CMakeLists.txt b/xo-reader/CMakeLists.txt new file mode 100644 index 00000000..7748dc4f --- /dev/null +++ b/xo-reader/CMakeLists.txt @@ -0,0 +1,27 @@ +# 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 "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- + +add_subdirectory(src/reader) +add_subdirectory(utest) + +# ---------------------------------------------------------------- +# provide find_package() support + +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) 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..6eadeb07 --- /dev/null +++ b/xo-reader/cmake/xo_readerConfig.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(xo_expression) +find_dependency(xo_tokenizer) +#find_dependency(subsys) +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") 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..880dd309 --- /dev/null +++ b/xo-reader/include/xo/reader/define_xs.hpp @@ -0,0 +1,121 @@ +/** @file define_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "xo/expression/DefineExpr.hpp" +#include "xo/expression/ConvertExpr.hpp" +#include "exprstate.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::ast::DefineExprAccess; + using ConvertExprAccess = xo::ast::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_; } + + virtual void on_expr(ref::brw expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(ref::brw 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_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: + 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*/ +} /*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..a0fcfb7d --- /dev/null +++ b/xo-reader/include/xo/reader/envframe.hpp @@ -0,0 +1,48 @@ +/* file envframe.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "xo/expression/Variable.hpp" +#include + +namespace xo { + namespace scm { + /** @class envframe + * @brief names/types of formal paremeters introduced by a function + * + * + **/ + class envframe { + public: + using Variable = xo::ast::Variable; + + public: + envframe() = default; + envframe(const std::vector> & argl) : argl_(argl) {} + + const std::vector> & argl() const { return argl_; } + + /** lookup variable by name. If found, return it. + * Otherwise return nullptr + **/ + rp lookup(const std::string & name) const; + + void print (std::ostream & os) const; + + private: + std::vector> argl_; + }; + + inline std::ostream & + operator<< (std::ostream & os, const envframe & x) { + x.print(os); + return os; + } + } /*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..e438f47f --- /dev/null +++ b/xo-reader/include/xo/reader/envframestack.hpp @@ -0,0 +1,78 @@ +/* file envframestack.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "envframe.hpp" + +namespace xo { + namespace scm { + /** @class envframestack + * @brief A stack of envframe objects + **/ + class envframestack { + public: + using Variable = xo::ast::Variable; + + 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. + **/ + rp lookup(const std::string & x) const; + + envframe & top_envframe(); + void push_envframe(envframe x); + void pop_envframe(); + + /** relative to top-of-stack. + * 0 -> top (last in), z-1 -> bottom (first in) + **/ + envframe & operator[](std::size_t i) { + std::size_t z = stack_.size(); + + assert(i < z); + + return stack_[z - i - 1]; + } + + const envframe & 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; + + 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..646a803a --- /dev/null +++ b/xo-reader/include/xo/reader/expect_expr_xs.hpp @@ -0,0 +1,77 @@ +/* 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 + **/ + 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 void on_lambda_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_f64_token(const token_type & tk, + parserstatemachine * p_psm) override; + + /** update exprstate in response to a successfully-parsed subexpression **/ + virtual void on_expr(ref::brw 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(ref::brw expr, + parserstatemachine * p_psm) 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..f3a6bf22 --- /dev/null +++ b/xo-reader/include/xo/reader/expect_formal_arglist_xs.hpp @@ -0,0 +1,83 @@ +/* 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 | argla + * 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::ast::Variable; + + public: + expect_formal_arglist_xs(); + + static void start(parserstatemachine * p_psm); + + 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..1a388f54 --- /dev/null +++ b/xo-reader/include/xo/reader/expect_type_xs.hpp @@ -0,0 +1,31 @@ +/* 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 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..05d0a6e3 --- /dev/null +++ b/xo-reader/include/xo/reader/exprseq_xs.hpp @@ -0,0 +1,45 @@ +/** @file exprseq_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +//#include + +namespace xo { + namespace scm { + /** @class exprseq_xs + * @brief parsing state-machine for top-level expression sequence + * + **/ + class exprseq_xs : public exprstate { + public: + exprseq_xs(); + + static void start(parserstatemachine * p_psm); + + public: + // ----- token input methods ----- + + 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; + + // ----- victory methods ----- + + virtual void on_typedescr(TypeDescr td, + parserstatemachine * p_psm) override; + virtual void on_expr(ref::brw expr, + parserstatemachine * p_psm) override; + + private: + static std::unique_ptr make(); + }; + } /*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..a6acad1f --- /dev/null +++ b/xo-reader/include/xo/reader/exprstate.hpp @@ -0,0 +1,208 @@ +/** @file exprstate.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "xo/expression/Expression.hpp" +#include "xo/tokenizer/token.hpp" +#include +//#include + +namespace xo { + namespace scm { + 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 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, + + 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; + + class formal_arg; + + /** state associated with a partially-parsed expression. + **/ + class exprstate { + public: + using Expression = xo::ast::Expression; + using Variable = xo::ast::Variable; + using exprtype = xo::ast::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); + + /** update exprstate in response to a successfully-parsed subexpression **/ + virtual void on_expr(ref::brw expr, + parserstatemachine * p_psm); + + /** update exprstate in response to a successfully-parsed subexpression, that ends with semicolon **/ + virtual void on_expr_with_semicolon(ref::brw 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; + + // ----- 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_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 floating-point-literal token **/ + virtual void on_f64_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; + + 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*/ +} /*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..b4e57449 --- /dev/null +++ b/xo-reader/include/xo/reader/exprstatestack.hpp @@ -0,0 +1,70 @@ +/* file exprstatestack.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" +#include "xo/indentlog/print/vector.hpp" + +namespace xo { + namespace scm { + /** @class exprstatestack + * @brief A stack of exprstate objects + **/ + class exprstatestack { + 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(); + + /** 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; + + 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..4f0afaef --- /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_; + }; + + 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/lambda_xs.hpp b/xo-reader/include/xo/reader/lambda_xs.hpp new file mode 100644 index 00000000..728bc9e3 --- /dev/null +++ b/xo-reader/include/xo/reader/lambda_xs.hpp @@ -0,0 +1,84 @@ +/** @file lambda_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +//#include + +namespace xo { + namespace scm { + /** + * lambda ( name(1) : type(1), ..., ) body-expr ; + * ^ ^ ^ ^ + * | | | | + * lm_0 lm_1 lm_2 lm_3 + * + * lm_0 --on_lambda_token()--> lm_1 + * lm_1 --on_formal_arglist()--> lm_2 + * lm_2 --on_expr()--> lm_3 + * lm_3 --on_semicolon_token()--> (done) + **/ + enum class lambdastatetype { + invalid = -1, + + lm_0, + lm_1, + lm_2, + lm_3, + + 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: + lambda_xs(); + + static void start(parserstatemachine * p_psm); + + virtual void on_lambda_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_formal_arglist(const std::vector> & argl, + parserstatemachine * p_psm) override; + virtual void on_expr(ref::brw expr, + parserstatemachine * p_psm) override; + virtual void on_expr_with_semicolon(ref::brw expr, + parserstatemachine * p_psm) override; + virtual void on_semicolon_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 **/ + lambdastatetype lmxs_type_ = lambdastatetype::lm_0; + + /** formal parameter list **/ + std::vector> argl_; + + /** body expression **/ + rp body_; + }; + } /*namespace scm*/ +} /*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..4486869f --- /dev/null +++ b/xo-reader/include/xo/reader/let1_xs.hpp @@ -0,0 +1,57 @@ +/* file let1_xs.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" + +namespace xo { + namespace scm { + class let1_xs : public exprstate { + 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(ref::brw 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 rhs); + + /** named ctor idiom **/ + static std::unique_ptr make(std::string lhs_name, + rp rhs); + + private: + /** name for new local variable **/ + std::string lhs_name_; + /** 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..514074d1 --- /dev/null +++ b/xo-reader/include/xo/reader/paren_xs.hpp @@ -0,0 +1,90 @@ +/** @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(ref::brw 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_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..30dd94ea --- /dev/null +++ b/xo-reader/include/xo/reader/parser.hpp @@ -0,0 +1,234 @@ +/* file parser.hpp + * + * author: Roland Conybeare, Jul 2024 + */ + +#pragma once + +#include "exprstatestack.hpp" +#include "envframestack.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}; + * + * def polar2rect(pt : polar) -> point { + * point(x = pt.mag * cos(arg), + * y = pt.mag * sin(arg)); + * } + * + * Grammar: + * toplevel-program = $expression(1); ..; $expression(n) + * + * type-decl = decltype $typename [<$tp1 .. $tpn>] + * expression = 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 = integer-literal + * | fp-literal + * | string-literal + * | symbol-literal + * | struct-literal + * + * 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::ast::Expression; + using token_type = exprstate::token_type; // token; + + public: + /** create parser in initial state; + * parser is ready to receive tokens via @ref include_token + **/ + parser() = default; + + /** for diagnostics: number of entries in parser stack **/ + std::size_t stack_size() const { return 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 = xs_stack_.size(); + + if (i < z) { + return xs_stack_[i]->exs_type(); + } + + /* out of bounds */ + return exprstatetype::invalid; + } + + exprstate const * i_exstate(std::size_t i) const { + std::size_t z = xs_stack_.size(); + + if (i < z) { + return 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 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 + **/ + rp include_token(const token_type & tk); + + /** print human-readable representation on stream @p os **/ + void print(std::ostream & os) const; + + private: + /** 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_; + + }; /*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/parserstatemachine.hpp b/xo-reader/include/xo/reader/parserstatemachine.hpp new file mode 100644 index 00000000..0e69d44c --- /dev/null +++ b/xo-reader/include/xo/reader/parserstatemachine.hpp @@ -0,0 +1,85 @@ +/* file parserstatemachine.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "exprstate.hpp" +#include "envframestack.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::ast::Expression; + using Variable = xo::ast::Variable; + using token_type = token; + + public: + parserstatemachine(exprstatestack * p_stack, + envframestack * p_env_stack, + rp * p_emit_expr) + : p_stack_{p_stack}, + p_env_stack_{p_env_stack}, + p_emit_expr_{p_emit_expr} {} + + std::unique_ptr pop_exprstate(); + exprstate & top_exprstate(); + void push_exprstate(std::unique_ptr x); + + /** lookup variable name in lexical context represented by + * this psm. nullptr if not found + **/ + rp lookup_var(const std::string & x) const; + + void push_envframe(envframe x); + void pop_envframe(); + + // ----- parsing outputs ----- + + void on_expr(ref::brw expr); + void on_expr_with_semicolon(ref::brw 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); + + /** write human-readable representation on @p os **/ + void print(std::ostream & os) const; + + public: + /** stack of incomplete parser work. + * generally speaking, push when to start new work for nested content; + * pop when work complete + **/ + exprstatestack * p_stack_; + /** stack of environment frames, one for each enclosing lambda **/ + envframestack * p_env_stack_; + /** if non-null, store next non-nested complete expressions in + * *p_emit_expr + **/ + rp * p_emit_expr_; + }; + + inline std::ostream & + operator<<(std::ostream & os, const parserstatemachine & x) { + x.print(os); + return os; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end parserstatemachine.hpp */ 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..6e9db243 --- /dev/null +++ b/xo-reader/include/xo/reader/progress_xs.hpp @@ -0,0 +1,126 @@ +/** @file progress_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.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_assign, + + op_add, + op_subtract, + + op_multiply, + 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 + **/ + 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; + + virtual void on_expr(ref::brw expr, + parserstatemachine * p_psm) override; + virtual void on_symbol_token(const token_type & tk, + 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_leftparen_token(const token_type & tk, + parserstatemachine * p_psm) override; + virtual void on_rightparen_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) 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(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(); + + 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*/ +} /*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..28e0b3a6 --- /dev/null +++ b/xo-reader/include/xo/reader/reader.hpp @@ -0,0 +1,107 @@ +/* file reader.hpp + * + * author: Roland Conybeare, Aug 2024 + */ + +#pragma once + +#include "parser.hpp" +#include "xo/expression/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::ast::Expression; + using span_type = span; + + reader_result(rp expr, span_type rem) + : expr_{std::move(expr)}, rem_{rem} {} + + /** 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_; + }; + + /** + * 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 res = 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() = default; + + /** 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); + + 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/sequence_xs.hpp b/xo-reader/include/xo/reader/sequence_xs.hpp new file mode 100644 index 00000000..b361bbf7 --- /dev/null +++ b/xo-reader/include/xo/reader/sequence_xs.hpp @@ -0,0 +1,47 @@ +/** @file sequence_xs.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include "exprstate.hpp" +#include + +namespace xo { + namespace ast { class Sequence; } + namespace ast { class Lambda; } + + namespace scm { + class sequence_xs : public exprstate { + public: + using Sequence = xo::ast::Sequence; + using Lambda = xo::ast::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(ref::brw expr, + parserstatemachine * p_psm) override; + + virtual void on_rightbrace_token(const token_type & tk, + parserstatemachine * p_psm) 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..0a3dee3e --- /dev/null +++ b/xo-reader/src/reader/CMakeLists.txt @@ -0,0 +1,29 @@ +# parser/CMakeLists.txt + +set(SELF_LIB xo_reader) +set(SELF_SRCS + parser.cpp + parserstatemachine.cpp + reader.cpp + exprstate.cpp + exprstatestack.cpp + define_xs.cpp + progress_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 + envframe.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/define_xs.cpp b/xo-reader/src/reader/define_xs.cpp new file mode 100644 index 00000000..19f6d83d --- /dev/null +++ b/xo-reader/src/reader/define_xs.cpp @@ -0,0 +1,276 @@ +/* @file define_xs.cpp */ + +#include "define_xs.hpp" +#include "exprstatestack.hpp" +#include "parserstatemachine.hpp" +#include "expect_symbol_xs.hpp" +#include "expect_expr_xs.hpp" +#include "expect_type_xs.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) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_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)} + {} + + void + define_xs::on_expr(ref::brw expr, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_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 + this->def_expr_->assign_rhs(rhs_value);; + + rp def_expr = this->def_expr_; + + this->defxs_type_ = defexprstatetype::def_6; + } else { + exprstate::on_expr(expr, p_psm); + } + } + + void + define_xs::on_expr_with_semicolon(ref::brw expr, + parserstatemachine * p_psm) + { + 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) + { + 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_1) { + this->defxs_type_ = defexprstatetype::def_2; + this->def_expr_->assign_lhs_name(symbol_name); + return; + } else { + exprstate::on_symbol(symbol_name, p_psm); + } + } + + void + define_xs::on_typedescr(TypeDescr td, + 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_3) { + this->defxs_type_ = defexprstatetype::def_4; + this->cvt_expr_ = ConvertExprAccess::make(td /*dest_type*/, + nullptr /*source_expr*/); + this->def_expr_->assign_rhs(this->cvt_expr_); + //this->def_lhs_td_ = td; + + return; + } else { + exprstate::on_typedescr(td, 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); + } else { + exprstate::on_def_token(tk, 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); + } else { + exprstate::on_colon_token(tk, p_psm); + } + } + + void + define_xs::on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) + { + /* def expr consumes semicolon */ + + 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_6) { + rp expr = this->def_expr_; + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->top_exprstate().on_expr(expr, p_psm); + } else { + exprstate::on_semicolon_token(tk, 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)); + + constexpr const char * self_name = "define_xs::on_singleassign_token"; + + log && log("defxs_type", defxs_type_); + + /* + * 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 ':' + */ + 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); + } else { + this->illegal_input_error(self_name, tk); + } + } + + 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 * self_name = "define_xs::on_rightparen"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "define_xs::on_f64"; + + this->illegal_input_error(self_name, tk); + } + + void + define_xs::print(std::ostream & os) const { + os << ""; + } + } /*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..38ff1fcb --- /dev/null +++ b/xo-reader/src/reader/envframe.cpp @@ -0,0 +1,34 @@ +/* file envframe.cpp + * + * author: Roland Conybeare + */ + +#include "envframe.hpp" +#include "xo/indentlog/print/vector.hpp" + +namespace xo { + using xo::ast::Variable; + + namespace scm { + rp + envframe::lookup(const std::string & x) const { + for (const auto & var : argl_) { + if (x == var->name()) + return var; + } + + return nullptr; + } + + void + envframe::print(std::ostream & os) const { + os << ""; + } + + } /*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..047e1789 --- /dev/null +++ b/xo-reader/src/reader/envframestack.cpp @@ -0,0 +1,86 @@ +/* file envframestack.cpp + * + * author: Roland Conybeare + */ + +#include "envframestack.hpp" + +namespace xo { + using xo::ast::Variable; + + namespace scm { + envframe & + envframestack::top_envframe() { + std::size_t z = stack_.size(); + + if (z == 0) { + throw std::runtime_error + ("parser::top_exprstate: unexpected empty stack"); + } + + return stack_[z-1]; + } + + void + envframestack::push_envframe(envframe 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] = std::move(frame); + } + + void + 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]); + + stack_.resize(z-1); + + //return top; + } else { + //return nullptr; + } + } + + rp + 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(x); + + if (retval) + return retval; + } + + return nullptr; + } + + void + envframestack::print(std::ostream & os) const { + os << "" << std::endl; + } + } /*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..1f07d131 --- /dev/null +++ b/xo-reader/src/reader/expect_expr_xs.cpp @@ -0,0 +1,227 @@ +/* file expect_expr_xs.cpp + * + * author: Roland Conybeare + */ + +#include "expect_expr_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "lambda_xs.hpp" +#include "define_xs.hpp" +#include "paren_xs.hpp" +#include "sequence_xs.hpp" +#include "progress_xs.hpp" +#include "xo/expression/Lambda.hpp" +#include "xo/expression/Constant.hpp" + +namespace xo { + using xo::ast::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} + {} + + void + expect_expr_xs::on_def_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_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) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + //constexpr const char * self_name = "exprstate::on_leftparen"; + + lambda_xs::start(p_psm); + } + + void + expect_expr_xs::on_leftparen_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_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) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_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) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("tk", tk)); + + /* 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) + */ + + rp var = p_psm->lookup_var(tk.text()); + + if (!var) { + throw std::runtime_error + (tostr("expect_expr_xs::on_symbol_token", + ": unknown symbol", + xtag("name", tk.text()))); + } + + /* e.g. + * def pi = 3.14159265; + * def mypi = pi; + * ^ + * def pi2 = pi * 2; + * ^ + * def y = foo(pi2); + * ^ + */ + progress_xs::start(var, 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_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 * 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_expr(ref::brw 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.promote())); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr(expr); + } /*on_expr*/ + + void + expect_expr_xs::on_expr_with_semicolon(ref::brw 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.promote())); + + std::unique_ptr self = p_psm->pop_exprstate(); + + p_psm->on_expr_with_semicolon(expr); + } /*on_expr_with_semicolon*/ + + } /*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..916841da --- /dev/null +++ b/xo-reader/src/reader/expect_formal_arglist_xs.cpp @@ -0,0 +1,113 @@ +/* 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()); + } + + 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..28e19ce4 --- /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::ast::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..0e01924e --- /dev/null +++ b/xo-reader/src/reader/expect_symbol_xs.cpp @@ -0,0 +1,47 @@ +/* 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)); + + /* 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..d994257e --- /dev/null +++ b/xo-reader/src/reader/expect_type_xs.cpp @@ -0,0 +1,66 @@ +/* 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) + {} + + 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() == "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) { + throw std::runtime_error + (tostr(c_self_name, + ": unknown type name", + " (expecting f64|f32|i16|i32|i64)", + xtag("typename", tk.text()))); + } + + 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..b934c2ef --- /dev/null +++ b/xo-reader/src/reader/exprseq_xs.cpp @@ -0,0 +1,81 @@ +/* @file exprseq_xs.cpp */ + +#include "exprseq_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "define_xs.hpp" +#include "expect_symbol_xs.hpp" + +namespace xo { + namespace scm { + std::unique_ptr + exprseq_xs::make() + { + return std::make_unique(exprseq_xs()); + } + + void + exprseq_xs::start(parserstatemachine * p_psm) + { + p_psm->push_exprstate(exprseq_xs::make()); + } + + exprseq_xs::exprseq_xs() + : exprstate(exprstatetype::expect_toplevel_expression_sequence) + { + } + + 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)); + + //constexpr const char * c_self_name = "exprseq_xs::on_def_token"; + + 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_symbol_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr const char * c_self_name = "exprseq_xs::on_symbol_token"; + + this->illegal_input_error(c_self_name, tk); + } + + 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(ref::brw expr, + parserstatemachine * p_psm) + { + /* toplevel expression sequence accepts an + * arbitrary number of expressions. + * + * parser::include_token() returns + */ + + auto p_emit_expr = p_psm->p_emit_expr_; + + *p_emit_expr = expr.promote(); + } /*on_expr*/ + + } /*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..77480dcb --- /dev/null +++ b/xo-reader/src/reader/exprstate.cpp @@ -0,0 +1,448 @@ +/* @file exprstate.cpp */ + +#include "exprstate.hpp" +#include "exprstatestack.hpp" +#include "parserstatemachine.hpp" +//#include "formal_arg.hpp" +#include "xo/expression/Variable.hpp" +#include "xo/indentlog/print/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::ast::Constant; + //using xo::reflect::Reflect; + using xo::reflect::TypeDescr; + + 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::parenexpr: + return "parenexpr"; + case exprstatetype::sequenceexpr: + return "sequenceexpr"; + case exprstatetype::let1expr: + return "let1expr"; + 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*/) + { + this->illegal_input_error("exprstate::on_def_token", tk); + } + + void + exprstate::on_lambda_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + this->illegal_input_error("exprstate::on_lambda_token", tk); + } + + 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"; + + this->illegal_input_error(c_self_name, tk); + } + + void + exprstate::on_typedescr(TypeDescr td, + 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_typedescr"; + + throw std::runtime_error(tostr(c_self_name, + ": unexpected typedescr for parsing state", + xtag("td", td), + xtag("state", *this))); + } + + 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"; + + throw std::runtime_error(tostr(c_self_name, + ": unexpected formal-arg for parsing state", + xtag("formal", formal.get()), + xtag("state", *this))); + } + + 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"; + + throw std::runtime_error(tostr(c_self_name, + ": unexpected formal-arg for parsing state", + xtag("argl", argl), + xtag("state", *this))); + } + + void + exprstate::on_colon_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + constexpr const char * self_name = "exprstate::on_colon"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "exprstate::on_comma"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "exprstate::on_semicolon"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "exprstate::on_singleassign_token"; + + this->illegal_input_error(self_name, tk); + } + + void + exprstate::on_leftparen_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + constexpr const char * self_name = "exprstate::on_leftparen_token"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "exprstate::on_rightparen"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "exprstate::on_leftbrace_token"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "exprstate::on_rightbrace_token"; + + this->illegal_input_error(self_name, tk); + } + + 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 * self_name = "exprstate::on_operator_token"; + + this->illegal_input_error(self_name, tk); + } + + void + exprstate::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 * self_name = "exprstate::on_f64"; + + this->illegal_input_error(self_name, tk); + } + + void + exprstate::on_input(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)); + log && log(xtag("state", *this)); + log && log(xtag("psm", *p_psm)); + + 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_i64: + assert(false); + return; + + case tokentype::tk_f64: + this->on_f64_token(tk, p_psm); + return; + + case tokentype::tk_string: + assert(false); + 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_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: + case tokentype::tk_yields: + + case tokentype::tk_plus: + case tokentype::tk_minus: + case tokentype::tk_star: + case tokentype::tk_slash: + this->on_operator_token(tk, p_psm); + return; + + case tokentype::tk_type: + case tokentype::tk_if: + 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); + } + + void + exprstate::on_expr(ref::brw 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)); + + assert(false); + } /*on_expr*/ + + void + exprstate::on_expr_with_semicolon(ref::brw expr, + parserstatemachine * /*p_psm*/) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + const char * c_self_name = "exprstate::on_expr_with_semicolon"; + + log && log(xtag("exstype", this->exs_type_), + xtag("expr", expr)); + + throw std::runtime_error + (tostr(c_self_name, + ": unexpected expression for parsing state", + xtag("expr", expr), + xtag("state", *this))); + + assert(false); + } /*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)); + + assert(false); + } + + void + exprstate::print(std::ostream & os) const { + os << ""; + } + + 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))); + } + } /*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..2e070c48 --- /dev/null +++ b/xo-reader/src/reader/exprstatestack.cpp @@ -0,0 +1,71 @@ +/* file exprstatestack.cpp + * + * author: Roland Conybeare + */ + +#include "exprstatestack.hpp" + +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::push_exprstate(std::unique_ptr exs) { + constexpr bool c_debug_flag = true; + 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 = true; + 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; + } + } /*namespace scm*/ +} /*namespace xo*/ + +/* end exprstatestack.cpp */ diff --git a/xo-reader/src/reader/lambda_xs.cpp b/xo-reader/src/reader/lambda_xs.cpp new file mode 100644 index 00000000..ab9b73d8 --- /dev/null +++ b/xo-reader/src/reader/lambda_xs.cpp @@ -0,0 +1,128 @@ +/* @file lambda_xs.cpp */ + +#include "lambda_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "expect_formal_arglist_xs.hpp" +#include "expect_expr_xs.hpp" +#include "xo/expression/Lambda.hpp" + +namespace xo { + using xo::ast::Lambda; + + 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"; + 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) {} + + 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->argl_ = argl; + + p_psm->push_envframe(envframe(argl)); + + expect_expr_xs::start(p_psm); + } else { + exprstate::on_formal_arglist(argl, p_psm); + } + } + + void + lambda_xs::on_expr(ref::brw expr, + parserstatemachine * p_psm) + { + if (lmxs_type_ == lambdastatetype::lm_2) { + this->lmxs_type_ = lambdastatetype::lm_3; + this->body_ = expr.promote(); + } else { + exprstate::on_expr(expr, p_psm); + } + } + + void + lambda_xs::on_expr_with_semicolon(ref::brw expr, + parserstatemachine * p_psm) + { + this->on_expr(expr, p_psm); + this->on_semicolon_token(token_type::semicolon(), p_psm); + } + + void + lambda_xs::on_semicolon_token(const token_type & tk, + parserstatemachine * p_psm) + { + if (lmxs_type_ == lambdastatetype::lm_3) { + /* done! */ + + std::unique_ptr self = p_psm->pop_exprstate(); + + std::string name = "fixmename"; + + rp lm = Lambda::make(name, argl_, body_); + + p_psm->pop_envframe(); + + 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::print(std::ostream & os) const { + os << ""; + } + + } /*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..0afbd415 --- /dev/null +++ b/xo-reader/src/reader/let1_xs.cpp @@ -0,0 +1,119 @@ +/* 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::ast::Sequence; + using DefineExpr = xo::ast::DefineExpr; + using Apply = xo::ast::Apply; + using Lambda = xo::ast::Lambda; + using LambdaAccess = xo::ast::LambdaAccess; + using Variable = xo::ast::Variable; + + namespace { + std::string gensym() { + return "genanotherxx"; + } + } + + namespace scm { + std::unique_ptr + let1_xs::make(std::string lhs_name, + rp rhs) + { + return std::make_unique(let1_xs(std::move(lhs_name), + std::move(rhs))); + } + + void + let1_xs::start(const std::string & lhs_name, + const rp & rhs, + parserstatemachine * p_psm) + { + p_psm->push_exprstate(let1_xs::make(lhs_name, rhs)); + + expect_expr_xs::start(true /*allow_defs*/, + true /*cxl_on_rightbrace*/, + p_psm); + } + + let1_xs::let1_xs(std::string lhs_name, + rp rhs) + : exprstate(), + lhs_name_{std::move(lhs_name)}, + rhs_{std::move(rhs)} + {} + + void + let1_xs::on_expr(ref::brw expr, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + ref::brw 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 + 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_); + + std::string argname = gensym(); + + rp lambda + = Lambda::make(this->lhs_name_, + {Variable::make(argname, + this->rhs_->valuetype())}, + 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..2c4ca484 --- /dev/null +++ b/xo-reader/src/reader/paren_xs.cpp @@ -0,0 +1,252 @@ +/* @file paren_xs.cpp */ + +#include "paren_xs.hpp" +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" +#include "progress_xs.hpp" +#include "expect_expr_xs.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_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(ref::brw 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..e71deb40 --- /dev/null +++ b/xo-reader/src/reader/parser.cpp @@ -0,0 +1,81 @@ +/* file parser.cpp + * + * author: Roland Conybeare + */ + +#include "parser.hpp" +#include "parserstatemachine.hpp" +#include "define_xs.hpp" +#include "exprseq_xs.hpp" +#include "xo/expression/DefineExpr.hpp" +#include "xo/expression/Constant.hpp" +#include "xo/expression/ConvertExpr.hpp" +//#include +#include + +namespace xo { + using xo::ast::Expression; + //using xo::ast::DefineExpr; + //using xo::ast::ConvertExpr; + //using xo::ast::Constant; + //using xo::reflect::Reflect; + using xo::reflect::TypeDescr; + + namespace scm { + // ----- parser ----- + + bool + parser::has_incomplete_expr() const { + return !xs_stack_.empty(); + } + + void + parser::begin_translation_unit() { + /* note: not using emit expr here */ + parserstatemachine psm(&xs_stack_, + &env_stack_, + nullptr /*p_emit_expr*/); + + exprseq_xs::start(&psm); + } + + rp + parser::include_token(const token_type & tk) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag), xtag("tk", tk)); + + if (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 */ + + rp retval; + + parserstatemachine psm(&xs_stack_, &env_stack_, &retval); + + xs_stack_.top_exprstate().on_input(tk, &psm); + + log && log(xtag("retval", retval)); + + return retval; + } /*include_token*/ + + void + parser::print(std::ostream & os) const { + os << "" << std::endl; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end parser.cpp */ diff --git a/xo-reader/src/reader/parserstatemachine.cpp b/xo-reader/src/reader/parserstatemachine.cpp new file mode 100644 index 00000000..373e83e3 --- /dev/null +++ b/xo-reader/src/reader/parserstatemachine.cpp @@ -0,0 +1,154 @@ +/* file parserstatemachine.cpp + * + * author: Roland Conybeare + */ + +#include "parserstatemachine.hpp" +#include "exprstatestack.hpp" + +namespace xo { + using xo::ast::Variable; + + namespace scm { + rp + parserstatemachine::lookup_var(const std::string & x) const { + return p_env_stack_->lookup(x); + } + + std::unique_ptr + parserstatemachine::pop_exprstate() { + return p_stack_->pop_exprstate(); + } + + exprstate & + parserstatemachine::top_exprstate() { + return p_stack_->top_exprstate(); + } + + void + parserstatemachine::push_exprstate(std::unique_ptr x) { + p_stack_->push_exprstate(std::move(x)); + } + + void + parserstatemachine::push_envframe(envframe x) { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("frame", x)); + + p_env_stack_->push_envframe(std::move(x)); + } + + void + parserstatemachine::pop_envframe() { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + p_env_stack_->pop_envframe(); + } + + void + parserstatemachine::on_expr(ref::brw x) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("x", x), + xtag("psm", *this)); + + this->p_stack_ + ->top_exprstate().on_expr(x, this); + } + + void + parserstatemachine::on_expr_with_semicolon(ref::brw x) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("x", x), + xtag("psm", *this)); + + this->p_stack_ + ->top_exprstate().on_expr_with_semicolon(x, this); + } + + void + parserstatemachine::on_symbol(const std::string & x) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("x", x), + xtag("psm", *this)); + + this->p_stack_ + ->top_exprstate().on_symbol(x, this); + } + + void + parserstatemachine::on_semicolon_token(const token_type & tk) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("tk", tk), + xtag("psm", *this)); + + this->p_stack_ + ->top_exprstate().on_semicolon_token(tk, this); + } + + void + parserstatemachine::on_operator_token(const token_type & tk) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("tk", tk), + xtag("psm", *this)); + + this->p_stack_ + ->top_exprstate().on_operator_token(tk, this); + } + + void + parserstatemachine::on_leftbrace_token(const token_type & tk) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("tk", tk), + xtag("psm", *this)); + + this->p_stack_ + ->top_exprstate().on_leftbrace_token(tk, this); + } + + void + parserstatemachine::on_rightbrace_token(const token_type & tk) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + log && log(xtag("tk", tk), + xtag("psm", *this)); + + this->p_stack_ + ->top_exprstate().on_rightbrace_token(tk, this); + } + + void + parserstatemachine::print(std::ostream & os) const { + os << ""; + } + } /*namespace scm*/ +} /*namespace xo*/ + + +/* end parserstatemachine.cpp */ diff --git a/xo-reader/src/reader/progress_xs.cpp b/xo-reader/src/reader/progress_xs.cpp new file mode 100644 index 00000000..40d857b6 --- /dev/null +++ b/xo-reader/src/reader/progress_xs.cpp @@ -0,0 +1,439 @@ +/* @file progress_xs.cpp */ + +#include "progress_xs.hpp" +#include "exprstatestack.hpp" +#include "expect_expr_xs.hpp" +#include "parserstatemachine.hpp" +#include "xo/expression/AssignExpr.hpp" +#include "xo/expression/Apply.hpp" + +namespace xo { + using xo::ast::Expression; + using xo::ast::AssignExpr; + using xo::ast::Variable; + using xo::ast::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_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_add: + case optype::op_subtract: + return 2; + + case optype::op_multiply: + case optype::op_divide: + return 3; + } + + 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; } + + void + progress_xs::on_def_token(const token_type & tk, + parserstatemachine * /*p_stack*/) + { + constexpr const char * self_name = "progress_xs::on_def"; + + /* nothing here - admits_definition unconditionally false */ + this->illegal_input_error(self_name, tk) ; + } + + rp + progress_xs::assemble_expr() { + /* 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)) { + throw std::runtime_error(tostr(c_self_name, + ": expected expr on rhs of operator", + xtag("lhs", lhs_), + xtag("op", op_type_))); + } + + /* 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: + { + ref::brw 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_add: + return Apply::make_add2_f64(this->lhs_, + this->rhs_); + + case optype::op_subtract: + return Apply::make_sub2_f64(this->lhs_, + this->rhs_); + + case optype::op_multiply: + return Apply::make_mul2_f64(this->lhs_, + this->rhs_); + + case optype::op_divide: + return Apply::make_div2_f64(this->lhs_, + this->rhs_); + + case optype::n_optype: + /* unreachable */ + assert(false); + return nullptr; + } + + return nullptr; + } + + void + progress_xs::on_expr(ref::brw expr, + parserstatemachine * /*p_psm*/) + { + /* 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"; + + + if (op_type_ == optype::invalid) { + throw std::runtime_error(tostr(c_self_name, + ": consecutive unseparated exprs not legal")); + } + +#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(); + } + + 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_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 * self_name = "progress_xs::on_colon"; + + this->illegal_input_error(self_name, tk); + } + + void + progress_xs::on_semicolon_token(const token_type & /*tk*/, + parserstatemachine * p_psm) + { + /* note: implementation parllels .on_rightparen_token() */ + + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + rp expr = this->assemble_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 * self_name = "progress_xs::on_singleassign"; + + this->illegal_input_error(self_name, tk); + } + + void + progress_xs::on_leftparen_token(const token_type & tk, + parserstatemachine * /*p_psm*/) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + constexpr const char * self_name = "exprstate::on_leftparen"; + + this->illegal_input_error(self_name, tk); + } + + void + progress_xs::on_rightparen_token(const token_type & tk, + parserstatemachine * p_psm) + { + /* note: implementation parallels .on_semicolon_token() */ + + + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + constexpr const char * self_name = "progress_xs::on_rightparen"; + + auto p_stack = p_psm->p_stack_; + + /* stack may be something like: + * + * lparen_0 + * expect_rhs_expression + * 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(); + + std::unique_ptr self = p_psm->pop_exprstate(); + + if (p_stack->empty()) { + throw std::runtime_error(tostr(self_name, + ": expected non-empty parsing stack")); + } + + log && log(xtag("stack", p_stack)); + + p_psm->top_exprstate().on_expr(expr, p_psm); + + /* now deliver rightparen */ + p_psm->top_exprstate().on_rightparen_token(tk, p_psm); + } + + namespace { + optype + tk2op(const tokentype & tktype) { + switch (tktype) { + 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; + default: + assert(false); + return optype::invalid; + } + return optype::invalid; + } + } + + void + progress_xs::on_operator_token(const token_type & tk, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_debug_flag)); + + 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(); + + /* 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_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 * self_name = "progress_xs::on_f64"; + + if (this->op_type_ == optype::invalid) { + this->illegal_input_error(self_name, tk); + } else { + exprstate::on_f64_token(tk, p_psm); + } + } + + void + progress_xs::print(std::ostream & os) const { + os << ""; + } + + + } /*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..d08b9552 --- /dev/null +++ b/xo-reader/src/reader/reader.cpp @@ -0,0 +1,98 @@ +/* @file reader.cpp */ + +#include "reader.hpp" + +namespace xo { + namespace scm { + 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) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_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 + */ + span_type expr_span = input.prefix(0ul); + + while (!input.empty()) { + /* read one token from input */ + auto sr = this->tokenizer_.scan2(input, eof); + const auto & tk = sr.first; + const span_type & used_span = sr.second; + + log && log(xtag("used_span", used_span)); + log && log(xtag("input.pre", input)); + + input = input.after_prefix(used_span); + + log && log(xtag("expr_span.pre", expr_span)); + + expr_span += used_span; + + if (tk.is_valid()) { + /* forward just-read token to parser */ + auto expr = this->parser_.include_token(tk); + + if (expr) { + log && log(xtag("outcome", "victory!"), + xtag("expr", expr)); + + /* token completes an expression -> victory */ + return reader_result(expr, expr_span); + } else { + /* token did not complete an expression + * (e.g. token for '[') + * + * input span may contain more tokens -> iterate + */ + } + } else { + assert(input.empty()); + + /* no more tokens in input */ + break; + } + } + + /* control here: either + * 1. input.empty (perhaps ate some whitespace, ok) + * 2. missing or incomplete token (ok unless eof) + */ + if (eof) { + 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")); + + return reader_result(nullptr, expr_span); + } + + } /*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..97aa0955 --- /dev/null +++ b/xo-reader/src/reader/sequence_xs.cpp @@ -0,0 +1,113 @@ +/* @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" + +namespace xo { + using xo::ast::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(ref::brw expr, + parserstatemachine * p_psm) + { + constexpr bool c_debug_flag = true; + scope log(XO_DEBUG(c_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. + */ + ref::brw 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_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); + } + + } /*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..e2190f91 --- /dev/null +++ b/xo-reader/utest/parser.test.cpp @@ -0,0 +1,279 @@ +/* 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::exprstatetype; + using xo::scm::define_xs; + using xo::scm::defexprstatetype; + using std::cerr; + using std::endl; + + //using xo::ast::Expression; + + namespace ut { + TEST_CASE("parser", "[parser]") { + for (std::size_t i_tc = 0; i_tc < 2; ++i_tc) { + parser_type parser; + + constexpr bool c_debug_flag = true; + 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.get() == nullptr); + + /* 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.get() == nullptr); + + /* 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.get() == nullptr); + + /* 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.get() == nullptr); + + 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.get() == nullptr); + + 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.get() == nullptr); + + /* 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.get() != nullptr); + + 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..d73fc972 --- /dev/null +++ b/xo-reader/utest/reader.test.cpp @@ -0,0 +1,67 @@ +/* @file reader.test.cpp */ + +#include "xo/reader/reader.hpp" +#include + +namespace xo { + using xo::scm::reader; + + 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 : f64 = 2.0 * 3.14159265;"}, + {"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; }"}, + }; + } + + 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]; + + reader rdr; + + 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_)); + + input = input.after_prefix(rr.rem_); + + log && log(xtag("post.input", input)); + + 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 */