ArkScript
A small, lisp-inspired, functional scripting language
Debugger.hpp
Go to the documentation of this file.
1/**
2 * @file Debugger.hpp
3 * @author Lexy Plateau (lexplt.dev@gmail.com)
4 * @brief Debugger used by the VM when an error or a breakpoint is reached
5 * @date 2026-01-12
6 *
7 * @copyright Copyright (c) 2026-01-12
8 *
9 */
10
11#ifndef ARK_VM_DEBUGGER_HPP
12#define ARK_VM_DEBUGGER_HPP
13
14#include <deque>
15#include <utility>
16#include <vector>
17#include <memory>
18#include <string>
19#include <optional>
20#include <functional>
21#include <filesystem>
22
26
27namespace Ark
28{
29 class VM;
30}
31
32namespace Ark::internal
33{
35 {
36 std::size_t ip;
37 std::size_t pp;
38 uint16_t sp;
39 uint16_t fc;
40 std::vector<ScopeView> locals;
41 std::vector<std::shared_ptr<ClosureScope>> closure_scopes;
42 };
43
45 {
46 std::vector<bytecode_t> pages;
47 std::vector<std::string> symbols;
48 std::vector<Value> constants;
49 };
50
52 {
53 uint8_t inst;
54 uint8_t padding;
55 uint16_t arg;
56 std::size_t ip;
57 std::size_t pp;
58 };
59
61 {
62 public:
63 /**
64 * @brief Create a new Debugger object
65 *
66 * @param context context from the VM before displaying a backtrace
67 * @param libenv
68 * @param symbols symbols table of the VM
69 * @param constants constants table of the VM
70 */
71 Debugger(const ExecutionContext& context, const std::vector<std::filesystem::path>& libenv, const std::vector<std::string>& symbols, const std::vector<Value>& constants);
72
73 /**
74 * @brief Create a new Debugger object that will use lines from a file as prompts, instead of waiting for user inputs
75 *
76 * @param libenv
77 * @param path_to_prompt_file
78 * @param os output stream
79 * @param symbols symbols table of the VM
80 * @param constants constants table of the VM
81 */
82 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);
83
84 /**
85 * @brief Save the current VM state, to get back to it once the debugger is done running
86 *
87 * @param context
88 */
89 void saveState(const ExecutionContext& context);
90
91 /**
92 * @brief Reset a VM context to the last state saved by the debugger
93 *
94 * @param context context to reset
95 */
97
98 /**
99 * @brief Start the debugger shell
100 *
101 * @param vm
102 * @param context
103 * @param from_breakpoint true if the debugger is being invoked from a breakpoint
104 */
105 void run(VM& vm, ExecutionContext& context, bool from_breakpoint);
106
107 void registerInstruction(uint8_t inst, uint8_t padding, uint16_t arg, std::size_t ip, std::size_t pp) noexcept;
108
109 [[nodiscard]] ARK_ALWAYS_INLINE bool isRunning() const noexcept
110 {
111 return m_running;
112 }
113
114 [[nodiscard]] ARK_ALWAYS_INLINE bool shouldQuitVM() const noexcept
115 {
116 return m_quit_vm;
117 }
118
119 private:
120 struct Command;
122 {
125 std::size_t ip, pp;
126 const Command& me;
127 };
128
130 {
131 std::string prefix;
132 };
133
134 struct Command
135 {
136 using Action_t = std::function<bool(const std::string&, const CommandArgs&)>;
137 using Args_t = std::vector<std::pair<std::string, std::string>>;
138
140 std::vector<std::string> names;
142 std::string description;
144
145 Command(std::string name, std::string desc, Action_t&& do_this) :
146 is_exact(true), names({ std::move(name) }), description(std::move(desc)), action(do_this)
147 {}
148
149 Command(const std::initializer_list<std::string> list_of_names, std::string desc, Action_t&& do_this) :
150 is_exact(true), names(list_of_names), description(std::move(desc)), action(do_this)
151 {}
152
153 Command(StartsWith start, std::vector<std::pair<std::string, std::string>> arguments, std::string desc, Action_t&& do_this) :
154 is_exact(false), names({ std::move(start.prefix) }), args(std::move(arguments)), description(std::move(desc)), action(std::move(do_this))
155 {}
156
157 [[nodiscard]] std::optional<Args_t> getArgs(const std::string& line, std::ostream& os) const;
158
159 [[nodiscard]] std::optional<std::size_t> argAsCount(const std::string& line, std::size_t idx, std::ostream& os) const;
160 };
161
162 std::vector<Command> m_commands;
163
164 std::vector<std::unique_ptr<SavedState>> m_states;
165 std::vector<std::filesystem::path> m_libenv;
166 std::vector<std::string> m_symbols;
167 std::vector<Value> m_constants;
168 bool m_running { false };
169 bool m_quit_vm { false };
170
171 std::deque<TracedInstruction> m_previous_insts;
172
173 std::ostream& m_os;
175 std::unique_ptr<std::istream> m_prompt_stream;
176 std::string m_code; ///< Code added while inside the debugger
177 std::size_t m_line_count { 0 };
178
179 void initCommands();
180 [[nodiscard]] std::optional<Command> matchCommand(const std::string& line) const;
181
182 void showContext(const VM& vm, const ExecutionContext& context) const;
183 void showStack(VM& vm, const ExecutionContext& context, std::size_t count) const;
184 void showLocals(VM& vm, ExecutionContext& context, std::size_t count) const;
185 void showScopes(VM& vm, ExecutionContext& context, std::size_t count) const;
186 void showPreviousInstructions(const VM& vm, std::size_t count) const;
187
188 void showLocals(const ScopeView& scope, VM& vm, std::optional<std::size_t> limit = std::nullopt) const;
189
190 std::optional<std::string> prompt(std::size_t ip, std::size_t pp, VM& vm, ExecutionContext& context);
191
192 /**
193 * @brief Take care of compiling new code using the existing data tables
194 *
195 * @param code
196 * @param start_page_at_offset offset to start the new pages at
197 * @return std::optional<CompiledPrompt> optional set of bytecode pages, symbols and constants if compilation succeeded
198 */
199 [[nodiscard]] std::optional<CompiledPrompt> compile(const std::string& code, std::size_t start_page_at_offset) const;
200 };
201}
202
203#endif // ARK_VM_DEBUGGER_HPP
Common code for the compiler.
Keeping track of the internal data needed by the VM.
Default value type handled by the virtual machine.
The ArkScript virtual machine, executing ArkScript bytecode.
Definition VM.hpp:40
void run(VM &vm, ExecutionContext &context, bool from_breakpoint)
Start the debugger shell.
Definition Debugger.cpp:67
std::string m_code
Code added while inside the debugger.
Definition Debugger.hpp:176
std::vector< std::unique_ptr< SavedState > > m_states
Definition Debugger.hpp:164
void showStack(VM &vm, const ExecutionContext &context, std::size_t count) const
Definition Debugger.cpp:319
std::optional< Command > matchCommand(const std::string &line) const
Definition Debugger.cpp:273
void showLocals(VM &vm, ExecutionContext &context, std::size_t count) const
Definition Debugger.cpp:342
std::deque< TracedInstruction > m_previous_insts
Definition Debugger.hpp:171
std::optional< std::string > prompt(std::size_t ip, std::size_t pp, VM &vm, ExecutionContext &context)
Definition Debugger.cpp:436
void resetContextToSavedState(ExecutionContext &context)
Reset a VM context to the last state saved by the debugger.
Definition Debugger.cpp:54
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.
Definition Debugger.cpp:20
void registerInstruction(uint8_t inst, uint8_t padding, uint16_t arg, std::size_t ip, std::size_t pp) noexcept
Definition Debugger.cpp:135
std::unique_ptr< std::istream > m_prompt_stream
Definition Debugger.hpp:175
std::ostream & m_os
Definition Debugger.hpp:173
std::vector< std::string > m_symbols
Definition Debugger.hpp:166
void showScopes(VM &vm, ExecutionContext &context, std::size_t count) const
Definition Debugger.cpp:357
ARK_ALWAYS_INLINE bool shouldQuitVM() const noexcept
Definition Debugger.hpp:114
std::vector< Command > m_commands
Definition Debugger.hpp:162
void showContext(const VM &vm, const ExecutionContext &context) const
Definition Debugger.cpp:294
void saveState(const ExecutionContext &context)
Save the current VM state, to get back to it once the debugger is done running.
Definition Debugger.cpp:42
std::vector< std::filesystem::path > m_libenv
Definition Debugger.hpp:165
std::vector< Value > m_constants
Definition Debugger.hpp:167
void showPreviousInstructions(const VM &vm, std::size_t count) const
Definition Debugger.cpp:414
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.
Definition Debugger.cpp:492
ARK_ALWAYS_INLINE bool isRunning() const noexcept
Definition Debugger.hpp:109
A class to handle the VM scope more efficiently.
Definition ScopeView.hpp:27
STL namespace.
std::vector< Value > constants
Definition Debugger.hpp:48
std::vector< std::string > symbols
Definition Debugger.hpp:47
std::vector< bytecode_t > pages
Definition Debugger.hpp:46
std::vector< std::pair< std::string, std::string > > Args_t
Definition Debugger.hpp:137
std::optional< std::size_t > argAsCount(const std::string &line, std::size_t idx, std::ostream &os) const
Definition Debugger.cpp:167
Command(const std::initializer_list< std::string > list_of_names, std::string desc, Action_t &&do_this)
Definition Debugger.hpp:149
std::vector< std::string > names
Definition Debugger.hpp:140
std::function< bool(const std::string &, const CommandArgs &)> Action_t
Definition Debugger.hpp:136
Command(StartsWith start, std::vector< std::pair< std::string, std::string > > arguments, std::string desc, Action_t &&do_this)
Definition Debugger.hpp:153
Command(std::string name, std::string desc, Action_t &&do_this)
Definition Debugger.hpp:145
std::optional< Args_t > getArgs(const std::string &line, std::ostream &os) const
Definition Debugger.cpp:146
std::vector< ScopeView > locals
Definition Debugger.hpp:40
std::vector< std::shared_ptr< ClosureScope > > closure_scopes
Definition Debugger.hpp:41