22
33#include < fmt/core.h>
44#include < fmt/color.h>
5+ #include < fmt/ostream.h>
56
67#include < Ark/State.hpp>
78#include < Ark/VM/VM.hpp>
1314namespace Ark ::internal
1415{
1516 Debugger::Debugger (const ExecutionContext& context, const std::vector<std::filesystem::path>& libenv, const std::vector<std::string>& symbols, const std::vector<Value>& constants) :
16- m_libenv (libenv), m_symbols(symbols), m_constants(constants), m_running( false ), m_quit_vm( false )
17+ m_libenv (libenv), m_symbols(symbols), m_constants(constants), m_os(std::cout ), m_colorize( true )
1718 {
1819 saveState (context);
1920 }
2021
22+ 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) :
23+ m_libenv (libenv), m_symbols(symbols), m_constants(constants), m_os(os), m_colorize(false )
24+ {
25+ m_prompt_stream = std::make_unique<std::ifstream>(path_to_prompt_file);
26+ }
27+
2128 void Debugger::saveState (const ExecutionContext& context)
2229 {
2330 m_states.emplace_back (
@@ -45,72 +52,89 @@ namespace Ark::internal
4552
4653 void Debugger::run (VM& vm, ExecutionContext& context)
4754 {
55+ showContext (vm, context);
56+
4857 m_running = true ;
4958 const bool is_vm_running = vm.m_running ;
50-
51- // show the line where the breakpoint hit
52- const auto maybe_source_loc = vm.findSourceLocation (context.ip , context.pp );
53- if (maybe_source_loc)
54- {
55- const auto filename = vm.m_state .m_filenames [maybe_source_loc->filename_id ];
56-
57- if (Utils::fileExists (filename))
58- {
59- fmt::println (" " );
60- Diagnostics::makeContext (
61- Diagnostics::ErrorLocation {
62- .filename = filename,
63- .start = FilePos { .line = maybe_source_loc->line , .column = 0 },
64- .end = std::nullopt },
65- std::cout,
66- /* maybe_context= */ std::nullopt ,
67- /* colorize= */ true );
68- fmt::println (" " );
69- }
70- }
59+ const std::size_t ip_at_breakpoint = context.ip ,
60+ pp_at_breakpoint = context.pp ;
61+ // create dedicated scope, so that we won't be overwriting existing variables
62+ context.locals .emplace_back (context.scopes_storage .data (), context.locals .back ().storageEnd ());
63+ std::size_t last_ip = 0 ;
7164
7265 while (true )
7366 {
74- std::optional<std::string> maybe_input = prompt ();
67+ std::optional<std::string> maybe_input = prompt (ip_at_breakpoint, pp_at_breakpoint );
7568
7669 if (maybe_input)
7770 {
7871 const std::string& line = maybe_input.value ();
7972
80- if (const auto pages = compile (m_code + line, vm.m_state .m_pages .size ()); pages .has_value ())
73+ if (const auto compiled = compile (m_code + line, vm.m_state .m_pages .size ()); compiled .has_value ())
8174 {
82- context.ip = 0 ;
75+ context.ip = last_ip ;
8376 context.pp = vm.m_state .m_pages .size ();
84- // create dedicated scope, so that we won't be overwriting existing variables
85- context.locals .emplace_back (context.scopes_storage .data (), context.locals .back ().storageEnd ());
8677
87- vm.m_state .extendBytecode (pages. value (), m_symbols, m_constants );
78+ vm.m_state .extendBytecode (compiled-> pages , compiled-> symbols , compiled-> constants );
8879
8980 if (vm.safeRun (context) == 0 )
9081 {
9182 // executing code worked
92- m_code += line;
83+ m_code += line + " \n " ;
84+ // place ip to end of bytecode instruction (HALT)
85+ last_ip = context.ip - 4 ;
9386
9487 const Value* maybe_value = vm.peekAndResolveAsPtr (context);
9588 if (maybe_value != nullptr && maybe_value->valueType () != ValueType::Undefined && maybe_value->valueType () != ValueType::InstPtr)
96- fmt::println (" {}" , fmt::styled (maybe_value->toString (vm), fmt::fg (fmt::color::chocolate)));
89+ fmt::println (
90+ m_os,
91+ " {}" ,
92+ fmt::styled (
93+ maybe_value->toString (vm),
94+ m_colorize ? fmt::fg (fmt::color::chocolate) : fmt::text_style ()));
9795 }
98-
99- context.locals .pop_back ();
10096 }
10197 }
10298 else
10399 break ;
104100 }
105101
106- m_running = false ;
102+ context.locals .pop_back ();
103+
107104 // we do not want to retain code from the past executions
108105 m_code.clear ();
106+ m_line_count = 0 ;
107+
109108 // we hit a HALT instruction that set 'running' to false, ignore that if we were still running!
110109 vm.m_running = is_vm_running;
110+ m_running = false ;
111111 }
112112
113- std::optional<std::string> Debugger::prompt ()
113+ void Debugger::showContext (const VM& vm, const ExecutionContext& context) const
114+ {
115+ // show the line where the breakpoint hit
116+ const auto maybe_source_loc = vm.findSourceLocation (context.ip , context.pp );
117+ if (maybe_source_loc)
118+ {
119+ const auto filename = vm.m_state .m_filenames [maybe_source_loc->filename_id ];
120+
121+ if (Utils::fileExists (filename))
122+ {
123+ fmt::println (m_os, " " );
124+ Diagnostics::makeContext (
125+ Diagnostics::ErrorLocation {
126+ .filename = filename,
127+ .start = FilePos { .line = maybe_source_loc->line , .column = 0 },
128+ .end = std::nullopt },
129+ m_os,
130+ /* maybe_context= */ std::nullopt ,
131+ /* colorize= */ m_colorize);
132+ fmt::println (m_os, " " );
133+ }
134+ }
135+ }
136+
137+ std::optional<std::string> Debugger::prompt (const std::size_t ip, const std::size_t pp)
114138 {
115139 std::string code;
116140 long open_parens = 0 ;
@@ -119,29 +143,42 @@ namespace Ark::internal
119143 while (true )
120144 {
121145 const bool unfinished_block = open_parens != 0 || open_braces != 0 ;
122- fmt::print (" dbg:{:0>3}{} " , m_line_count, unfinished_block ? " :" : " >" );
146+ fmt::print (
147+ m_os,
148+ " dbg[{},{}]:{:0>3}{} " ,
149+ fmt::format (" pp:{}" , fmt::styled (pp, m_colorize ? fmt::fg (fmt::color::green) : fmt::text_style ())),
150+ fmt::format (" ip:{}" , fmt::styled (ip, m_colorize ? fmt::fg (fmt::color::cyan) : fmt::text_style ())),
151+ m_line_count,
152+ unfinished_block ? " :" : " >" );
153+
123154 std::string line;
124- std::getline (std::cin, line);
155+ if (m_prompt_stream)
156+ {
157+ std::getline (*m_prompt_stream, line);
158+ fmt::println (m_os, " {}" , line); // because nothing is printed otherwise, and prompts get printed on the same line
159+ }
160+ else
161+ std::getline (std::cin, line);
125162
126163 Utils::trimWhitespace (line);
127164
128165 if (line == " c" || line == " continue" || line.empty ())
129166 {
130- fmt::println (" dbg: continue" );
167+ fmt::println (m_os, " dbg: continue" );
131168 return std::nullopt ;
132169 }
133170 else if (line == " q" || line == " quit" )
134171 {
135- fmt::println (" dbg: stop" );
172+ fmt::println (m_os, " dbg: stop" );
136173 m_quit_vm = true ;
137174 return std::nullopt ;
138175 }
139176 else if (line == " help" )
140177 {
141- fmt::println (" Available commands:" );
142- fmt::println (" help -- display this message" );
143- fmt::println (" c, continue -- resume execution" );
144- fmt::println (" q, quit -- quit the debugger, stopping the script execution" );
178+ fmt::println (m_os, " Available commands:" );
179+ fmt::println (m_os, " help -- display this message" );
180+ fmt::println (m_os, " c, continue -- resume execution" );
181+ fmt::println (m_os, " q, quit -- quit the debugger, stopping the script execution" );
145182 }
146183 else
147184 {
@@ -159,7 +196,7 @@ namespace Ark::internal
159196 return code;
160197 }
161198
162- std::optional<std::vector< bytecode_t >> Debugger::compile (const std::string& code, const std::size_t start_page_at_offset)
199+ std::optional<CompiledPrompt> Debugger::compile (const std::string& code, const std::size_t start_page_at_offset) const
163200 {
164201 Welder welder (0 , m_libenv, DefaultFeatures);
165202 if (!welder.computeASTFromStringWithKnownSymbols (code, m_symbols))
@@ -175,9 +212,6 @@ namespace Ark::internal
175212 const auto inst_locs = bcr.instLocations (files);
176213 const auto [pages, _] = bcr.code (inst_locs);
177214
178- m_symbols = syms.symbols ;
179- m_constants = vals.values ;
180-
181- return pages;
215+ return std::optional (CompiledPrompt (pages, syms.symbols , vals.values ));
182216 }
183217}
0 commit comments