5#include <fmt/ostream.h>
19 Debugger::Debugger(
const ExecutionContext& context,
const std::vector<std::filesystem::path>& libenv,
const std::vector<std::string>& symbols,
const std::vector<Value>& constants) :
22 m_constants(constants),
29 Debugger::Debugger(
const std::vector<std::filesystem::path>& libenv,
const std::string& path_to_prompt_file, std::ostream& os,
const std::vector<std::string>& symbols,
const std::vector<Value>& constants) :
32 m_constants(constants),
35 m_prompt_stream(
std::make_unique<
std::ifstream>(path_to_prompt_file))
41 std::make_unique<SavedState>(
52 const auto& [ip, pp, sp, fc, locals, closure_scopes] = *
m_states.back();
65 using namespace std::chrono_literals;
72 const std::size_t ip_at_breakpoint = context.
ip,
73 pp_at_breakpoint = context.
pp;
76 std::size_t last_ip = 0;
80 std::optional<std::string> maybe_input =
prompt(ip_at_breakpoint, pp_at_breakpoint, vm, context);
84 const std::string& line = maybe_input.value();
98 last_ip = context.
ip - 4;
101 if (maybe_value !=
nullptr &&
110 m_colorize ? fmt::fg(fmt::color::chocolate) : fmt::text_style()));
114 std::this_thread::sleep_for(50ms);
120 context.
locals.pop_back();
135 if (maybe_source_loc)
141 fmt::println(
m_os,
"");
144 .filename = filename,
145 .start =
FilePos { .line = maybe_source_loc->line, .column = 0 },
147 .maybe_content = std::nullopt },
151 fmt::println(
m_os,
"");
164 const auto color =
m_colorize ? fmt::fg(i % 2 == 0 ? fmt::color::forest_green : fmt::color::cornflower_blue) : fmt::text_style();
168 fmt::styled(context.
sp - i, color),
169 fmt::styled(context.
stack[context.
sp - i].toString(vm,
true), color));
174 fmt::println(
m_os,
"Stack is empty");
176 fmt::println(
m_os,
"");
181 const std::size_t limit = context.
locals[context.
locals.size() - 2].size();
182 if (limit > 0 && count > 0)
184 fmt::println(
m_os,
"scope size: {}", limit);
185 fmt::println(
m_os,
"index | id | name | type | value");
193 auto& [id, value] = context.
locals[context.
locals.size() - 2].atPosReverse(i);
194 const auto color =
m_colorize ? fmt::fg(i % 2 == 0 ? fmt::color::forest_green : fmt::color::cornflower_blue) : fmt::text_style();
198 "{:>5} | {:3} | {:14} | {:>9} | {}",
199 fmt::styled(limit - i - 1, color),
200 fmt::styled(
id, color),
203 fmt::styled(value.toString(vm,
true), color));
208 fmt::println(
m_os,
"Current scope is empty");
210 fmt::println(
m_os,
"");
215 std::string arg = line.substr(command.size());
225 std::size_t result = 0;
226 auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result);
228 if (ec == std::errc())
236 std::size_t count = default_value;
240 count = maybe_int.value();
243 fmt::println(
m_os,
"Couldn't parse argument as an integer");
254 long open_parens = 0;
255 long open_braces = 0;
259 const bool unfinished_block = open_parens != 0 || open_braces != 0;
262 "dbg[{},{}]:{:0>3}{} ",
263 fmt::format(
"pp:{}", fmt::styled(pp,
m_colorize ? fmt::fg(fmt::color::green) : fmt::text_style())),
264 fmt::format(
"ip:{}", fmt::styled(ip / 4,
m_colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style())),
266 unfinished_block ?
":" :
">");
272 fmt::println(
m_os,
"{}", line);
275 std::getline(std::cin, line);
279 if (line ==
"c" || line ==
"continue" || line.empty())
281 fmt::println(
m_os,
"dbg: continue");
284 else if (line ==
"q" || line ==
"quit")
286 fmt::println(
m_os,
"dbg: stop");
290 else if (line.starts_with(
"stack"))
297 else if (line.starts_with(
"locals"))
304 else if (line ==
"help")
306 fmt::println(
m_os,
"Available commands:");
307 fmt::println(
m_os,
" help -- display this message");
308 fmt::println(
m_os,
" c, continue -- resume execution");
309 fmt::println(
m_os,
" q, quit -- quit the debugger, stopping the script execution");
310 fmt::println(
m_os,
" stack <n=5> -- show the last n values on the stack");
311 fmt::println(
m_os,
" locals <n=5> -- show the last n values on the locals' stack");
321 if (open_braces == 0 && open_parens == 0)
329 std::optional<CompiledPrompt>
Debugger::compile(
const std::string& code,
const std::size_t start_page_at_offset)
const
339 const auto syms = bcr.
symbols();
340 const auto vals = bcr.
values(syms);
343 const auto [pages, _] = bcr.
code(inst_locs);
345 return std::optional(
CompiledPrompt(pages, syms.symbols, vals.values));
A bytecode disassembler for ArkScript.
Debugger used by the VM when an error or a breakpoint is reached.
Tools to report code errors nicely to the user.
Lots of utilities about the filesystem.
State used by the virtual machine: it loads the bytecode, can compile it if needed,...
The ArkScript virtual machine.
In charge of welding everything needed to compile code.
This class is just a helper to.
Filenames filenames(const Values &values) const
InstLocations instLocations(const Filenames &filenames) const
Code code(const InstLocations &instLocations) const
Values values(const Symbols &symbols) const
void feed(const std::string &file)
Construct needed data before displaying information about a given file.
std::vector< std::string > m_filenames
std::vector< std::string > m_symbols
std::vector< bytecode_t > m_pages
void extendBytecode(const std::vector< bytecode_t > &pages, const std::vector< std::string > &symbols, const std::vector< Value > &constants)
Used by the debugger to add code to the VM at runtime.
The ArkScript virtual machine, executing ArkScript 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...
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 * 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.
ValueType valueType() const noexcept
std::string toString(VM &vm, bool show_as_code=false) const noexcept
The welder joins all the compiler passes.
bool generateBytecodeUsingTables(const std::vector< std::string > &symbols, const std::vector< Value > &constants, std::size_t start_page_at_offset)
Compile the AST processed by computeASTFromFile / computeASTFromString, with prefilled symbols and co...
const bytecode_t & bytecode() const noexcept
bool computeASTFromStringWithKnownSymbols(const std::string &code, const std::vector< std::string > &symbols)
Compile code from a string, with a set of known symbols (useful for the debugger)
void run(VM &vm, ExecutionContext &context, bool from_breakpoint)
Start the debugger shell.
std::string m_code
Code added while inside the debugger.
std::vector< std::unique_ptr< SavedState > > m_states
static std::optional< std::size_t > parseStringAsInt(const std::string &str)
void showStack(VM &vm, const ExecutionContext &context, std::size_t count) const
void showLocals(VM &vm, ExecutionContext &context, std::size_t count) const
std::optional< std::string > prompt(std::size_t ip, std::size_t pp, VM &vm, ExecutionContext &context)
void resetContextToSavedState(ExecutionContext &context)
Reset a VM context to the last state saved by the debugger.
Debugger(const ExecutionContext &context, const std::vector< std::filesystem::path > &libenv, const std::vector< std::string > &symbols, const std::vector< Value > &constants)
Create a new Debugger object.
std::optional< std::size_t > getArgAndParseOrError(const std::string &command, const std::string &line, std::size_t default_value) const
std::unique_ptr< std::istream > m_prompt_stream
std::vector< std::string > m_symbols
static std::optional< std::string > getCommandArg(const std::string &command, const std::string &line)
void showContext(const VM &vm, const ExecutionContext &context) const
void saveState(const ExecutionContext &context)
Save the current VM state, to get back to it once the debugger is done running.
std::vector< std::filesystem::path > m_libenv
std::vector< Value > m_constants
std::optional< CompiledPrompt > compile(const std::string &code, std::size_t start_page_at_offset) const
Take care of compiling new code using the existing data tables.
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.
ARK_API long countOpenEnclosures(const std::string &line, char open, char close)
Count the open enclosure and its counterpart: (), {}, [].
ARK_API void trimWhitespace(std::string &line)
Remove whitespaces at the start and end of a string.
constexpr uint16_t DefaultFeatures
@ Garbage
Used to signal a value was used and can/should be collected and removed from the stack.
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::size_t pp
Page pointer.
std::vector< ScopeView > locals
std::array< Value, VMStackSizeWithOverflowBuffer > stack
uint16_t sp
Stack pointer.
std::size_t ip
Instruction pointer.