ArkScript
A small, lisp-inspired, functional scripting language
StaticScope.cpp
Go to the documentation of this file.
2
3#include <utility>
4#include <algorithm>
5#include <fmt/format.h>
6
8
9namespace Ark::internal
10{
11 std::string StaticScope::add(const std::string& name, bool is_mutable)
12 {
13 m_vars.emplace(name, name, is_mutable);
14 return name;
15 }
16
17 std::optional<Declaration> StaticScope::get(const std::string& name, [[maybe_unused]] const std::string& origin_namespace, [[maybe_unused]] const bool extensive_lookup)
18 {
19 if (const auto it = std::ranges::find(m_vars, name, &Declaration::name); it != m_vars.end())
20 return *it;
21 return std::nullopt;
22 }
23
24 std::string StaticScope::fullyQualifiedName(const std::string& name) const
25 {
26 return name;
27 }
28
29 bool StaticScope::saveNamespace([[maybe_unused]] std::unique_ptr<StaticScope>&)
30 {
31 // the scope can not be saved on a static scope
32 return false;
33 }
34
36 {
37 return false;
38 }
39
40 NamespaceScope::NamespaceScope(std::string name, const bool with_prefix, const bool is_glob, const std::vector<std::string>& symbols) :
42 m_namespace(std::move(name)),
43 m_with_prefix(with_prefix),
44 m_is_glob(is_glob),
45 m_symbols(symbols)
46 {}
47
48 std::string NamespaceScope::add(const std::string& name, bool is_mutable)
49 {
50 // Since we do multiple passes on namespaces, we need to check if the given name is already hidden,
51 // so that we can save the name as it was on the first pass
52 if (name.ends_with(HiddenSymbolSuffix))
53 {
54 std::string std_name = name.substr(0, name.find_first_of('#'));
55 return m_vars.emplace(name, std_name, is_mutable).first->name;
56 }
57
58 // Otherwise, we also have to check for the presence of a namespace prefix,
59 // and remove it when checking against the symbols list, to determine if we
60 // need to hide the name or not
61 const bool starts_with_prefix = !m_namespace.empty() && name.starts_with(m_namespace + ":");
62 const std::string fqn = fullyQualifiedName(name);
63 const std::string unprefixed_name = starts_with_prefix ? name.substr(name.find_first_of(':') + 1) : name;
64
65 if (!m_symbols.empty() && !hasSymbol(unprefixed_name) && !m_with_prefix && !m_is_glob)
66 return m_vars.emplace(fqn + std::string(HiddenSymbolSuffix), fqn, is_mutable).first->name;
67 return m_vars.emplace(fqn, fqn, is_mutable).first->name;
68 }
69
70 std::optional<Declaration> NamespaceScope::get(const std::string& name, const std::string& origin_namespace, const bool extensive_lookup)
71 {
72 const bool starts_with_prefix = !m_namespace.empty() && name.starts_with(m_namespace + ":");
73 // If the name starts with the namespace, and we imported the namespace with prefix
74 // search for name in the namespace
75 if (starts_with_prefix && m_with_prefix)
76 {
77 if (const auto it = std::ranges::find(m_vars, name, &Declaration::name); it != m_vars.end())
78 return *it;
79 }
80 // If the name does not start with the prefix, and we import through either glob or symbol list
81 // search for the name in the namespace
82 // If the name does not start with the prefix, in a namespace with a symbol list but can be resolved,
83 // modify it to hide it to the end user
84 // If the name wasn't qualified, in the current prefixed namespace, look up for it but by qualifying the name
85 else if (!starts_with_prefix)
86 {
87 const auto fqn = fullyQualifiedName(name);
88
89 auto it = m_vars.end();
90 auto it_original = m_vars.end();
91 for (auto j = m_vars.begin(), end = m_vars.end(); j != end; ++j)
92 {
93 if (j->name == fqn)
94 it = j;
95 if (j->original_name == fqn)
96 it_original = j;
97
98 if (it != end && it_original != end)
99 break;
100 }
101
102 if ((m_is_glob || hasSymbol(name) || (m_with_prefix && origin_namespace == m_namespace)) && it != m_vars.end())
103 return *it;
104 if (!m_symbols.empty() && it_original != m_vars.end())
105 return *it_original;
106 }
107 // lookup in the additional saved namespaces
108 if (extensive_lookup)
109 {
110 std::optional<Declaration> decl;
111 for (const auto& scope : m_additional_namespaces)
112 {
113 if (auto maybe_decl = scope->get(name, origin_namespace, extensive_lookup); maybe_decl.has_value())
114 {
115 // prioritize non-hidden declarations
116 if ((decl.has_value() && decl->name.ends_with(HiddenSymbolSuffix)) || !decl.has_value())
117 decl = maybe_decl;
118 }
119 }
120 return decl;
121 }
122 // otherwise we didn't find the name in the namespace
123 return std::nullopt;
124 }
125
126 std::string NamespaceScope::fullyQualifiedName(const std::string& name) const
127 {
128 const bool starts_with_prefix = !m_namespace.empty() && name.starts_with(m_namespace + ":");
129 if (!m_namespace.empty() && !starts_with_prefix)
130 return fmt::format("{}:{}", m_namespace, name);
131 return name;
132 }
133
134 bool NamespaceScope::saveNamespace(std::unique_ptr<StaticScope>& scope)
135 {
136 m_additional_namespaces.push_back(std::move(scope));
137 return true;
138 }
139
141 {
142 return true;
143 }
144
145 bool NamespaceScope::recursiveHasSymbol(const std::string& symbol)
146 {
147 if (hasSymbol(symbol))
148 return true;
149 if (isGlob() && std::ranges::find(m_vars, fullyQualifiedName(symbol), &Declaration::name) != m_vars.end())
150 return true;
151
152 return std::ranges::any_of(
154 [&symbol](const auto& saved_scope) {
155 return saved_scope->recursiveHasSymbol(symbol);
156 });
157 }
158}
Common code for the compiler.
Static scopes (for functions, loops) and namespace scopes (for packages) definitions,...
bool saveNamespace(std::unique_ptr< StaticScope > &) override
Save a namespace scope to help with lookup.
std::string fullyQualifiedName(const std::string &name) const override
Given a Declaration name, compute its fully qualified name.
std::unordered_set< Declaration > m_vars
bool isGlob() const override
bool hasSymbol(const std::string &symbol) const override
bool isNamespace() const override
NamespaceScope(std::string name, bool with_prefix, bool is_glob, const std::vector< std::string > &symbols)
std::vector< std::string > m_symbols
std::string add(const std::string &name, bool is_mutable) override
Add a Declaration to the scope, given a mutability status.
std::optional< Declaration > get(const std::string &name, const std::string &origin_namespace, bool extensive_lookup) override
Try to return a Declaration from this scope with a given name.
std::vector< std::unique_ptr< StaticScope > > m_additional_namespaces
bool recursiveHasSymbol(const std::string &symbol) override
std::unordered_set< Declaration > m_vars
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.
virtual bool saveNamespace(std::unique_ptr< StaticScope > &)
Save a namespace scope to help with lookup.
virtual bool isNamespace() const
virtual std::string add(const std::string &name, bool is_mutable)
Add a Declaration to the scope, given a mutability status.
virtual std::string fullyQualifiedName(const std::string &name) const
Given a Declaration name, compute its fully qualified name.
constexpr std::string_view HiddenSymbolSuffix
Definition Common.hpp:104
STL namespace.
std::string name
End name, can be modified to be hidden.