src/symboltablebuilder/SymbolTable.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | #include "SymbolTable.h" | ||
| 4 | |||
| 5 | #include <SourceFile.h> | ||
| 6 | #include <ast/ASTNodes.h> | ||
| 7 | #include <symboltablebuilder/Scope.h> | ||
| 8 | #include <symboltablebuilder/SymbolTableBuilder.h> | ||
| 9 | #include <util/CodeLoc.h> | ||
| 10 | #include <util/CompilerWarning.h> | ||
| 11 | |||
| 12 | namespace spice::compiler { | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Insert a new symbol into the current symbol table. If it is a parameter, append its name to the paramNames vector | ||
| 16 | * | ||
| 17 | * @param name Name of the symbol | ||
| 18 | * @param declNode AST node where the symbol is declared | ||
| 19 | * @param isAnonymousSymbol If this symbol should be anonymous | ||
| 20 | * @return Inserted entry | ||
| 21 | */ | ||
| 22 | 61336 | SymbolTableEntry *SymbolTable::insert(const std::string &name, ASTNode *declNode, bool isAnonymousSymbol) { | |
| 23 | 61336 | const bool isGlobal = parent == nullptr; | |
| 24 | 61336 | size_t orderIndex = SIZE_MAX; | |
| 25 |
2/2✓ Branch 2 → 3 taken 58884 times.
✓ Branch 2 → 5 taken 2452 times.
|
61336 | if (!isAnonymousSymbol) |
| 26 | 450765 | orderIndex = std::ranges::count_if(symbols, [](const auto &entry) { return !entry.second.anonymous; }); | |
| 27 | // Insert into symbols map. The type is 'dyn', because concrete types are determined by the type checker later on | ||
| 28 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 61336 times.
|
61336 | assert(!symbols.contains(name)); |
| 29 |
5/10✓ Branch 8 → 9 taken 61336 times.
✗ Branch 8 → 45 not taken.
✓ Branch 9 → 10 taken 61336 times.
✗ Branch 9 → 44 not taken.
✓ Branch 10 → 11 taken 61336 times.
✗ Branch 10 → 42 not taken.
✓ Branch 11 → 12 taken 61336 times.
✗ Branch 11 → 40 not taken.
✓ Branch 12 → 13 taken 61336 times.
✗ Branch 12 → 38 not taken.
|
61336 | symbols.insert({name, SymbolTableEntry(name, QualType(TY_INVALID), scope, declNode, orderIndex, isGlobal)}); |
| 30 | // Set entry to declared | ||
| 31 | 61336 | SymbolTableEntry *entry = &symbols.at(name); | |
| 32 |
1/2✓ Branch 17 → 18 taken 61336 times.
✗ Branch 17 → 48 not taken.
|
61336 | entry->updateState(DECLARED, declNode); |
| 33 | |||
| 34 | // Check if shadowed | ||
| 35 |
10/10✓ Branch 18 → 19 taken 50175 times.
✓ Branch 18 → 26 taken 11161 times.
✓ Branch 20 → 21 taken 850 times.
✓ Branch 20 → 26 taken 49325 times.
✓ Branch 22 → 23 taken 14 times.
✓ Branch 22 → 26 taken 836 times.
✓ Branch 24 → 25 taken 2 times.
✓ Branch 24 → 26 taken 12 times.
✓ Branch 27 → 28 taken 2 times.
✓ Branch 27 → 36 taken 61334 times.
|
61336 | if (parent != nullptr && parent->lookup(name) != nullptr && !declNode->isParam() && name != RETURN_VARIABLE_NAME) { |
| 36 |
2/4✓ Branch 28 → 29 taken 2 times.
✗ Branch 28 → 51 not taken.
✓ Branch 29 → 30 taken 2 times.
✗ Branch 29 → 49 not taken.
|
2 | const std::string warningMsg = "Variable '" + name + "' shadows a variable in a parent scope"; |
| 37 |
1/2✓ Branch 31 → 32 taken 2 times.
✗ Branch 31 → 54 not taken.
|
2 | const CompilerWarning warning(declNode->codeLoc, SHADOWED_VARIABLE, warningMsg); |
| 38 |
1/2✓ Branch 32 → 33 taken 2 times.
✗ Branch 32 → 52 not taken.
|
2 | scope->sourceFile->compilerOutput.warnings.push_back(warning); |
| 39 | 2 | } | |
| 40 | |||
| 41 | 61336 | return entry; | |
| 42 | } | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Insert a new anonymous symbol into the current symbol table. | ||
| 46 | * The anonymous symbol will be identified via the definition code location | ||
| 47 | * | ||
| 48 | * @param qualType Type of the symbol | ||
| 49 | * @param declNode AST node where the anonymous symbol is declared | ||
| 50 | * @param numericSuffix Custom numeric suffix | ||
| 51 | * @return Inserted entry | ||
| 52 | */ | ||
| 53 | 2462 | SymbolTableEntry *SymbolTable::insertAnonymous(const QualType &qualType, ASTNode *declNode, size_t numericSuffix) { | |
| 54 | // Check if the anonymous entry already exists | ||
| 55 |
3/4✓ Branch 2 → 3 taken 2462 times.
✗ Branch 2 → 34 not taken.
✓ Branch 3 → 4 taken 10 times.
✓ Branch 3 → 5 taken 2452 times.
|
2462 | if (SymbolTableEntry *anonSymbol = lookupAnonymous(declNode, numericSuffix)) |
| 56 | 10 | return anonSymbol; | |
| 57 | // Otherwise, create an anonymous entry | ||
| 58 |
1/2✓ Branch 5 → 6 taken 2452 times.
✗ Branch 5 → 34 not taken.
|
2452 | std::stringstream name; |
| 59 |
5/10✓ Branch 6 → 7 taken 2452 times.
✗ Branch 6 → 32 not taken.
✓ Branch 7 → 8 taken 2452 times.
✗ Branch 7 → 26 not taken.
✓ Branch 8 → 9 taken 2452 times.
✗ Branch 8 → 24 not taken.
✓ Branch 9 → 10 taken 2452 times.
✗ Branch 9 → 24 not taken.
✓ Branch 10 → 11 taken 2452 times.
✗ Branch 10 → 24 not taken.
|
2452 | name << "anon." << declNode->codeLoc.toString() << "." << reinterpret_cast<size_t>(declNode); |
| 60 |
2/2✓ Branch 12 → 13 taken 50 times.
✓ Branch 12 → 15 taken 2402 times.
|
2452 | if (numericSuffix > 0) |
| 61 |
2/4✓ Branch 13 → 14 taken 50 times.
✗ Branch 13 → 32 not taken.
✓ Branch 14 → 15 taken 50 times.
✗ Branch 14 → 32 not taken.
|
50 | name << "." << numericSuffix; |
| 62 |
2/4✓ Branch 15 → 16 taken 2452 times.
✗ Branch 15 → 29 not taken.
✓ Branch 16 → 17 taken 2452 times.
✗ Branch 16 → 27 not taken.
|
2452 | SymbolTableEntry *anonSymbol = insert(name.str(), declNode, true); |
| 63 |
1/2✓ Branch 18 → 19 taken 2452 times.
✗ Branch 18 → 32 not taken.
|
2452 | anonSymbol->updateType(qualType, false); |
| 64 |
1/2✓ Branch 19 → 20 taken 2452 times.
✗ Branch 19 → 30 not taken.
|
2452 | anonSymbol->updateState(DECLARED, declNode); |
| 65 |
1/2✓ Branch 20 → 21 taken 2452 times.
✗ Branch 20 → 31 not taken.
|
2452 | anonSymbol->updateState(INITIALIZED, declNode); |
| 66 | 2452 | anonSymbol->anonymous = true; | |
| 67 | 2452 | anonSymbol->used = true; | |
| 68 | 2452 | return anonSymbol; | |
| 69 | 2452 | } | |
| 70 | |||
| 71 | /** | ||
| 72 | * Copy a symbol by its name | ||
| 73 | * | ||
| 74 | * @param originalName Original symbol name | ||
| 75 | * @param newName New symbol name | ||
| 76 | * @return Copied entry | ||
| 77 | */ | ||
| 78 | 3637 | SymbolTableEntry *SymbolTable::copySymbol(const std::string &originalName, const std::string &newName) { | |
| 79 |
1/2✓ Branch 2 → 3 taken 3637 times.
✗ Branch 2 → 19 not taken.
|
3637 | SymbolTableEntry *entryToCopy = lookupStrict(originalName); |
| 80 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3637 times.
|
3637 | assert(entryToCopy != nullptr); |
| 81 |
2/4✓ Branch 5 → 6 taken 3637 times.
✗ Branch 5 → 18 not taken.
✓ Branch 6 → 7 taken 3637 times.
✗ Branch 6 → 16 not taken.
|
3637 | auto [it, success] = symbols.insert({newName, *entryToCopy}); |
| 82 |
1/2✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 3637 times.
|
3637 | assert(success); |
| 83 | 7274 | return &it->second; | |
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Check if a symbol exists in the current or any parent scope and return it if possible | ||
| 88 | * | ||
| 89 | * @param name Name of the desired symbol | ||
| 90 | * @return Desired symbol / nullptr if the symbol was not found | ||
| 91 | */ | ||
| 92 | 304519 | SymbolTableEntry *SymbolTable::lookup(const std::string &name) { // NOLINT(misc-no-recursion) | |
| 93 | // Check if the symbol exists in the current scope. If yes, take it | ||
| 94 |
2/2✓ Branch 3 → 4 taken 95113 times.
✓ Branch 3 → 5 taken 209406 times.
|
304519 | if (SymbolTableEntry *entry = lookupStrict(name)) |
| 95 | 95113 | return entry; | |
| 96 | |||
| 97 | // Symbol was not found in the current scope | ||
| 98 | // We reached the root scope, the symbol does not exist at all | ||
| 99 |
2/2✓ Branch 5 → 6 taken 84956 times.
✓ Branch 5 → 7 taken 124450 times.
|
209406 | if (!parent) |
| 100 | 84956 | return nullptr; | |
| 101 | // If we search for the result variable, we want to stop the search when exiting a lambda body | ||
| 102 |
6/6✓ Branch 8 → 9 taken 7461 times.
✓ Branch 8 → 11 taken 116989 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 7460 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 124449 times.
|
124450 | if (name == RETURN_VARIABLE_NAME && scope->type == ScopeType::LAMBDA_BODY) |
| 103 | 1 | return nullptr; | |
| 104 | // If there is a parent scope, continue the search there | ||
| 105 | 124449 | SymbolTableEntry *entry = parent->lookup(name); | |
| 106 | // Symbol was also not found in all the parent scopes, return nullptr | ||
| 107 |
2/2✓ Branch 15 → 16 taken 62767 times.
✓ Branch 15 → 17 taken 61682 times.
|
124449 | if (!entry) |
| 108 | 62767 | return nullptr; | |
| 109 | // Check if this scope requires capturing and capture the variable if appropriate | ||
| 110 |
9/14✓ Branch 17 → 18 taken 23 times.
✓ Branch 17 → 24 taken 61659 times.
✓ Branch 18 → 19 taken 23 times.
✗ Branch 18 → 37 not taken.
✓ Branch 19 → 20 taken 23 times.
✗ Branch 19 → 24 not taken.
✓ Branch 20 → 21 taken 23 times.
✗ Branch 20 → 37 not taken.
✓ Branch 21 → 22 taken 23 times.
✗ Branch 21 → 37 not taken.
✓ Branch 22 → 23 taken 23 times.
✗ Branch 22 → 24 not taken.
✓ Branch 25 → 26 taken 23 times.
✓ Branch 25 → 35 taken 61659 times.
|
61682 | if (capturingRequired && !captures.contains(name) && !entry->getQualType().isOneOf({TY_IMPORT, TY_FUNCTION, TY_PROCEDURE})) { |
| 111 | // We need to make the symbol volatile if we are in an async scope and try to access a symbol that is not in an async scope | ||
| 112 |
3/4✓ Branch 27 → 28 taken 7 times.
✓ Branch 27 → 31 taken 16 times.
✓ Branch 29 → 30 taken 7 times.
✗ Branch 29 → 31 not taken.
|
23 | entry->isVolatile = scope->isInAsyncScope() && !entry->scope->isInAsyncScope(); |
| 113 | // Add the capture to the current scope | ||
| 114 |
2/4✓ Branch 32 → 33 taken 23 times.
✗ Branch 32 → 38 not taken.
✓ Branch 33 → 34 taken 23 times.
✗ Branch 33 → 38 not taken.
|
23 | captures.emplace(name, Capture(entry)); |
| 115 | } | ||
| 116 | 61682 | return entry; | |
| 117 | } | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Check if a symbol exists in the current or any parent scope and return it if possible. | ||
| 121 | * If the target symbol is an alias symbol, resolve the alias container entry. | ||
| 122 | * | ||
| 123 | * @param name Name of the desired symbol | ||
| 124 | * @return Desired symbol / nullptr if the symbol was not found + alias or not | ||
| 125 | */ | ||
| 126 | 18095 | std::pair<SymbolTableEntry *, bool> SymbolTable::lookupWithAliasResolution(const std::string &name) { | |
| 127 |
1/2✓ Branch 2 → 3 taken 18095 times.
✗ Branch 2 → 30 not taken.
|
18095 | SymbolTableEntry *entry = lookup(name); |
| 128 |
8/10✓ Branch 3 → 4 taken 3895 times.
✓ Branch 3 → 7 taken 14200 times.
✓ Branch 4 → 5 taken 3895 times.
✗ Branch 4 → 30 not taken.
✓ Branch 5 → 6 taken 3895 times.
✗ Branch 5 → 30 not taken.
✓ Branch 6 → 7 taken 3893 times.
✓ Branch 6 → 8 taken 2 times.
✓ Branch 9 → 10 taken 18093 times.
✓ Branch 9 → 13 taken 2 times.
|
18095 | if (!entry || !entry->getQualType().is(TY_ALIAS)) |
| 129 | 18093 | return {entry, false}; | |
| 130 | |||
| 131 | // We have an alias type here, resolve it | ||
| 132 |
1/2✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 2 times.
|
4 | assert(entry->scope->isRootScope()); |
| 133 | 2 | entry->used = true; | |
| 134 |
1/2✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 30 not taken.
|
2 | const std::string aliasedContainerEntryName = entry->name + ALIAS_CONTAINER_SUFFIX; |
| 135 |
1/2✓ Branch 18 → 19 taken 2 times.
✗ Branch 18 → 28 not taken.
|
2 | SymbolTableEntry *aliasedTypeContainerEntry = entry->scope->lookupStrict(aliasedContainerEntryName); |
| 136 |
1/2✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 2 times.
|
2 | assert(aliasedTypeContainerEntry != nullptr); |
| 137 | 2 | return {aliasedTypeContainerEntry, true}; | |
| 138 | 2 | } | |
| 139 | |||
| 140 | /** | ||
| 141 | * Check if a symbol exists in the current scope and return it if possible | ||
| 142 | * | ||
| 143 | * @param symbolName Name of the desired symbol | ||
| 144 | * @return Desired symbol / nullptr if the symbol was not found | ||
| 145 | */ | ||
| 146 | 492897 | SymbolTableEntry *SymbolTable::lookupStrict(const std::string &symbolName) { | |
| 147 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 492897 times.
|
492897 | if (symbolName.empty()) |
| 148 | ✗ | return nullptr; | |
| 149 | // Check if a symbol with this name exists in this scope | ||
| 150 |
2/2✓ Branch 6 → 7 taken 253024 times.
✓ Branch 6 → 9 taken 239873 times.
|
492897 | if (symbols.contains(symbolName)) |
| 151 | 253024 | return &symbols.at(symbolName); | |
| 152 | // Check if a capture with this name exists in this scope | ||
| 153 |
2/2✓ Branch 10 → 11 taken 3 times.
✓ Branch 10 → 13 taken 239870 times.
|
239873 | if (captures.contains(symbolName)) |
| 154 | 3 | return captures.at(symbolName).capturedSymbol; | |
| 155 | // Otherwise, return a nullptr | ||
| 156 | 239870 | return nullptr; | |
| 157 | } | ||
| 158 | |||
| 159 | /** | ||
| 160 | * Check if a symbol exists in one of the composed field scopes of the current scope and return it if possible. | ||
| 161 | * This only works if the current scope is a struct scope. | ||
| 162 | * | ||
| 163 | * @param name Name of the desired symbol | ||
| 164 | * @param indexPath How to index the found symbol using order indices (e.g. for GEP) | ||
| 165 | * @return Desired symbol / nullptr if the symbol was not found | ||
| 166 | */ | ||
| 167 | 33729 | SymbolTableEntry *SymbolTable::lookupInComposedFields(const std::string &name, // NOLINT(misc-no-recursion) | |
| 168 | std::vector<size_t> &indexPath) { | ||
| 169 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 33729 times.
|
33729 | assert(scope->type == ScopeType::STRUCT); |
| 170 | |||
| 171 | // Check if we have a symbol with this name in the current scope | ||
| 172 |
2/2✓ Branch 5 → 6 taken 33636 times.
✓ Branch 5 → 8 taken 93 times.
|
33729 | if (SymbolTableEntry *result = lookupStrict(name)) { |
| 173 | 33636 | indexPath.push_back(result->orderIndex); | |
| 174 | 33636 | return result; | |
| 175 | } | ||
| 176 | |||
| 177 | // If it was not found in the current scope, loop through all composed fields in this scope | ||
| 178 |
2/2✓ Branch 25 → 9 taken 105 times.
✓ Branch 25 → 26 taken 13 times.
|
118 | for (size_t i = 0; i < scope->getFieldCount(); i++) { |
| 179 | 105 | const SymbolTableEntry *fieldEntry = lookupStrictByIndex(i); | |
| 180 | |||
| 181 | // Skip all fields that are not composition fields | ||
| 182 |
2/2✓ Branch 12 → 13 taken 14 times.
✓ Branch 12 → 14 taken 91 times.
|
105 | if (!fieldEntry->getQualType().isComposition()) |
| 183 | 14 | continue; | |
| 184 | |||
| 185 | // Add the current field's order index to the index path | ||
| 186 | 91 | indexPath.push_back(fieldEntry->orderIndex); | |
| 187 | |||
| 188 | // Search in the composed field's body scope | ||
| 189 | 91 | Scope *searchScope = fieldEntry->getQualType().getBodyScope(); | |
| 190 |
1/2✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 91 times.
|
91 | assert(searchScope != nullptr); |
| 191 |
2/2✓ Branch 20 → 21 taken 80 times.
✓ Branch 20 → 22 taken 11 times.
|
91 | if (SymbolTableEntry *result = searchScope->symbolTable.lookupInComposedFields(name, indexPath)) |
| 192 | 80 | return result; | |
| 193 | |||
| 194 | // Remove the current field's order index from the index path | ||
| 195 | 11 | indexPath.pop_back(); | |
| 196 | } | ||
| 197 | |||
| 198 | // Symbol was not found in current scope, return nullptr | ||
| 199 | 13 | return nullptr; | |
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * Check if an order index exists in the current or any parent scope and returns it if possible. | ||
| 204 | * Warning: Unlike the `lookup` method, this one doesn't consider the parent scopes | ||
| 205 | * | ||
| 206 | * @param orderIndex Order index of the desired symbol | ||
| 207 | * @return Desired symbol / nullptr if the symbol was not found | ||
| 208 | */ | ||
| 209 | 41496 | SymbolTableEntry *SymbolTable::lookupStrictByIndex(unsigned int orderIndex) { | |
| 210 |
4/8✓ Branch 2 → 3 taken 41496 times.
✗ Branch 2 → 14 not taken.
✓ Branch 3 → 4 taken 41496 times.
✗ Branch 3 → 14 not taken.
✓ Branch 4 → 5 taken 41496 times.
✗ Branch 4 → 14 not taken.
✓ Branch 11 → 6 taken 401459 times.
✗ Branch 11 → 12 not taken.
|
401459 | for (auto &val : symbols | std::views::values) { |
| 211 |
2/2✓ Branch 7 → 8 taken 41496 times.
✓ Branch 7 → 9 taken 359963 times.
|
401459 | if (val.orderIndex == orderIndex) |
| 212 | 41496 | return &val; | |
| 213 | } | ||
| 214 | ✗ | return nullptr; | |
| 215 | } | ||
| 216 | |||
| 217 | /** | ||
| 218 | * Check if an anonymous symbol exists in the current scope and return it if possible | ||
| 219 | * | ||
| 220 | * @param declNode AST node where the anonymous symbol is declared | ||
| 221 | * @param numericSuffix Numeric suffix of the anonymous symbol | ||
| 222 | * @return Anonymous symbol | ||
| 223 | */ | ||
| 224 | 6605 | SymbolTableEntry *SymbolTable::lookupAnonymous(const ASTNode *declNode, size_t numericSuffix) { | |
| 225 |
1/2✓ Branch 2 → 3 taken 6605 times.
✗ Branch 2 → 27 not taken.
|
6605 | std::stringstream name; |
| 226 |
5/10✓ Branch 3 → 4 taken 6605 times.
✗ Branch 3 → 25 not taken.
✓ Branch 4 → 5 taken 6605 times.
✗ Branch 4 → 21 not taken.
✓ Branch 5 → 6 taken 6605 times.
✗ Branch 5 → 19 not taken.
✓ Branch 6 → 7 taken 6605 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 6605 times.
✗ Branch 7 → 19 not taken.
|
6605 | name << "anon." << declNode->codeLoc.toString() << "." << reinterpret_cast<size_t>(declNode); |
| 227 |
2/2✓ Branch 9 → 10 taken 100 times.
✓ Branch 9 → 12 taken 6505 times.
|
6605 | if (numericSuffix > 0) |
| 228 |
2/4✓ Branch 10 → 11 taken 100 times.
✗ Branch 10 → 25 not taken.
✓ Branch 11 → 12 taken 100 times.
✗ Branch 11 → 25 not taken.
|
100 | name << "." << numericSuffix; |
| 229 |
2/4✓ Branch 12 → 13 taken 6605 times.
✗ Branch 12 → 24 not taken.
✓ Branch 13 → 14 taken 6605 times.
✗ Branch 13 → 22 not taken.
|
13210 | return lookup(name.str()); |
| 230 | 6605 | } | |
| 231 | |||
| 232 | /** | ||
| 233 | * Check if a capture exists in the current or any parent scope and return it if possible | ||
| 234 | * | ||
| 235 | * @param name Name of the desired captured symbol | ||
| 236 | * @return Capture / nullptr if the capture was not found | ||
| 237 | */ | ||
| 238 | 243172 | Capture *SymbolTable::lookupCapture(const std::string &name) { // NOLINT(misc-no-recursion) | |
| 239 | // Check if the capture exists in the current scope. If yes, take it | ||
| 240 |
2/2✓ Branch 3 → 4 taken 33 times.
✓ Branch 3 → 5 taken 243139 times.
|
243172 | if (Capture *capture = lookupCaptureStrict(name)) |
| 241 | 33 | return capture; | |
| 242 | |||
| 243 | // We reached the root scope, the symbol does not exist at all | ||
| 244 |
2/2✓ Branch 5 → 6 taken 69585 times.
✓ Branch 5 → 7 taken 173554 times.
|
243139 | if (parent == nullptr) |
| 245 | 69585 | return nullptr; | |
| 246 | |||
| 247 | 173554 | return parent->lookupCapture(name); | |
| 248 | } | ||
| 249 | |||
| 250 | /** | ||
| 251 | * Check if a capture exists in the current scope and return it if possible | ||
| 252 | * | ||
| 253 | * @param name Name of the desired captured symbol | ||
| 254 | * @return Capture / nullptr if the capture was not found | ||
| 255 | */ | ||
| 256 | 243172 | Capture *SymbolTable::lookupCaptureStrict(const std::string &name) { | |
| 257 | // If available in the current scope, return it | ||
| 258 |
2/2✓ Branch 3 → 4 taken 33 times.
✓ Branch 3 → 6 taken 243139 times.
|
243172 | if (captures.contains(name)) |
| 259 | 33 | return &captures.at(name); | |
| 260 | // Otherwise, return nullptr | ||
| 261 | 243139 | return nullptr; | |
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * Set capturing for this scope required. | ||
| 266 | */ | ||
| 267 | 37 | void SymbolTable::setCapturingRequired() { capturingRequired = true; } | |
| 268 | |||
| 269 | /** | ||
| 270 | * Deletes an existing anonymous symbol | ||
| 271 | * | ||
| 272 | * @param name Anonymous symbol name | ||
| 273 | */ | ||
| 274 | 2110 | void SymbolTable::deleteAnonymous(const std::string &name) { symbols.erase(name); } | |
| 275 | |||
| 276 | /** | ||
| 277 | * Stringify a symbol table to a human-readable form. This is used to realize dumps of symbol tables | ||
| 278 | * | ||
| 279 | * Example: | ||
| 280 | * { | ||
| 281 | * "name": "<SymbolTableName>" | ||
| 282 | * "symbols": [ | ||
| 283 | * ... (SymbolTableEntry) | ||
| 284 | * ], | ||
| 285 | * "children": [ | ||
| 286 | * ... (SymbolTable) | ||
| 287 | * ] | ||
| 288 | * } | ||
| 289 | * | ||
| 290 | * @return Symbol table if form of a string | ||
| 291 | */ | ||
| 292 | 80642 | nlohmann::json SymbolTable::toJSON() const { | |
| 293 | // Collect all symbols | ||
| 294 | 80642 | std::vector<nlohmann::json> jsonSymbols; | |
| 295 |
1/2✓ Branch 3 → 4 taken 80642 times.
✗ Branch 3 → 59 not taken.
|
80642 | jsonSymbols.reserve(symbols.size()); |
| 296 |
5/8✓ Branch 4 → 5 taken 80642 times.
✗ Branch 4 → 44 not taken.
✓ Branch 5 → 6 taken 80642 times.
✗ Branch 5 → 44 not taken.
✓ Branch 6 → 7 taken 80642 times.
✗ Branch 6 → 44 not taken.
✓ Branch 14 → 8 taken 181815 times.
✓ Branch 14 → 15 taken 80642 times.
|
262457 | for (const SymbolTableEntry &symbol : symbols | std::views::values) |
| 297 |
2/4✓ Branch 9 → 10 taken 181815 times.
✗ Branch 9 → 43 not taken.
✓ Branch 10 → 11 taken 181815 times.
✗ Branch 10 → 41 not taken.
|
181815 | jsonSymbols.emplace_back(symbol.toJSON()); |
| 298 | |||
| 299 | // Collect all captures | ||
| 300 | 80642 | std::vector<nlohmann::json> jsonCaptures; | |
| 301 |
1/2✓ Branch 16 → 17 taken 80642 times.
✗ Branch 16 → 57 not taken.
|
80642 | jsonCaptures.reserve(captures.size()); |
| 302 |
5/8✓ Branch 17 → 18 taken 80642 times.
✗ Branch 17 → 48 not taken.
✓ Branch 18 → 19 taken 80642 times.
✗ Branch 18 → 48 not taken.
✓ Branch 19 → 20 taken 80642 times.
✗ Branch 19 → 48 not taken.
✓ Branch 27 → 21 taken 25 times.
✓ Branch 27 → 28 taken 80642 times.
|
80667 | for (const Capture &capture : captures | std::views::values) |
| 303 |
2/4✓ Branch 22 → 23 taken 25 times.
✗ Branch 22 → 47 not taken.
✓ Branch 23 → 24 taken 25 times.
✗ Branch 23 → 45 not taken.
|
25 | jsonCaptures.emplace_back(capture.toJSON()); |
| 304 | |||
| 305 | // Generate json | ||
| 306 | 80642 | nlohmann::json result; | |
| 307 |
2/4✓ Branch 29 → 30 taken 80642 times.
✗ Branch 29 → 51 not taken.
✓ Branch 30 → 31 taken 80642 times.
✗ Branch 30 → 49 not taken.
|
80642 | result["symbols"] = jsonSymbols; |
| 308 |
2/4✓ Branch 33 → 34 taken 80642 times.
✗ Branch 33 → 54 not taken.
✓ Branch 34 → 35 taken 80642 times.
✗ Branch 34 → 52 not taken.
|
80642 | result["captures"] = jsonCaptures; |
| 309 | 80642 | return result; | |
| 310 | 80642 | } | |
| 311 | |||
| 312 | } // namespace spice::compiler | ||
| 313 |