Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
32d3095
feat(tests): unittests target can take a filter as an argument
SuperFola Aug 15, 2025
ceca72c
feat(parser): all the subparsers now take a FilePosition
SuperFola Aug 15, 2025
d9b7574
fix(parser): fixing the position of the function calls in the parser
SuperFola Aug 15, 2025
df8b462
fix(parser): set position for each macro node
SuperFola Aug 15, 2025
5be0391
fix(parser): set position for each function node
SuperFola Aug 15, 2025
f99eefd
fix(parser): set position for each block/begin node
SuperFola Aug 15, 2025
743ffce
fix(parser): set position for each import node
SuperFola Aug 15, 2025
8f30b1b
fix(parser): set position for each while node
SuperFola Aug 15, 2025
7f6a379
fix(parser): set position for each cond node
SuperFola Aug 15, 2025
9db4643
fix(parser): set position for each del node
SuperFola Aug 15, 2025
08dfabe
fix(parser): set position for each var node
SuperFola Aug 15, 2025
c691034
fix(parser): remove Parser::setNodeAndFilePos, fix source location fo…
SuperFola Aug 15, 2025
6cc4dfb
refactor(parser): better comment handling, no more playing with pointers
SuperFola Aug 15, 2025
2895e44
refactor(diagnostics): creating a separate file for diagnostics relat…
SuperFola Aug 16, 2025
7a0ef02
refactor(diagnostics): introducing a source printer for the diagnosti…
SuperFola Aug 16, 2025
4015ec3
refactor(diagnostics): revamp diagnostics::makeContext arguments, to …
SuperFola Aug 16, 2025
cf508c2
refactor(diagnostics): remove whole_line qualifier as we can make use…
SuperFola Aug 16, 2025
d7b3079
refactor(node position): trying to introduce a better positioning sys…
SuperFola Aug 16, 2025
c26da43
refactor: using a single position tracking struct for tokens and all
SuperFola Aug 18, 2025
39cb704
refactor(node): using position and setPositionFrom instead of setPos/…
SuperFola Aug 18, 2025
25e201c
feat(parser): saving the position of the end of a parsed node
SuperFola Aug 18, 2025
b9e8f43
chore(tests): remove the ^At .+\n line in .expected files
SuperFola Aug 19, 2025
67c8f29
refactor(parser, diagnostics): use an end position for each node, ins…
SuperFola Aug 19, 2025
ba90cef
fix(diagnostics): diagnostics with context are now properly aligned
SuperFola Aug 20, 2025
bea78a4
fix: default init FilePos
SuperFola Aug 20, 2025
582ae8a
chore: update modules to use the new cli argument order
SuperFola Aug 20, 2025
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 include/Ark/Ark.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#ifndef ARK_ARK_HPP
#define ARK_ARK_HPP

#include <Ark/Exceptions.hpp>
#include <Ark/Error/Exceptions.hpp>
#include <Ark/Constants.hpp>
#include <Ark/Utils/Utils.hpp>
#include <Ark/VM/VM.hpp>
Expand Down
19 changes: 11 additions & 8 deletions include/Ark/Compiler/AST/BaseParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <initializer_list>

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

Expand All @@ -30,8 +30,9 @@ namespace Ark::internal
std::string m_str;
std::vector<std::pair<std::string::iterator, std::size_t>> m_it_to_row; ///< A crude map of \n position to line number to speed up line number computing
std::string::iterator m_it, m_next_it;
utf8_char_t m_sym; ///< The current utf8 character we're on
FilePosition m_filepos; ///< The position of the cursor in the file
utf8_char_t m_sym; ///< The current utf8 character we're on
FilePosition m_filepos; ///< The position of the cursor in the file
FilePosition m_previous_filepos; ///< The previous position of the cursor in the file

/**
* @brief Register the position of a new line, with an iterator pointing to the new line and the row number
Expand All @@ -52,16 +53,18 @@ namespace Ark::internal
void initParser(const std::string& filename, const std::string& code);

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

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

/**
* @brief Create an error context and throw an error containing said context
*
* @param error an error message
* @param exp the expression causing the error
* @param start_at position in the file where the parsing for the erroneous token started
* @param additional_context optional context created when a node is being parsed
*/
void error(const std::string& error, std::string exp, const std::optional<CodeErrorContext>& additional_context = std::nullopt);
void error(const std::string& error, FilePosition start_at, const std::optional<CodeErrorContext>& additional_context = std::nullopt) const;

/**
* @brief Fetch the next token (space and paren delimited) to generate an error
Expand Down Expand Up @@ -129,8 +132,8 @@ namespace Ark::internal
bool space(std::string* s = nullptr);
bool inlineSpace(std::string* s = nullptr);
bool comment(std::string* s = nullptr);
bool spaceComment(std::string* s = nullptr);
bool newlineOrComment(std::string* s = nullptr);
[[nodiscard]] std::string spaceComment();
[[nodiscard]] std::string newlineOrComment();
bool prefix(char c);
bool number(std::string* s = nullptr);
bool signedNumber(std::string* s = nullptr);
Expand Down
31 changes: 9 additions & 22 deletions include/Ark/Compiler/AST/Node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <Ark/Compiler/AST/Namespace.hpp>
#include <Ark/Compiler/Common.hpp>
#include <Ark/Utils/Platform.hpp>
#include <Ark/Utils/Position.hpp>

namespace Ark::internal
{
Expand Down Expand Up @@ -162,19 +163,11 @@ namespace Ark::internal
void setString(const std::string& value) noexcept;

/**
* @brief Set the Position of the node in the text
* @brief Position the current node at a given span in a file
*
* @param line
* @param col
* @param source node to copy filename and position from
*/
void setPos(std::size_t line, std::size_t col) noexcept;

/**
* @brief Set the original Filename where the node was
*
* @param filename
*/
void setFilename(const std::string& filename) noexcept;
void setPositionFrom(const Node& source) noexcept;

/**
* @brief Set the comment field with the nearest comment before this node
Expand Down Expand Up @@ -216,18 +209,11 @@ namespace Ark::internal
[[nodiscard]] bool isAltSyntax() const;

/**
* @brief Get the line at which this node was created
*
* @return std::size_t
*/
[[nodiscard]] std::size_t line() const noexcept;

/**
* @brief Get the column at which this node was created
* @brief Get the span of the node (start and end)
*
* @return std::size_t
* @return const FileSpan
*/
[[nodiscard]] std::size_t col() const noexcept;
[[nodiscard]] FileSpan position() const noexcept;

/**
* @brief Return the filename in which this node was created
Expand Down Expand Up @@ -263,13 +249,14 @@ namespace Ark::internal

friend bool operator==(const Node& A, const Node& B);
friend bool operator<(const Node& A, const Node& B);
friend class Parser;

private:
NodeType m_type { NodeType::Unused };
Value m_value;
std::optional<std::string> m_unqualified_name { std::nullopt }; ///< Used by Capture nodes, to have the FQN in the value, and the captured name here
// position of the node in the original code, useful when it comes to parser errors
std::size_t m_line = 0, m_col = 0;
FileSpan m_pos;
std::string m_filename;
std::string m_comment;
std::string m_after_comment; ///< Comment after node
Expand Down
216 changes: 25 additions & 191 deletions include/Ark/Compiler/AST/Parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,198 +63,32 @@ namespace Ark::internal
std::vector<Import> m_imports;
unsigned m_allow_macro_behavior; ///< Toggled on when inside a macro definition, off afterward
std::size_t m_nested_nodes; ///< Nested node counter
std::vector<std::function<std::optional<Node>()>> m_parsers;
std::vector<std::function<std::optional<Node>(FilePosition)>> m_parsers;

/**
* @brief Update a node given a file position
* @param node node to update
* @param cursor the node position in file
* @return Node& the modified node
*/
Node& setNodePosAndFilename(Node& node, const std::optional<FilePosition>& cursor = std::nullopt) const;
[[nodiscard]] Node positioned(Node node, FilePosition cursor) const;
[[nodiscard]] std::optional<Node>& positioned(std::optional<Node>& node, FilePosition cursor) const;

std::optional<Node> node();
std::optional<Node> letMutSet();
std::optional<Node> del();
std::optional<Node> condition();
std::optional<Node> loop();
std::optional<Node> import_();
std::optional<Node> block();
std::optional<Node> functionArgs();
std::optional<Node> function();
std::optional<Node> macroCondition();
std::optional<Node> macroArgs();
std::optional<Node> macro();
std::optional<Node> functionCall();
std::optional<Node> list();

std::optional<Node> number()
{
auto pos = getCount();

std::string res;
if (signedNumber(&res))
{
double output;
if (Utils::isDouble(res, &output))
return std::optional<Node>(output);
backtrack(pos);
error("Is not a valid number", res);
}
return std::nullopt;
}

std::optional<Node> string()
{
std::string res;
if (accept(IsChar('"')))
{
while (true)
{
if (accept(IsChar('\\')))
{
if (!m_interpret)
res += '\\';

if (accept(IsChar('"')))
res += '"';
else if (accept(IsChar('\\')))
res += '\\';
else if (accept(IsChar('n')))
res += m_interpret ? '\n' : 'n';
else if (accept(IsChar('t')))
res += m_interpret ? '\t' : 't';
else if (accept(IsChar('v')))
res += m_interpret ? '\v' : 'v';
else if (accept(IsChar('r')))
res += m_interpret ? '\r' : 'r';
else if (accept(IsChar('a')))
res += m_interpret ? '\a' : 'a';
else if (accept(IsChar('b')))
res += m_interpret ? '\b' : 'b';
else if (accept(IsChar('f')))
res += m_interpret ? '\f' : 'f';
else if (accept(IsChar('u')))
{
std::string seq;
if (hexNumber(4, &seq))
{
if (m_interpret)
{
char utf8_str[5];
utf8::decode(seq.c_str(), utf8_str);
if (*utf8_str == '\0')
error("Invalid escape sequence", "\\u" + seq);
res += utf8_str;
}
else
res += "u" + seq;
}
else
error("Invalid escape sequence", "\\u");
}
else if (accept(IsChar('U')))
{
std::string seq;
if (hexNumber(8, &seq))
{
if (m_interpret)
{
std::size_t begin = 0;
for (; seq[begin] == '0'; ++begin)
;
char utf8_str[5];
utf8::decode(seq.c_str() + begin, utf8_str);
if (*utf8_str == '\0')
error("Invalid escape sequence", "\\U" + seq);
res += utf8_str;
}
else
res += "U" + seq;
}
else
error("Invalid escape sequence", "\\U");
}
else
{
backtrack(getCount() - 1);
error("Unknown escape sequence", "\\");
}
}
else
accept(IsNot(IsEither(IsChar('\\'), IsChar('"'))), &res);

if (accept(IsChar('"')))
break;
if (isEOF())
expectSuffixOrError('"', "after string");
}

return { Node(NodeType::String, res) };
}
return std::nullopt;
}

std::optional<Node> field()
{
std::string sym;
if (!name(&sym))
return std::nullopt;

std::optional<Node> leaf { Node(NodeType::Field) };
setNodePosAndFilename(leaf.value());
leaf->push_back(Node(NodeType::Symbol, sym));

while (true)
{
if (leaf->list().size() == 1 && !accept(IsChar('.'))) // Symbol:abc
return std::nullopt;

if (leaf->list().size() > 1 && !accept(IsChar('.')))
break;
std::string res;
if (!name(&res))
errorWithNextToken("Expected a field name: <symbol>.<field>");
leaf->push_back(Node(NodeType::Symbol, res));
}

return leaf;
}

std::optional<Node> symbol()
{
std::string res;
if (!name(&res))
return std::nullopt;
return { Node(NodeType::Symbol, res) };
}

std::optional<Node> spread()
{
std::string res;
if (sequence("..."))
{
if (!name(&res))
errorWithNextToken("Expected a name for the variadic");
return { Node(NodeType::Spread, res) };
}
return std::nullopt;
}

std::optional<Node> nil()
{
if (!accept(IsChar('(')))
return std::nullopt;

std::string comment;
newlineOrComment(&comment);
if (!accept(IsChar(')')))
return std::nullopt;

if (m_interpret)
return { Node(NodeType::Symbol, "nil").attachNearestCommentBefore(comment) };
return { Node(NodeType::List).attachNearestCommentBefore(comment) };
}
std::optional<Node> letMutSet(FilePosition filepos);
std::optional<Node> del(FilePosition filepos);
std::optional<Node> condition(FilePosition filepos);
std::optional<Node> loop(FilePosition filepos);
std::optional<Node> import_(FilePosition filepos);
std::optional<Node> block(FilePosition filepos);
std::optional<Node> functionArgs(FilePosition filepos);
std::optional<Node> function(FilePosition filepos);
std::optional<Node> macroCondition(FilePosition filepos);
std::optional<Node> macroArgs(FilePosition filepos);
std::optional<Node> macro(FilePosition filepos);
std::optional<Node> functionCall(FilePosition filepos);
std::optional<Node> list(FilePosition filepos);

std::optional<Node> number(FilePosition filepos);
std::optional<Node> string(FilePosition filepos);
std::optional<Node> field(FilePosition filepos);
std::optional<Node> symbol(FilePosition filepos);
std::optional<Node> spread(FilePosition filepos);
std::optional<Node> nil(FilePosition filepos);

/**
* @brief Try to parse an atom (number, string, spread, field, symbol, nil)
Expand All @@ -264,7 +98,7 @@ namespace Ark::internal

/**
* @brief Try to parse an atom, if any, match its type against the given list
* @param types autorized types
* @param types authorized types
* @return std::optional<Node> std::nullopt if the parsed atom didn't match the given types
*/
std::optional<Node> anyAtomOf(std::initializer_list<NodeType> types);
Expand All @@ -281,7 +115,7 @@ namespace Ark::internal
* @param name construction name, eg "let", "condition"
* @return std::optional<Node> std::nullopt if the parser didn't match
*/
std::optional<Node> wrapped(std::optional<Node> (Parser::*parser)(), const std::string& name);
std::optional<Node> wrapped(std::optional<Node> (Parser::*parser)(FilePosition), const std::string& name);
};
}

Expand Down
4 changes: 2 additions & 2 deletions include/Ark/Compiler/Package/ImportSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ namespace Ark::internal
* @brief Search for an import file, using the root file path
*
* @param file path to the file containing the import
* @param import current import directive
* @param import_ current import directive
* @return std::filesystem::path
*/
[[nodiscard]] std::filesystem::path findFile(const std::filesystem::path& file, const Import& import) const;
[[nodiscard]] std::filesystem::path findFile(const std::filesystem::path& file, const Import& import_) const;
};
}

Expand Down
Loading
Loading