xo-interpreter2 stack: work on runtime error representation [WIP]

This commit is contained in:
Roland Conybeare 2026-02-12 18:46:43 -05:00
commit 497dc8a626
5 changed files with 86 additions and 13 deletions

View file

@ -10,6 +10,7 @@
#include "VsmFrame.hpp" #include "VsmFrame.hpp"
#include "DLocalEnv.hpp" #include "DLocalEnv.hpp"
#include "DGlobalEnv.hpp" #include "DGlobalEnv.hpp"
#include <xo/object2/RuntimeError.hpp>
#include <xo/reader2/SchematikaReader.hpp> #include <xo/reader2/SchematikaReader.hpp>
#include <xo/expression2/Expression.hpp> #include <xo/expression2/Expression.hpp>
#include <xo/gc/GCObject.hpp> #include <xo/gc/GCObject.hpp>
@ -17,6 +18,10 @@
namespace xo { namespace xo {
namespace scm { namespace scm {
#ifdef OBSOLETE // see DVsmError
// TODO: move error to collected space?
// or special arena?
//
struct EvaluationError { struct EvaluationError {
/** source location (in vsm implementation) at which error identified **/ /** source location (in vsm implementation) at which error identified **/
std::string_view src_function_; std::string_view src_function_;
@ -24,6 +29,7 @@ namespace xo {
std::string_view error_description_; std::string_view error_description_;
// TODO: info about location in schematika source // TODO: info about location in schematika source
}; };
#endif
/** similar to @ref xo::scm::ReaderResult **/ /** similar to @ref xo::scm::ReaderResult **/
struct VsmResult { struct VsmResult {
@ -31,17 +37,17 @@ namespace xo {
using span_type = xo::mm::span<const char>; using span_type = xo::mm::span<const char>;
VsmResult() = default; VsmResult() = default;
VsmResult(obj<AGCObject> value) : result_{value} {} explicit VsmResult(obj<AGCObject> value) : result_{value} {}
VsmResult(TokenizerError err) : result_{err} {} explicit VsmResult(TokenizerError err) : result_{err} {}
bool is_value() const { return std::holds_alternative<obj<AGCObject>>(result_); } bool is_value() const { return std::holds_alternative<obj<AGCObject>>(result_); }
bool is_tk_error() const { return std::holds_alternative<TokenizerError>(result_); } bool is_tk_error() const { return std::holds_alternative<TokenizerError>(result_); }
bool is_eval_error() const { return std::holds_alternative<EvaluationError>(result_); } bool is_eval_error() const;
const obj<AGCObject> * value() const { return std::get_if<obj<AGCObject>>(&result_); } const obj<AGCObject> * value() const { return std::get_if<obj<AGCObject>>(&result_); }
/** result of evaluating first expression encountered in input **/ /** result of evaluating first expression encountered in input **/
std::variant<obj<AGCObject>, TokenizerError, EvaluationError> result_; std::variant<obj<AGCObject>, TokenizerError> result_;
}; };
/** vsm result + reamining span **/ /** vsm result + reamining span **/
@ -72,6 +78,8 @@ namespace xo {
/** allocator for schematika data **/ /** allocator for schematika data **/
obj<AAllocator> allocator() const noexcept; obj<AAllocator> allocator() const noexcept;
/** allocator for runtime errors **/
obj<AAllocator> error_allocator() const noexcept;
/** visit vsm-owned memory pools; call visitor(info) for each **/ /** visit vsm-owned memory pools; call visitor(info) for each **/
void visit_pools(const MemorySizeVisitor & visitor) const; void visit_pools(const MemorySizeVisitor & visitor) const;
@ -185,11 +193,19 @@ namespace xo {
/** configuration **/ /** configuration **/
VsmConfig config_; VsmConfig config_;
/** allocator (likely collector) for /** allocator (likely DX1Collector or similar) for
* expressions and values * expressions and values
**/ **/
box<AAllocator> mm_; box<AAllocator> mm_;
/** Sidecar allocator for error reporting.
* Separate to mitigate interference with @ref mm_
* (separate memory so we can for example report
* an out-of-memory error).
* Likely DArena or similar
**/
box<AAllocator> error_mm_;
/** runtime context for this vsm. /** runtime context for this vsm.
* For example, provides allocator to primitives * For example, provides allocator to primitives
**/ **/

View file

@ -7,6 +7,7 @@
#include <xo/reader2/ReaderConfig.hpp> #include <xo/reader2/ReaderConfig.hpp>
#include <xo/gc/X1CollectorConfig.hpp> #include <xo/gc/X1CollectorConfig.hpp>
#include <xo/arena/ArenaConfig.hpp>
namespace xo { namespace xo {
namespace scm { namespace scm {
@ -14,6 +15,7 @@ namespace xo {
**/ **/
struct VsmConfig { struct VsmConfig {
using X1CollectorConfig = xo::mm::X1CollectorConfig; using X1CollectorConfig = xo::mm::X1CollectorConfig;
using ArenaConfig = xo::mm::ArenaConfig;
VsmConfig() = default; VsmConfig() = default;
@ -26,6 +28,10 @@ namespace xo {
* TODO: may want to make CollectorConfig polymorphic * TODO: may want to make CollectorConfig polymorphic
**/ **/
X1CollectorConfig x1_config_ = X1CollectorConfig().with_size(4*1024*1024); X1CollectorConfig x1_config_ = X1CollectorConfig().with_size(4*1024*1024);
/** Configuration for error allocator
* TODO: may want to make ArenaConfig polymorphic
**/
ArenaConfig error_config_ = ArenaConfig().with_size(64*1024);
}; };
} /*namespace scm*/ } /*namespace scm*/
} /*namespace xo*/ } /*namespace xo*/

View file

@ -6,6 +6,8 @@
#include "Closure.hpp" #include "Closure.hpp"
#include "LambdaExpr.hpp" #include "LambdaExpr.hpp"
#include "LocalEnv.hpp" #include "LocalEnv.hpp"
#include "VsmRcx.hpp"
#include <xo/indentlog/scope.hpp>
#include <cstddef> #include <cstddef>
namespace xo { namespace xo {
@ -33,7 +35,30 @@ namespace xo {
DClosure::apply_nocheck(obj<ARuntimeContext> rcx, DClosure::apply_nocheck(obj<ARuntimeContext> rcx,
const DArray * args) const DArray * args)
{ {
(void)rcx; scope log(XO_DEBUG(true));
auto vsm_rcx
= obj<ARuntimeContext,DVsmRcx>::from(rcx);
log && log(xtag("vsm_rcx.data", (void*)vsm_rcx.data()));
// let's try a not-implemented error
// don't want to clutter Procedure facet, since it's
// lower-level than xo-interpreter2
#ifdef NOT_YET
// TODO: verify arguments against type signature.
// unless we have evidence that program is type correct
int32_t n_args = this->n_args();
if (n_args != args->size()) {
//
}
#endif
(void)args; (void)args;
assert(false); assert(false);

View file

@ -15,6 +15,7 @@
//#include <xo/procedure2/SimpleRcx.hpp> //#include <xo/procedure2/SimpleRcx.hpp>
#include <xo/gc/DX1Collector.hpp> #include <xo/gc/DX1Collector.hpp>
#include <xo/gc/detail/IAllocator_DX1Collector.hpp> #include <xo/gc/detail/IAllocator_DX1Collector.hpp>
#include <xo/alloc2/Arena.hpp>
#include <xo/printable2/Printable.hpp> #include <xo/printable2/Printable.hpp>
#include <xo/facet/FacetRegistry.hpp> #include <xo/facet/FacetRegistry.hpp>
#include <cassert> #include <cassert>
@ -27,12 +28,25 @@ namespace xo {
//using xo::mm::MemorySizeInfo; // not used yet //using xo::mm::MemorySizeInfo; // not used yet
using xo::mm::AAllocator; using xo::mm::AAllocator;
using xo::mm::DX1Collector; using xo::mm::DX1Collector;
using xo::mm::DArena;
using xo::facet::FacetRegistry; using xo::facet::FacetRegistry;
using std::cout; using std::cout;
namespace scm { namespace scm {
// NOTE: using heap here for {DX1Collector, DVsmRcx} instances bool
VsmResult::is_eval_error() const
{
if (std::holds_alternative<obj<AGCObject>>(result_)) {
auto err = obj<AGCObject,DRuntimeError>::from(*(this->value()));
return err;
} else {
return false;
}
}
// NOTE: using heap here for {DX1Collector, DArena, DVsmRcx} instances
// (though DX1Collector allocations will be from explictly mmap'd memory) // (though DX1Collector allocations will be from explictly mmap'd memory)
// //
VirtualSchematikaMachine::VirtualSchematikaMachine(const VsmConfig & config) VirtualSchematikaMachine::VirtualSchematikaMachine(const VsmConfig & config)
@ -41,6 +55,14 @@ namespace xo {
rcx_(box<ARuntimeContext,DVsmRcx>(new DVsmRcx(this))), rcx_(box<ARuntimeContext,DVsmRcx>(new DVsmRcx(this))),
reader_{config.rdr_config_, mm_.to_op()} reader_{config.rdr_config_, mm_.to_op()}
{ {
{
DArena * arena = new DArena();
assert(arena);
*arena = DArena::map(config_.error_config_);
error_mm_.adopt(obj<AAllocator,DArena>(arena));
}
// TODO: allocate global_env // TODO: allocate global_env
} }
@ -50,6 +72,12 @@ namespace xo {
return mm_.to_op(); return mm_.to_op();
} }
obj<AAllocator>
VirtualSchematikaMachine::error_allocator() const noexcept
{
return error_mm_.to_op();
}
void void
VirtualSchematikaMachine::visit_pools(const MemorySizeVisitor & visitor) const VirtualSchematikaMachine::visit_pools(const MemorySizeVisitor & visitor) const
{ {
@ -113,7 +141,7 @@ namespace xo {
{ {
this->pc_ = VsmInstr::c_eval; this->pc_ = VsmInstr::c_eval;
this->expr_ = expr; this->expr_ = expr;
this->value_ = obj<AGCObject>(); this->value_ = VsmResult(obj<AGCObject>());
this->cont_ = VsmInstr::c_halt; this->cont_ = VsmInstr::c_halt;
this->run(); this->run();
@ -200,7 +228,7 @@ namespace xo {
auto expr auto expr
= obj<AExpression,DConstant>::from(expr_); = obj<AExpression,DConstant>::from(expr_);
this->value_ = expr.data()->value(); this->value_ = VsmResult(expr.data()->value());
this->pc_ = this->cont_; this->pc_ = this->cont_;
} }
@ -250,7 +278,7 @@ namespace xo {
local_env_); local_env_);
this->value_ this->value_
= obj<AGCObject>(obj<AGCObject,DClosure>(closure)); = VsmResult(obj<AGCObject>(obj<AGCObject,DClosure>(closure)));
this->pc_ = this->cont_; this->pc_ = this->cont_;
} }
@ -341,7 +369,7 @@ namespace xo {
// TODO: check argument types // TODO: check argument types
this->value_ = fn_.apply_nocheck(rcx_.to_op(), args_); this->value_ = VsmResult(fn_.apply_nocheck(rcx_.to_op(), args_));
this->pc_ = cont_; this->pc_ = cont_;
return; return;

View file

@ -225,7 +225,6 @@ namespace xo {
vsm.visit_pools(visitor); vsm.visit_pools(visitor);
} }
#ifdef NOT_YET
TEST_CASE("VirtualSchematikaMachine-apply2", "[interpreter2][VSM]") TEST_CASE("VirtualSchematikaMachine-apply2", "[interpreter2][VSM]")
{ {
scope log(XO_DEBUG(true)); scope log(XO_DEBUG(true));
@ -265,7 +264,6 @@ namespace xo {
FacetRegistry::instance().visit_pools(visitor); FacetRegistry::instance().visit_pools(visitor);
vsm.visit_pools(visitor); vsm.visit_pools(visitor);
} }
#endif
} /*namespace ut*/ } /*namespace ut*/
} /*namespace xo*/ } /*namespace xo*/