Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ Huge thanks to those people for their donations to support the project:

## Credits

This project was inspired by [game programing patterns](http://gameprogrammingpatterns.com/bytecode.html) and [ofan lisp.cpp](https://gist.github.com/ofan/721464)
This project was inspired by [game programing patterns](http://gameprogrammingpatterns.com/bytecode.html) and [anthay/Lisp90](https://github.com/anthay/Lisp90)

## Copyright and Licence information

Expand Down
3 changes: 3 additions & 0 deletions include/Ark/Error/Diagnostics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Ark::Diagnostics
std::string filename; ///< Complete path to the file where the error is
internal::FilePos start;
std::optional<internal::FilePos> end;
std::optional<std::string> maybe_content;

[[nodiscard]] bool wholeLineIsError() const
{
Expand Down Expand Up @@ -63,6 +64,8 @@ namespace Ark::Diagnostics
*/
std::string makeContextWithNode(const std::string& message, const internal::Node& node);

ARK_API void generateWithCode(const CodeError& e, const std::string& code, std::ostream& os = std::cout, bool colorize = true);

/**
* @brief Generate a diagnostic from an error and print it to the standard output
*
Expand Down
3 changes: 2 additions & 1 deletion include/Ark/Error/PrettyPrinting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ namespace Ark::Diagnostics
* @param target_line line of the error (0-indexed)
* @param end_target_line optional end line for the error (0-indexed)
* @param colorize if we should colorize the output or not
* @param maybe_content optional file content, if it was originally a string (via State.doString)
*/
Printer(const std::string& filename, std::size_t target_line, std::optional<std::size_t> end_target_line, bool colorize);
Printer(const std::string& filename, std::size_t target_line, std::optional<std::size_t> end_target_line, bool colorize, const std::optional<std::string>& maybe_content = std::nullopt);

/**
* @brief Slice the source code to get code between two cursors
Expand Down
2 changes: 1 addition & 1 deletion include/Ark/VM/VM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ namespace Ark
*/
[[noreturn]] static void throwVMError(internal::ErrorKind kind, const std::string& message);

inline const bytecode_t& bytecode() const
[[nodiscard]] inline const bytecode_t& bytecode() const
{
return m_state.m_bytecode;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/std
Submodule std updated 2 files
+60 −0 Dict.ark
+6 −0 tests/dict-tests.ark
5 changes: 4 additions & 1 deletion src/arkreactor/Compiler/Welder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,10 @@ namespace Ark
if ((m_features & FeatureTestFailOnException) > 0)
throw;

Diagnostics::generate(e);
if (filename != ARK_NO_NAME_FILE)
Diagnostics::generate(e);
else
Diagnostics::generateWithCode(e, code);
return false;
}
}
Expand Down
25 changes: 22 additions & 3 deletions src/arkreactor/Error/Diagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ namespace Ark::Diagnostics
{
assert(!(maybe_context && loc.wholeLineIsError()) && "Can not create error context when a context is given AND the whole line has to be underlined");

Printer source_printer(loc.filename, loc.start.line, loc.maybeEndLine(), colorize);
Printer source_printer(loc.filename, loc.start.line, loc.maybeEndLine(), colorize, loc.maybe_content);
if (!source_printer.hasContent())
{
showFileLocation(os, loc);
Expand Down Expand Up @@ -193,15 +193,17 @@ namespace Ark::Diagnostics

void helper(std::ostream& os, const std::string& message, const bool colorize,
const std::string& filename, const internal::FileSpan& at,
const std::optional<CodeErrorContext>& maybe_context = std::nullopt)
const std::optional<CodeErrorContext>& maybe_context = std::nullopt,
const std::optional<std::string>& maybe_file_content = std::nullopt)
{
std::string uniformised_filename;
std::ranges::replace_copy(filename, std::back_inserter(uniformised_filename), '\\', '/');
makeContext(
ErrorLocation {
.filename = uniformised_filename,
.start = at.start,
.end = at.end },
.end = at.end,
.maybe_content = maybe_file_content },
os, maybe_context, colorize);

for (const auto& text : Utils::splitString(message, '\n'))
Expand All @@ -222,6 +224,23 @@ namespace Ark::Diagnostics
return ss.str();
}

void generateWithCode(const CodeError& e, const std::string& code, std::ostream& os, bool colorize)
{
#ifdef ARK_BUILD_EXE
if (const char* nocolor = std::getenv("NOCOLOR"); nocolor != nullptr)
colorize = false;
#endif

helper(
os,
e.what(),
colorize,
e.context.filename,
e.context.at,
e.additional_context,
code);
}

void generate(const CodeError& e, std::ostream& os, bool colorize)
{
#ifdef ARK_BUILD_EXE
Expand Down
5 changes: 3 additions & 2 deletions src/arkreactor/Error/PrettyPrinting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ namespace Ark::Diagnostics

Printer::Printer(
const std::string& filename, const std::size_t target_line,
const std::optional<std::size_t> end_target_line, const bool colorize) :
const std::optional<std::size_t> end_target_line, const bool colorize,
const std::optional<std::string>& maybe_content) :
m_should_colorize(colorize)
{
const std::string code = filename == ARK_NO_NAME_FILE ? "" : Utils::readFile(filename);
const std::string code = filename == ARK_NO_NAME_FILE ? maybe_content.value_or("") : Utils::readFile(filename);
m_source = Utils::splitString(code, '\n');

m_window = Window(target_line, end_target_line.value_or(target_line), m_source.size());
Expand Down
3 changes: 2 additions & 1 deletion src/arkreactor/VM/Debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ namespace Ark::internal
Diagnostics::ErrorLocation {
.filename = filename,
.start = FilePos { .line = maybe_source_loc->line, .column = 0 },
.end = std::nullopt },
.end = std::nullopt,
.maybe_content = std::nullopt },
m_os,
/* maybe_context= */ std::nullopt,
/* colorize= */ m_colorize);
Expand Down
3 changes: 2 additions & 1 deletion src/arkreactor/VM/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2366,7 +2366,8 @@ namespace Ark
Diagnostics::ErrorLocation {
.filename = filename,
.start = FilePos { .line = maybe_location->line, .column = 0 },
.end = std::nullopt },
.end = std::nullopt,
.maybe_content = std::nullopt },
os,
/* maybe_context= */ std::nullopt,
/* colorize= */ colorize);
Expand Down
41 changes: 35 additions & 6 deletions tests/unittests/Suites/EmbeddingSuite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
#include <Ark/Utils/Literals.hpp>
#include <Ark/Utils/Utils.hpp>
#include <vector>
#include <TestsHelper.hpp>
#include <iostream>

using namespace boost;
using namespace Ark::literals;

// cppcheck-suppress [constParameterCallback, constParameterReference]
Ark::Value my_function(std::vector<Ark::Value>& args, Ark::VM* vm [[maybe_unused]])
{
// checking argument number
Expand Down Expand Up @@ -187,6 +189,7 @@ ut::suite<"Embedding"> embedding_suite = [] {
Ark::State state;

int capture = 42;
// cppcheck-suppress constParameterReference
state.loadFunction("my_function", [=](std::vector<Ark::Value>& args, [[maybe_unused]] Ark::VM* /*vm*/) {
int solution = 0;
for (const Ark::Value& value : args)
Expand Down Expand Up @@ -215,7 +218,8 @@ ut::suite<"Embedding"> embedding_suite = [] {
"[load cpp function with captured reference]"_test = [] {
Ark::State state;

std::string name = "";
std::string name;
// cppcheck-suppress constParameterReference
state.loadFunction("my_function", [&name](std::vector<Ark::Value>& args, [[maybe_unused]] Ark::VM* /*vm*/) {
for (const Ark::Value& value : args)
{
Expand Down Expand Up @@ -243,6 +247,7 @@ ut::suite<"Embedding"> embedding_suite = [] {
"[load cpp function and call it from arkscript]"_test = [] {
Ark::State state;
state.loadFunction("my_function", my_function);
// cppcheck-suppress constParameterReference
state.loadFunction("foo", [](std::vector<Ark::Value>& args, Ark::VM* /*vm*/) {
return Ark::Value(static_cast<int>(args.size()));
});
Expand Down Expand Up @@ -305,6 +310,33 @@ ut::suite<"Embedding"> embedding_suite = [] {
};
};

"[fail to compile embedded code]"_test = [] {
constexpr uint16_t features = Ark::DefaultFeatures | Ark::FeatureTestFailOnException;
Ark::State state({ ARK_TESTS_ROOT "lib" });
const std::string code = "(import std.Sys) (let foo sys:args) (let b bar)";
const std::string expected = R"( 1 | (import std.Sys) (let foo sys:args) (let b bar)
| ^~~
Unbound variable error "bar" (variable is used but not defined))";

should("compile the string with an error") = [&] {
try
{
const bool ok = mut(state).doString(code, features);
expect(!ok) << fatal; // we shouldn't be here, the compilation has to fail
}
catch (const Ark::CodeError& e)
{
std::stringstream stream;
Ark::Diagnostics::generateWithCode(e, code, stream, /* colorize= */ false);
std::string diag = stream.str();
diag.erase(std::ranges::remove(diag, '\r').begin(), diag.end());
Ark::Utils::rtrim(diag);

expectOrDiff(expected, diag);
}
};
};

"[retrieve sys:args in embedded code]"_test = [] {
Ark::State state({ ARK_TESTS_ROOT "lib" });

Expand All @@ -313,10 +345,8 @@ ut::suite<"Embedding"> embedding_suite = [] {
};

Ark::VM vm(state);
double timestamp = 0.0;
should("return exit code 0") = [&] {
expect(mut(vm).run() == 0_i);
timestamp = vm["t"].number();
};

should("have symbol foo registered") = [&] {
Expand All @@ -335,10 +365,8 @@ ut::suite<"Embedding"> embedding_suite = [] {
};

Ark::VM vm(state);
double timestamp = 0.0;
should("return exit code 0") = [&] {
expect(mut(vm).run() == 0_i);
timestamp = vm["t"].number();
};

should("have symbol foo registered") = [&] {
Expand All @@ -353,6 +381,7 @@ ut::suite<"Embedding"> embedding_suite = [] {

"[load usertype and cpp lambdas and call them from arkscript]"_test = [] {
Ark::State state;
// cppcheck-suppress constParameterReference
state.loadFunction("getBreakfast", [](std::vector<Ark::Value>& n [[maybe_unused]], Ark::VM* vm [[maybe_unused]]) -> Ark::Value {
// we need to send the address of the object, which will be cast
// to void* internally
Expand All @@ -366,7 +395,7 @@ ut::suite<"Embedding"> embedding_suite = [] {
state.loadFunction("useBreakfast", [](std::vector<Ark::Value>& n, Ark::VM* vm [[maybe_unused]]) -> Ark::Value {
if (n[0].valueType() == Ark::ValueType::User && n[0].usertype().is<Breakfast>())
{
auto& bf = n[0].usertypeRef().as<Breakfast>();
const auto& bf = n[0].usertypeRef().as<Breakfast>();
if (bf == Breakfast::Pizza)
return Ark::Value(1);
return Ark::Value(2);
Expand Down
Loading