ArkScript
A small, lisp-inspired, functional scripting language
LocalsLocator.cpp
Go to the documentation of this file.
2
3#include <ranges>
4
5namespace Ark::internal
6{
8 {
9 // create a default scope
10 m_scopes.emplace_back();
11 }
12
13 void LocalsLocator::addLocal(const std::string& name)
14 {
15 auto& scope = m_scopes.back();
16 if (std::ranges::find_if(
17 scope.data,
18 [&name](const Scope::Var& v) {
19 return name == v.name;
20 }) == scope.data.end())
21 scope.data.push_back(Scope::Var { .name = name, .unreachable = false });
22 }
23
24 std::optional<std::size_t> LocalsLocator::lookupLastScopeByName(const std::string& name)
25 {
26 auto& [data, type] = m_scopes.back();
27
28 if (type != ScopeType::Closure)
29 {
30 // Compute the index of the variable in the active scope from the end.
31 for (auto it = data.rbegin(); it != data.rend(); ++it)
32 {
33 // If a variable is marked unreachable, then anything before it can not be accessed as well
34 if (it->unreachable)
35 return std::nullopt;
36 if (it->name == name)
37 return static_cast<std::size_t>(std::distance(data.rbegin(), it));
38 }
39 }
40
41 return std::nullopt;
42 }
43
45 {
46 m_scopes.emplace_back(Scope {
47 .data = {},
48 .type = type });
49 }
50
52 {
53 m_scopes.pop_back();
54 }
55
57 {
58 m_drop_for_conds.push_back(m_scopes.back().data.size());
59 }
60
62 {
63 const auto old_length = m_drop_for_conds.back();
64 m_drop_for_conds.pop_back();
65
66 auto& back = m_scopes.back();
67 if (back.data.size() > old_length)
68 {
69 back.data.erase(
70 back.data.begin() + static_cast<decltype(back.data)::difference_type>(old_length),
71 back.data.end());
72 return true;
73 }
74 return false;
75 }
76
78 {
79 if (!m_scopes.back().data.empty())
80 m_scopes.back().data.back().unreachable = true;
81 }
82}
Track locals at compile.
void saveScopeLengthForBranch()
Save the current scope length before entering a branch, so that we can ignore variable definitions in...
std::vector< Scope > m_scopes
std::vector< std::size_t > m_drop_for_conds
Needed to drop variables inside if/else branches since they don't have their own scope.
std::optional< std::size_t > lookupLastScopeByName(const std::string &name)
Search for a local in the current scope. Returns std::nullopt in case of closure scopes or if the var...
bool dropVarsForBranch()
Drop potentially defined variables in the last saved branch.
void deleteScope()
Delete the last scope.
void addLocal(const std::string &name)
Register a local in the current scope, triggered by a STORE instruction. If the local already exists,...
LocalsLocator()
Create a new LocalsLocator to track the position of variables in the scope stack.
void createScope(ScopeType type=ScopeType::Default)
Create a new scope.
void markLastLocalAsUnreachable()
Mark the last variable of a scope as unreachable, blocking lookupLastScopeByName(....