ArkScript
A small, lisp-inspired, functional scripting language
ScopeResolver.cpp
Go to the documentation of this file.
2
3#include <ranges>
4#include <algorithm>
5
7
8namespace Ark::internal
9{
11 {
12 createNewNamespace("", /* with_prefix= */ false, /* is_glob= */ true, /* symbols= */ {});
13 }
14
16 {
17 m_scopes.emplace_back(std::make_unique<StaticScope>());
18 }
19
21 {
22 m_scopes.pop_back();
23 }
24
25 void ScopeResolver::createNewNamespace(const std::string& name, bool with_prefix, bool is_glob, const std::vector<std::string>& symbols)
26 {
27 m_scopes.emplace_back(std::make_unique<NamespaceScope>(name, with_prefix, is_glob, symbols));
28 }
29
30 std::string ScopeResolver::registerInCurrent(const std::string& name, const bool is_mutable)
31 {
32 return m_scopes.back()->add(name, is_mutable);
33 }
34
36 {
37 for (auto& m_scope : std::ranges::reverse_view(m_scopes) | std::ranges::views::drop(1))
38 {
39 if (m_scope->saveNamespace(m_scopes.back()))
40 break;
41 }
42
43 m_scopes.pop_back();
44 }
45
46 std::optional<bool> ScopeResolver::isImmutable(const std::string& name) const
47 {
48 for (const auto& m_scope : std::ranges::reverse_view(m_scopes))
49 {
50 if (auto maybe = m_scope->get(name, currentNamespace(), true); maybe.has_value())
51 return !maybe.value().is_mutable;
52 }
53 return std::nullopt;
54 }
55
56 bool ScopeResolver::isRegistered(const std::string& name) const
57 {
58 const std::string origin_namespace = currentNamespace();
59 return std::ranges::any_of(std::ranges::reverse_view(m_scopes), [&name, &origin_namespace](const auto& scope) {
60 return scope->get(name, origin_namespace, true).has_value();
61 });
62 }
63
64 bool ScopeResolver::isInScope(const std::string& name) const
65 {
66 return m_scopes.back()->get(name, currentNamespace(), false).has_value();
67 }
68
69 std::string ScopeResolver::getFullyQualifiedNameInNearestScope(const std::string& name) const
70 {
71 const std::string prefix = currentNamespace();
72 std::optional<std::string> maybe_name;
73 for (const auto& scope : std::ranges::reverse_view(m_scopes))
74 {
75 if (auto maybe_fqn = scope->get(name, prefix, true); maybe_fqn.has_value())
76 {
77 // prioritize non-hidden symbols
78 if ((maybe_name.has_value() &&
79 maybe_name.value().ends_with(HiddenSymbolSuffix) &&
80 !maybe_fqn.value().name.ends_with(HiddenSymbolSuffix)) ||
81 !maybe_name.has_value())
82 maybe_name = maybe_fqn.value().name;
83 }
84 }
85 return maybe_name.value_or(name);
86 }
87
88 std::pair<bool, std::string> ScopeResolver::canFullyQualifyName(const std::string& name)
89 {
90 // a given name can be fully qualified if
91 // old == new
92 // old != new and new has prefix
93 // if the prefix namespace is glob
94 // if the prefix namespace has name in its symbols
95 // if the prefix namespace is with_prefix && it is the top most scope
96 const std::string maybe_fqn = getFullyQualifiedNameInNearestScope(name);
97
98 if (maybe_fqn == name)
99 return std::make_pair(true, maybe_fqn);
100
101 const std::string prefix = maybe_fqn.substr(0, maybe_fqn.find_first_of(':'));
102 const std::string unprefixed_name = name.substr(name.find_first_of(':') + 1);
103 auto namespaces =
104 std::ranges::reverse_view(m_scopes) | std::ranges::views::filter([](const auto& e) {
105 return e->isNamespace();
106 });
107 bool top = true;
108 for (auto& scope : namespaces)
109 {
110 if (top && prefix == scope->prefix())
111 return std::make_pair(true, maybe_fqn);
112 if (!top && prefix == scope->prefix() && (scope->isGlob() || scope->hasSymbol(name)))
113 return std::make_pair(true, maybe_fqn);
114
115 // check for the presence of the symbol in symbol imports and glob imports
116 if (scope->recursiveHasSymbol(unprefixed_name))
117 return std::make_pair(true, maybe_fqn);
118
119 top = false;
120 }
121
122 return std::make_pair(false, maybe_fqn);
123 }
124
126 {
127 if (!m_scopes.empty()) [[likely]]
128 return m_scopes.back().get();
129 return nullptr;
130 }
131
133 {
134 for (const auto& scope : std::ranges::reverse_view(m_scopes))
135 {
136 if (scope->isNamespace())
137 return scope->prefix();
138 }
139
140 // no namespace name, thus no prefix ; "" is either the default namespace, a function scope or a while loop
141 return "";
142 }
143}
Common code for the compiler.
Handle scope resolution at compile time.
std::string registerInCurrent(const std::string &name, bool is_mutable)
Register a Declaration in the current (last) scope.
ScopeResolver()
Create a ScopeResolver.
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.
std::vector< std::unique_ptr< StaticScope > > m_scopes
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::string currentNamespace() const
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, const std::string &origin_namespace, bool extensive_lookup)
Try to return a Declaration from this scope with a given name.
constexpr std::string_view HiddenSymbolSuffix
Definition Common.hpp:104