10 Pass(
"NameResolution", debug),
52 return fully_qualified_name;
61 const std::string old_name = node.
string();
68 for (
auto& child : node.
list())
70 const std::string old_name = child.string();
90 const auto funcname = node.
constList()[0].string();
91 const auto arg = node.
constList()[1].string();
95 fmt::format(
"MutabilityError: Can not modify the constant list `{}' using `{}'", arg, funcname),
105 for (std::size_t i = 2, end = node.
constList().size(); i < end; ++i)
109 fmt::format(
"MutabilityError: Can not {} the list `{}' to itself", funcname, arg),
119 for (
auto& child : node.
list())
120 visit(child, register_declarations);
134 visit(*namespace_.ast,
true);
136 visit(*namespace_.ast,
false);
139 if (!namespace_.symbols.empty())
141 for (
const auto& sym : namespace_.symbols)
143 if (!scope->
get(sym,
true).has_value())
145 fmt::format(
"ImportError: Can not import symbol {} from {}, as it isn't in the package", sym, namespace_.name),
147 namespace_.ast->filename(),
148 namespace_.ast->line(),
149 namespace_.ast->col(),
175 visit(node.
list()[2], register_declarations);
178 const std::string& name = node.
constList()[1].string();
181 fmt::format(
"Can not use a reserved identifier ('{}') as a {} name.", name, keyword ==
Keyword::Let ?
"constant" :
"variable"),
190 fmt::format(
"MutabilityError: Can not use 'let' to redefine variable `{}'", name),
200 fmt::format(
"MutabilityError: Can not set the constant `{}' to {}", name, node.
constList()[2].repr()),
214 if (register_declarations)
215 node.
list()[1].setString(fully_qualified_name);
228 for (
auto& child : node.
list())
229 visit(child, register_declarations);
240 for (
auto& child : node.
list()[1].list())
246 fmt::format(
"Can not capture `{}' because it is referencing a variable defined in an unreachable scope.", child.string()),
263 visit(node.
list()[2], register_declarations);
270 for (
auto& child : node.
list())
271 visit(child, register_declarations);
278 const std::string& name = symbol.
string();
285 if (!old_name.empty())
287 auto it = std::ranges::find_if(
m_symbol_nodes, [&old_name, &symbol](
const Node& sym_node) ->
bool {
288 return sym_node.
string() == old_name &&
289 sym_node.
col() == symbol.
col() &&
300 const auto it = std::ranges::find_if(
m_symbol_nodes, [&name](
const Node& sym_node) ->
bool {
301 return sym_node.
string() == name;
310 const auto it = std::ranges::find_if(
312 [&splitted](
const std::string& plugin) ->
bool {
313 return plugin == splitted;
315 return it != m_plugin_names.end();
326 "Symbol `{}' was resolved to `{}', which is also a builtin name. Either the symbol or the package it's in needs to be renamed to avoid conflicting with the builtin.",
337 if (fqn.ends_with(
"#hidden"))
338 message = fmt::format(
339 R
"(Unbound variable "{}". However, it exists in a namespace as "{}", did you forget to add it to the symbol list while importing?)",
341 fqn.substr(0, fqn.find_first_of('#')));
343 message = fmt::format(R
"(Unbound variable "{}". However, it exists in a namespace as "{}", did you forget to prefix it with its namespace?)", symbol.string(), fqn);
365 const auto& str = sym.string();
373 if (suggestion.empty())
374 message = fmt::format(R
"(Unbound variable error "{}" (variable is used but not defined))", str);
377 const std::string prefix = suggestion.substr(0, suggestion.find_first_of(
':'));
378 const std::string note_about_prefix = fmt::format(
379 " You either forgot to import it in the symbol list (eg `(import {} :{})') or need to fully qualify it by adding the namespace",
382 const bool add_note = suggestion.ends_with(
":" + str);
383 message = fmt::format(R
"(Unbound variable error "{}" (did you mean "{}"?{}))", str, suggestion, add_note ? note_about_prefix : "");
393 auto iterate = [](
const std::string& word,
const std::unordered_set<std::string>& dict) -> std::string {
394 std::string suggestion;
396 std::size_t suggestion_distance = word.size() / 2;
397 for (
const std::string& symbol : dict)
400 if (current_distance <= suggestion_distance)
402 suggestion_distance = current_distance;
411 if (suggestion.empty())
414 if (suggestion.empty())
416 if (
const auto it = std::ranges::find_if(
m_defined_symbols, [&str](
const std::string& symbol) {
417 return symbol.ends_with(
":" + str);
Lots of utilities about string, filesystem and more.
Host the declaration of all the ArkScript builtins.
ArkScript homemade exceptions.
Resolves names and fully qualify them in the AST (prefixing them with the package they are from)
void trace(const char *fmt, Args &&... args)
Write a trace level log using fmtlib.
void traceStart(std::string &&trace_name)
std::vector< std::string > m_plugin_names
void visit(Node &node, bool register_declarations)
Recursively visit nodes.
void visitKeyword(Node &node, Keyword keyword, bool register_declarations)
const Node & ast() const noexcept override
Unused overload that return the input AST (untouched as this pass only generates errors)
void checkForUndefinedSymbol() const
Checks for undefined symbols, not present in the defined symbols table.
std::string offerSuggestion(const std::string &str) const
Suggest a symbol of what the user may have meant to input.
std::unordered_set< std::string > m_language_symbols
Precomputed set of language symbols that can't be used to define variables.
std::unordered_set< std::string > m_defined_symbols
bool mayBeFromPlugin(const std::string &name) const noexcept
Checking if a symbol may be coming from a plugin.
void process(const Node &ast) override
Start visiting the given AST, checking for mutability violation and unbound variables.
std::string addDefinedSymbol(const std::string &sym, bool is_mutable)
Register a symbol as defined, so that later we can throw errors on undefined symbols.
std::string updateSymbolWithFullyQualifiedName(Node &symbol)
void addSymbolNode(const Node &symbol, const std::string &old_name="")
Register a given node in the symbol table.
NameResolutionPass(unsigned debug)
Create a NameResolutionPass.
ScopeResolver m_scope_resolver
std::vector< Node > m_symbol_nodes
A node of an Abstract Syntax Tree for ArkScript.
NodeType nodeType() const noexcept
Return the node type.
const std::string & filename() const noexcept
Return the filename in which this node was created.
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Namespace & arkNamespace() noexcept
Return the namespace held by the value (if the node type allows it)
std::string repr() const noexcept
Compute a representation of the node without any comments or additional sugar, colors,...
std::ostream & debugPrint(std::ostream &os) const noexcept
Print a node to an output stream with added type annotations.
std::size_t col() const noexcept
Get the column at which this node was created.
void setString(const std::string &value) noexcept
Set the String object.
std::size_t line() const noexcept
Get the line at which this node was created.
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
An interface to describe compiler passes.
std::string registerInCurrent(const std::string &name, bool is_mutable)
Register a Declaration in the current (last) scope.
void createNewNamespace(const std::string &name, bool with_prefix, bool is_glob, const std::vector< std::string > &symbols)
Create a new namespace scope.
void saveNamespaceAndRemove()
Save the last scope as a namespace, by attaching it to the nearest namespace scope.
std::string getFullyQualifiedNameInNearestScope(const std::string &name) const
Get a FQN from a variable name in the nearest scope it is declared in.
bool isRegistered(const std::string &name) const
Checks if any scope has 'name', in reverse order.
void createNew()
Create a new scope.
StaticScope * currentScope() const
Return a non-owning raw pointer to the current scope.
bool isInScope(const std::string &name) const
Checks if 'name' is in the current scope.
void removeLastScope()
Remove the last scope.
std::optional< bool > isImmutable(const std::string &name) const
Checks the scopes in reverse order for 'name' and returns its mutability status.
std::pair< bool, std::string > canFullyQualifyName(const std::string &name)
Checks if a name can be fully qualified (allows only unprefixed names to be resolved by glob namespac...
virtual std::optional< Declaration > get(const std::string &name, bool extensive_lookup)
Try to return a Declaration from this scope with a given name.
std::vector< std::string > splitString(const std::string &source, const char sep)
Cut a string into pieces, given a character separator.
ARK_API std::size_t levenshteinDistance(const std::string &str1, const std::string &str2)
Calculate the Levenshtein distance between two strings.
ARK_API const std::vector< std::pair< std::string, Value > > builtins
constexpr std::array< std::string_view, 9 > listInstructions
constexpr std::string_view AppendInPlace
constexpr std::array< std::string_view, 24 > operators
constexpr std::string_view ConcatInPlace
constexpr std::string_view SysArgs
constexpr std::string_view And
constexpr std::array UpdateRef
All the builtins that modify in place a variable.
constexpr std::string_view Or
Keyword
The different keywords available.
CodeError thrown by the compiler (parser, macro processor, optimizer, and compiler itself)