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