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 | 60961 | SymbolTableEntry *SymbolTable::insert(const std::string &name, ASTNode *declNode, bool isAnonymousSymbol) { | |
| 23 | 60961 | const bool isGlobal = parent == nullptr; | |
| 24 | 60961 | size_t orderIndex = SIZE_MAX; | |
| 25 |
2/2✓ Branch 2 → 3 taken 58495 times.
✓ Branch 2 → 5 taken 2466 times.
|
60961 | if (!isAnonymousSymbol) |
| 26 | 444652 | 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 60961 times.
|
60961 | assert(!symbols.contains(name)); |
| 29 |
5/10✓ Branch 8 → 9 taken 60961 times.
✗ Branch 8 → 45 not taken.
✓ Branch 9 → 10 taken 60961 times.
✗ Branch 9 → 44 not taken.
✓ Branch 10 → 11 taken 60961 times.
✗ Branch 10 → 42 not taken.
✓ Branch 11 → 12 taken 60961 times.
✗ Branch 11 → 40 not taken.
✓ Branch 12 → 13 taken 60961 times.
✗ Branch 12 → 38 not taken.
|
60961 | symbols.insert({name, SymbolTableEntry(name, QualType(TY_INVALID), scope, declNode, orderIndex, isGlobal)}); |
| 30 | // Set entry to declared | ||
| 31 | 60961 | SymbolTableEntry *entry = &symbols.at(name); | |
| 32 |
1/2✓ Branch 17 → 18 taken 60961 times.
✗ Branch 17 → 48 not taken.
|
60961 | entry->updateState(DECLARED, declNode); |
| 33 | |||
| 34 | // Check if shadowed | ||
| 35 |
10/10✓ Branch 18 → 19 taken 49926 times.
✓ Branch 18 → 26 taken 11035 times.
✓ Branch 20 → 21 taken 850 times.
✓ Branch 20 → 26 taken 49076 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 60959 times.
|
60961 | 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 | 60961 | 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 | 2476 | 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 2476 times.
✗ Branch 2 → 34 not taken.
✓ Branch 3 → 4 taken 10 times.
✓ Branch 3 → 5 taken 2466 times.
|
2476 | if (SymbolTableEntry *anonSymbol = lookupAnonymous(declNode, numericSuffix)) |
| 56 | 10 | return anonSymbol; | |
| 57 | // Otherwise, create an anonymous entry | ||
| 58 |
1/2✓ Branch 5 → 6 taken 2466 times.
✗ Branch 5 → 34 not taken.
|
2466 | std::stringstream name; |
| 59 |
5/10✓ Branch 6 → 7 taken 2466 times.
✗ Branch 6 → 32 not taken.
✓ Branch 7 → 8 taken 2466 times.
✗ Branch 7 → 26 not taken.
✓ Branch 8 → 9 taken 2466 times.
✗ Branch 8 → 24 not taken.
✓ Branch 9 → 10 taken 2466 times.
✗ Branch 9 → 24 not taken.
✓ Branch 10 → 11 taken 2466 times.
✗ Branch 10 → 24 not taken.
|
2466 | name << "anon." << declNode->codeLoc.toString() << "." << reinterpret_cast<size_t>(declNode); |
| 60 |
2/2✓ Branch 12 → 13 taken 50 times.
✓ Branch 12 → 15 taken 2416 times.
|
2466 | 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 2466 times.
✗ Branch 15 → 29 not taken.
✓ Branch 16 → 17 taken 2466 times.
✗ Branch 16 → 27 not taken.
|
2466 | SymbolTableEntry *anonSymbol = insert(name.str(), declNode, true); |
| 63 |
1/2✓ Branch 18 → 19 taken 2466 times.
✗ Branch 18 → 32 not taken.
|
2466 | anonSymbol->updateType(qualType, false); |
| 64 |
1/2✓ Branch 19 → 20 taken 2466 times.
✗ Branch 19 → 30 not taken.
|
2466 | anonSymbol->updateState(DECLARED, declNode); |
| 65 |
1/2✓ Branch 20 → 21 taken 2466 times.
✗ Branch 20 → 31 not taken.
|
2466 | anonSymbol->updateState(INITIALIZED, declNode); |
| 66 | 2466 | anonSymbol->anonymous = true; | |
| 67 | 2466 | anonSymbol->used = true; | |
| 68 | 2466 | return anonSymbol; | |
| 69 | 2466 | } | |
| 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 | 3659 | SymbolTableEntry *SymbolTable::copySymbol(const std::string &originalName, const std::string &newName) { | |
| 79 |
1/2✓ Branch 2 → 3 taken 3659 times.
✗ Branch 2 → 19 not taken.
|
3659 | SymbolTableEntry *entryToCopy = lookupStrict(originalName); |
| 80 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 3659 times.
|
3659 | assert(entryToCopy != nullptr); |
| 81 |
2/4✓ Branch 5 → 6 taken 3659 times.
✗ Branch 5 → 18 not taken.
✓ Branch 6 → 7 taken 3659 times.
✗ Branch 6 → 16 not taken.
|
3659 | auto [it, success] = symbols.insert({newName, *entryToCopy}); |
| 82 |
1/2✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 3659 times.
|
3659 | assert(success); |
| 83 | 7318 | 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 | 302785 | 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 94389 times.
✓ Branch 3 → 5 taken 208396 times.
|
302785 | if (SymbolTableEntry *entry = lookupStrict(name)) |
| 95 | 94389 | 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 84628 times.
✓ Branch 5 → 7 taken 123768 times.
|
208396 | if (!parent) |
| 100 | 84628 | 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 7438 times.
✓ Branch 8 → 11 taken 116330 times.
✓ Branch 9 → 10 taken 1 time.
✓ Branch 9 → 11 taken 7437 times.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 123767 times.
|
123768 | 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 | 123767 | 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 62647 times.
✓ Branch 15 → 17 taken 61120 times.
|
123767 | if (!entry) |
| 108 | 62647 | return nullptr; | |
| 109 | // Check if this scope requires capturing and capture the variable if appropriate | ||
| 110 |
9/14✓ Branch 17 → 18 taken 24 times.
✓ Branch 17 → 24 taken 61096 times.
✓ Branch 18 → 19 taken 24 times.
✗ Branch 18 → 37 not taken.
✓ Branch 19 → 20 taken 24 times.
✗ Branch 19 → 24 not taken.
✓ Branch 20 → 21 taken 24 times.
✗ Branch 20 → 37 not taken.
✓ Branch 21 → 22 taken 24 times.
✗ Branch 21 → 37 not taken.
✓ Branch 22 → 23 taken 24 times.
✗ Branch 22 → 24 not taken.
✓ Branch 25 → 26 taken 24 times.
✓ Branch 25 → 35 taken 61096 times.
|
61120 | 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 8 times.
✓ Branch 27 → 31 taken 16 times.
✓ Branch 29 → 30 taken 8 times.
✗ Branch 29 → 31 not taken.
|
24 | entry->isVolatile = scope->isInAsyncScope() && !entry->scope->isInAsyncScope(); |
| 113 | // Add the capture to the current scope | ||
| 114 |
2/4✓ Branch 32 → 33 taken 24 times.
✗ Branch 32 → 38 not taken.
✓ Branch 33 → 34 taken 24 times.
✗ Branch 33 → 38 not taken.
|
24 | captures.emplace(name, Capture(entry)); |
| 115 | } | ||
| 116 | 61120 | 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 | 17977 | std::pair<SymbolTableEntry *, bool> SymbolTable::lookupWithAliasResolution(const std::string &name) { | |
| 127 |
1/2✓ Branch 2 → 3 taken 17977 times.
✗ Branch 2 → 30 not taken.
|
17977 | SymbolTableEntry *entry = lookup(name); |
| 128 |
8/10✓ Branch 3 → 4 taken 3823 times.
✓ Branch 3 → 7 taken 14154 times.
✓ Branch 4 → 5 taken 3823 times.
✗ Branch 4 → 30 not taken.
✓ Branch 5 → 6 taken 3823 times.
✗ Branch 5 → 30 not taken.
✓ Branch 6 → 7 taken 3821 times.
✓ Branch 6 → 8 taken 2 times.
✓ Branch 9 → 10 taken 17975 times.
✓ Branch 9 → 13 taken 2 times.
|
17977 | if (!entry || !entry->getQualType().is(TY_ALIAS)) |
| 129 | 17975 | 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 | 489802 | SymbolTableEntry *SymbolTable::lookupStrict(const std::string &symbolName) { | |
| 147 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 489802 times.
|
489802 | 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 251249 times.
✓ Branch 6 → 9 taken 238553 times.
|
489802 | if (symbols.contains(symbolName)) |
| 151 | 251249 | return &symbols.at(symbolName); | |
| 152 | // Check if a capture with this name exists in this scope | ||
| 153 |
2/2✓ Branch 10 → 11 taken 17 times.
✓ Branch 10 → 13 taken 238536 times.
|
238553 | if (captures.contains(symbolName)) |
| 154 | 17 | return captures.at(symbolName).capturedSymbol; | |
| 155 | // Otherwise, return a nullptr | ||
| 156 | 238536 | 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 | 33439 | 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 33439 times.
|
33439 | 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 33410 times.
✓ Branch 5 → 8 taken 29 times.
|
33439 | if (SymbolTableEntry *result = lookupStrict(name)) { |
| 173 | 33410 | indexPath.push_back(result->orderIndex); | |
| 174 | 33410 | 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 41 times.
✓ Branch 25 → 26 taken 13 times.
|
54 | for (size_t i = 0; i < scope->getFieldCount(); i++) { |
| 179 | 41 | 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 27 times.
|
41 | if (!fieldEntry->getQualType().isComposition()) |
| 183 | 14 | continue; | |
| 184 | |||
| 185 | // Add the current field's order index to the index path | ||
| 186 | 27 | indexPath.push_back(fieldEntry->orderIndex); | |
| 187 | |||
| 188 | // Search in the composed field's body scope | ||
| 189 | 27 | Scope *searchScope = fieldEntry->getQualType().getBodyScope(); | |
| 190 |
1/2✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 27 times.
|
27 | assert(searchScope != nullptr); |
| 191 |
2/2✓ Branch 20 → 21 taken 16 times.
✓ Branch 20 → 22 taken 11 times.
|
27 | if (SymbolTableEntry *result = searchScope->symbolTable.lookupInComposedFields(name, indexPath)) |
| 192 | 16 | 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 | 41835 | SymbolTableEntry *SymbolTable::lookupStrictByIndex(unsigned int orderIndex) { | |
| 210 |
4/8✓ Branch 2 → 3 taken 41835 times.
✗ Branch 2 → 14 not taken.
✓ Branch 3 → 4 taken 41835 times.
✗ Branch 3 → 14 not taken.
✓ Branch 4 → 5 taken 41835 times.
✗ Branch 4 → 14 not taken.
✓ Branch 11 → 6 taken 407943 times.
✗ Branch 11 → 12 not taken.
|
407943 | for (auto &val : symbols | std::views::values) { |
| 211 |
2/2✓ Branch 7 → 8 taken 41835 times.
✓ Branch 7 → 9 taken 366108 times.
|
407943 | if (val.orderIndex == orderIndex) |
| 212 | 41835 | 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 | 6613 | SymbolTableEntry *SymbolTable::lookupAnonymous(const ASTNode *declNode, size_t numericSuffix) { | |
| 225 |
1/2✓ Branch 2 → 3 taken 6613 times.
✗ Branch 2 → 27 not taken.
|
6613 | std::stringstream name; |
| 226 |
5/10✓ Branch 3 → 4 taken 6613 times.
✗ Branch 3 → 25 not taken.
✓ Branch 4 → 5 taken 6613 times.
✗ Branch 4 → 21 not taken.
✓ Branch 5 → 6 taken 6613 times.
✗ Branch 5 → 19 not taken.
✓ Branch 6 → 7 taken 6613 times.
✗ Branch 6 → 19 not taken.
✓ Branch 7 → 8 taken 6613 times.
✗ Branch 7 → 19 not taken.
|
6613 | name << "anon." << declNode->codeLoc.toString() << "." << reinterpret_cast<size_t>(declNode); |
| 227 |
2/2✓ Branch 9 → 10 taken 100 times.
✓ Branch 9 → 12 taken 6513 times.
|
6613 | 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 6613 times.
✗ Branch 12 → 24 not taken.
✓ Branch 13 → 14 taken 6613 times.
✗ Branch 13 → 22 not taken.
|
13226 | return lookup(name.str()); |
| 230 | 6613 | } | |
| 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 | 241464 | 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 36 times.
✓ Branch 3 → 5 taken 241428 times.
|
241464 | if (Capture *capture = lookupCaptureStrict(name)) |
| 241 | 36 | return capture; | |
| 242 | |||
| 243 | // We reached the root scope, the symbol does not exist at all | ||
| 244 |
2/2✓ Branch 5 → 6 taken 69117 times.
✓ Branch 5 → 7 taken 172311 times.
|
241428 | if (parent == nullptr) |
| 245 | 69117 | return nullptr; | |
| 246 | |||
| 247 | 172311 | 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 | 241464 | Capture *SymbolTable::lookupCaptureStrict(const std::string &name) { | |
| 257 | // If available in the current scope, return it | ||
| 258 |
2/2✓ Branch 3 → 4 taken 36 times.
✓ Branch 3 → 6 taken 241428 times.
|
241464 | if (captures.contains(name)) |
| 259 | 36 | return &captures.at(name); | |
| 260 | // Otherwise, return nullptr | ||
| 261 | 241428 | return nullptr; | |
| 262 | } | ||
| 263 | |||
| 264 | /** | ||
| 265 | * Set capturing for this scope required. | ||
| 266 | */ | ||
| 267 | 48 | void SymbolTable::setCapturingRequired() { capturingRequired = true; } | |
| 268 | |||
| 269 | /** | ||
| 270 | * Deletes an existing anonymous symbol | ||
| 271 | * | ||
| 272 | * @param name Anonymous symbol name | ||
| 273 | */ | ||
| 274 | 2122 | 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 | 80815 | nlohmann::json SymbolTable::toJSON() const { | |
| 293 | // Collect all symbols | ||
| 294 | 80815 | std::vector<nlohmann::json> jsonSymbols; | |
| 295 |
1/2✓ Branch 3 → 4 taken 80815 times.
✗ Branch 3 → 59 not taken.
|
80815 | jsonSymbols.reserve(symbols.size()); |
| 296 |
5/8✓ Branch 4 → 5 taken 80815 times.
✗ Branch 4 → 44 not taken.
✓ Branch 5 → 6 taken 80815 times.
✗ Branch 5 → 44 not taken.
✓ Branch 6 → 7 taken 80815 times.
✗ Branch 6 → 44 not taken.
✓ Branch 14 → 8 taken 182157 times.
✓ Branch 14 → 15 taken 80815 times.
|
262972 | for (const SymbolTableEntry &symbol : symbols | std::views::values) |
| 297 |
2/4✓ Branch 9 → 10 taken 182157 times.
✗ Branch 9 → 43 not taken.
✓ Branch 10 → 11 taken 182157 times.
✗ Branch 10 → 41 not taken.
|
182157 | jsonSymbols.emplace_back(symbol.toJSON()); |
| 298 | |||
| 299 | // Collect all captures | ||
| 300 | 80815 | std::vector<nlohmann::json> jsonCaptures; | |
| 301 |
1/2✓ Branch 16 → 17 taken 80815 times.
✗ Branch 16 → 57 not taken.
|
80815 | jsonCaptures.reserve(captures.size()); |
| 302 |
5/8✓ Branch 17 → 18 taken 80815 times.
✗ Branch 17 → 48 not taken.
✓ Branch 18 → 19 taken 80815 times.
✗ Branch 18 → 48 not taken.
✓ Branch 19 → 20 taken 80815 times.
✗ Branch 19 → 48 not taken.
✓ Branch 27 → 21 taken 26 times.
✓ Branch 27 → 28 taken 80815 times.
|
80841 | for (const Capture &capture : captures | std::views::values) |
| 303 |
2/4✓ Branch 22 → 23 taken 26 times.
✗ Branch 22 → 47 not taken.
✓ Branch 23 → 24 taken 26 times.
✗ Branch 23 → 45 not taken.
|
26 | jsonCaptures.emplace_back(capture.toJSON()); |
| 304 | |||
| 305 | // Generate json | ||
| 306 | 80815 | nlohmann::json result; | |
| 307 |
2/4✓ Branch 29 → 30 taken 80815 times.
✗ Branch 29 → 51 not taken.
✓ Branch 30 → 31 taken 80815 times.
✗ Branch 30 → 49 not taken.
|
80815 | result["symbols"] = jsonSymbols; |
| 308 |
2/4✓ Branch 33 → 34 taken 80815 times.
✗ Branch 33 → 54 not taken.
✓ Branch 34 → 35 taken 80815 times.
✗ Branch 34 → 52 not taken.
|
80815 | result["captures"] = jsonCaptures; |
| 309 | 80815 | return result; | |
| 310 | 80815 | } | |
| 311 | |||
| 312 | } // namespace spice::compiler | ||
| 313 |