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
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -380,12 +380,13 @@ jobs:
sudo apt-get update --fix-missing
sudo apt-get install -y clang-16 lld-16 libc++-16-dev libc++abi-16-dev clang-tools-16

- name: Install valgrind 3.24.0
- name: Install valgrind 3.24+
shell: bash
run: |
wget --no-check-certificate http://ftp.us.debian.org/debian/pool/main/v/valgrind/valgrind_3.24.0-2_amd64.deb
valgrind_ver=3.24.0-3_amd64
wget --no-check-certificate http://ftp.us.debian.org/debian/pool/main/v/valgrind/valgrind_${valgrind_ver}.deb
sudo apt remove needrestart
sudo apt install ./valgrind_3.24.0-2_amd64.deb
sudo apt install ./valgrind_${valgrind_ver}.deb

- name: Valgrind checks for memory leaks
shell: bash
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
- when possible, accessing variables from the current scope is compiled to a new instruction `LOAD_SYMBOL_BY_INDEX`, to avoid the sometimes expansive lookup by id
- this works inside normal scopes (introduced by while loops) and functions scopes, but not for closures
- VM stack size is now 4096 instead of 8192
- `Ark::CodeError` now takes a `CodeErrorContext` to store the source (filename, line, column, expression) of an error

### Removed
- removed unused `NodeType::Closure`
Expand Down
34 changes: 23 additions & 11 deletions include/Ark/Compiler/AST/BaseParser.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#ifndef SRC_BASEPARSER_HPP
#define SRC_BASEPARSER_HPP
#ifndef ARK_COMPILER_AST_BASEPARSER_HPP
#define ARK_COMPILER_AST_BASEPARSER_HPP

#include <string>
#include <vector>
#include <initializer_list>

#include <Ark/Platform.hpp>
#include <Ark/Exceptions.hpp>
#include <Ark/Compiler/AST/Predicates.hpp>
#include <Ark/Compiler/AST/utf8_char.hpp>

Expand Down Expand Up @@ -50,29 +51,34 @@ namespace Ark::internal

void initParser(const std::string& filename, const std::string& code);

FilePosition getCursor() const;
[[nodiscard]] FilePosition getCursor() const;

[[nodiscard]] CodeErrorContext generateErrorContext(const std::string& expr);

/**
*
* @param error an error message
* @param exp the expression causing the error
* @param additional_context optional context created when a node is being parsed
*/
void error(const std::string& error, std::string exp);
void error(const std::string& error, std::string exp, const std::optional<CodeErrorContext>& additional_context = std::nullopt);

/**
* @brief Fetch the next token (space and paren delimited) to generate an error
*
* @param message an error message
* @param additional_context optional context created when a node is being parsed
*/
void errorWithNextToken(const std::string& message);
void errorWithNextToken(const std::string& message, const std::optional<CodeErrorContext>& additional_context = std::nullopt);

/**
* @brief Generate an error for a given node when a suffix is missing
* @brief Check for a closing char or generate an error
*
* @param suffix a suffix char, eg " or )
* @param node_name can be "string", "node" ; represents a structure
* @param context can be "string", "node" ; represents a structure
* @param additional_context optional context created when a node is being parsed
*/
void errorMissingSuffix(char suffix, const std::string& node_name);
void expectSuffixOrError(char suffix, const std::string& context, const std::optional<CodeErrorContext>& additional_context = std::nullopt);

/**
*
Expand All @@ -84,14 +90,19 @@ namespace Ark::internal
*
* @return file size in bytes
*/
std::size_t getSize() const { return m_str.size(); }
[[nodiscard]] std::size_t getSize() const { return m_str.size(); }

/**
*
* @return true if the cursor is positioned at the end of the file
*/
[[nodiscard]] bool isEOF() const { return m_it == m_str.end(); }

/**
* @brief Backtrack to a given position (this is NOT an offset!)
*
* @param n position in the source file (byte number)
*/
void backtrack(long n);

/**
Expand All @@ -111,6 +122,8 @@ namespace Ark::internal
*/
bool expect(const CharPred& t, std::string* s = nullptr);

[[nodiscard]] std::string peek() const;

// basic parsers

bool space(std::string* s = nullptr);
Expand All @@ -119,7 +132,6 @@ namespace Ark::internal
bool spaceComment(std::string* s = nullptr);
bool newlineOrComment(std::string* s = nullptr);
bool prefix(char c);
bool suffix(char c);
bool number(std::string* s = nullptr);
bool signedNumber(std::string* s = nullptr);
bool hexNumber(unsigned length, std::string* s = nullptr);
Expand Down Expand Up @@ -147,4 +159,4 @@ namespace Ark::internal
};
}

#endif
#endif // ARK_COMPILER_AST_BASEPARSER_HPP
2 changes: 1 addition & 1 deletion include/Ark/Compiler/AST/Parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ namespace Ark::internal
if (accept(IsChar('"')))
break;
if (isEOF())
errorMissingSuffix('"', "string");
expectSuffixOrError('"', "after string");
}

return { Node(NodeType::String, res) };
Expand Down
10 changes: 9 additions & 1 deletion include/Ark/Compiler/Macros/Executor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ namespace Ark::internal
*/
virtual bool canHandle(Node& node) = 0;

/**
* @brief Returns the macro node that will be expanded
*
* @param node AST node on which the executor will run
* @return Node
*/
virtual Node macroNode(Node& node) = 0;

protected:
unsigned int m_debug;
MacroProcessor* m_processor; ///< This is a non-owned pointer.
Expand Down Expand Up @@ -118,7 +126,7 @@ namespace Ark::internal
* @param message the error
* @param node the node in which there is an error
*/
[[noreturn]] static void throwMacroProcessingError(const std::string& message, const Node& node);
[[noreturn]] void throwMacroProcessingError(const std::string& message, const Node& node);
};

}
Expand Down
2 changes: 2 additions & 0 deletions include/Ark/Compiler/Macros/Executors/Conditional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ namespace Ark::internal
* @return true if the executor can handle the given node
*/
[[nodiscard]] bool canHandle(Node& node) override;

[[nodiscard]] Node macroNode(Node& node) override;
};

}
Expand Down
4 changes: 3 additions & 1 deletion include/Ark/Compiler/Macros/Executors/Function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Ark::internal
/**
*
* @param node
* @param depth depth of the macro processor evalution
* @param depth depth of the macro processor evaluation
* @return true if the applying worked
*/
bool applyMacro(Node& node, unsigned depth) override;
Expand All @@ -40,6 +40,8 @@ namespace Ark::internal
*/
[[nodiscard]] bool canHandle(Node& node) override;

[[nodiscard]] Node macroNode(Node& node) override;

private:
void unify(const std::unordered_map<std::string, Node>& map, Node& target, Node* parent, std::size_t index = 0, std::size_t unify_depth = 0);
};
Expand Down
4 changes: 3 additions & 1 deletion include/Ark/Compiler/Macros/Executors/Symbol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace Ark::internal
/**
*
* @param node
* @param depth depth of the macro processor evalution
* @param depth depth of the macro processor evaluation
* @return true if the applying worked
*/
bool applyMacro(Node& node, unsigned depth) override;
Expand All @@ -39,6 +39,8 @@ namespace Ark::internal
* @return true if the executor can handle the given node
*/
[[nodiscard]] bool canHandle(Node& node) override;

[[nodiscard]] Node macroNode(Node& node) override;
};

}
Expand Down
13 changes: 7 additions & 6 deletions include/Ark/Compiler/Macros/Processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ namespace Ark::internal
friend class MacroExecutor;

private:
Node m_ast; ///< The modified AST
std::vector<MacroScope> m_macros; ///< Handling macros in a scope fashion
Node m_ast; ///< The modified AST
std::vector<MacroScope> m_macros; ///< Handling macros in a scope fashion
std::vector<Node> m_macros_being_applied; ///< Stack of macros being applied. The last one is the current one we are working on
std::shared_ptr<MacroExecutor> m_conditional_executor;
std::vector<std::shared_ptr<MacroExecutor>> m_executors;
std::unordered_map<std::string, Node> m_defined_functions;
Expand Down Expand Up @@ -152,7 +153,7 @@ namespace Ark::internal
* @param name the name of the macro being applied
* @param kind the macro kind, empty by default (eg "operator", "condition")
*/
static void checkMacroArgCountEq(const Node& node, std::size_t expected, const std::string& name, const std::string& kind = "");
void checkMacroArgCountEq(const Node& node, std::size_t expected, const std::string& name, const std::string& kind = "");

/**
* @brief Check if the given node has at least the provided argument count, otherwise throws an error
Expand All @@ -162,7 +163,7 @@ namespace Ark::internal
* @param name the name of the macro being applied
* @param kind the macro kind, empty by default (eg "operator", "condition")
*/
static void checkMacroArgCountGe(const Node& node, std::size_t expected, const std::string& name, const std::string& kind = "");
void checkMacroArgCountGe(const Node& node, std::size_t expected, const std::string& name, const std::string& kind = "");

/**
* @brief Evaluate only the macros
Expand All @@ -181,15 +182,15 @@ namespace Ark::internal
* @return true
* @return false
*/
static bool isTruthy(const Node& node);
bool isTruthy(const Node& node);

/**
* @brief Throw a macro processing error
*
* @param message the error
* @param node the node in which there is an error
*/
[[noreturn]] static void throwMacroProcessingError(const std::string& message, const Node& node);
[[noreturn]] void throwMacroProcessingError(const std::string& message, const Node& node) const;
};
}

Expand Down
14 changes: 10 additions & 4 deletions include/Ark/Compiler/Package/ImportSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,17 @@ namespace Ark::internal
[[nodiscard]] const Node& ast() const noexcept override;

private:
struct ImportWithSource
{
std::filesystem::path file;
Import import;
};

unsigned m_debug_level;
std::vector<std::filesystem::path> m_libenv;
std::filesystem::path m_root; ///< Folder were the entry file is
Node m_ast;
std::stack<Import> m_imports;
std::stack<ImportWithSource> m_imports;
std::unordered_map<std::string, Package> m_packages; ///< Package name to package AST & data mapping
std::vector<std::string> m_imported; ///< List of imports, in the order they were found and parsed

Expand All @@ -67,11 +73,11 @@ namespace Ark::internal
* @brief Parse a given file and returns a list of its imports.
* The AST is parsed and stored in m_modules[import.prefix]
*
* @param base_path path to the file containing the import
* @param source path to the file containing the import
* @param import current import directive
* @return std::vector<Import> imports found in the processed file
* @return std::vector<ImportWithSource> imports found in the processed file
*/
std::vector<Import> parseImport(const std::filesystem::path& base_path, const Import& import);
std::vector<ImportWithSource> parseImport(const std::filesystem::path& source, const Import& import);

/**
* @brief Search for an import file, using the root file path
Expand Down
63 changes: 47 additions & 16 deletions include/Ark/Exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define INCLUDE_ARK_EXCEPTIONS_HPP

#include <string>
#include <utility>
#include <vector>
#include <stdexcept>
#include <optional>
Expand Down Expand Up @@ -95,27 +96,46 @@ namespace Ark
std::string m_details;
};

/**
* @brief CodeError thrown by the compiler (parser, macro processor, optimizer, and compiler itself)
*
*/
struct ARK_API CodeError final : Error
struct ARK_API CodeErrorContext final
{
const std::string filename;
const std::size_t line;
const std::size_t col;
const std::string expr;
const std::optional<internal::utf8_char_t> symbol;
const bool is_macro_expansion = false;

CodeErrorContext(std::string filename_, const std::size_t lineNum, const std::size_t column, std::string expression, const std::optional<internal::utf8_char_t> maybe_symbol = std::nullopt) :
filename(std::move(filename_)),
line(lineNum),
col(column),
expr(std::move(expression)),
symbol(maybe_symbol)
{}

CodeErrorContext(std::string filename_, const std::size_t lineNum, const std::size_t column, std::string expression, const bool from_macro_expansion) :
filename(std::move(filename_)),
line(lineNum),
col(column),
expr(std::move(expression)),
symbol(std::nullopt),
is_macro_expansion(from_macro_expansion)
{}
};

/**
* @brief CodeError thrown by the compiler (parser, macro processor, optimizer, and compiler itself)
*
*/
struct ARK_API CodeError final : Error
{
const CodeErrorContext context;
const std::optional<CodeErrorContext> additional_context;

CodeError(
const std::string& what,
std::string filename_,
const std::size_t lineNum,
const std::size_t column,
std::string exp,
const std::optional<internal::utf8_char_t> opt_sym = std::nullopt) :
CodeError(const std::string& what, CodeErrorContext ctx, std::optional<CodeErrorContext> maybe_more_context = std::nullopt) :
Error(what),
filename(std::move(filename_)), line(lineNum), col(column), expr(std::move(exp)), symbol(opt_sym)
context(std::move(ctx)),
additional_context(std::move(maybe_more_context))
{}
};

Expand All @@ -125,14 +145,25 @@ namespace Ark
* @brief Helper to create a colorized context to report errors to the user
*
* @param os stream in which the error will be written
* @param code content of the source file where the error is
* @param filename path to the file in which the error is
* @param expr optional expression causing the error
* @param sym_size length of expression to underline (can be 0)
* @param target_line line where the error is
* @param col_start where the error starts on the given line
* @param sym_size bad expression that triggered the error
* @param maybe_context optional context, parent of the error
* @param whole_line when true, underline the whole line, disregarding col_start and sym_size
* @param colorize generate colors or not
*/
ARK_API void makeContext(std::ostream& os, const std::string& code, std::size_t target_line, std::size_t col_start, std::size_t sym_size, bool whole_line, bool colorize);
ARK_API void makeContext(
std::ostream& os,
const std::string& filename,
const std::optional<std::string>& expr,
std::size_t sym_size,
std::size_t target_line,
std::size_t col_start,
const std::optional<CodeErrorContext>& maybe_context,
bool whole_line,
bool colorize);

/**
* @brief Helper used by the compiler to generate a colorized context from a node
Expand Down
Loading
Loading