8#include <fmt/ostream.h>
20 using namespace internal;
23 m_state(state), m_exit_code(0), m_running(
false)
25 m_execution_contexts.emplace_back(std::make_unique<ExecutionContext>());
49 context.
locals.reserve(64);
70 "`{}' is a {}, not a Closure, can not get the field `{}' from it",
78 "{} is not a Closure, can not get the field `{}' from it",
96 "`{0}' isn't in the closure environment: {1}",
102 "`{0}' isn't in the closure environment: {1}. A variable in the package might have the same name as '{0}', "
103 "and name resolution tried to fully qualify it. Rename either the variable or the capture to solve this",
113 l.
list().reserve(count);
115 for (std::size_t i = 0; i < count; ++i)
125 std::vector<Value> args = { *list };
126 for (std::size_t i = 0; i < count; ++i)
134 for (std::size_t i = 0; i < count; ++i)
141 const auto it = std::ranges::find(m_state.m_symbols, name);
142 if (it == m_state.m_symbols.end())
148 const auto dist = std::distance(m_state.m_symbols.begin(), it);
153 const auto id =
static_cast<uint16_t
>(dist);
154 Value* var = findNearestVariable(
id, context);
165 namespace fs = std::filesystem;
169 std::string path = file;
172 path = (fs::path(
m_state.
m_filename).parent_path() / fs::path(file)).relative_path().string();
174 std::shared_ptr<SharedLibrary> lib;
177 lib = std::make_shared<SharedLibrary>(path);
182 std::string lib_path = (fs::path(v) / fs::path(file)).
string();
186 return (val->path() == path || val->path() == lib_path);
193 lib = std::make_shared<SharedLibrary>(lib_path);
201 auto lib_path = std::accumulate(
205 [](
const std::string& a,
const fs::path& b) -> std::string {
206 return a +
"\n\t- " + b.string();
210 fmt::format(
"Could not find module '{}'. Searched under\n\t- {}\n\t- {}", file, path, lib_path));
218 std::vector<ScopeView::pair_t> data;
222 while (map[i].name !=
nullptr)
226 data.emplace_back(
static_cast<uint16_t
>(std::distance(
m_state.
m_symbols.begin(), it)),
Value(map[i].value));
231 context.
locals.back().insertFront(data);
233 catch (
const std::system_error& e)
238 "An error occurred while loading module '{}': {}\nIt is most likely because the versions of the module and the language don't match.",
251 const std::lock_guard lock(
m_mutex);
262 const auto it = std::ranges::find_if(
264 [](
const std::unique_ptr<ExecutionContext>& context) ->
bool {
265 return !context->primary && context->isFree();
284 assert(!ctx->
primary &&
"The new context shouldn't be marked as primary!");
293 for (
const auto& scope_view : primary_ctx.
locals)
296 for (std::size_t i = 0; i < scope_view.size(); ++i)
298 const auto& [id, val] = scope_view.atPos(i);
299 new_scope.pushBack(
id, val);
308 const std::lock_guard lock(
m_mutex);
314 std::ranges::remove_if(
316 [ec](
const std::unique_ptr<ExecutionContext>& ctx) {
317 return ctx.get() == ec;
338 m_futures.push_back(std::make_unique<Future>(ctx,
this, args));
348 [f](
const std::unique_ptr<Future>& future) {
349 return future.get() == f;
360 const mapping* map = shared_lib->get<
mapping* (*)()>(
"getFunctionsMapping")();
363 while (map[i].name !=
nullptr)
370 Value(map[i].value));
378 catch (
const std::system_error&)
391 throw std::runtime_error(std::string(
errorKinds[
static_cast<std::size_t
>(kind)]) +
": " + message +
"\n");
417 catch (
const Error& e)
419 if (fail_with_exception)
421 std::stringstream stream;
429 catch (
const std::exception& e)
431 if (fail_with_exception)
433 std::stringstream stream;
441 if (fail_with_exception)
444# ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
447 fmt::println(
"Unknown error");
457 template <
bool WithDebugger>
460#if ARK_USE_COMPUTED_GOTOS
461# define TARGET(op) TARGET_##op:
462# define DISPATCH_GOTO() \
463 _Pragma("GCC diagnostic push") \
464 _Pragma("GCC diagnostic ignored \"-Wpedantic\"") goto* opcode_targets[inst];
465 _Pragma(
"GCC diagnostic pop")
466# define GOTO_HALT() goto dispatch_end
468# define TARGET(op) case op:
469# define DISPATCH_GOTO() goto dispatch_opcode
470# define GOTO_HALT() break
473#define FETCH_NEXT_INSTRUCTION() \
476 inst = m_state.inst(context.pp, context.ip); \
477 padding = m_state.inst(context.pp, context.ip + 1); \
478 arg = static_cast<uint16_t>((m_state.inst(context.pp, context.ip + 2) << 8) + \
479 m_state.inst(context.pp, context.ip + 3)); \
481 context.inst_exec_counter = (context.inst_exec_counter + 1) % VMOverflowBufferSize; \
482 if constexpr (WithDebugger) \
484 if (!m_debugger) initDebugger(context); \
485 m_debugger->registerInstruction(inst, padding, arg, context.ip - 4, context.pp); \
487 if (context.inst_exec_counter < 2 && context.sp >= VMStackSize) \
488 stackOverflowError(context); \
491 FETCH_NEXT_INSTRUCTION(); \
493#define UNPACK_ARGS() \
496 secondary_arg = static_cast<uint16_t>((padding << 4) | (arg & 0xf000) >> 12); \
497 primary_arg = arg & 0x0fff; \
500#if ARK_USE_COMPUTED_GOTOS
501# pragma GCC diagnostic push
502# pragma GCC diagnostic ignored "-Wpedantic"
503 constexpr std::array opcode_targets = {
505# define X(name, value) &&TARGET_##name,
506# include <Ark/Compiler/Instructions.x>
512 static_assert(opcode_targets.size() ==
static_cast<std::size_t
>(Instruction::InstructionsCount) &&
"Some instructions are not implemented in the VM");
513# pragma GCC diagnostic pop
519 uint16_t primary_arg = 0;
520 uint16_t secondary_arg = 0;
525#if !ARK_USE_COMPUTED_GOTOS
530#pragma region "Instructions"
542 TARGET(LOAD_FAST_BY_INDEX)
579 store(arg, tmp, context);
619 push(std::move(ts), context);
629 push(std::move(ts), context);
641 "Unhandled case when returning from function call. TS=({}){}, TS1=({}){}",
647 if (context.
fc <= untilFrameCount)
660 TARGET(PUSH_RETURN_ADDRESS)
680 context.
locals.back().reset();
703 TARGET(RENAME_NEXT_CAPTURE)
720 var->usertypeRef().del();
742 TARGET(GET_FIELD_AS_CLOSURE)
767 std::vector<Value> args = { *list };
768 for (uint16_t i = 0; i < arg; ++i)
776 const auto size =
static_cast<uint16_t
>(list->
constList().size());
779 obj.
list().reserve(size + arg);
781 for (uint16_t i = 0; i < arg; ++i)
783 push(std::move(obj), context);
794 for (uint16_t i = 0; i < arg; ++i)
804 std::ranges::copy(next->
list(), std::back_inserter(obj.list()));
806 push(std::move(obj), context);
822 for (uint16_t i = 0; i < arg; ++i)
832 std::ranges::copy(next->
list(), std::back_inserter(list->
list()));
849 long idx =
static_cast<long>(number.
number());
850 idx = idx < 0 ? static_cast<long>(list.
list().size()) + idx : idx;
851 if (std::cmp_greater_equal(idx, list.
list().size()) || idx < 0)
854 fmt::format(
"pop index ({}) out of range (list size: {})", idx, list.
list().size()));
856 list.
list().erase(list.
list().begin() + idx);
874 long idx =
static_cast<long>(number.
number());
875 idx = idx < 0 ? static_cast<long>(list->
list().size()) + idx : idx;
876 if (std::cmp_greater_equal(idx, list->
list().size()) || idx < 0)
879 fmt::format(
"pop! index ({}) out of range (list size: {})", idx, list->
list().size()));
885 number = list->
list()[
static_cast<std::size_t
>(idx)];
886 list->
list().erase(list->
list().begin() + idx);
888 push(number, context);
918 { *container, key, new_value });
924 push(new_value, context);
929 long idx =
static_cast<long>(key.
number());
930 idx = idx < 0 ? static_cast<long>(size) + idx : idx;
931 if (std::cmp_greater_equal(idx, size) || idx < 0)
934 fmt::format(
"@= index ({}) out of range (indexable size: {})", idx, size));
938 container->
list()[
static_cast<std::size_t
>(idx)] = new_value;
940 push(new_value, context);
944 container->
stringRef()[
static_cast<std::size_t
>(idx)] = new_value.
string()[0];
969 { *list, x, y, new_value });
971 long idx_y =
static_cast<long>(x.
number());
972 idx_y = idx_y < 0 ? static_cast<long>(list->
list().size()) + idx_y : idx_y;
973 if (std::cmp_greater_equal(idx_y, list->
list().size()) || idx_y < 0)
976 fmt::format(
"@@= index (y: {}) out of range (list size: {})", idx_y, list->
list().size()));
978 if (!list->
list()[
static_cast<std::size_t
>(idx_y)].isIndexable() ||
992 { *list, x, y, new_value });
994 const bool is_list = list->
list()[
static_cast<std::size_t
>(idx_y)].valueType() ==
ValueType::List;
995 const std::size_t size =
997 ? list->
list()[
static_cast<std::size_t
>(idx_y)].list().size()
998 : list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef().size();
1000 long idx_x =
static_cast<long>(y.
number());
1001 idx_x = idx_x < 0 ? static_cast<long>(size) + idx_x : idx_x;
1002 if (std::cmp_greater_equal(idx_x, size) || idx_x < 0)
1005 fmt::format(
"@@= index (x: {}) out of range (inner indexable size: {})", idx_x, size));
1009 list->
list()[
static_cast<std::size_t
>(idx_y)].list()[
static_cast<std::size_t
>(idx_x)] = new_value;
1011 push(new_value, context);
1015 list->
list()[
static_cast<std::size_t
>(idx_y)].stringRef()[
static_cast<std::size_t
>(idx_x)] = new_value.
string()[0];
1055 context.
locals.back().reset();
1065 *ts = *ts->reference();
1067 context.
locals.pop_back();
1091 { func, args_list });
1094 push(func, context);
1098 call(context,
static_cast<uint16_t
>(args_list.
constList().size()));
1105#pragma region "Operators"
1110 bool breakpoint_active =
true;
1118 m_debugger->resetContextToSavedState(context);
1181 throwVMError(ErrorKind::DivisionByZero, fmt::format(
"Can not compute expression (/ {} {})", a->toString(*
this), b->
toString(*
this)));
1366 { *closure, *field });
1375 auto id =
static_cast<std::uint16_t
>(std::distance(
m_state.
m_symbols.begin(), it));
1390#pragma region "Super Instructions"
1391 TARGET(LOAD_CONST_LOAD_CONST)
1407 TARGET(LOAD_CONST_SET_VAL)
1435 TARGET(SET_VAL_FROM_INDEX)
1458 { *var,
Value(secondary_arg) });
1463 TARGET(INCREMENT_BY_INDEX)
1479 { *var,
Value(secondary_arg) });
1497 setVal(primary_arg, &val, context);
1503 { *var,
Value(secondary_arg) });
1524 { *var,
Value(secondary_arg) });
1529 TARGET(DECREMENT_BY_INDEX)
1545 { *var,
Value(secondary_arg) });
1563 setVal(primary_arg, &val, context);
1569 { *var,
Value(secondary_arg) });
1580 store(secondary_arg, &tail, context);
1585 TARGET(STORE_TAIL_BY_INDEX)
1591 store(secondary_arg, &tail, context);
1602 store(secondary_arg, &head, context);
1607 TARGET(STORE_HEAD_BY_INDEX)
1613 store(secondary_arg, &head, context);
1623 store(secondary_arg, &l, context);
1634 setVal(secondary_arg, &tail, context);
1639 TARGET(SET_VAL_TAIL_BY_INDEX)
1645 setVal(secondary_arg, &tail, context);
1656 setVal(secondary_arg, &head, context);
1661 TARGET(SET_VAL_HEAD_BY_INDEX)
1667 setVal(secondary_arg, &head, context);
1687 TARGET(CALL_BUILTIN_WITHOUT_RETURN_ADDRESS)
1704 TARGET(LT_CONST_JUMP_IF_FALSE)
1709 jump(secondary_arg, context);
1713 TARGET(LT_CONST_JUMP_IF_TRUE)
1718 jump(secondary_arg, context);
1722 TARGET(LT_SYM_JUMP_IF_FALSE)
1726 if (!(*sym < *
loadSymbol(primary_arg, context)))
1727 jump(secondary_arg, context);
1731 TARGET(GT_CONST_JUMP_IF_TRUE)
1737 jump(secondary_arg, context);
1741 TARGET(GT_CONST_JUMP_IF_FALSE)
1747 jump(secondary_arg, context);
1751 TARGET(GT_SYM_JUMP_IF_FALSE)
1757 jump(secondary_arg, context);
1761 TARGET(EQ_CONST_JUMP_IF_TRUE)
1766 jump(secondary_arg, context);
1770 TARGET(EQ_SYM_INDEX_JUMP_IF_TRUE)
1775 jump(secondary_arg, context);
1779 TARGET(NEQ_CONST_JUMP_IF_TRUE)
1784 jump(secondary_arg, context);
1788 TARGET(NEQ_SYM_JUMP_IF_FALSE)
1792 if (*sym == *
loadSymbol(primary_arg, context))
1793 jump(secondary_arg, context);
1806 TARGET(CALL_SYMBOL_BY_INDEX)
1815 TARGET(CALL_CURRENT_PAGE)
1819 call(context, secondary_arg,
nullptr,
static_cast<PageAddr_t>(context.
pp));
1825 TARGET(GET_FIELD_FROM_SYMBOL)
1832 TARGET(GET_FIELD_FROM_SYMBOL_INDEX)
1846 TARGET(AT_SYM_INDEX_SYM_INDEX)
1853 TARGET(AT_SYM_INDEX_CONST)
1874 TARGET(CHECK_TYPE_OF_BY_INDEX)
1888 TARGET(APPEND_IN_PLACE_SYM)
1895 TARGET(APPEND_IN_PLACE_SYM_INDEX)
1911 len =
Value(
static_cast<int>(a->
string().size()));
1918 store(secondary_arg, &len, context);
1923 TARGET(LT_LEN_SYM_JUMP_IF_FALSE)
1933 size =
Value(
static_cast<int>(sym->
string().size()));
1942 jump(secondary_arg, context);
1952 const int other =
static_cast<int>(secondary_arg) - 2048;
1964 { *var,
Value(other) });
1974 const int other =
static_cast<int>(secondary_arg) - 2048;
1986 { *var,
Value(other) });
1996 const int other =
static_cast<int>(secondary_arg) - 2048;
2005 setVal(primary_arg, &val, context);
2011 { *var,
Value(other) });
2018 const auto op1 =
static_cast<Instruction>(padding),
2019 op2 =
static_cast<Instruction>((arg & 0xff00) >> 8),
2021 const std::size_t arg_count = (op1 != NOP) + (op2 != NOP) + (op3 != NOP);
2038 { *b,
Value(temp) });
2043 else if (arg_count == 3)
2050 { *a,
Value(temp) });
2058 "FUSED_MATH got {} arguments, expected 2 or 3. Arguments: {:x}{:x}{:x}. There is a bug in the codegen!",
2059 arg_count,
static_cast<uint8_t
>(op1),
static_cast<uint8_t
>(op2),
static_cast<uint8_t
>(op3)));
2064#if ARK_USE_COMPUTED_GOTOS
2075 for (
auto& local : std::ranges::reverse_view(context.locals))
2077 if (
const auto id = local.idFromValue(value);
id < m_state.m_symbols.size())
2085 std::vector<std::string> arg_names;
2086 arg_names.reserve(expected_arg_count + 1);
2088 std::size_t index = 0;
2097 if (arg_names.empty() && index == 0)
2099 for (std::size_t i = 0; i < expected_arg_count; ++i)
2100 arg_names.emplace_back(1,
static_cast<char>(
'a' + i));
2102 if (expected_arg_count > 0)
2103 arg_names.insert(arg_names.begin(),
"");
2105 std::vector<std::string> arg_vals;
2106 arg_vals.reserve(passed_arg_count + 1);
2108 for (std::size_t i = 0; i < passed_arg_count && i + 1 <= context.
sp; ++i)
2110 arg_vals.push_back(context.
stack[context.
sp - passed_arg_count + i].toString(*
this));
2111 if (passed_arg_count > 0)
2112 arg_vals.insert(arg_vals.begin(),
"");
2116 if (context.
sp >= 2 + passed_arg_count)
2119 context.
ip = context.
stack[context.
sp - 1 - (skip_function ? 1 : 0) - passed_arg_count].pageAddr();
2120 context.
pp = context.
stack[context.
sp - 2 - (skip_function ? 1 : 0) - passed_arg_count].pageAddr();
2132 "When calling `({}{})', received {} argument{}, but expected {}: `({}{})'",
2134 fmt::join(arg_vals,
" "),
2136 passed_arg_count > 1 ?
"s" :
"",
2139 fmt::join(arg_names,
" ")));
2152 std::string text = e.what();
2153 if (!text.empty() && text.back() !=
'\n')
2155 fmt::println(std::cerr,
"{}", text);
2162 const std::size_t saved_ip = context.
ip;
2163 const std::size_t saved_pp = context.
pp;
2164 const uint16_t saved_sp = context.
sp;
2170 "At IP: {}, PP: {}, SP: {}",
2172 fmt::styled(saved_ip / 4, fmt::fg(fmt::color::cyan)),
2173 fmt::styled(saved_pp, fmt::fg(fmt::color::green)),
2174 fmt::styled(saved_sp, fmt::fg(fmt::color::yellow)));
2178 m_debugger->resetContextToSavedState(context);
2182#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2192 std::optional<InstLoc> match = std::nullopt;
2196 if (location.page_pointer == pp && !match)
2202 if (location.page_pointer == pp && match && location.inst_pointer < ip / 4)
2206 if (location.page_pointer > pp || (location.page_pointer == pp && location.inst_pointer >= ip / 4))
2217 if (maybe_source_loc)
2220 return fmt::format(
"{}:{} -- IP: {}, PP: {}", filename, maybe_source_loc->line + 1, maybe_source_loc->inst_pointer, maybe_source_loc->page_pointer);
2222 return "No source location found";
2227 constexpr std::size_t max_consecutive_traces = 7;
2237 .filename = filename,
2238 .start =
FilePos { .line = maybe_location->line, .column = 0 },
2239 .end = std::nullopt,
2240 .maybe_content = std::nullopt },
2244 fmt::println(os,
"");
2252 std::string previous_trace;
2253 std::size_t displayed_traces = 0;
2254 std::size_t consecutive_similar_traces = 0;
2256 while (context.
fc != 0 && context.
pp != 0 && context.
sp > 0)
2259 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2266 if (func_name + loc_as_text != previous_trace)
2270 "[{:4}] In function `{}'{}",
2271 fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2272 fmt::styled(func_name, colorize ? fmt::fg(fmt::color::green) : fmt::text_style()),
2274 previous_trace = func_name + loc_as_text;
2276 consecutive_similar_traces = 0;
2278 else if (consecutive_similar_traces == 0)
2280 fmt::println(os,
" ...");
2281 ++consecutive_similar_traces;
2294 if (displayed_traces > max_consecutive_traces)
2296 fmt::println(os,
" ...");
2301 if (context.
pp == 0)
2304 const auto loc_as_text = maybe_call_loc ? fmt::format(
" ({}:{})",
m_state.
m_filenames[maybe_call_loc->filename_id], maybe_call_loc->line + 1) :
"";
2305 fmt::println(os,
"[{:4}] In global scope{}", fmt::styled(context.
fc, colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()), loc_as_text);
2309 fmt::println(os,
"\nCurrent scope variables values:");
2310 for (std::size_t i = 0, size = old_scope.
size(); i < size; ++i)
2315 fmt::styled(
m_state.
m_symbols[old_scope.
atPos(i).first], colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style()),
2316 old_scope.
atPos(i).second.toString(*
this));
Lots of utilities about string, filesystem and more.
Tools to report code errors nicely to the user.
Define how dictionaries are handled.
Lots of utilities about the filesystem.
The ArkScript virtual machine.
virtual std::string details(bool colorize, VM &vm) const
Ark state to handle the dirty job of loading and compiling ArkScript code.
std::vector< std::filesystem::path > m_libenv
std::unordered_map< std::string, Value > m_bound
Values bound to the State, to be used by the VM.
ARK_ALWAYS_INLINE constexpr uint8_t inst(const std::size_t pp, const std::size_t ip) const noexcept
Get an instruction in a given page, with a given instruction pointer.
std::vector< Value > m_constants
std::vector< internal::InstLoc > m_inst_locations
std::vector< std::string > m_filenames
std::vector< std::string > m_symbols
std::unique_ptr< internal::Debugger > m_debugger
void throwArityError(std::size_t passed_arg_count, std::size_t expected_arg_count, internal::ExecutionContext &context, bool skip_function=true)
void deleteContext(internal::ExecutionContext *ec)
Free a given execution context.
ARK_ALWAYS_INLINE void returnFromFuncCall(internal::ExecutionContext &context)
Destroy the current frame and get back to the previous one, resuming execution.
void showBacktraceWithException(const std::exception &e, internal::ExecutionContext &context)
std::vector< std::unique_ptr< internal::Future > > m_futures
Storing the promises while we are resolving them.
int m_exit_code
VM exit code, defaults to 0. Can be changed through sys:exit
Value & operator[](const std::string &name) noexcept
Retrieve a value from the virtual machine, given its symbol name.
std::vector< std::shared_ptr< internal::SharedLibrary > > m_shared_lib_objects
std::vector< std::unique_ptr< internal::ExecutionContext > > m_execution_contexts
ARK_ALWAYS_INLINE Value * peekAndResolveAsPtr(internal::ExecutionContext &context, std::size_t offset=0)
Return a pointer to the top of the stack without consuming it, and resolve it if possible.
std::string debugShowSource() const
void listAppendInPlace(Value *list, std::size_t count, internal::ExecutionContext &context)
ARK_ALWAYS_INLINE void jump(uint16_t address, internal::ExecutionContext &context)
ARK_ALWAYS_INLINE Value * loadSymbol(uint16_t id, internal::ExecutionContext &context)
Load a symbol by its id in the current context. Performs a lookup in the scope stack,...
void unsafeRun(internal::ExecutionContext &context, std::size_t untilFrameCount=0)
uint16_t findNearestVariableIdWithValue(const Value &value, internal::ExecutionContext &context) const noexcept
Find the nearest variable id with a given value.
ARK_ALWAYS_INLINE void setVal(uint16_t id, const Value *val, internal::ExecutionContext &context)
Change the value of a symbol given its identifier.
bool forceReloadPlugins() const
Used by the REPL to force reload all the plugins and their bound methods.
Value getField(Value *closure, uint16_t id, const internal::ExecutionContext &context, bool push_with_env=false)
internal::ExecutionContext * createAndGetContext()
Create an execution context and returns it.
void loadPlugin(uint16_t id, internal::ExecutionContext &context)
Load a plugin from a constant id.
void callBuiltin(internal::ExecutionContext &context, const Value &builtin, uint16_t argc, bool remove_return_address=true, bool remove_builtin=true)
Builtin called when the CALL_BUILTIN instruction is met in the bytecode.
int safeRun(internal::ExecutionContext &context, std::size_t untilFrameCount=0, bool fail_with_exception=false)
Run ArkScript bytecode inside a try catch to retrieve all the exceptions and display a stack trace if...
void initDebugger(internal::ExecutionContext &context)
ARK_ALWAYS_INLINE void push(const Value &value, internal::ExecutionContext &context) noexcept
Push a value on the stack.
ARK_ALWAYS_INLINE Value * peek(internal::ExecutionContext &context, std::size_t offset=0)
Return a pointer to the top of the stack without consuming it.
void deleteFuture(internal::Future *f)
Free a given future.
ARK_ALWAYS_INLINE Value * popAndResolveAsPtr(internal::ExecutionContext &context)
Pop a value from the stack and resolve it if possible, then return it.
std::mutex m_mutex_futures
Value call(const std::string &name, Args &&... args)
Call a function from ArkScript, by giving it arguments.
ARK_ALWAYS_INLINE Value * findNearestVariable(uint16_t id, internal::ExecutionContext &context) noexcept
Find the nearest variable of a given id.
void backtrace(internal::ExecutionContext &context, std::ostream &os=std::cerr, bool colorize=true)
Display a backtrace when the VM encounter an exception.
ARK_ALWAYS_INLINE Value * loadSymbolFromIndex(uint16_t index, internal::ExecutionContext &context)
Load a symbol by its (reversed) index in the current scope.
friend class internal::Closure
std::optional< internal::InstLoc > findSourceLocation(std::size_t ip, std::size_t pp) const
Find the nearest source location information given instruction and page pointers.
Value createList(std::size_t count, internal::ExecutionContext &context)
ARK_ALWAYS_INLINE Value * pop(internal::ExecutionContext &context)
Pop a value from the stack.
void init() noexcept
Initialize the VM according to the parameters.
static void throwVMError(internal::ErrorKind kind, const std::string &message)
Throw a VM error message.
VM(State &state) noexcept
Construct a new vm t object.
internal::Future * createFuture(std::vector< Value > &args)
Create a Future object from a function and its arguments and return a managed pointer to it.
int run(bool fail_with_exception=false)
Run the bytecode held in the state.
void usePromptFileForDebugger(const std::string &path, std::ostream &os=std::cout)
Configure the debugger to use a prompt file instead of asking the user for an input.
void exit(int code) noexcept
Ask the VM to exit with a given exit code.
ARK_ALWAYS_INLINE Value * loadConstAsPtr(uint16_t id) const
Load a constant from the constant table by its id.
ARK_ALWAYS_INLINE void store(uint16_t id, const Value *val, internal::ExecutionContext &context)
Create a new symbol with an associated value in the current scope.
const Dict_t & dict() const
const String_t & string() const
const List_t & constList() const
internal::Closure & refClosure()
void push_back(const Value &value)
Add an element to the list held by the value (if the value type is set to list)
ValueType valueType() const noexcept
std::string toString(VM &vm, bool show_as_code=false) const noexcept
bool isIndexable() const noexcept
internal::PageAddr_t pageAddr() const
A class to store fields captured by a closure.
std::string toString(VM &vm) const noexcept
Print the closure to a string.
ClosureScope & refScope() const noexcept
bool hasFieldEndingWith(const std::string &end, const VM &vm) const
Used when generating error messages in the VM, to see if a symbol might have been wrongly fully quali...
const std::shared_ptr< ClosureScope > & scopePtr() const
void set(const Value &key, const Value &value)
Assign a key to a value inside the dict.
std::size_t size() const
Compute the number of (key, value) pairs in the dict.
A class to handle the VM scope more efficiently.
ARK_ALWAYS_INLINE const pair_t & atPos(const std::size_t i) const noexcept
Return the element at index in scope.
ARK_ALWAYS_INLINE std::size_t size() const noexcept
Return the size of the scope.
ARK_API void makeContext(const ErrorLocation &loc, std::ostream &os, const std::optional< CodeErrorContext > &maybe_context, bool colorize)
Helper to create a colorized context to report errors to the user.
bool fileExists(const std::string &name) noexcept
Checks if a file exists.
bool isDouble(const std::string &s, double *output=nullptr)
Checks if a string is a valid double.
ARK_ALWAYS_INLINE Value head(Value *a)
ARK_ALWAYS_INLINE Value at(Value &container, Value &index, VM &vm)
ARK_ALWAYS_INLINE double doMath(double a, double b, const Instruction op)
ARK_ALWAYS_INLINE Value atAt(const Value *x, const Value *y, Value &list)
ARK_ALWAYS_INLINE Value tail(Value *a)
ARK_ALWAYS_INLINE std::string mathInstToStr(const Instruction op)
ARK_API const std::vector< std::pair< std::string, Value > > builtins
constexpr std::array< std::string_view, 7 > errorKinds
Instruction
The different bytecodes are stored here.
constexpr uint16_t MaxValue16Bits
@ Garbage
Used to signal a value was used and can/should be collected and removed from the stack.
@ Any
Used only for typechecking.
constexpr uint16_t FeatureVMDebugger
Disabled by default because embedding ArkScript should not launch the debugger on every error when ru...
std::string to_string(const Ark::ValueType type) noexcept
std::array< ScopeView::pair_t, ScopeStackSize > scopes_storage
All the ScopeView use this array to store id->value.
std::vector< std::shared_ptr< ClosureScope > > stacked_closure_scopes
Stack the closure scopes to keep the closure alive as long as we are calling them.
std::optional< uint16_t > capture_rename_id
std::size_t pp
Page pointer.
std::vector< ScopeView > locals
std::array< Value, VMStackSizeWithOverflowBuffer > stack
void setActive(const bool toggle)
const bool primary
Tells if the current ExecutionContext is the primary one or not.
uint16_t sp
Stack pointer.
uint16_t inst_exec_counter
std::size_t ip
Instruction pointer.
std::optional< ClosureScope > saved_scope
Scope created by CAPTURE <x> instructions, used by the MAKE_CLOSURE instruction.
A contract is a list of typed arguments that a function can follow.
A type definition within a contract.