| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* Copyright 2025 Jonathan S. Arney | ||
| 2 | * | ||
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | * you may not use this file except in compliance with the License. | ||
| 5 | * You may obtain a copy of the License at | ||
| 6 | * | ||
| 7 | * https://github.com/jarney/gyoji/blob/master/LICENSE | ||
| 8 | * | ||
| 9 | * Unless required by applicable law or agreed to in writing, software | ||
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | * See the License for the specific language governing permissions and | ||
| 13 | * limitations under the License. | ||
| 14 | */ | ||
| 15 | #include <gyoji-frontend.hpp> | ||
| 16 | #include <gyoji.l.hpp> | ||
| 17 | #include <gyoji.y.hpp> | ||
| 18 | #include <gyoji-misc/jstring.hpp> | ||
| 19 | |||
| 20 | using namespace Gyoji::context; | ||
| 21 | using namespace Gyoji::frontend; | ||
| 22 | using namespace Gyoji::frontend::namespaces; | ||
| 23 | using namespace Gyoji::frontend::tree; | ||
| 24 | |||
| 25 | 240 | ParseResult::ParseResult( | |
| 26 | Gyoji::context::CompilerContext & _compiler_context, | ||
| 27 | Gyoji::owned<NS2Context> _ns2_context | ||
| 28 | 240 | ) | |
| 29 | 240 | : compiler_context(_compiler_context) | |
| 30 | 240 | , ns2_context(std::move(_ns2_context)) | |
| 31 | 240 | , translation_unit(nullptr) | |
| 32 | 240 | {} | |
| 33 | 240 | ParseResult::~ParseResult() | |
| 34 | 240 | {} | |
| 35 | |||
| 36 | const NS2Context & | ||
| 37 | 48 | ParseResult::get_ns2_context() const | |
| 38 | { | ||
| 39 | 48 | return *ns2_context; | |
| 40 | } | ||
| 41 | |||
| 42 | Errors & | ||
| 43 | 6 | ParseResult::get_errors() const | |
| 44 | { | ||
| 45 | 6 | return compiler_context.get_errors(); | |
| 46 | } | ||
| 47 | |||
| 48 | const TranslationUnit & | ||
| 49 | 196 | ParseResult::get_translation_unit() const | |
| 50 | { | ||
| 51 | 196 | return *translation_unit; | |
| 52 | } | ||
| 53 | bool | ||
| 54 | 46 | ParseResult::has_translation_unit() const | |
| 55 | 46 | { return translation_unit.get() != nullptr; } | |
| 56 | |||
| 57 | bool | ||
| 58 | 194 | ParseResult::has_errors() const | |
| 59 | { | ||
| 60 | 194 | return compiler_context.get_errors().size() != 0; | |
| 61 | } | ||
| 62 | |||
| 63 | const TokenStream & | ||
| 64 | 48 | ParseResult::get_token_stream() const | |
| 65 | { | ||
| 66 | 48 | return compiler_context.get_token_stream(); | |
| 67 | } | ||
| 68 | const Gyoji::context::CompilerContext & | ||
| 69 | ✗ | ParseResult::get_compiler_context() const | |
| 70 | ✗ | { return compiler_context; } | |
| 71 | |||
| 72 | void | ||
| 73 | 238 | ParseResult::set_translation_unit(Gyoji::owned<TranslationUnit> _translation_unit) | |
| 74 | { | ||
| 75 | 238 | translation_unit = std::move(_translation_unit); | |
| 76 | 238 | } | |
| 77 | |||
| 78 | void | ||
| 79 | ✗ | ParseResult::symbol_define(std::string _symbol, const SourceReference &src_ref) | |
| 80 | { | ||
| 81 | ✗ | Symbol symbol(_symbol, src_ref); | |
| 82 | ✗ | symbol_table.insert(std::pair(_symbol, symbol)); | |
| 83 | ✗ | } | |
| 84 | |||
| 85 | const Symbol * | ||
| 86 | ✗ | ParseResult::symbol_get(std::string name) const | |
| 87 | { | ||
| 88 | ✗ | const auto & symbol = symbol_table.find(name); | |
| 89 | ✗ | if (symbol == symbol_table.end()) { | |
| 90 | ✗ | return nullptr; | |
| 91 | } | ||
| 92 | ✗ | return &symbol->second; | |
| 93 | } | ||
| 94 | |||
| 95 | |||
| 96 | void | ||
| 97 | ✗ | ParseResult::symbol_table_dump() | |
| 98 | { | ||
| 99 | ✗ | for (const auto & symbol : symbol_table) { | |
| 100 | ✗ | fprintf(stderr, "Symbol table %s\n", symbol.first.c_str()); | |
| 101 | } | ||
| 102 | ✗ | } | |
| 103 | |||
| 104 | const Symbol * | ||
| 105 | ✗ | ParseResult::symbol_get_or_create(std::string symbol_name, const SourceReference & src_ref) | |
| 106 | { | ||
| 107 | ✗ | const Symbol *found = symbol_get(symbol_name); | |
| 108 | ✗ | if (found) return found; | |
| 109 | |||
| 110 | ✗ | symbol_define(symbol_name, src_ref); | |
| 111 | ✗ | return symbol_get(symbol_name); | |
| 112 | |||
| 113 | } | ||
| 114 | |||
| 115 | NS2Entity * | ||
| 116 | 5844 | ParseResult::identifier_get_or_create( | |
| 117 | std::string name, | ||
| 118 | bool allow_placement_in_namespace, | ||
| 119 | const SourceReference & _source_ref | ||
| 120 | ) | ||
| 121 | { | ||
| 122 | // Check to see if we already have this as an identifier. | ||
| 123 | // If so, just go with it. | ||
| 124 | 5844 | NS2Entity *entity = ns2_context->namespace_find(name); | |
| 125 |
2/2✓ Branch 0 taken 3906 times.
✓ Branch 1 taken 1938 times.
|
5844 | if (entity != nullptr) { |
| 126 |
1/2✓ Branch 1 taken 3906 times.
✗ Branch 2 not taken.
|
3906 | if (entity->get_type() == NS2Entity::ENTITY_TYPE_IDENTIFIER) { |
| 127 | 3906 | return entity; | |
| 128 | } | ||
| 129 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Identifier ") + name + std::string(" is ambiguous")); | |
| 130 | ✗ | error->add_message(_source_ref, | |
| 131 | ✗ | std::string("Identifier ") + name + std::string(" was declared as a different type of identifier.")); | |
| 132 | ✗ | error->add_message(entity->get_source_ref(), | |
| 133 | ✗ | std::string("First declared here")); | |
| 134 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 135 | ✗ | return nullptr; | |
| 136 | ✗ | } | |
| 137 | |||
| 138 | // Is this a simple name? If so, go ahead and define | ||
| 139 | // it in the current namespace. | ||
| 140 | 1938 | std::vector<std::string> name_components = Gyoji::misc::string_split(name, NS2Context::NAMESPACE_DELIMITER); | |
| 141 |
2/2✓ Branch 1 taken 1934 times.
✓ Branch 2 taken 4 times.
|
1938 | if (name_components.size() == 1) { |
| 142 | 1934 | entity = ns2_context->get_current()->add_identifier( | |
| 143 | name, | ||
| 144 | _source_ref | ||
| 145 | ); | ||
| 146 | 1934 | return entity; | |
| 147 | } | ||
| 148 | // Next, try to identify the namespace | ||
| 149 | // it's declared in, then try to define | ||
| 150 | // it inside that namespace (if we're allowed it). | ||
| 151 | |||
| 152 | #if 0 | ||
| 153 | if (!allow_placement_in_namespace) { | ||
| 154 | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier ") + name + std::string(".")); | ||
| 155 | error->add_message(_source_ref, std::string("Identifier must be a simple identifier ") + name + std::string(" and must not contain '::'.")); | ||
| 156 | compiler_context.get_errors().add_error(std::move(error)); | ||
| 157 | return nullptr; | ||
| 158 | } | ||
| 159 | else { | ||
| 160 | #endif | ||
| 161 | 4 | std::string simple_name = name_components.at(name_components.size()-1); | |
| 162 | 4 | name_components.pop_back(); | |
| 163 | 4 | std::string namespace_part = Gyoji::misc::join(name_components, NS2Context::NAMESPACE_DELIMITER); | |
| 164 | |||
| 165 | 4 | NS2Entity *namespace_entity = ns2_context->namespace_find(namespace_part); | |
| 166 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (namespace_entity == nullptr) { |
| 167 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier") + name + std::string(".")); | |
| 168 | ✗ | error->add_message(_source_ref, std::string("") + namespace_part + std::string(" is not a class or namespace.")); | |
| 169 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 170 | ✗ | return nullptr; | |
| 171 | ✗ | } | |
| 172 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
8 | if (namespace_entity->get_type() == NS2Entity::ENTITY_TYPE_NAMESPACE || |
| 173 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | namespace_entity->get_type() == NS2Entity::ENTITY_TYPE_CLASS) { |
| 174 | 4 | fprintf(stderr, "Namespace was found and is a namespace or class\n"); | |
| 175 | 4 | return namespace_entity->add_identifier(simple_name, _source_ref); | |
| 176 | } | ||
| 177 | else { | ||
| 178 | ✗ | fprintf(stderr, "Namespace was found but was not suitable to hold our child\n"); | |
| 179 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier") + name + std::string(".")); | |
| 180 | ✗ | error->add_message(_source_ref, std::string("Identifier must ") + name + std::string(" must be placed inside a namespace or class.")); | |
| 181 | ✗ | error->add_message(namespace_entity->get_source_ref(), std::string("Namespace is ") + namespace_part + std::string(" is not a namespace or class")); | |
| 182 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 183 | ✗ | return nullptr; | |
| 184 | ✗ | } | |
| 185 | #if 0 | ||
| 186 | } | ||
| 187 | #endif | ||
| 188 | 1938 | } | |
| 189 | |||
| 190 | NS2Entity * | ||
| 191 | 118 | ParseResult::namespace_get_or_create( | |
| 192 | std::string name, | ||
| 193 | const Gyoji::context::SourceReference & _source_ref | ||
| 194 | ) | ||
| 195 | { | ||
| 196 | // The _name might be a composite name, so we need to split it and handle | ||
| 197 | // each component separately as its own namespace. | ||
| 198 | 118 | std::vector<std::string> name_components = Gyoji::misc::string_split(name, NS2Context::NAMESPACE_DELIMITER); | |
| 199 | |||
| 200 | // For each component, first try to find it | ||
| 201 | // as an entity or namespace name. | ||
| 202 | 118 | NS2Entity *current = ns2_context->get_current(); | |
| 203 |
2/2✓ Branch 5 taken 118 times.
✓ Branch 6 taken 118 times.
|
236 | for (const std::string & name : name_components) { |
| 204 | // So what can happen is we have an enitity that is like a class | ||
| 205 | // that is an entity but also a namespace of the same name | ||
| 206 | // containing other things. But if that happens, we can't define | ||
| 207 | // it this way through a 'namespace' statement, so it's correct | ||
| 208 | // to discard it this way. | ||
| 209 | |||
| 210 | 118 | NS2Entity *entity = current->get_entity(name); | |
| 211 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 118 times.
|
118 | if (entity != nullptr) { |
| 212 | ✗ | if (entity->get_type() != NS2Entity::ENTITY_TYPE_NAMESPACE) { | |
| 213 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier") + name + std::string(".")); | |
| 214 | ✗ | error->add_message(_source_ref, std::string("Namespace ") + name + std::string(" must be placed inside another namespace or at the root.")); | |
| 215 | ✗ | error->add_message(entity->get_source_ref(), std::string("Identifier ") + name + std::string(" is not a namespace")); | |
| 216 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 217 | ✗ | return nullptr; | |
| 218 | ✗ | } | |
| 219 | ✗ | current = entity; | |
| 220 | } | ||
| 221 | else { | ||
| 222 | 118 | NS2Entity* newcurrent = current->add_namespace(name, _source_ref); | |
| 223 | 118 | current = newcurrent; | |
| 224 | } | ||
| 225 | } | ||
| 226 | 118 | return current; | |
| 227 | |||
| 228 | 118 | } | |
| 229 | |||
| 230 | NS2Entity* | ||
| 231 | 240 | ParseResult::type_get_or_create( | |
| 232 | std::string name, | ||
| 233 | const Gyoji::context::SourceReference & _source_ref | ||
| 234 | ) | ||
| 235 | { | ||
| 236 | // Check to see if we already have this as an identifier. | ||
| 237 | // If so, just go with it. | ||
| 238 | 240 | NS2Entity *entity = ns2_context->namespace_find(name); | |
| 239 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
|
240 | if (entity != nullptr) { |
| 240 | ✗ | if (entity->get_type() == NS2Entity::ENTITY_TYPE_TYPE) { | |
| 241 | ✗ | return entity; | |
| 242 | } | ||
| 243 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Type name ") + name + std::string(" is ambiguous")); | |
| 244 | ✗ | error->add_message(_source_ref, | |
| 245 | ✗ | std::string("Identifier ") + name + std::string(" was declared as a different type of identifier.")); | |
| 246 | ✗ | error->add_message(entity->get_source_ref(), | |
| 247 | ✗ | std::string("First declared here")); | |
| 248 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 249 | ✗ | return nullptr; | |
| 250 | ✗ | } | |
| 251 | |||
| 252 | 240 | std::vector<std::string> name_components = Gyoji::misc::string_split(name, NS2Context::NAMESPACE_DELIMITER); | |
| 253 |
1/2✓ Branch 1 taken 240 times.
✗ Branch 2 not taken.
|
240 | if (name_components.size() == 1) { |
| 254 | 240 | entity = ns2_context->get_current()->add_type( | |
| 255 | name, | ||
| 256 | _source_ref | ||
| 257 | ); | ||
| 258 | 240 | return entity; | |
| 259 | } | ||
| 260 | |||
| 261 | ✗ | std::string simple_name = name_components.at(name_components.size()-1); | |
| 262 | ✗ | name_components.pop_back(); | |
| 263 | ✗ | std::string namespace_part = Gyoji::misc::join(name_components, NS2Context::NAMESPACE_DELIMITER); | |
| 264 | |||
| 265 | ✗ | NS2Entity *namespace_entity = ns2_context->namespace_find(namespace_part); | |
| 266 | ✗ | if (namespace_entity == nullptr) { | |
| 267 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier") + name + std::string(".")); | |
| 268 | ✗ | error->add_message(_source_ref, std::string("") + namespace_part + std::string(" is not a class or namespace.")); | |
| 269 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 270 | ✗ | return nullptr; | |
| 271 | ✗ | } | |
| 272 | ✗ | if (namespace_entity->get_type() == NS2Entity::ENTITY_TYPE_NAMESPACE) { | |
| 273 | ✗ | return namespace_entity->add_class(simple_name, _source_ref); | |
| 274 | } | ||
| 275 | else { | ||
| 276 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier") + name + std::string(".")); | |
| 277 | ✗ | error->add_message(_source_ref, std::string("Identifier must ") + name + std::string(" must be placed inside a namespace.")); | |
| 278 | ✗ | error->add_message(namespace_entity->get_source_ref(), std::string("Namespace is ") + namespace_part + std::string(" is not a namespace")); | |
| 279 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 280 | ✗ | return nullptr; | |
| 281 | ✗ | } | |
| 282 | 240 | } | |
| 283 | |||
| 284 | NS2Entity* | ||
| 285 | 140 | ParseResult::class_get_or_create( | |
| 286 | std::string name, | ||
| 287 | const Gyoji::context::SourceReference & _source_ref | ||
| 288 | ) | ||
| 289 | { | ||
| 290 | // Check to see if we already have this as an identifier. | ||
| 291 | // If so, just go with it. | ||
| 292 | 140 | NS2Entity *entity = ns2_context->namespace_find(name); | |
| 293 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 140 times.
|
140 | if (entity != nullptr) { |
| 294 | ✗ | if (entity->get_type() == NS2Entity::ENTITY_TYPE_CLASS) { | |
| 295 | ✗ | return entity; | |
| 296 | } | ||
| 297 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Class name ") + name + std::string(" is ambiguous")); | |
| 298 | ✗ | error->add_message(_source_ref, | |
| 299 | ✗ | std::string("Identifier ") + name + std::string(" was declared as a different type of identifier.")); | |
| 300 | ✗ | error->add_message(entity->get_source_ref(), | |
| 301 | ✗ | std::string("First declared here")); | |
| 302 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 303 | ✗ | return nullptr; | |
| 304 | ✗ | } | |
| 305 | |||
| 306 | 140 | std::vector<std::string> name_components = Gyoji::misc::string_split(name, NS2Context::NAMESPACE_DELIMITER); | |
| 307 |
1/2✓ Branch 1 taken 140 times.
✗ Branch 2 not taken.
|
140 | if (name_components.size() == 1) { |
| 308 | 140 | entity = ns2_context->get_current()->add_class( | |
| 309 | name, | ||
| 310 | _source_ref | ||
| 311 | ); | ||
| 312 | 140 | return entity; | |
| 313 | } | ||
| 314 | |||
| 315 | ✗ | std::string simple_name = name_components.at(name_components.size()-1); | |
| 316 | ✗ | name_components.pop_back(); | |
| 317 | ✗ | std::string namespace_part = Gyoji::misc::join(name_components, NS2Context::NAMESPACE_DELIMITER); | |
| 318 | |||
| 319 | ✗ | NS2Entity *namespace_entity = ns2_context->namespace_find(namespace_part); | |
| 320 | ✗ | if (namespace_entity == nullptr) { | |
| 321 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier") + name + std::string(".")); | |
| 322 | ✗ | error->add_message(_source_ref, std::string("") + namespace_part + std::string(" is not a class or namespace.")); | |
| 323 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 324 | ✗ | return nullptr; | |
| 325 | ✗ | } | |
| 326 | ✗ | if (namespace_entity->get_type() == NS2Entity::ENTITY_TYPE_NAMESPACE) { | |
| 327 | ✗ | return namespace_entity->add_class(simple_name, _source_ref); | |
| 328 | } | ||
| 329 | else { | ||
| 330 | ✗ | auto error = std::make_unique<Gyoji::context::Error>(std::string("Invalid identifier") + name + std::string(".")); | |
| 331 | ✗ | error->add_message(_source_ref, std::string("Identifier must ") + name + std::string(" must be placed inside a namespace.")); | |
| 332 | ✗ | error->add_message(namespace_entity->get_source_ref(), std::string("Namespace is ") + namespace_part + std::string(" is not a namespace")); | |
| 333 | ✗ | compiler_context.get_errors().add_error(std::move(error)); | |
| 334 | ✗ | return nullptr; | |
| 335 | ✗ | } | |
| 336 | 140 | } | |
| 337 | |||
| 338 | |||
| 339 | /////////////////////////////////////////////////// | ||
| 340 | // Symbol | ||
| 341 | /////////////////////////////////////////////////// | ||
| 342 | |||
| 343 | ✗ | Symbol::Symbol(std::string _name, const SourceReference & _src_ref) | |
| 344 | ✗ | : name(_name) | |
| 345 | ✗ | , src_ref(_src_ref) | |
| 346 | ✗ | {} | |
| 347 | ✗ | Symbol::~Symbol() | |
| 348 | ✗ | {} | |
| 349 | ✗ | Symbol::Symbol(const Symbol & _other) | |
| 350 | ✗ | : name(_other.name) | |
| 351 | ✗ | , src_ref(_other.src_ref) | |
| 352 | ✗ | {} | |
| 353 |