GCC Code Coverage Report


Directory: src/
File: src/frontend/parse-result.cpp
Date: 2025-10-24 11:14:59
Exec Total Coverage
Lines: 68 182 37.4%
Functions: 13 21 61.9%
Branches: 16 46 34.8%

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