.xo-interpreter subrepo tidy
This commit is contained in:
parent
9eaae04fdf
commit
d11f6ca597
38 changed files with 0 additions and 3210 deletions
|
|
@ -1,41 +0,0 @@
|
|||
# xo-interpreter/CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project (xo_interpreter 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") # gcc-only!
|
||||
add_definitions(${PROJECT_CXX_FLAGS})
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# note on ordering: must read .cmake defn of lib before configuring any examples
|
||||
add_subdirectory(src/interpreter)
|
||||
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
add_subdirectory(example)
|
||||
add_subdirectory(utest)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
#if (XO_ENABLE_EXAMPLES)
|
||||
# install(TARGETS xo_interpreter_ex1 DESTINATION bin/xo/example)
|
||||
#endif()
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# docs targets depend on all other library/utest targets
|
||||
#
|
||||
#add_subdirectory(docs)
|
||||
|
||||
# end CMakeLists.txt
|
||||
|
|
@ -1 +0,0 @@
|
|||
# xo-interpreter
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# ----------------------------------------------------------------
|
||||
# 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 (XO_SUBMODULE_BUILD)
|
||||
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
|
||||
# local version of xo-cmake macros
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/xo-cmake/cmake")
|
||||
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
|
||||
endif()
|
||||
else()
|
||||
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()
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(xo_alloc)
|
||||
#find_dependency(xo_flatstring)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Share.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# xo-alloc/docs/CMakeLists.txt
|
||||
|
||||
xo_doxygen_collect_deps()
|
||||
xo_docdir_doxygen_config()
|
||||
xo_docdir_sphinx_config(
|
||||
index.rst install.rst)
|
||||
|
||||
# see xo-reader/doc or xo-unit/doc for working examples
|
||||
# example.rst install.rst implementation.rst
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
standalone build
|
||||
|
||||
+-----------------------------------------------+
|
||||
| cmake |
|
||||
| CMakeLists.txt |
|
||||
| $PREFIX/share/cmake/xo_macros/xo_cxx.cmake |
|
||||
+-----------------------------------------------+
|
||||
|
|
||||
| +----------------------+
|
||||
+------------------------------------------------->| .build/docs/Doxyfile |
|
||||
| +----------------------+
|
||||
| ^
|
||||
| (cmake) |
|
||||
| /------------/
|
||||
| |
|
||||
| +---------------------------------------+ +-----------------+
|
||||
+---->| doxygen |--------->| .build/docs/dox |
|
||||
| | $PREFIX/share/xo-macros/Doxyfile.in | (doxygen)| +- html/ |
|
||||
| +---------------------------------------+ | +- xml/ |
|
||||
| +-----------------+
|
||||
| |
|
||||
| |(sphinx)
|
||||
| |
|
||||
| v
|
||||
| +---------------------------------------+ +--------------------+
|
||||
\---->| sphinx |------->| .build/docs/sphinx |
|
||||
| +- conf.py | | +- html/ |
|
||||
| +- _static/ | +--------------------+
|
||||
| +- *.rst |
|
||||
+---------------------------------------+
|
||||
|
||||
umbrella build relies on top-level cmake macros
|
||||
|
||||
files
|
||||
|
||||
README this file
|
||||
CMakeLists.txt build entry point
|
||||
conf.py sphinx config
|
||||
_static static files for sphinx
|
||||
|
||||
index.rst toplevel sphinx document; entry point
|
||||
1
.xo-interpreter/docs/_static/README
vendored
1
.xo-interpreter/docs/_static/README
vendored
|
|
@ -1 +0,0 @@
|
|||
add any static {.html, .js, ..} files for sphinx to pickup here
|
||||
BIN
.xo-interpreter/docs/_static/img/favicon.ico
vendored
BIN
.xo-interpreter/docs/_static/img/favicon.ico
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 303 KiB |
|
|
@ -1,39 +0,0 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'xo interpreter documentation'
|
||||
copyright = '2025, Roland Conybeare'
|
||||
author = 'Roland Conybeare'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
#extensions = []
|
||||
extensions = [ "breathe",
|
||||
"sphinx.ext.mathjax", # inline math
|
||||
"sphinx.ext.autodoc", # generate info from docstrings
|
||||
"sphinxcontrib.ditaa", # diagrams-through-ascii-art
|
||||
"sphinxcontrib.plantuml" # text -> uml diagrams
|
||||
]
|
||||
|
||||
# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in
|
||||
# match project name in Doxyfile.in
|
||||
breathe_default_project = "xodoxxml"
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
#html_theme = 'alabaster'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
html_static_path = ['_static']
|
||||
html_favicon = '_static/img/favicon.ico'
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
.. xo-interpreter documentation master file.
|
||||
|
||||
xo-interpreter documentation
|
||||
============================
|
||||
|
||||
xo-interpreter provides an interpreter for the Schematika language
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: xo-interpreter contents
|
||||
|
||||
install
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
.. _install:
|
||||
|
||||
.. toctree:
|
||||
:maxdepth: 2
|
||||
|
||||
Source
|
||||
======
|
||||
|
||||
Source code lives on github `here`_
|
||||
|
||||
.. _here: https://github.com/rconybea/xo-interpreter
|
||||
|
||||
To clone from git:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/rconybea/xo-interpreter
|
||||
|
||||
Tested with gcc 14.2
|
||||
|
||||
Install
|
||||
=======
|
||||
|
||||
One-step Install
|
||||
----------------
|
||||
|
||||
Install xo-interpreter along with the rest of *XO* from `xo-umbrella2 source`_:
|
||||
see install instructions for xo-umbrella2.
|
||||
|
||||
.. _xo-umbrella2 source: https://github.com/rconybea/xo-umbrella2
|
||||
|
||||
Essential Xo Dependencies
|
||||
-------------------------
|
||||
|
||||
``xo-interpreter`` uses several supporting libraries from elsewhere in the *XO* project:
|
||||
|
||||
- `xo-reader source`_ (Schematika expression parser)
|
||||
- `xo-expression source`_ (Schematika AST representation)
|
||||
- `xo-tokenizer source`_ (Schematika lexer)
|
||||
- `xo-object source`_ (gc-eligible runtime polymorphism)
|
||||
- `xo-randomgen source`_ (fast pseudo-random number generators)
|
||||
- `xo-alloc source`_ (arena allocators, garbage collector)
|
||||
- `xo-unit source`_ (dimension checking library)
|
||||
- `xo-ratio source`_ (exact ratio library)
|
||||
- `xo-flatstring source`_ (no-allocation string library)
|
||||
- `xo-callback source`_ (callback library)
|
||||
- `xo-reflectutil source`_ (reflection utils for participating libs)
|
||||
- `xo-reflect source`_ (reflection library)
|
||||
- `xo-refcnt source`_ (reference-counting library)
|
||||
- `xo-subsys source`_ (utility library)
|
||||
- `xo-indentlog source`_ (structured logging, pretty-printing)
|
||||
- `xo-cmake source`_ (shared cmake macros)
|
||||
|
||||
.. _xo-reader source: https://github.com/rconybea/xo-reader
|
||||
.. _xo-expression source: https://github.com/rconybea/xo-expression
|
||||
.. _xo-tokenizer source: https://github.com/rconybea/xo-tokenizer
|
||||
.. _xo-object source: https://github.com/rconybea/xo-object
|
||||
.. _xo-randomgen source: https://github.com/rconybea/xo-randomgen
|
||||
.. _xo-alloc source: https://github.com/rconybea/xo-alloc
|
||||
.. _xo-unit source: https://github.com/rconybea/xo-unit
|
||||
.. _xo-ratio source: https://github.com/rconybea/xo-ratio
|
||||
.. _xo-flatstring source: https://github.comr/rconybea/xo-flatstring
|
||||
.. _xo-callback source: https://github.com/rconybea/xo-callback
|
||||
.. _xo-reflect source: https://github.com/rconybea/xo-reflect
|
||||
.. _xo-refcnt source: https://github.com/rconybea/refcnt
|
||||
.. _xo-subsys source: https://github.com/rconybea/subsys
|
||||
.. _xo-indentlog source: https://github.com/rconybea/indentlog
|
||||
.. _xo-cmake source: https://github.com/rconybea/xo-cmake
|
||||
|
||||
Building from source
|
||||
--------------------
|
||||
|
||||
Instructions for building xo-interpreter from source, along with only its essential dependencies.
|
||||
|
||||
Install scripts for XO libraries depend on helper scripts installed from `xo-cmake`.
|
||||
|
||||
Preamble:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
mkdir -p ~/proj/xo
|
||||
cd ~/proj/xo
|
||||
|
||||
git clone https://github.com/rconybea/xo-cmake
|
||||
|
||||
PREFIX=$HOME/local # or desired installation path
|
||||
|
||||
# will want PREFIX/bin in PATH to use xo-cmake helpers
|
||||
PATH=$PREFIX/bin:$PATH
|
||||
|
||||
Isntall `xo-cmake`:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cmake -B xo-cmake/.build -S xo-cmake
|
||||
cmake --install xo-cmake/.build
|
||||
|
||||
Now that we have xo-build in PATH, can build+install XO components in topological order:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
xo-build --clone --configure --build --install xo-indentlog
|
||||
xo-build --clone --configure --build --install xo-subsys
|
||||
xo-build --clone --configure --build --install xo-refcnt
|
||||
xo-build --clone --configure --build --install xo-reflect
|
||||
xo-build --clone --configure --build --install xo-reflectutil
|
||||
xo-build --clone --configure --build --install xo-callback
|
||||
xo-build --clone --configure --build --install xo-flatstring
|
||||
xo-build --clone --configure --build --install xo-ratio
|
||||
xo-build --clone --configure --build --install xo-unit
|
||||
xo-build --clone --configure --build --install xo-alloc
|
||||
xo-build --clone --configure --build --install xo-randomgen
|
||||
xo-build --clone --configure --build --install xo-object
|
||||
xo-build --clone --configure --build --install xo-tokenizer
|
||||
xo-build --clone --configure --build --install xo-expression
|
||||
xo-build --clone --configure --build --install xo-reader
|
||||
|
||||
Directories under ``PREFIX`` will then contain something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
PREFIX
|
||||
+= bin
|
||||
| +- xo-build
|
||||
│ +- xo-cmake-config
|
||||
│ \- xo-cmake-lcov-harness
|
||||
+─ include
|
||||
| \- xo
|
||||
│ +- alloc/
|
||||
| +- callback/
|
||||
| +- cxxutil/
|
||||
| +- expression/
|
||||
| | +- typeinf/
|
||||
| | ..
|
||||
| +- flatstring/
|
||||
| +- indentlog/
|
||||
| | +- machdep/
|
||||
| | +- print/
|
||||
| | +- timeutil/
|
||||
| | ..
|
||||
| +- object/
|
||||
| +- randomgen/
|
||||
| +- ratio/
|
||||
| +- reader/
|
||||
| +- refcnt/
|
||||
| +- reflect/
|
||||
| | +- atomic/
|
||||
| | +- function/
|
||||
| | +- pointer/
|
||||
| | +- struct/
|
||||
| | +- vector/
|
||||
| | ..
|
||||
| +- reflectutil/
|
||||
| +- subsys/
|
||||
| +- tokenizer/
|
||||
| \- unit/
|
||||
+- lib
|
||||
| +- cmake
|
||||
| | +- callback/
|
||||
| | +- indentlog/
|
||||
| | +- randomgen/
|
||||
| | +- refcnt/
|
||||
| | +- reflect/
|
||||
| | +- subsys/
|
||||
| | +- xo_alloc/
|
||||
| | +- xo_expression/
|
||||
| | +- xo_flatstring/
|
||||
| | +- xo_object/
|
||||
| | +- xo_ratio/
|
||||
| | +- xo_reader/
|
||||
| | +- xo_reflectutil/
|
||||
| | +- xo_tokenizer/
|
||||
| | \- xo_unit/
|
||||
| +- librefcnt.so
|
||||
| ..
|
||||
\- share
|
||||
+- cmake
|
||||
| \- xo-macros
|
||||
| +- code-coverage.cmake
|
||||
| +- xo_cxx.cmake
|
||||
| \- xo-project-macros.cmake
|
||||
+- etc
|
||||
| \- xo
|
||||
| \- subsystem_list
|
||||
+- xo-macros
|
||||
+- Doxyfile.in
|
||||
+- gen-ccov.in
|
||||
\- xo-bootstrap-macros.cmake
|
||||
|
||||
CMake Support
|
||||
-------------
|
||||
|
||||
To use built-in cmake support, when using ``xo-interpreter`` from another project:
|
||||
|
||||
Make sure ``PREFIX/lib/cmake`` is searched by cmake (for example include it in ``CMAKE_PREFIX_PATH``)
|
||||
|
||||
Add to your ``CMakeLists.txt``:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
FindPackage(xo_interpreter CONFIG REQUIRED)
|
||||
target_link_libraries(mytarget PUBLIC xo_interpreter)
|
||||
|
|
@ -1 +0,0 @@
|
|||
add_subdirectory(replxx)
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
# xo-interpreter/example/replxx/CMakeLists.txt
|
||||
|
||||
set(SELF_EXE xo_interpreter_replxx)
|
||||
set(SELF_SRCS replxx.cpp)
|
||||
|
||||
if (XO_ENABLE_EXAMPLES)
|
||||
xo_add_executable(${SELF_EXE} ${SELF_SRCS})
|
||||
xo_self_dependency(${SELF_EXE} xo_interpreter)
|
||||
# TODO: consider promoting to regular app
|
||||
xo_dependency(${SELF_EXE} xo_reader)
|
||||
xo_external_target_dependency(${SELF_EXE} replxx replxx::replxx)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(${SELF_EXE} PUBLIC Threads::Threads)
|
||||
endif()
|
||||
|
||||
# end CMakeLists.txt
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
/** @file replxx.cpp **/
|
||||
|
||||
#include "xo/interpreter/Schematika.hpp"
|
||||
|
||||
int
|
||||
main(int argc, char ** argv)
|
||||
{
|
||||
using xo::log_level;
|
||||
using xo::scm::Schematika;
|
||||
|
||||
Schematika::Config cfg;
|
||||
cfg.debug_flag = true;
|
||||
cfg.vsm_log_level_ = log_level::verbose;
|
||||
Schematika scm = Schematika::make(cfg);
|
||||
|
||||
scm.interactive_repl();
|
||||
}
|
||||
|
||||
/* end replxx.cpp */
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/** @file BuiltinPrimitives.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#include "xo/object/ObjectConverter.hpp"
|
||||
#include "xo/allocutil/IAlloc.hpp"
|
||||
#include "Primitive.hpp"
|
||||
#include "GlobalEnv.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
struct BuiltinPrimitives {
|
||||
public:
|
||||
using ObjectConverter = xo::obj::ObjectConverter;
|
||||
|
||||
template <typename Expr>
|
||||
static void install_pm(gc::IAlloc * mm, rp<Expr> pm_expr, gp<GlobalEnv> env) {
|
||||
gp<Object> rhs
|
||||
= xo::obj::make_primitive(mm, pm_expr->name(), pm_expr->value());
|
||||
|
||||
/* store in env using this variable-expr */
|
||||
rp<Variable> lhs
|
||||
= Variable::make(pm_expr->name(), pm_expr->value_td());
|
||||
|
||||
gp<Object> * addr = env->establish_var(lhs.borrow());
|
||||
|
||||
*addr = rhs;
|
||||
}
|
||||
|
||||
static void install(gc::IAlloc * mm, gp<GlobalEnv> env);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* end BuiltinPrimitives.hpp */
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
/** @file Env.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/alloc/Object.hpp"
|
||||
#include "xo/refcnt/Refcounted.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
class Variable; // see xo::scm::Variable in xo/expression/Variable.hpp
|
||||
|
||||
/** @class Env
|
||||
* @brief runtime environment, holding variable bindings for schematika interpreter
|
||||
*
|
||||
* Garbage-collected
|
||||
*
|
||||
* TODO: rename xo-expression xo::scm::Environment -> xo::scm::SymbolTable
|
||||
**/
|
||||
class Env : public Object {
|
||||
public:
|
||||
/** true iff @p vname is present in Symtab for innermost environment **/
|
||||
virtual bool local_contains_var(const std::string & vname) const = 0;
|
||||
|
||||
/** Fetch storage location for innermost binding of variable with name @p vname.
|
||||
* nullptr if not found
|
||||
**/
|
||||
virtual gp<Object> * lookup_slot(const std::string & vname) = 0;
|
||||
|
||||
/** require storage for variable @p v.
|
||||
* will also establish binding path.
|
||||
*
|
||||
* Intended for introducing a new variable,
|
||||
* replacing any previous variable with the same name.
|
||||
*
|
||||
* Beware of invalidating type correctness
|
||||
*
|
||||
* @return slot address for runtime value of @p v
|
||||
**/
|
||||
virtual gp<Object> * establish_var(bp<Variable> v) = 0;
|
||||
|
||||
//gp<Object> lookup_symbol(const std::string & name) const;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
/** @file ExpressionBoxed.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/alloc/Object.hpp"
|
||||
#include "xo/expression/Expression.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** @class ExpressionBoxed
|
||||
* @brief xo::scm::Expression, adapted to xo::Object interface
|
||||
**/
|
||||
class ExpressionBoxed : public Object {
|
||||
public:
|
||||
explicit ExpressionBoxed(bp<Expression> c);
|
||||
|
||||
/** create boxed version of @p c, using allocator @p mm **/
|
||||
static gp<ExpressionBoxed> make(gc::IAlloc * mm,
|
||||
bp<Expression> c);
|
||||
|
||||
/** runtime downcast **/
|
||||
static gp<ExpressionBoxed> from(gp<Object> x) {
|
||||
return gp<ExpressionBoxed>::from(x);
|
||||
}
|
||||
|
||||
const rp<Expression> & contents() const { return contents_; }
|
||||
|
||||
// inherited from Object
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy(gc::IAlloc * mm) const final override;
|
||||
virtual std::size_t _forward_children(gc::IAlloc * /*gc*/) final override;
|
||||
|
||||
private:
|
||||
/** reference-counted Expression pointer
|
||||
*
|
||||
* NOTE correctness requires finalization support in xo::gc::GC
|
||||
**/
|
||||
rp<Expression> contents_;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end ExpressionBoxed.hpp */
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/** @file GlobalEnv.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Env.hpp"
|
||||
#include "xo/allocutil/IAlloc.hpp"
|
||||
#include "xo/expression/GlobalSymtab.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** @class GlobalEnv
|
||||
* @brief Top-level global environment
|
||||
**/
|
||||
class GlobalEnv : public Env {
|
||||
public:
|
||||
using map_type = std::map<std::string, gp<Object>>;
|
||||
|
||||
public:
|
||||
/** Create top-level global environment, allocating via @p mm.
|
||||
* Expect one of these per interpreter session.
|
||||
**/
|
||||
static gp<GlobalEnv> make_empty(gc::IAlloc * mm,
|
||||
const rp<GlobalSymtab> & symtab);
|
||||
|
||||
#ifdef NOT_USING
|
||||
gc::IAlloc * get_mm() const { return mm_; }
|
||||
#endif
|
||||
|
||||
const rp<GlobalSymtab> & symtab() const { return symtab_; }
|
||||
|
||||
// inherited from Env..
|
||||
virtual bool local_contains_var(const std::string & vname) const final override;
|
||||
virtual gp<Object> * lookup_slot(const std::string & vname) final override;
|
||||
virtual gp<Object> * establish_var(bp<Variable> var) final override;
|
||||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy(gc::IAlloc * mm) const final override;
|
||||
virtual std::size_t _forward_children(gc::IAlloc * mm) final override;
|
||||
|
||||
private:
|
||||
GlobalEnv(const GlobalEnv & x);
|
||||
GlobalEnv(gc::IAlloc * mm, const rp<GlobalSymtab> & symtab);
|
||||
|
||||
private:
|
||||
/** memory manager to use **/
|
||||
gc::IAlloc * mm_ = nullptr;
|
||||
|
||||
/** global symbol table.
|
||||
* variables known to @c symtab_ are represented by
|
||||
* corresponding values in @p slot_map_
|
||||
**/
|
||||
rp<GlobalSymtab> symtab_;
|
||||
|
||||
/** environment contents.
|
||||
* expression @c symtab_->lookup_binding(vname)
|
||||
* has associated value @c slot_map_.at(vname)
|
||||
*
|
||||
* TODO: replace with something subject to GC ?
|
||||
* every member of @ref slot_map_ will have to be a
|
||||
* GC root
|
||||
*
|
||||
* TODO: probably want to hash here instead.
|
||||
* May also want lhs names to be separately hashed symbols
|
||||
**/
|
||||
up<map_type> slot_map_;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GlobalEnv.hpp */
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
/** @file LocalEnv.hpp **/
|
||||
|
||||
#include "Env.hpp"
|
||||
#include "CVector.hpp"
|
||||
#include "xo/allocutil/IAlloc.hpp"
|
||||
#include "xo/expression/LocalSymtab.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** @class LocalEnv
|
||||
* @brief Represent a single runtime stack frame for a Schematika function
|
||||
*
|
||||
* LocalEnv intended to be used for interpreted functions.
|
||||
*
|
||||
* Compiled functions will still likely have stack frames, but need not use the
|
||||
* @ref LocalEnv class
|
||||
*
|
||||
* memory layout:
|
||||
* ^
|
||||
* +-----------------------+ |
|
||||
* | vtable | |
|
||||
* +-----------------------+ |
|
||||
* | .parent +------/
|
||||
* +------------+----------+
|
||||
* | .slot_v_ | .n_ |
|
||||
* | +----------+
|
||||
* | | .v_ +------\
|
||||
* +------------+----------+ <--/
|
||||
* | .v_[0] +---------> Object(1)
|
||||
* +-----------------------+
|
||||
* . .. .
|
||||
* +-----------------------+
|
||||
* | .v_[.n_-1] +---------> Object(n)
|
||||
* +-----------------------+
|
||||
**/
|
||||
class LocalEnv : public Env {
|
||||
public:
|
||||
using TaggedPtr = xo::reflect::TaggedPtr;
|
||||
|
||||
public:
|
||||
LocalEnv(gc::IAlloc * mm, gp<LocalEnv> p, const rp<LocalSymtab> & s, std::size_t n);
|
||||
|
||||
/** create frame using allocator @p mm,
|
||||
* with parent @p p and exactly @p n_slot object pointers.
|
||||
* variable types are taken from symbol table @p s.
|
||||
**/
|
||||
static gp<LocalEnv> make(gc::IAlloc * mm,
|
||||
gp<LocalEnv> p,
|
||||
const rp<LocalSymtab> & s,
|
||||
std::size_t n_slot);
|
||||
|
||||
/** reflect LocalEnv object representation **/
|
||||
static void reflect_self();
|
||||
|
||||
gp<LocalEnv> parent() const { return parent_; }
|
||||
std::size_t size() const { return slot_v_.size(); }
|
||||
|
||||
gp<Object> operator[](std::size_t i) const { return slot_v_[i]; }
|
||||
gp<Object> & operator[](std::size_t i) { return slot_v_[i]; }
|
||||
|
||||
// inherited from Env..
|
||||
|
||||
virtual bool local_contains_var(const std::string & vname) const final override;
|
||||
|
||||
virtual gp<Object> * lookup_slot(const std::string & vname) final override;
|
||||
|
||||
/** LocalEnv policy is that variable can be established once only.
|
||||
* For example function arguments must all have distinct names.
|
||||
**/
|
||||
virtual gp<Object> * establish_var(bp<Variable> v) final override;
|
||||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy(gc::IAlloc * mm) const final override;
|
||||
virtual std::size_t _forward_children(gc::IAlloc * /*gc*/) final override;
|
||||
|
||||
private:
|
||||
/** parent stack frame **/
|
||||
gp<LocalEnv> parent_;
|
||||
/** origin symbol table. records variable names and bindings.
|
||||
* for a binding path p with leaf slot index j = p.j_slot_:
|
||||
* @c slot_v_[j] holds value associated with variable @c symtab_->argv_[j]
|
||||
**/
|
||||
rp<LocalSymtab> symtab_;
|
||||
/** environment contents **/
|
||||
obj::CVector<gp<Object>> slot_v_;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end LocalEnv.hpp */
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/** @file Schematika.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/alloc/GC.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** @class Schematika
|
||||
* @brief schematika interpreter state
|
||||
**/
|
||||
class Schematika {
|
||||
public:
|
||||
class Impl;
|
||||
|
||||
struct Config {
|
||||
/** true to enable welcome message **/
|
||||
bool welcome_flag_ = true;
|
||||
/** number of command history items to preserve **/
|
||||
std::size_t history_size = 100;
|
||||
/** on startup: load command history from this file;
|
||||
persist last @ref history_size commands to the same file
|
||||
**/
|
||||
std::string history_file = "scm_history.txt";
|
||||
/** when true enable console logging for repl internals **/
|
||||
bool debug_flag = false;
|
||||
|
||||
/** garbage collector configuration **/
|
||||
gc::Config gc_config_;
|
||||
|
||||
/** control schematika vsm logging **/
|
||||
log_level vsm_log_level_;
|
||||
};
|
||||
|
||||
using IAlloc = xo::gc::IAlloc;
|
||||
|
||||
public:
|
||||
~Schematika();
|
||||
|
||||
/** create instance with configuration @p cfg **/
|
||||
static Schematika make(const Config & cfg);
|
||||
|
||||
/** interactive read-eval-print loop.
|
||||
* Uses replxx to read from stdin.
|
||||
* If stdin is interactive, accepts line editing commands:
|
||||
* - ctrl-a goto beginning of line
|
||||
* - ctrl-e goto end of line
|
||||
* - ctrl-k delete to end of line
|
||||
* - meta-<bs> backwards delete word
|
||||
* - meta-p|<up> retrieve previous command from history
|
||||
* - meta-n|<down> retrieve next command from history
|
||||
* - <pgup>/<pgdown> page through history faster
|
||||
* - ctrl-s forward history search
|
||||
* - ctrl-r backward history search
|
||||
**/
|
||||
void interactive_repl();
|
||||
|
||||
private:
|
||||
explicit Schematika(const Config & cfg);
|
||||
|
||||
private:
|
||||
up<Impl> p_impl_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* end Schematika.hpp */
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
/** @file SchematikaError.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
class SchematikaError {
|
||||
public:
|
||||
SchematikaError() = default;
|
||||
explicit SchematikaError(std::string x) : what_{std::move(x)} {}
|
||||
|
||||
const std::string & what() const { return what_; }
|
||||
|
||||
bool is_error() const { return !what_.empty(); }
|
||||
bool is_not_an_error() const { return what_.empty(); }
|
||||
|
||||
private:
|
||||
std::string what_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* end SchematikaError.hpp */
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
/** @file VirtualSchematikaMachine.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VsmInstr.hpp"
|
||||
#include "VsmStackFrame.hpp"
|
||||
#include "SchematikaError.hpp"
|
||||
#include "GlobalEnv.hpp"
|
||||
#include "xo/expression/Expression.hpp"
|
||||
#include "xo/object/ObjectConverter.hpp"
|
||||
#include "xo/alloc/Object.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** @brief state that may be shared across VirtualSchematikaMachine instances **/
|
||||
struct VirtualSchematikaMachineFlyweight {
|
||||
explicit VirtualSchematikaMachineFlyweight(gc::IAlloc * mm,
|
||||
gp<GlobalEnv> env,
|
||||
log_level log_level);
|
||||
|
||||
/** memory allocator for interpreter operation. **/
|
||||
gc::IAlloc * object_mm_ = nullptr;
|
||||
/** global environment **/
|
||||
gp<GlobalEnv> toplevel_env_;
|
||||
/** convert TaggedPtr->Object **/
|
||||
xo::obj::ObjectConverter object_converter_;
|
||||
/** control logging level. higher values -> more logging **/
|
||||
log_level log_level_;
|
||||
};
|
||||
|
||||
/** @class VirtualSchematikaMachine
|
||||
* @brief Virtual machine implementing a Schematika interpreter
|
||||
*
|
||||
**/
|
||||
class VirtualSchematikaMachine {
|
||||
public:
|
||||
using IAlloc = xo::gc::IAlloc;
|
||||
|
||||
public:
|
||||
VirtualSchematikaMachine(IAlloc * mm, gp<GlobalEnv> toplevel_env, log_level log_level);
|
||||
~VirtualSchematikaMachine();
|
||||
|
||||
gp<GlobalEnv> toplevel_env() const { return flyweight_.toplevel_env_; }
|
||||
|
||||
/** evaluate expression @p expr.
|
||||
* borrows calling thread until completion
|
||||
* return [value, error]. error ignored unless value is nullptr.
|
||||
* conversely when value is nullptr, error gives details of original
|
||||
* error.
|
||||
*
|
||||
* Evaluate schematika expression @p expr in environment @p env
|
||||
**/
|
||||
std::pair<gp<Object>, SchematikaError> eval(bp<Expression> expr, gp<GlobalEnv> env);
|
||||
|
||||
/** evaluate expression @p expr in toplevel environment **/
|
||||
std::pair<gp<Object>, SchematikaError> toplevel_eval(bp<Expression> expr);
|
||||
|
||||
private:
|
||||
/** Not moveable or copyable.
|
||||
* One constraint is member variables added to flyweight_.object_mm_
|
||||
* as GC roots, with no provision for unwinding later.
|
||||
**/
|
||||
VirtualSchematikaMachine(const VirtualSchematikaMachine &) = delete;
|
||||
VirtualSchematikaMachine(VirtualSchematikaMachine &&) = delete;
|
||||
|
||||
/** borrow calling thread to run schematika machine
|
||||
* indefinitely, or until null continuation
|
||||
**/
|
||||
void run();
|
||||
|
||||
/** execute vsm instruction in program counter.
|
||||
* Note: may possibly be able to replace with just opcode
|
||||
*
|
||||
* Registers:
|
||||
* - expr_ input, caller saves
|
||||
* - env_ input, caller saves
|
||||
* - cont_ input, caller saves
|
||||
* - stack_ input, caller saves
|
||||
* - value_ output
|
||||
* - error_ output
|
||||
*
|
||||
**/
|
||||
void execute_one();
|
||||
|
||||
/* design note:
|
||||
* - eval_xxx_op() methods are primary VSM transitions,
|
||||
* in the sense that they begin a sequence of transitions to interpret a
|
||||
* particular kind of expression
|
||||
* - do_xxx_op() methods represent secondary VSM transitions,
|
||||
* that continue an instruction sequence that was initiated by a preceding
|
||||
* eval_xxx_op() method
|
||||
*/
|
||||
|
||||
/** interpret literal constant expression **/
|
||||
void eval_constant_op();
|
||||
|
||||
/** interpreter literal primitive expression
|
||||
* (these appear implicitly as result of builtin operators like {+, ==, ..})
|
||||
**/
|
||||
void eval_primitive_op();
|
||||
|
||||
/** execute define expression (finished in do_complete_assign_op()) **/
|
||||
void eval_define_op();
|
||||
/** execute assign expression (finishes in do_complete_assign_op()) **/
|
||||
void eval_assign_op();
|
||||
/** continue after establishing value fo rhs of define exprsssion **/
|
||||
void do_complete_assign_op();
|
||||
|
||||
/** interpret variable expression **/
|
||||
void eval_variable_op();
|
||||
|
||||
/** interpret if-expression **/
|
||||
void eval_ifexpr_op();
|
||||
/** continue after establish value of test expression **/
|
||||
void do_complete_ifexpr_op();
|
||||
|
||||
/** interpret sequence **/
|
||||
void eval_sequence_op();
|
||||
/** continue after establishing value for a sequence element **/
|
||||
void do_complete_sequence_op();
|
||||
|
||||
/** interpret apply-expression (i.e. function call) **/
|
||||
void eval_apply_op();
|
||||
/** continue assembling args for a function call;
|
||||
* transition to (interpretation of) called function once all arguments
|
||||
* are evaluated.
|
||||
**/
|
||||
void do_complete_evalargs_op();
|
||||
|
||||
/** execute function application, given actuals in top stack frame **/
|
||||
void apply_op();
|
||||
|
||||
/** goto error state with message @p err **/
|
||||
void report_error(const std::string & err);
|
||||
|
||||
/** implementation class; contains instruction implementations **/
|
||||
friend struct VsmOps;
|
||||
|
||||
private:
|
||||
/** program counter.
|
||||
* (Perhaps replace with VsmInstr::Opcode ?)
|
||||
**/
|
||||
const VsmInstr * pc_ = nullptr;
|
||||
|
||||
/** register to hold Schematika expression to drive @ref execute_one.
|
||||
*
|
||||
* caller saves!
|
||||
**/
|
||||
rp<Expression> expr_;
|
||||
/** holds bindings for all schematika variables, to drive @ref execute_one.
|
||||
* execute_one will not save this
|
||||
*
|
||||
* caller saves!
|
||||
**/
|
||||
gp<GlobalEnv> env_;
|
||||
|
||||
/** vsm stack. callee saves!
|
||||
**/
|
||||
gp<VsmStackFrame> stack_;
|
||||
|
||||
/** non-error result value from eval() / apply()
|
||||
*
|
||||
* output register: caller must save
|
||||
**/
|
||||
gp<Object> value_;
|
||||
|
||||
/** error result value from eval() / apply()
|
||||
*
|
||||
* output regisetr: caller must save
|
||||
**/
|
||||
SchematikaError error_;
|
||||
|
||||
/** continuation
|
||||
* (Perhaps replace with VsmInstr::Opcode ?)
|
||||
*
|
||||
* input register: callee saves!
|
||||
**/
|
||||
const VsmInstr * cont_ = nullptr;
|
||||
|
||||
/** possibly-shared data **/
|
||||
VirtualSchematikaMachineFlyweight flyweight_;
|
||||
};
|
||||
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end VirtualSchematikaMachine.hpp */
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/** @file VsmInstr.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
class VirtualSchematikaMachine; // see VirtualSchematikaMachine.hpp
|
||||
|
||||
/** @class VmInstr
|
||||
* @brief Represent a particular vritual schematika machine instruction
|
||||
*
|
||||
* A vsm instruction acts on a virtual schematika machine instance.
|
||||
**/
|
||||
class VsmInstr
|
||||
{
|
||||
public:
|
||||
enum class Opcode {
|
||||
/** Halt virtual schematika machine **/
|
||||
halt,
|
||||
|
||||
/** Evaluate a schematika expression.
|
||||
* See VirtualSchematikaMachine::eval()
|
||||
**/
|
||||
eval,
|
||||
|
||||
/** assign to variable + continue
|
||||
*
|
||||
* stack: frame with:
|
||||
* [0] lhs : variable to assign
|
||||
**/
|
||||
complete_assign,
|
||||
|
||||
/** execute ifexpr branch, given
|
||||
* result of test expression has been established
|
||||
**/
|
||||
complete_ifexpr,
|
||||
|
||||
/** execute remainder of expression sequence
|
||||
**/
|
||||
complete_sequence,
|
||||
|
||||
/** execute remainder of argument sequence evaluation;
|
||||
* subsidiary to marshalling a function call
|
||||
**/
|
||||
complete_evalargs,
|
||||
|
||||
/** Call a function. Arguments have been evaluated
|
||||
* and are in top stack frame, in order,
|
||||
* starting with target function itself
|
||||
**/
|
||||
apply,
|
||||
|
||||
/** choose branch of if-expression + continue
|
||||
*
|
||||
* stack: frame with
|
||||
* [0] ifexpr : original if-expression
|
||||
**/
|
||||
N_Opcode
|
||||
};
|
||||
|
||||
//using ActionFn = void (*)(VirtualSchematikaMachine * vm);
|
||||
|
||||
public:
|
||||
VsmInstr(Opcode opcode, std::string_view name);
|
||||
|
||||
Opcode opcode() const { return opcode_; }
|
||||
|
||||
private:
|
||||
/** unique opcode for this instruction **/
|
||||
Opcode opcode_;
|
||||
/** **/
|
||||
std::string_view name_;
|
||||
//ActionFn action_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/* end VsmInstr.hpp */
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/** @file VsmStackFrame.hpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VsmInstr.hpp"
|
||||
#include "xo/object/CVector.hpp"
|
||||
#include "xo/alloc/Object.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** @class VsmStackFrame
|
||||
* @brief Virtual Schematika Machine stack frame
|
||||
*
|
||||
* Intending to use the "cheney on the MTA" strategy,
|
||||
* i.e. allocate frames using GC's bump allocator.
|
||||
*
|
||||
* Parallels LocalEnv, but VSM implementation isn't reflected
|
||||
**/
|
||||
class VsmStackFrame : public Object {
|
||||
public:
|
||||
VsmStackFrame(gc::IAlloc * mm, gp<VsmStackFrame> p, std::size_t n, const VsmInstr * cont);
|
||||
|
||||
/** create frame using allocator @p mm,
|
||||
* with parent @p p and exactly @p n_slot object pointers.
|
||||
**/
|
||||
static gp<VsmStackFrame> make(gc::IAlloc * mm,
|
||||
gp<VsmStackFrame> p,
|
||||
std::size_t n_slot,
|
||||
const VsmInstr * cont);
|
||||
|
||||
/** create new stack frame using allocator @p mm,
|
||||
* with parent frame @p p; new frame contains values @p s0
|
||||
**/
|
||||
static gp<VsmStackFrame> push1(gc::IAlloc * mm,
|
||||
gp<VsmStackFrame> p,
|
||||
gp<Object> s0,
|
||||
const VsmInstr * cont);
|
||||
|
||||
/** create new stack frame using allocator @p mm,
|
||||
* with parent frame @p p; new frame contains values @p s0, @p s1
|
||||
**/
|
||||
static gp<VsmStackFrame> push2(gc::IAlloc * mm,
|
||||
gp<VsmStackFrame> p,
|
||||
gp<Object> s0,
|
||||
gp<Object> s1,
|
||||
const VsmInstr * cont);
|
||||
|
||||
|
||||
/** reflect VsmStackFrame object representation **/
|
||||
static void reflect_self();
|
||||
|
||||
gp<VsmStackFrame> parent() const { return parent_; }
|
||||
std::size_t size() const { return slot_v_.size(); }
|
||||
const obj::CVector<gp<Object>> & argv() const { return slot_v_; }
|
||||
const VsmInstr * continuation() const { return cont_; }
|
||||
|
||||
gp<Object> operator[](std::size_t i) const { return slot_v_[i]; }
|
||||
gp<Object> & operator[](std::size_t i) { return slot_v_[i]; }
|
||||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy(gc::IAlloc *) const final override;
|
||||
virtual std::size_t _forward_children(gc::IAlloc *) final override;
|
||||
|
||||
private:
|
||||
/** parent stack frame **/
|
||||
gp<VsmStackFrame> parent_;
|
||||
|
||||
/** stored state **/
|
||||
obj::CVector<gp<Object>> slot_v_;
|
||||
|
||||
/** proceed to this continuation when popping this frame **/
|
||||
const VsmInstr * cont_ = nullptr;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end VsmStackFrame.hpp */
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
/** @file init_interpreter.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/subsys/Subsystem.hpp"
|
||||
|
||||
namespace xo {
|
||||
/* tag to represent the interpreter/ subsystem in ordered initialization */
|
||||
enum S_interpreter_tag {};
|
||||
|
||||
template<>
|
||||
struct InitSubsys<S_interpreter_tag> {
|
||||
static void init();
|
||||
static InitEvidence require();
|
||||
};
|
||||
}
|
||||
|
||||
/* end init_interpreter.hpp */
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/** @file BuiltinPrimitives.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#include "BuiltinPrimitives.hpp"
|
||||
#include "Integer.hpp"
|
||||
#include "Primitive.hpp"
|
||||
#include "xo/expression/PrimitiveExpr.hpp"
|
||||
#include "xo/object/ObjectConversion.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::Reflect;
|
||||
using xo::reflect::TaggedPtr;
|
||||
using xo::reflect::TypeDescr;
|
||||
|
||||
namespace scm {
|
||||
int64_t
|
||||
add64(int64_t x, int64_t y)
|
||||
{
|
||||
return x + y;
|
||||
}
|
||||
|
||||
void
|
||||
BuiltinPrimitives::install(gc::IAlloc * mm, gp<GlobalEnv> env)
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
// add(x,y)
|
||||
{
|
||||
gp<Object> rhs = xo::obj::make_primitive(mm, "add", add64);
|
||||
|
||||
TypeDescr td = Reflect::require_function<decltype(&add64)>();
|
||||
|
||||
rp<Variable> lhs = Variable::make("add", td);
|
||||
gp<Object> * addr = env->establish_var(lhs.borrow());
|
||||
|
||||
*addr = rhs;
|
||||
}
|
||||
|
||||
// i64 comparisons
|
||||
|
||||
// @cmp_eq2_i64
|
||||
install_pm(mm, PrimitiveExpr_cmp_i64::make_cmp_eq2_i64(), env);
|
||||
|
||||
// @cmp_ne2_i64
|
||||
install_pm(mm, PrimitiveExpr_cmp_i64::make_cmp_ne2_i64(), env);
|
||||
|
||||
// @cmp_lt2_i64
|
||||
install_pm(mm, PrimitiveExpr_cmp_i64::make_cmp_lt2_i64(), env);
|
||||
|
||||
// @cmp_le2_i64
|
||||
install_pm(mm, PrimitiveExpr_cmp_i64::make_cmp_le2_i64(), env);
|
||||
|
||||
// @cmp_gt2_i64
|
||||
install_pm(mm, PrimitiveExpr_cmp_i64::make_cmp_gt2_i64(), env);
|
||||
|
||||
// @cmp_ge2_i64
|
||||
install_pm(mm, PrimitiveExpr_cmp_i64::make_cmp_ge2_i64(), env);
|
||||
|
||||
// i64 arithmetic
|
||||
|
||||
// @add2_i64
|
||||
install_pm(mm, PrimitiveExpr_i64::make_add2_i64(), env);
|
||||
|
||||
// @sub2_i64
|
||||
install_pm(mm, PrimitiveExpr_i64::make_sub2_i64(), env);
|
||||
|
||||
// @mul2_i64
|
||||
install_pm(mm, PrimitiveExpr_i64::make_mul2_i64(), env);
|
||||
|
||||
// @div2_i64
|
||||
install_pm(mm, PrimitiveExpr_i64::make_div2_i64(), env);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// @add2_f64
|
||||
install_pm(mm, PrimitiveExpr_f64::make_add2_f64(), env);
|
||||
|
||||
// @sub2_f64
|
||||
install_pm(mm, PrimitiveExpr_f64::make_sub2_f64(), env);
|
||||
|
||||
// @mul2_f64
|
||||
install_pm(mm, PrimitiveExpr_f64::make_mul2_f64(), env);
|
||||
|
||||
// @div2_f64
|
||||
install_pm(mm, PrimitiveExpr_f64::make_div2_f64(), env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* end BuiltinPrimitives.cpp */
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
# interpreter/CMakeLists.txt
|
||||
|
||||
set(SELF_LIB xo_interpreter)
|
||||
set(SELF_SRCS
|
||||
init_interpreter.cpp
|
||||
Schematika.cpp
|
||||
BuiltinPrimitives.cpp
|
||||
LocalEnv.cpp
|
||||
GlobalEnv.cpp
|
||||
VirtualSchematikaMachine.cpp
|
||||
VsmInstr.cpp
|
||||
VsmStackFrame.cpp
|
||||
ExpressionBoxed.cpp
|
||||
)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
|
||||
xo_dependency(${SELF_LIB} xo_object)
|
||||
xo_dependency(${SELF_LIB} xo_expression)
|
||||
xo_dependency(${SELF_LIB} xo_reader)
|
||||
xo_external_target_dependency(${SELF_LIB} replxx replxx::replxx)
|
||||
target_link_libraries(${SELF_LIB} PUBLIC Threads::Threads)
|
||||
xo_headeronly_dependency(${SELF_LIB} subsys)
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
/** @file ExpressionBoxed.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#include "ExpressionBoxed.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::Reflect;
|
||||
using xo::reflect::TaggedPtr;
|
||||
|
||||
namespace scm {
|
||||
ExpressionBoxed::ExpressionBoxed(bp<Expression> c) : contents_{c.promote()}
|
||||
{}
|
||||
|
||||
gp<ExpressionBoxed>
|
||||
ExpressionBoxed::make(gc::IAlloc * mm,
|
||||
bp<Expression> c)
|
||||
{
|
||||
return new (MMPtr(mm)) ExpressionBoxed(c);
|
||||
}
|
||||
|
||||
|
||||
TaggedPtr
|
||||
ExpressionBoxed::self_tp() const
|
||||
{
|
||||
return Reflect::make_tp(const_cast<ExpressionBoxed *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
ExpressionBoxed::display(std::ostream & os) const
|
||||
{
|
||||
os << contents_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ExpressionBoxed::_shallow_size() const
|
||||
{
|
||||
return sizeof(ExpressionBoxed);
|
||||
}
|
||||
|
||||
Object *
|
||||
ExpressionBoxed::_shallow_copy(gc::IAlloc * mm) const
|
||||
{
|
||||
Cpof cpof(mm, this);
|
||||
|
||||
return new (cpof) ExpressionBoxed(*this);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ExpressionBoxed::_forward_children(gc::IAlloc *)
|
||||
{
|
||||
return _shallow_size();
|
||||
}
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end ExpressionBoxed.cpp */
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/** @file GlobalEnv.cpp **/
|
||||
|
||||
#include "GlobalEnv.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::Reflect;
|
||||
using xo::reflect::TaggedPtr;
|
||||
|
||||
namespace scm {
|
||||
gp<GlobalEnv>
|
||||
GlobalEnv::make_empty(gc::IAlloc * mm, const rp<GlobalSymtab> & symtab)
|
||||
{
|
||||
/* by design: GlobalEnv and GlobalEnv.slot_map_ are heap-allocated */
|
||||
|
||||
return new GlobalEnv(mm, symtab);
|
||||
}
|
||||
|
||||
GlobalEnv::GlobalEnv(const GlobalEnv & x)
|
||||
: mm_{x.mm_},
|
||||
symtab_{x.symtab_},
|
||||
slot_map_{std::make_unique<map_type>(*x.slot_map_)}
|
||||
{
|
||||
}
|
||||
|
||||
GlobalEnv::GlobalEnv(gc::IAlloc * mm,
|
||||
const rp<GlobalSymtab> & symtab) : mm_{mm},
|
||||
symtab_{symtab},
|
||||
slot_map_{std::make_unique<map_type>()}
|
||||
{}
|
||||
|
||||
bool
|
||||
GlobalEnv::local_contains_var(const std::string & vname) const
|
||||
{
|
||||
return symtab_->lookup_local(vname).get();
|
||||
}
|
||||
|
||||
gp<Object> *
|
||||
GlobalEnv::lookup_slot(const std::string & vname)
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("name", vname));
|
||||
|
||||
assert(slot_map_.get());
|
||||
|
||||
auto ix = slot_map_->find(vname);
|
||||
|
||||
if (ix == slot_map_->end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
log && log("binding found", xtag("vname", vname));
|
||||
return &(ix->second);
|
||||
}
|
||||
}
|
||||
|
||||
gp<Object> *
|
||||
GlobalEnv::establish_var(bp<Variable> var)
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("name", var->name()), xtag("type", var->valuetype()));
|
||||
|
||||
// Warning: altering declared type for an already-existing variable
|
||||
// invalidates any type checking that relied on that variable.
|
||||
//
|
||||
// Ignoring this problem for now.
|
||||
//
|
||||
// Actual solution might look like:
|
||||
// - keep track of which functions/defs depend on each global variable.
|
||||
// - invalidate any jit / types for such variables.
|
||||
// - maybe use seqno's to track
|
||||
// - re-check / re-complie
|
||||
// - need to admit invalid states.
|
||||
// suppose have mutually recursive functions f(), g()
|
||||
// want ability to modify type signatures separately
|
||||
//
|
||||
// Alternatives:
|
||||
// - forbid changing type of an already-established variable
|
||||
// Actually: can't even change values if we intend supporting dependent types
|
||||
// - quietly number variables so new definitions shadow old ones but don't
|
||||
// affect previously-encountered expressions
|
||||
|
||||
this->symtab_->require_global(var->name(), var);
|
||||
|
||||
gp<Object> &slot = (*this->slot_map_)[var->name()];
|
||||
|
||||
/* discard any pre-existing value, we're redefining a variable */
|
||||
slot = gp<Object>();
|
||||
|
||||
return &slot;
|
||||
}
|
||||
|
||||
TaggedPtr
|
||||
GlobalEnv::self_tp() const
|
||||
{
|
||||
return Reflect::make_tp(const_cast<GlobalEnv *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
GlobalEnv::display(std::ostream & os) const
|
||||
{
|
||||
os << "<global-env" << xtag("n", slot_map_->size()) << ">";
|
||||
}
|
||||
|
||||
std::size_t
|
||||
GlobalEnv::_shallow_size() const
|
||||
{
|
||||
return sizeof(GlobalEnv);
|
||||
}
|
||||
|
||||
Object *
|
||||
GlobalEnv::_shallow_copy(gc::IAlloc * mm) const
|
||||
{
|
||||
Cpof cpof(mm, this);
|
||||
|
||||
return new (cpof) GlobalEnv(*this);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
GlobalEnv::_forward_children(gc::IAlloc * gc)
|
||||
{
|
||||
for (auto & ix : *slot_map_) {
|
||||
Object::_forward_inplace(ix.second, gc);
|
||||
}
|
||||
return _shallow_size();
|
||||
}
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GlobalEnv.cpp */
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
/** @file LocalEnv.cpp **/
|
||||
|
||||
#include "LocalEnv.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
#include "xo/reflect/StructReflector.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::Reflect;
|
||||
using xo::reflect::StructReflector;
|
||||
using xo::reflect::TypeDescrW;
|
||||
using xo::reflect::TaggedPtr;
|
||||
using xo::reflect::TypeDescrExtra;
|
||||
using xo::reflect::EstablishTypeDescr;
|
||||
using xo::reflect::StlVectorTdx;
|
||||
using xo::print::quot;
|
||||
|
||||
namespace scm {
|
||||
namespace {
|
||||
std::size_t
|
||||
slot_array_size(std::size_t n) {
|
||||
return n * sizeof(gp<Object>);
|
||||
}
|
||||
}
|
||||
|
||||
gp<LocalEnv>
|
||||
LocalEnv::make(gc::IAlloc * mm,
|
||||
gp<LocalEnv> p,
|
||||
const rp<LocalSymtab> & s,
|
||||
std::size_t n)
|
||||
{
|
||||
if (s) {
|
||||
assert(static_cast<int>(n) == s->n_arg());
|
||||
}
|
||||
|
||||
return new (MMPtr(mm)) LocalEnv(mm, p, s, n);
|
||||
}
|
||||
|
||||
LocalEnv::LocalEnv(gc::IAlloc * mm,
|
||||
gp<LocalEnv> p,
|
||||
const rp<LocalSymtab> & s,
|
||||
std::size_t n) : parent_{p},
|
||||
symtab_{s},
|
||||
slot_v_{mm, n}
|
||||
{}
|
||||
|
||||
bool
|
||||
LocalEnv::local_contains_var(const std::string & vname) const
|
||||
{
|
||||
assert(symtab_.get());
|
||||
|
||||
return symtab_->lookup_local(vname);
|
||||
}
|
||||
|
||||
gp<Object> *
|
||||
LocalEnv::lookup_slot(const std::string & vname)
|
||||
{
|
||||
binding_path b = symtab_->lookup_local_binding(vname);
|
||||
|
||||
if (b.i_link_ == 0) {
|
||||
assert((b.j_slot_ >= 0) && (static_cast<size_t>(b.j_slot_) < slot_v_.size()));
|
||||
|
||||
return &(slot_v_[b.j_slot_]);
|
||||
}
|
||||
|
||||
if (parent_.get()) {
|
||||
return parent_->lookup_slot(vname);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gp<Object> *
|
||||
LocalEnv::establish_var(bp<Variable> v)
|
||||
{
|
||||
assert(v);
|
||||
|
||||
throw std::runtime_error(tostr("LocalEnv::establish_var:"
|
||||
" inserting new variables not supported for LocalEnv",
|
||||
xtag("v.name", v->name())));
|
||||
}
|
||||
|
||||
TaggedPtr
|
||||
LocalEnv::self_tp() const
|
||||
{
|
||||
return Reflect::make_tp(const_cast<LocalEnv *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
LocalEnv::display(std::ostream & os) const
|
||||
{
|
||||
os << "<local-env"
|
||||
<< xtag("n", slot_v_.size());
|
||||
|
||||
#ifdef NOT_YET
|
||||
for (std::size_t i = 0, n = n_slot(); i < n; ++i) {
|
||||
char buf[24];
|
||||
snprintf(buf, sizeof(buf), "v[%lu]", i);
|
||||
|
||||
os << xtag(buf, lookup(i));
|
||||
}
|
||||
#endif
|
||||
|
||||
os << ">";
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LocalEnv::_shallow_size() const
|
||||
{
|
||||
std::size_t retval = sizeof(LocalEnv);
|
||||
|
||||
retval += gc::IAlloc::with_padding(slot_array_size(slot_v_.size()));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
Object *
|
||||
LocalEnv::_shallow_copy(gc::IAlloc * mm) const
|
||||
{
|
||||
Cpof cpof(mm, this);
|
||||
|
||||
size_t z = this->size();
|
||||
|
||||
LocalEnv * copy = new (cpof) LocalEnv(cpof.mm_, parent_, symtab_, z);
|
||||
|
||||
void * v_dest = copy->slot_v_.v_;
|
||||
|
||||
if (slot_v_.v_) {
|
||||
::memcpy(v_dest, slot_v_.v_, slot_array_size(z));
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
for (size_t i = 0, n = n_slot_; i < n; ++i) {
|
||||
copy->v_[i] = v_[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LocalEnv::_forward_children(gc::IAlloc * gc)
|
||||
{
|
||||
static_assert(decltype(symtab_)::is_gc_ptr == false);
|
||||
|
||||
Object::_forward_inplace(parent_, gc);
|
||||
// Object::_forward_inplace(symtab_); // not a gp yet
|
||||
for (std::size_t i = 0, n = slot_v_.size(); i < n; ++i) {
|
||||
Object::_forward_inplace((*this)[i], gc);
|
||||
}
|
||||
|
||||
return _shallow_size();
|
||||
}
|
||||
|
||||
void
|
||||
LocalEnv::reflect_self()
|
||||
{
|
||||
StructReflector<LocalEnv> sr;
|
||||
|
||||
if (sr.is_incomplete()) {
|
||||
/* reflect CVector<gp<Object>>
|
||||
*
|
||||
* note: placement here works b/c CVector<T> not used anywhere else
|
||||
*/
|
||||
using VectorType = obj::CVector<gp<Object>>;
|
||||
|
||||
/* custom reflection for array of Object pointers.
|
||||
* Can use StlVectorTdx here, treating CVector<T> as a vector
|
||||
* via .size() and .operator[] members
|
||||
*/
|
||||
std::unique_ptr<TypeDescrExtra> tdx1
|
||||
= std::make_unique<StlVectorTdx<VectorType>>();
|
||||
TypeDescrW td1
|
||||
= EstablishTypeDescr::establish<VectorType>();
|
||||
td1->assign_tdextra(Reflect::get_final_invoker<VectorType>(),
|
||||
std::move(tdx1));
|
||||
|
||||
REFLECT_MEMBER(sr, parent);
|
||||
REFLECT_MEMBER(sr, slot_v);
|
||||
}
|
||||
}
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end LocalEnv.cpp */
|
||||
|
|
@ -1,284 +0,0 @@
|
|||
/** @file Schematika.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#include "Schematika.hpp"
|
||||
#include "VirtualSchematikaMachine.hpp"
|
||||
#include "BuiltinPrimitives.hpp"
|
||||
#include "GlobalEnv.hpp"
|
||||
#include "xo/reader/reader.hpp"
|
||||
#include <replxx.hxx>
|
||||
#include <ostream>
|
||||
#include <unistd.h> // for STDIN_FILENO on OSX
|
||||
|
||||
namespace xo {
|
||||
using xo::gc::IAlloc;
|
||||
using xo::gc::GC;
|
||||
using xo::print::ppconfig;
|
||||
using xo::print::ppstate_standalone;
|
||||
using replxx::Replxx;
|
||||
using namespace std;
|
||||
|
||||
namespace scm {
|
||||
|
||||
class Schematika::Impl {
|
||||
public:
|
||||
/** note: choosing to have Schemtika::Impl
|
||||
* rather than VirtualSchematikaMachine to own allocator
|
||||
* to preserve option to share it
|
||||
**/
|
||||
Impl(const Config & config, up<IAlloc> mm, gp<GlobalEnv> toplevel_env);
|
||||
~Impl();
|
||||
|
||||
/** create instance + allocator **/
|
||||
static up<Impl> make(const Config & cfg);
|
||||
|
||||
/** borrow calling thread to run interactive read-eval-print loop;
|
||||
* input from stdin, output to stdout.
|
||||
**/
|
||||
void interactive_repl();
|
||||
|
||||
void welcome(std::ostream & os);
|
||||
|
||||
/** get one line of input. prompt if @p interactive,
|
||||
* with prompt depending on @p parser_stack_size.
|
||||
* Use @p rx to perform line editing (when @p interactive).
|
||||
* Store completed line in @p input.
|
||||
**/
|
||||
bool replxx_getline(bool interactive,
|
||||
std::size_t parser_stack_size,
|
||||
replxx::Replxx & rx,
|
||||
std::string & input);
|
||||
|
||||
private:
|
||||
/** configuration **/
|
||||
Config config_;
|
||||
/** ownership for memory allocator / garbage collector;
|
||||
* @ref vsm_ holds naked pointer, so this could in principle be nullptr
|
||||
* in case want to maintain allocator ownership from outside.
|
||||
*
|
||||
* note: must appear before @ref vsm_, so latter gets destroyed first
|
||||
**/
|
||||
up<IAlloc> mm_;
|
||||
/** schematika interpreter **/
|
||||
VirtualSchematikaMachine vsm_;
|
||||
};
|
||||
|
||||
Schematika::Impl::Impl(const Config & config, up<IAlloc> mm, gp<GlobalEnv> toplevel_env) :
|
||||
config_{config},
|
||||
mm_{std::move(mm)},
|
||||
vsm_{mm_.get(), toplevel_env, config.vsm_log_level_}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Schematika::Impl::~Impl() = default;
|
||||
|
||||
up<Schematika::Impl>
|
||||
Schematika::Impl::make(const Config & cfg)
|
||||
{
|
||||
up<IAlloc> mm = GC::make(cfg.gc_config_);
|
||||
rp<GlobalSymtab> symtab = GlobalSymtab::make_empty();
|
||||
gp<GlobalEnv> env = GlobalEnv::make_empty(mm.get(), symtab);
|
||||
|
||||
/* also see VirtualSchematikaMachineFlyweight::Impl::Impl,
|
||||
* for BuiltinPrimitives::install_interpreter_conversions()
|
||||
*/
|
||||
BuiltinPrimitives::install(mm.get(), env);
|
||||
|
||||
return std::make_unique<Impl>(cfg, std::move(mm), env);
|
||||
}
|
||||
|
||||
void
|
||||
Schematika::Impl::welcome(std::ostream & os)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
os << "read-eval-print loop for schematika expressions" << endl;
|
||||
os << " ctrl-a/ctrl-e beginning/end of line" << endl;
|
||||
os << " ctrl-u delete entire line" << endl;
|
||||
os << " ctrl-k delete to end of line" << endl;
|
||||
os << " meta-<bs> backward delete word" << endl;
|
||||
os << " <up>|meta-p previous command from history" << endl;
|
||||
os << " <down>|meta-n next command from history" << endl;
|
||||
os << " <pgup>/<pgdown> page through history faster" << endl;
|
||||
os << " ctrl-s/ctrl-r forward/backward history search" << endl;
|
||||
os << endl;
|
||||
}
|
||||
|
||||
// similar helper in exprreplxx.cpp
|
||||
//
|
||||
bool
|
||||
Schematika::Impl::replxx_getline(bool interactive,
|
||||
std::size_t parser_stack_size,
|
||||
replxx::Replxx & rx,
|
||||
std::string & input)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
char const * prompt = "";
|
||||
|
||||
if (interactive) {
|
||||
if (parser_stack_size <= 1)
|
||||
prompt = "> ";
|
||||
else
|
||||
prompt = ". ";
|
||||
}
|
||||
|
||||
/* input_cstr: next line of input from replxx library */
|
||||
const char * input_cstr = rx.input(prompt);
|
||||
|
||||
bool retval = (input_cstr != nullptr);
|
||||
|
||||
if (retval) {
|
||||
/* got new input */
|
||||
input = input_cstr;
|
||||
}
|
||||
|
||||
rx.history_add(input);
|
||||
|
||||
input.push_back('\n');
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
Schematika::Impl::interactive_repl()
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
using span_type = xo::scm::span<const char>;
|
||||
|
||||
bool interactive = isatty(STDIN_FILENO);
|
||||
|
||||
Replxx rx;
|
||||
rx.set_max_history_size(config_.history_size);
|
||||
rx.history_load(config_.history_file);
|
||||
// rx.bind_key_internal(Replxx::KEY::control('p'), "history_previous");
|
||||
// rx.bind_key_internal(Replxx::KEY::control('n'), "history_next");
|
||||
|
||||
reader rdr(vsm_.toplevel_env()->symtab(), config_.debug_flag);
|
||||
rdr.begin_interactive_session();
|
||||
|
||||
string input_str;
|
||||
|
||||
bool eof = false;
|
||||
|
||||
span_type input;
|
||||
std::size_t parser_stack_size = 0;
|
||||
|
||||
if (config_.welcome_flag_)
|
||||
welcome(cerr);
|
||||
|
||||
while (replxx_getline(interactive, parser_stack_size, rx, input_str)) {
|
||||
input = span_type::from_string(input_str);
|
||||
|
||||
while (!input.empty()) {
|
||||
/**
|
||||
* Three cases here:
|
||||
* 1. available input is invalid (does not conform to schematika syntax).
|
||||
* 1a. expr=nullptr
|
||||
* 1b. consumed reads all available input
|
||||
* 1c. psz=0
|
||||
* 1d. error.is_error(); details including exact location where parsing failed.
|
||||
* 1e. parser reset to top level.
|
||||
* 2. available input represents prefix of a possibly-valid expression
|
||||
* 2a. expr=nullptr;
|
||||
* 2b. consumed reads all available input
|
||||
* 2c. psz reflects nesting level after reading available input.
|
||||
* 2d. error.is_not_an_error()
|
||||
* 3. available input completes at least one expression
|
||||
* 3a. expr contains first completed top-level expression
|
||||
* 3b. consumed reports portion of input up to end of expr
|
||||
* 3c. psz=0
|
||||
* 3d. error.is_not_an_error()
|
||||
*
|
||||
* expr :: rp<Expression> if non-null: the next expression from input
|
||||
* consumed :: span<char> extent of input read up to next Expression
|
||||
* psz :: size_t parser stack size
|
||||
* error :: reader_error error details on parsing failure
|
||||
**/
|
||||
auto [expr, consumed, psz, error] = rdr.read_expr(input, eof);
|
||||
|
||||
if (expr) {
|
||||
/** configuration for pretty-printing **/
|
||||
ppconfig ppc;
|
||||
ppstate_standalone pps(&cout, 0, &ppc);
|
||||
|
||||
//pps.prettyn(expr);
|
||||
|
||||
// TODO:
|
||||
auto [ value, scm_error ] = this->vsm_.toplevel_eval(expr);
|
||||
|
||||
if (scm_error.is_error()) {
|
||||
/* print error */
|
||||
|
||||
cout << "scm error: " << scm_error.what() << endl;
|
||||
cout << "top-level expression: " << expr << endl;
|
||||
} else {
|
||||
/* print value */
|
||||
|
||||
cout << "scm result:" << endl;
|
||||
cout << value << endl;
|
||||
//pps.pretty(value);
|
||||
}
|
||||
|
||||
} else if (error.is_error()) {
|
||||
cout << "parsing error (detected in " << error.src_function() << "): " << endl << endl;
|
||||
error.report(cout);
|
||||
|
||||
/* discard stashed remainder of input line
|
||||
* (for nicely-formatted errors)
|
||||
*/
|
||||
rdr.reset_to_idle_toplevel();
|
||||
break;
|
||||
}
|
||||
|
||||
input = input.after_prefix(consumed);
|
||||
parser_stack_size = psz;
|
||||
}
|
||||
|
||||
/* here: input.empty() or error encountered */
|
||||
|
||||
cerr << endl;
|
||||
}
|
||||
|
||||
auto [expr, _1, _2, error] = rdr.read_expr(input, true /*eof*/);
|
||||
|
||||
if (expr) {
|
||||
ppconfig ppc;
|
||||
ppstate_standalone pps(&cout, 0, &ppc);
|
||||
|
||||
pps.prettyn<rp<Expression>>(rp<Expression>(expr));
|
||||
} else if (error.is_error()) {
|
||||
cout << "parsing error (detected in " << error.src_function() << "): " << endl;
|
||||
error.report(cout);
|
||||
}
|
||||
|
||||
rx.history_save("repl_history.txt");
|
||||
}
|
||||
|
||||
// ----- Schematika -----
|
||||
|
||||
Schematika::Schematika(const Config & cfg) : p_impl_{Impl::make(cfg)}
|
||||
{}
|
||||
|
||||
Schematika::~Schematika()
|
||||
{}
|
||||
|
||||
Schematika
|
||||
Schematika::make(const Config & cfg)
|
||||
{
|
||||
return Schematika(cfg);
|
||||
}
|
||||
|
||||
void
|
||||
Schematika::interactive_repl()
|
||||
{
|
||||
p_impl_->interactive_repl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* end Schematika.cpp */
|
||||
|
|
@ -1,865 +0,0 @@
|
|||
/** @file VirtualSchematikaMachine.cpp **/
|
||||
|
||||
#include "VirtualSchematikaMachine.hpp"
|
||||
#include "VsmInstr.hpp"
|
||||
#include "BuiltinPrimitives.hpp"
|
||||
#include "ExpressionBoxed.hpp"
|
||||
#include "xo/expression/Constant.hpp"
|
||||
#include "xo/expression/PrimitiveExprInterface.hpp"
|
||||
#include "xo/expression/DefineExpr.hpp"
|
||||
#include "xo/expression/AssignExpr.hpp"
|
||||
#include "xo/expression/Variable.hpp"
|
||||
#include "xo/expression/IfExpr.hpp"
|
||||
#include "xo/expression/Sequence.hpp"
|
||||
#include "xo/expression/Apply.hpp"
|
||||
#include "xo/object/Procedure.hpp"
|
||||
#include "xo/object/Primitive.hpp"
|
||||
#include "xo/object/Integer.hpp"
|
||||
#include "xo/object/Boolean.hpp"
|
||||
#include "xo/alloc/GC.hpp"
|
||||
|
||||
/** continue after completing a VSM instruction;
|
||||
* achieve by jumping to continuation.
|
||||
**/
|
||||
#define VSM_CONTINUE() this->pc_ = this->cont_; return;
|
||||
|
||||
/** report error and terminate VSM execution
|
||||
**/
|
||||
#define VSM_ERROR(msg) report_error(msg); return;
|
||||
|
||||
|
||||
|
||||
namespace xo {
|
||||
using xo::gc::GC;
|
||||
using xo::obj::Procedure;
|
||||
using xo::obj::Integer;
|
||||
using xo::obj::Boolean;
|
||||
|
||||
namespace scm {
|
||||
struct VsmOps {
|
||||
/** halt virtual scheme machine.
|
||||
* This will cause innermost run() to return to its caller
|
||||
**/
|
||||
static VsmInstr halt_op;
|
||||
|
||||
/** evaluate an expression.
|
||||
* - opcode is Opcode::eval
|
||||
* - expression in register @ref expr_
|
||||
**/
|
||||
static VsmInstr eval_op;
|
||||
|
||||
/** assign variable after evaluating rhs of a define-expression or assign-expression
|
||||
* - opcode is Opcode::complete_assign
|
||||
* - top stack frame contains {lhs, cont}
|
||||
**/
|
||||
static VsmInstr complete_assign_op;
|
||||
|
||||
/** choose branch of if-expr after evaluating test condition.
|
||||
* - opcode is Opcode::complete_ifexpr
|
||||
* - top stack frame contains {ifexpr, cont}
|
||||
**/
|
||||
static VsmInstr complete_ifexpr_op;
|
||||
|
||||
/** proceed to next element of sequence-expr.
|
||||
* - opcode is Opcode::complete_sequence
|
||||
* - top stack frame contains {seq, next, cont}
|
||||
*/
|
||||
static VsmInstr complete_sequence_op;
|
||||
|
||||
/** proceed to next argument in apply-expr
|
||||
* - opcode is Opcode::eval_collect_args
|
||||
* - top stack frame contains {apply, targetarg, cont}
|
||||
*/
|
||||
static VsmInstr complete_evalargs_op;
|
||||
|
||||
/** call a procedure, where evaluated arguments (including target function)
|
||||
* are in top stack frame.
|
||||
* - opcode is Opcode::apply
|
||||
* - top stack frame contains evaluated arguments.
|
||||
**/
|
||||
static VsmInstr apply_op;
|
||||
};
|
||||
|
||||
VsmInstr
|
||||
VsmOps::halt_op{VsmInstr::Opcode::halt, "halt"};
|
||||
|
||||
VsmInstr
|
||||
VsmOps::eval_op{VsmInstr::Opcode::eval, "eval"};
|
||||
|
||||
VsmInstr
|
||||
VsmOps::complete_assign_op{VsmInstr::Opcode::complete_assign, "complete-assign"};
|
||||
|
||||
VsmInstr
|
||||
VsmOps::complete_ifexpr_op{VsmInstr::Opcode::complete_ifexpr, "complete-ifexpr"};
|
||||
|
||||
VsmInstr
|
||||
VsmOps::complete_sequence_op{VsmInstr::Opcode::complete_sequence, "complete-sequence"};
|
||||
|
||||
VsmInstr
|
||||
VsmOps::complete_evalargs_op{VsmInstr::Opcode::complete_evalargs, "complete-evalargs"};
|
||||
|
||||
VsmInstr
|
||||
VsmOps::apply_op{VsmInstr::Opcode::apply, "apply"};
|
||||
|
||||
// ----- VirtualSchematikaMachineFlyweight -----
|
||||
|
||||
VirtualSchematikaMachineFlyweight::VirtualSchematikaMachineFlyweight(gc::IAlloc * mm,
|
||||
gp<GlobalEnv> env,
|
||||
log_level ll) :
|
||||
object_mm_{mm},
|
||||
toplevel_env_{env},
|
||||
log_level_{ll}
|
||||
{
|
||||
}
|
||||
|
||||
// ----- VirtualSchematikaMachine -----
|
||||
|
||||
VirtualSchematikaMachine::VirtualSchematikaMachine(gc::IAlloc * mm,
|
||||
gp<GlobalEnv> env,
|
||||
log_level ll) : flyweight_{mm, env, ll}
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("env", env), xtag("symtab", env->symtab()));
|
||||
|
||||
this->env_ = env;
|
||||
|
||||
// gc roots
|
||||
gc::GC * gc = GC::from(mm);
|
||||
|
||||
if (gc) {
|
||||
assert((gc->gc_in_progress() == false) && "cannot add roots while GC running");
|
||||
|
||||
gc->add_gc_root_dwim(&env_);
|
||||
gc->add_gc_root_dwim(&value_);
|
||||
} else {
|
||||
// Want to support VSM with arena-allocator-only;
|
||||
// if only for unit testing.
|
||||
}
|
||||
|
||||
// TODO: install builtin primitives here
|
||||
}
|
||||
|
||||
VirtualSchematikaMachine::~VirtualSchematikaMachine()
|
||||
{
|
||||
gc::GC * gc = GC::from(flyweight_.object_mm_);
|
||||
|
||||
if (gc) {
|
||||
assert((gc->gc_in_progress() == false) && "cannot remove roots while GC running");
|
||||
|
||||
gc->remove_gc_root_dwim(&env_);
|
||||
gc->remove_gc_root_dwim(&value_);
|
||||
} else {
|
||||
// nothing to do in arena-only mode
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<gp<Object>,
|
||||
SchematikaError>
|
||||
VirtualSchematikaMachine::toplevel_eval(bp<Expression> expr)
|
||||
{
|
||||
return this->eval(expr, this->env_);
|
||||
}
|
||||
|
||||
std::pair<gp<Object>,
|
||||
SchematikaError>
|
||||
VirtualSchematikaMachine::eval(bp<Expression> expr, gp<GlobalEnv> env)
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("env", env), xtag("symtab", env->symtab()));
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
this->expr_ = expr.promote();
|
||||
this->env_ = env;
|
||||
this->stack_ = nullptr;
|
||||
this->value_ = nullptr;
|
||||
this->error_ = SchematikaError();
|
||||
this->cont_ = &VsmOps::halt_op;
|
||||
|
||||
this->run();
|
||||
|
||||
return std::make_pair(this->value_, this->error_);
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::run()
|
||||
{
|
||||
while(pc_)
|
||||
this->execute_one();
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::execute_one()
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
log && log(xtag("pc", pc_), xtag("cont", cont_));
|
||||
log && log(xtag("stack", stack_));
|
||||
|
||||
using Opcode = VsmInstr::Opcode;
|
||||
|
||||
switch (pc_->opcode()) {
|
||||
|
||||
case Opcode::halt:
|
||||
{
|
||||
this->pc_ = nullptr;
|
||||
this->cont_ = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
case Opcode::eval:
|
||||
{
|
||||
log && log("Opcode::eval");
|
||||
|
||||
/* generally speaking: opcode will be 1:1 with extypes */
|
||||
|
||||
switch (expr_->extype()) {
|
||||
case exprtype::constant:
|
||||
log && log("eval -> constant");
|
||||
this->eval_constant_op();
|
||||
break;
|
||||
|
||||
case exprtype::primitive:
|
||||
log && log("eval -> primitive");
|
||||
this->eval_primitive_op();
|
||||
break;
|
||||
|
||||
case exprtype::define:
|
||||
log && log("eval -> define");
|
||||
this->eval_define_op();
|
||||
break;
|
||||
|
||||
case exprtype::assign:
|
||||
log && log("eval -> assign");
|
||||
this->eval_assign_op();
|
||||
break;
|
||||
|
||||
case exprtype::variable:
|
||||
log && log("eval -> variable");
|
||||
this->eval_variable_op();
|
||||
break;
|
||||
|
||||
case exprtype::ifexpr:
|
||||
log && log("eval -> ifexpr");
|
||||
this->eval_ifexpr_op();
|
||||
break;
|
||||
|
||||
case exprtype::sequence:
|
||||
log && log("eval -> sequence");
|
||||
this->eval_sequence_op();
|
||||
break;
|
||||
|
||||
case exprtype::apply:
|
||||
log && log("eval -> apply");
|
||||
this->eval_apply_op();
|
||||
break;
|
||||
|
||||
case exprtype::invalid:
|
||||
|
||||
case exprtype::lambda:
|
||||
case exprtype::convert:
|
||||
case exprtype::n_expr:
|
||||
this->pc_ = nullptr;
|
||||
this->value_ = nullptr;
|
||||
this->error_ = SchematikaError(tostr("execute_vsm: not implemented",
|
||||
xtag("extype", expr_->extype())));
|
||||
this->cont_ = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Opcode::complete_assign:
|
||||
this->do_complete_assign_op();
|
||||
break;
|
||||
|
||||
case Opcode::complete_ifexpr:
|
||||
this->do_complete_ifexpr_op();
|
||||
break;
|
||||
|
||||
case Opcode::complete_sequence:
|
||||
this->do_complete_sequence_op();
|
||||
break;
|
||||
|
||||
case Opcode::complete_evalargs:
|
||||
this->do_complete_evalargs_op();
|
||||
break;
|
||||
|
||||
case Opcode::apply:
|
||||
this->apply_op();
|
||||
break;
|
||||
|
||||
case Opcode::N_Opcode:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::report_error(const std::string & err)
|
||||
{
|
||||
/* error short-circuits vsm operation */
|
||||
|
||||
this->pc_ = nullptr;
|
||||
this->value_ = nullptr;
|
||||
this->error_ = SchematikaError(err);
|
||||
this->cont_ = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_constant_op()
|
||||
{
|
||||
using xo::scm::ConstantInterface;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
bp<ConstantInterface> expr = ConstantInterface::from(expr_);
|
||||
|
||||
assert(expr);
|
||||
|
||||
this->value_ = flyweight_.object_converter_.tp_to_object(flyweight_.object_mm_,
|
||||
expr->value_tp(),
|
||||
false);
|
||||
if (this->value_.ptr()) {
|
||||
log && log("got object: ", xtag("value", value_));
|
||||
|
||||
VSM_CONTINUE();
|
||||
} else {
|
||||
/* see ObjectConverter::ctor to add more builtin types
|
||||
*
|
||||
* generally conversion for a type Foo will appear in Foo.hpp
|
||||
* see
|
||||
* xo/object/Boolean.hpp
|
||||
* xo/object/Integer.hpp
|
||||
* xo/object/Float.hpp
|
||||
* xo/object/String.hpp
|
||||
*/
|
||||
|
||||
VSM_ERROR(tostr("constant_op: unable to convert native value to object",
|
||||
xtag("id", expr->value_tp().td()->id()),
|
||||
xtag("short_name", expr->value_tp().td()->short_name())));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_primitive_op()
|
||||
{
|
||||
using xo::obj::Primitive;
|
||||
using xo::reflect::TaggedPtr;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
bp<PrimitiveExprInterface> expr = PrimitiveExprInterface::from(expr_);
|
||||
|
||||
const gp<Object> * slot = env_->lookup_slot(expr->name());
|
||||
|
||||
if (slot) {
|
||||
this->value_ = *slot;
|
||||
this->pc_ = cont_;
|
||||
} else {
|
||||
std::string err = tostr("no binding for primitive", xtag("name", expr->name()));
|
||||
|
||||
this->value_ = nullptr;
|
||||
this->error_ = SchematikaError(err);
|
||||
|
||||
/* note: poor man's exception */
|
||||
this->pc_ = nullptr;
|
||||
this->cont_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_define_op()
|
||||
{
|
||||
using xo::scm::DefineExpr;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
auto mm = flyweight_.object_mm_;
|
||||
|
||||
bp<DefineExpr> expr = DefineExpr::from(expr_);
|
||||
|
||||
assert(expr);
|
||||
assert(env_.get());
|
||||
|
||||
// note: expecting nested define to have expanded iteself into
|
||||
// applying a lambda
|
||||
|
||||
// note: establish lhs_var first, to allow for recursion, for example:
|
||||
// def fact(n: i64) { if (n == 0) then 1; else n * fact(n-1); }
|
||||
|
||||
/** remembers promised variable type **/
|
||||
this->env_->establish_var(expr->lhs_variable());
|
||||
|
||||
/** must promote rp<Expression> -> gp<ExpressionBoxed> **/
|
||||
gp<ExpressionBoxed> lhs_0 = ExpressionBoxed::make(mm, expr->lhs_variable());
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
this->expr_ = expr->rhs();
|
||||
|
||||
/* when control arrives at .cont_, will have:
|
||||
* .value_ -> result of evaluating expr->rhs()
|
||||
*/
|
||||
this->stack_ = VsmStackFrame::push1(mm, this->stack_, lhs_0, cont_);
|
||||
|
||||
/* .stack_:
|
||||
* frame
|
||||
* [0] = lhs_0 (boxed lhs Variable)
|
||||
* ..
|
||||
*/
|
||||
|
||||
this->cont_ = &VsmOps::complete_assign_op;
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_assign_op()
|
||||
{
|
||||
using xo::scm::AssignExpr;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
auto mm = flyweight_.object_mm_;
|
||||
|
||||
bp<AssignExpr> assign = AssignExpr::from(expr_);
|
||||
|
||||
assert(assign.get());
|
||||
assert(env_.get());
|
||||
|
||||
assert(assign->lhs().get());
|
||||
assert(assign->rhs().get());
|
||||
|
||||
/* verify slot exists, before we evaluate rhs */
|
||||
gp<Object> * slot = env_->lookup_slot(assign->lhs()->name());
|
||||
|
||||
if (slot) {
|
||||
/** must promote rp<Expression> -> gp<ExpressionBoxed> **/
|
||||
gp<ExpressionBoxed> lhs = ExpressionBoxed::make(mm, assign->lhs());
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
this->expr_ = assign->rhs();
|
||||
|
||||
/* when control arrives at .cont_, will have:
|
||||
* .value_ -> result of evaluating assign->rhs()
|
||||
*/
|
||||
this->stack_ = VsmStackFrame::push1(mm, this->stack_, lhs, cont_);
|
||||
|
||||
/* .stack_:
|
||||
* frame
|
||||
* [0] = lhs (boxed lhs Variable)
|
||||
* ..
|
||||
*/
|
||||
|
||||
this->cont_ = &VsmOps::complete_assign_op;
|
||||
} else {
|
||||
std::string err = tostr("no binding for lhs of assignment", xtag("name", assign->lhs()->name()));
|
||||
|
||||
this->value_ = nullptr;
|
||||
this->error_ = SchematikaError(err);
|
||||
|
||||
/* note: poor man's exception */
|
||||
this->pc_ = nullptr;
|
||||
this->cont_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::do_complete_assign_op()
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
/*
|
||||
* - value: contains result of evaluating rhs of define
|
||||
* - stack: top frame has 1 slot, holds variable to receive assignment
|
||||
*/
|
||||
assert(value_.get());
|
||||
assert(stack_.get());
|
||||
assert(env_.get());
|
||||
|
||||
gp<VsmStackFrame> sp0 = this->stack_;
|
||||
|
||||
bp<Variable> var = Variable::from(ExpressionBoxed::from((*sp0)[0])->contents());
|
||||
|
||||
assert(var.get());
|
||||
|
||||
gp<Object> * slot = this->env_->establish_var(var);
|
||||
assert(slot);
|
||||
|
||||
*slot = this->value_;
|
||||
|
||||
//this->value_ = this->value_; // preserve value from rhs of defexpr
|
||||
|
||||
this->stack_ = sp0->parent();
|
||||
this->pc_ = this->cont_ = sp0->continuation();
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_variable_op()
|
||||
{
|
||||
using xo::scm::Variable;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
bp<Variable> var = Variable::from(expr_);
|
||||
|
||||
assert(var.get());
|
||||
assert(env_.get());
|
||||
|
||||
const gp<Object> * slot = env_->lookup_slot(var->name());
|
||||
|
||||
if (slot) {
|
||||
this->value_ = *slot;
|
||||
this->pc_ = cont_;
|
||||
} else {
|
||||
/* Unknown variable error will often be recognized in expression parser,
|
||||
* in such cases this path won't be used.
|
||||
*
|
||||
* In interactive environment will need some kind of support for modifying
|
||||
* code (e.g. replacing top-level functions/variables), and in particular,
|
||||
* replacements may have different type signature.
|
||||
* It's possible that allowing for such replacements winds up giving up
|
||||
* typesafety guarantees. In that case this path may get activated after
|
||||
* all.
|
||||
*/
|
||||
|
||||
std::string err = tostr("no binding for variable", xtag("name", var->name()));
|
||||
|
||||
this->value_ = nullptr;
|
||||
this->error_ = SchematikaError(err);
|
||||
|
||||
/* note: poor man's exception */
|
||||
this->pc_ = nullptr;
|
||||
this->cont_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_ifexpr_op()
|
||||
{
|
||||
using xo::scm::IfExpr;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
gc::IAlloc * mm = flyweight_.object_mm_;
|
||||
|
||||
/** must promote bp<IfExpr> -> gp<ExpressionBoxed> **/
|
||||
gp<ExpressionBoxed> ifexpr_boxed = ExpressionBoxed::make(mm, expr_);
|
||||
bp<IfExpr> ifexpr = IfExpr::from(expr_);
|
||||
|
||||
assert(ifexpr.get());
|
||||
assert(env_.get());
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
this->expr_ = ifexpr->test();
|
||||
|
||||
|
||||
/* when control arrives at .cont_ will have:
|
||||
* .value_ -> result of evaluating ifexpr->test()
|
||||
*/
|
||||
this->stack_ = VsmStackFrame::push1(mm, this->stack_, ifexpr_boxed, cont_);
|
||||
|
||||
/* .stack_:
|
||||
* frame
|
||||
* [0] = ifexpr (boxed expression)
|
||||
*/
|
||||
|
||||
this->cont_ = &VsmOps::complete_ifexpr_op;
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::do_complete_ifexpr_op()
|
||||
{
|
||||
using xo::scm::IfExpr;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
/*
|
||||
* - value: contains result of evaluating test condition of if-expr
|
||||
* - stack: top frame has 1 slot, holds (boxed) if-expr itself
|
||||
*/
|
||||
assert(value_.get());
|
||||
assert(stack_.get());
|
||||
assert(env_.get());
|
||||
|
||||
gp<Boolean> test_value = gp<Boolean>::from(value_);
|
||||
|
||||
if (test_value.get()) {
|
||||
gp<VsmStackFrame> sp0 = this->stack_;
|
||||
|
||||
bp<IfExpr> ifexpr = IfExpr::from(ExpressionBoxed::from((*sp0)[0])->contents());
|
||||
|
||||
assert(ifexpr.get());
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
|
||||
if (test_value->value()) {
|
||||
this->expr_ = ifexpr->when_true();
|
||||
} else {
|
||||
if (ifexpr->when_false()) {
|
||||
this->expr_ = ifexpr->when_false();
|
||||
} else {
|
||||
/* 1-sided if-expr; evaluate to false */
|
||||
this->expr_ = Constant<bool>::make(false);
|
||||
}
|
||||
}
|
||||
|
||||
this->stack_ = sp0->parent();
|
||||
this->cont_ = sp0->continuation();
|
||||
} else {
|
||||
std::string err = tostr("expect boolean value for result of if-expr test", xtag("value", test_value));
|
||||
|
||||
this->value_ = nullptr;
|
||||
this->error_ = SchematikaError(err);
|
||||
|
||||
/* note: poor man's exception */
|
||||
this->pc_ = nullptr;
|
||||
this->cont_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_sequence_op()
|
||||
{
|
||||
using xo::scm::Sequence;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
gc::IAlloc * mm = flyweight_.object_mm_;
|
||||
|
||||
/** must promote bp<Sequence> -> gp<ExpressionBoxed> **/
|
||||
gp<ExpressionBoxed> seq_boxed = ExpressionBoxed::make(mm, expr_);
|
||||
bp<Sequence> seq = Sequence::from(expr_);
|
||||
|
||||
assert(seq.get());
|
||||
assert(env_.get());
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
|
||||
if (seq->size() == 0) {
|
||||
/* for 0-size sequence, invent an expression */
|
||||
this->expr_ = Constant<bool>::make(false);
|
||||
} else {
|
||||
this->expr_ = (*seq)[0];
|
||||
}
|
||||
|
||||
if (seq->size() > 1) {
|
||||
/* remainder */
|
||||
|
||||
gp<Integer> next = Integer::make(mm, 1);
|
||||
|
||||
/* when control arrives at .cont_ will have:
|
||||
* .value_ -> result of evaluating last expr in seq
|
||||
*/
|
||||
this->stack_ = VsmStackFrame::push2(mm, stack_, seq_boxed, next, cont_);
|
||||
|
||||
/* .stack_:
|
||||
* frame
|
||||
* [0] = seq (boxed sequence)
|
||||
* [1] = next (index of next seq member to evaluate)
|
||||
* ..
|
||||
*/
|
||||
|
||||
this->cont_ = &VsmOps::complete_sequence_op;
|
||||
} else {
|
||||
/* sequence completes when expr_ evaluated
|
||||
* -> proceed with o.g. cont_
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::do_complete_sequence_op()
|
||||
{
|
||||
using xo::scm::Sequence;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
/* - stack: top frame has 2 slots:
|
||||
* [0] : seq (boxed Sequence)
|
||||
* [1] : next (index of next seq element to eval
|
||||
*/
|
||||
|
||||
assert(value_.get());
|
||||
assert(stack_.get());
|
||||
|
||||
gp<VsmStackFrame> sp0 = this->stack_;
|
||||
|
||||
assert(sp0->size() == 2);
|
||||
|
||||
bp<Sequence> seq = Sequence::from(ExpressionBoxed::from((*sp0)[0])->contents());
|
||||
gp<Integer> next_obj = Integer::from((*sp0)[1]);
|
||||
size_t i_next = next_obj->value();
|
||||
|
||||
assert(i_next < seq->size());
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
this->expr_ = (*seq)[i_next];
|
||||
|
||||
if (i_next + 1 == seq->size()) {
|
||||
/* last member of sequence -> tail call optimization */
|
||||
this->stack_ = sp0->parent();
|
||||
this->cont_ = sp0->continuation();
|
||||
} else {
|
||||
/* we can modify next_obj in place,
|
||||
* since it's unique to frame sp0
|
||||
*/
|
||||
next_obj->assign_value(i_next + 1);
|
||||
this->cont_ = &VsmOps::complete_sequence_op;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::eval_apply_op()
|
||||
{
|
||||
/* strategy:
|
||||
* 1. calling sequence will involve two stack frames.
|
||||
* 1a. the outer frame will hold 'final evaluated arguments'
|
||||
* to the called function. When control transfers to that
|
||||
* function, this frame will be at the top of stack_
|
||||
* 1b. innert frame will be used by eval_apply_op() and
|
||||
* helper do_eval_collect_args() to evaluate function
|
||||
* arguments, and populate the outer frame.
|
||||
*/
|
||||
|
||||
using xo::scm::Apply;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
gc::IAlloc * mm = flyweight_.object_mm_;
|
||||
|
||||
/** must promote bp<Apply> -> gp<ExpressionBoxed> **/
|
||||
gp<ExpressionBoxed> apply_boxed = ExpressionBoxed::make(mm, expr_);
|
||||
bp<Apply> apply = Apply::from(expr_);
|
||||
|
||||
assert(apply.get());
|
||||
|
||||
size_t n = apply->n_arg() + 1;
|
||||
|
||||
/* reminder: argument 0 refers to the function being called */
|
||||
gp<Integer> targetarg = Integer::make(mm, 0);
|
||||
|
||||
/* outer frame */
|
||||
gp<VsmStackFrame> argstack = VsmStackFrame::make(mm, stack_, n, cont_);
|
||||
|
||||
/* scratch frame during call sequence.
|
||||
* probably collect->cont_ will not be used?
|
||||
*/
|
||||
gp<VsmStackFrame> collect = VsmStackFrame::push2(mm,
|
||||
argstack,
|
||||
apply_boxed,
|
||||
targetarg,
|
||||
&VsmOps::complete_evalargs_op);
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
this->expr_ = apply->fn();
|
||||
this->stack_ = collect;
|
||||
this->cont_ = &VsmOps::complete_evalargs_op;
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::do_complete_evalargs_op()
|
||||
{
|
||||
using xo::scm::Apply;
|
||||
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
/* - stack: top frame has 2 slots
|
||||
* [0] : apply (boxed Apply)
|
||||
* [1] : targetarg index of next evaluated argument to deliver.
|
||||
* (to corresponding slot in 2nd frame)
|
||||
* - 2nd frame has n slots, where n = #of arguments at this site
|
||||
* [0] : actual #0
|
||||
* ..
|
||||
* [targetarg-1] : actual #{targetarg-1}
|
||||
*/
|
||||
|
||||
assert(value_.get());
|
||||
assert(stack_.get());
|
||||
|
||||
gp<VsmStackFrame> sp0 = this->stack_;
|
||||
|
||||
assert(sp0.get());
|
||||
assert(sp0->size() == 2);
|
||||
|
||||
bp<Apply> apply = Apply::from(ExpressionBoxed::from((*sp0)[0])->contents());
|
||||
assert(apply.get());
|
||||
gp<Integer> targetarg_obj = Integer::from((*stack_)[1]);
|
||||
size_t targetarg = targetarg_obj->value();
|
||||
|
||||
/* note: apply->n_arg() doesn't count function itself */
|
||||
assert(targetarg < apply->n_arg() + 1);
|
||||
|
||||
gp<VsmStackFrame> argstack = sp0->parent();
|
||||
|
||||
assert(argstack.get());
|
||||
|
||||
/* storing actual parameter in its correct position in call stackframe */
|
||||
(*argstack)[targetarg] = value_;
|
||||
|
||||
++targetarg;
|
||||
|
||||
if (targetarg < apply->n_arg() + 1) {
|
||||
/*
|
||||
* arguments 0 .. #targetarg-1 already present in argstack
|
||||
* arguments #targetarg .. #n still need to be evaluated
|
||||
*/
|
||||
|
||||
/* ok to update in place, since Integer in sp0 is unique */
|
||||
targetarg_obj->assign_value(targetarg);
|
||||
|
||||
rp<Expression> targetexpr = apply->lookup_arg(targetarg - 1);
|
||||
|
||||
this->pc_ = &VsmOps::eval_op;
|
||||
this->expr_ = targetexpr;
|
||||
assert(this->stack_.get() == sp0.get());
|
||||
this->cont_ = &VsmOps::complete_evalargs_op;
|
||||
} else {
|
||||
/* all args evaluated: proceed to invoke evaluated function */
|
||||
|
||||
this->pc_ = &VsmOps::apply_op;
|
||||
this->expr_ = nullptr;
|
||||
this->stack_ = argstack;
|
||||
/* unnecessary - will actually be set by apply_op() */
|
||||
this->cont_ = argstack->continuation();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VirtualSchematikaMachine::apply_op()
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
auto mm = flyweight_.object_mm_;
|
||||
|
||||
// NOTE: Closures will have special handling.
|
||||
// Could alternatively forward the whole problem
|
||||
// (along with VSM state) to procedure implementation
|
||||
|
||||
/* stack: top frame has n slots for procedure with n canonical args */
|
||||
|
||||
gp<VsmStackFrame> sp0 = stack_;
|
||||
|
||||
assert(sp0->size() > 0);
|
||||
|
||||
gp<Procedure> fn = Procedure::from((*sp0)[0]);
|
||||
|
||||
if (fn->n_args() + 1 != sp0->size()) {
|
||||
throw std::runtime_error(tostr("VirtualSchematikaMachine::apply_op:"
|
||||
" argument mismatch in apply"
|
||||
": k arguments supplied where n expected",
|
||||
xtag("k", sp0->size() - 1),
|
||||
xtag("n", fn->n_args())));
|
||||
}
|
||||
|
||||
/* todo:
|
||||
* check function signature?
|
||||
* should have been guaranteed by expression parser,
|
||||
* but complications in interactive session when variables redefined.
|
||||
*/
|
||||
|
||||
gp<Object> retval = fn->apply_nocheck(mm, sp0->argv());
|
||||
|
||||
this->pc_ = this->cont_;
|
||||
this->stack_ = sp0->parent();
|
||||
this->value_ = retval;
|
||||
}
|
||||
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end VirtualSchematikaMachine.cpp */
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
/** @file VsmInstr.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#include "VsmInstr.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
VsmInstr::VsmInstr(Opcode opcode,
|
||||
std::string_view name) : opcode_{opcode}, name_{name}
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
/* end VsmInstr.cpp */
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
/** @file VsmStackFrame.cpp
|
||||
*
|
||||
* @author Roland Conybeare, Nov 2025
|
||||
**/
|
||||
|
||||
#include "VsmStackFrame.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
#include "xo/reflect/StructReflector.hpp"
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::Reflect;
|
||||
using xo::reflect::StructReflector;
|
||||
using xo::reflect::TypeDescrW;
|
||||
using xo::reflect::TaggedPtr;
|
||||
using xo::reflect::TypeDescrExtra;
|
||||
using xo::reflect::EstablishTypeDescr;
|
||||
using xo::reflect::StlVectorTdx;
|
||||
|
||||
namespace scm {
|
||||
namespace {
|
||||
// TOOD: move into CVector
|
||||
|
||||
std::size_t
|
||||
slot_array_size(std::size_t n) {
|
||||
return n * sizeof(gp<Object>);
|
||||
}
|
||||
}
|
||||
|
||||
VsmStackFrame::VsmStackFrame(gc::IAlloc * mm,
|
||||
gp<VsmStackFrame> p,
|
||||
std::size_t n,
|
||||
const VsmInstr * cont) : parent_{p},
|
||||
slot_v_{mm, n},
|
||||
cont_{cont}
|
||||
{}
|
||||
|
||||
gp<VsmStackFrame>
|
||||
VsmStackFrame::make(gc::IAlloc * mm,
|
||||
gp<VsmStackFrame> p,
|
||||
std::size_t n,
|
||||
const VsmInstr * cont)
|
||||
{
|
||||
gp<VsmStackFrame> retval = new (MMPtr(mm)) VsmStackFrame(mm, p, n, cont);
|
||||
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
(*retval)[i] = nullptr;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
gp<VsmStackFrame>
|
||||
VsmStackFrame::push1(gc::IAlloc * mm,
|
||||
gp<VsmStackFrame> p,
|
||||
gp<Object> s0,
|
||||
const VsmInstr * cont)
|
||||
{
|
||||
gp<VsmStackFrame> retval = new (MMPtr(mm)) VsmStackFrame(mm, p, 1, cont);
|
||||
|
||||
(*retval)[0] = s0;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
gp<VsmStackFrame>
|
||||
VsmStackFrame::push2(gc::IAlloc * mm,
|
||||
gp<VsmStackFrame> p,
|
||||
gp<Object> s0,
|
||||
gp<Object> s1,
|
||||
const VsmInstr * cont)
|
||||
{
|
||||
gp<VsmStackFrame> retval = new (MMPtr(mm)) VsmStackFrame(mm, p, 2, cont);
|
||||
|
||||
(*retval)[0] = s0;
|
||||
(*retval)[1] = s1;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
TaggedPtr
|
||||
VsmStackFrame::self_tp() const
|
||||
{
|
||||
return Reflect::make_tp(const_cast<VsmStackFrame *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
VsmStackFrame::display(std::ostream & os) const
|
||||
{
|
||||
os << "<vsm-stack-frame"
|
||||
<< xtag("n", slot_v_.size());
|
||||
|
||||
#ifdef NOT_YET
|
||||
for (std::size_t i = 0, n = n_slot(); i < n; ++i) {
|
||||
char buf[24];
|
||||
snprintf(buf, sizeof(buf), "v[%lu]", i);
|
||||
|
||||
os << xtag(buf, lookup(i));
|
||||
}
|
||||
#endif
|
||||
|
||||
os << ">";
|
||||
}
|
||||
|
||||
std::size_t
|
||||
VsmStackFrame::_shallow_size() const
|
||||
{
|
||||
std::size_t retval = sizeof(VsmStackFrame);
|
||||
|
||||
retval += gc::IAlloc::with_padding(slot_array_size(slot_v_.size()));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
Object *
|
||||
VsmStackFrame::_shallow_copy(gc::IAlloc * mm) const
|
||||
{
|
||||
Cpof cpof(mm, this);
|
||||
|
||||
size_t n = this->size();
|
||||
|
||||
VsmStackFrame * copy = new (cpof) VsmStackFrame(cpof.mm_, parent_, n, cont_);
|
||||
|
||||
void * v_dest = copy->slot_v_.v_;
|
||||
|
||||
if (slot_v_.v_) {
|
||||
::memcpy(v_dest, slot_v_.v_, slot_array_size(n));
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
for (size_t i = 0, n = n_slot_; i < n; ++i) {
|
||||
copy->v_[i] = v_[i];
|
||||
}
|
||||
#endif
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
VsmStackFrame::_forward_children(gc::IAlloc * gc)
|
||||
{
|
||||
Object::_forward_inplace(parent_, gc);
|
||||
|
||||
for (std::size_t i = 0, n = slot_v_.size(); i < n; ++i) {
|
||||
Object::_forward_inplace((*this)[i], gc);
|
||||
}
|
||||
|
||||
return _shallow_size();
|
||||
}
|
||||
|
||||
void
|
||||
VsmStackFrame::reflect_self()
|
||||
{
|
||||
StructReflector<VsmStackFrame> sr;
|
||||
|
||||
if (sr.is_incomplete() ) {
|
||||
/* reflect CVector<gp<Object>>.
|
||||
* duplicates similar code in LocalEnv::reflect_self()
|
||||
*/
|
||||
using VectorType = obj::CVector<gp<Object>>;
|
||||
|
||||
/* custom reflection for array of Object pointers.
|
||||
* Can use StlVectorTdx here, treating CVector<T> as a vector
|
||||
* via .size() and .operator[] members
|
||||
*/
|
||||
std::unique_ptr<TypeDescrExtra> tdx1
|
||||
= std::make_unique<StlVectorTdx<VectorType>>();
|
||||
TypeDescrW td1
|
||||
= EstablishTypeDescr::establish<VectorType>();
|
||||
td1->assign_tdextra(Reflect::get_final_invoker<VectorType>(),
|
||||
std::move(tdx1));
|
||||
|
||||
REFLECT_MEMBER(sr, parent);
|
||||
REFLECT_MEMBER(sr, slot_v);
|
||||
}
|
||||
}
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end VsmStackFrame.cpp */
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/** @file init_interpreter.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Nov 2025
|
||||
*/
|
||||
|
||||
#include "init_interpreter.hpp"
|
||||
#include "LocalEnv.hpp"
|
||||
#include "xo/subsys/Subsystem.hpp"
|
||||
|
||||
namespace xo {
|
||||
using xo::scm::LocalEnv;
|
||||
|
||||
void
|
||||
InitSubsys<S_interpreter_tag>::init()
|
||||
{
|
||||
LocalEnv::reflect_self();
|
||||
}
|
||||
|
||||
InitEvidence
|
||||
InitSubsys<S_interpreter_tag>::require()
|
||||
{
|
||||
return Subsystem::provide<S_interpreter_tag>("interpreter", &init);
|
||||
}
|
||||
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end init_interpreter.cpp */
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# build unittest interpreter/utest
|
||||
|
||||
set(UTEST_EXE utest.interpreter)
|
||||
set(UTEST_SRCS
|
||||
interpreter_utest_main.cpp
|
||||
LocalEnv.test.cpp
|
||||
)
|
||||
|
||||
xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS})
|
||||
xo_self_dependency(${UTEST_EXE} xo_interpreter)
|
||||
xo_dependency(${UTEST_EXE} xo_object)
|
||||
xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2)
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/** @file LocalEnv.test.cpp **/
|
||||
|
||||
#include "xo/interpreter/init_interpreter.hpp"
|
||||
#include "xo/interpreter/LocalEnv.hpp"
|
||||
#include "xo/object/Integer.hpp"
|
||||
#include "xo/alloc/GC.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
using xo::scm::LocalEnv;
|
||||
using xo::obj::Integer;
|
||||
using xo::gc::GC;
|
||||
using xo::gc::ArenaAlloc;
|
||||
using xo::gc::generation;
|
||||
using xo::gc::generation_result;
|
||||
using xo::reflect::TaggedPtr;
|
||||
|
||||
namespace ut {
|
||||
static InitEvidence s_init = (InitSubsys<S_interpreter_tag>::require());
|
||||
|
||||
namespace {
|
||||
struct Testcase_LocalEnv {
|
||||
Testcase_LocalEnv(const std::vector<std::int32_t> & contents) : contents_{contents} {}
|
||||
|
||||
/* build xo::obj::Integer for each contents_[i], store in F[i] for new LocalEnv F */
|
||||
std::vector<std::int32_t> contents_;
|
||||
};
|
||||
|
||||
std::vector<Testcase_LocalEnv>
|
||||
s_testcase_v = {
|
||||
Testcase_LocalEnv({}),
|
||||
Testcase_LocalEnv({}),
|
||||
Testcase_LocalEnv({111}),
|
||||
Testcase_LocalEnv({111, 222}),
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("LocalEnv", "[LocalEnv][interpreter]")
|
||||
{
|
||||
Subsystem::initialize_all();
|
||||
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
||||
scope log(XO_DEBUG(c_debug_flag), xtag("test", "LocalEnv2"), xtag("i_tc", i_tc));
|
||||
|
||||
const Testcase_LocalEnv & tc = s_testcase_v[i_tc];
|
||||
|
||||
up<ArenaAlloc> alloc = ArenaAlloc::make("utest", 16384, c_debug_flag);
|
||||
REQUIRE(alloc.get());
|
||||
Object::mm = alloc.get();
|
||||
|
||||
std::size_t n = tc.contents_.size();
|
||||
gp<LocalEnv> frame = LocalEnv::make(alloc.get(), nullptr /*parent*/, nullptr /*symtab*/, n);
|
||||
|
||||
TaggedPtr tp = frame->self_tp();
|
||||
|
||||
REQUIRE(tp.is_struct());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("LocalEnv2", "[LocalEnv][gc][interpreter]")
|
||||
{
|
||||
Subsystem::initialize_all();
|
||||
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
try {
|
||||
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
||||
scope log(XO_DEBUG(c_debug_flag), xtag("test", "LocalEnv2"), xtag("i_tc", i_tc));
|
||||
|
||||
const Testcase_LocalEnv & tc = s_testcase_v[i_tc];
|
||||
|
||||
up<GC> gc = GC::make(
|
||||
{.initial_nursery_z_ = 16384,
|
||||
.initial_tenured_z_ = 32768,
|
||||
.incr_gc_threshold_ = 4096,
|
||||
.full_gc_threshold_ = 4096,
|
||||
.object_stats_flag_ = true,
|
||||
.debug_flag_ = c_debug_flag,
|
||||
});
|
||||
|
||||
REQUIRE(gc.get());
|
||||
|
||||
/* use gc for all Object allocs */
|
||||
GC * mm = gc.get();
|
||||
Object::mm = mm;
|
||||
|
||||
std::size_t n = tc.contents_.size();
|
||||
|
||||
gp<Integer> x = Integer::make(gc.get(), 42);
|
||||
gc->add_gc_root(reinterpret_cast<IObject **>(&x));
|
||||
REQUIRE(gc->tospace_generation_of(x.ptr()) == generation_result::nursery);
|
||||
|
||||
gp<LocalEnv> frame = LocalEnv::make(gc.get(), nullptr /*parent*/, nullptr /*symtab*/, n);
|
||||
LocalEnv ** frame_pp = frame.ptr_address();
|
||||
gc->add_gc_root(reinterpret_cast<IObject **>(frame_pp));
|
||||
|
||||
/* verifying allocated in N1 */
|
||||
REQUIRE(gc->tospace_generation_of(frame.ptr()) == generation_result::nursery);
|
||||
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
(*frame)[i] = Integer::make(mm, tc.contents_.at(i));
|
||||
|
||||
std::size_t expected_alloc_z = frame->_shallow_size();
|
||||
REQUIRE(expected_alloc_z >= sizeof(LocalEnv) + n * sizeof(gp<Object>));
|
||||
|
||||
gc->request_gc(generation::nursery); // <<<<<<<<< GC here <<<<<<<<<
|
||||
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
|
||||
/* verify Integer x preserved across gc */
|
||||
REQUIRE(gc->tospace_generation_of(x.ptr()) == generation_result::nursery);
|
||||
|
||||
/* verify LocalEnv preserved across gc */
|
||||
REQUIRE(gc->tospace_generation_of(frame.ptr()) == generation_result::nursery);
|
||||
REQUIRE(frame->size() == n);
|
||||
for (std::size_t i = 0; i < n; ++i) {
|
||||
//REQUIRE(Integer::from(frame->lookup(i)).ptr());
|
||||
//REQUIRE(Integer::from(frame->lookup(i))->value() == tc.contents_.at(i));
|
||||
}
|
||||
}
|
||||
} catch (std::exception & ex) {
|
||||
std::cerr << "exception: " << ex.what() << std::endl;
|
||||
REQUIRE(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* end LocalEnv.test.cpp */
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
/** @file interpreter_utest_main.cpp **/
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
|
||||
/* end interpreter_utest_main.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue