GCC Code Coverage Report


Directory: src/
File: src/frontend/ns2.cpp
Date: 2025-10-24 11:14:59
Exec Total Coverage
Lines: 171 193 88.6%
Functions: 26 29 89.7%
Branches: 38 46 82.6%

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/ns2.hpp>
16 #include <gyoji-misc/jstring.hpp>
17
18 const std::string Gyoji::frontend::namespaces::NS2Context::NAMESPACE_DELIMITER("::");
19
20 using namespace Gyoji::frontend::namespaces;
21
22
23 ///////////////////////////////////////////////////
24 // NS2Entity
25 ///////////////////////////////////////////////////
26 6180 NS2Entity::NS2Entity(
27 std::string _name,
28 EntityType _type,
29 NS2Entity* _parent,
30 const Gyoji::context::SourceReference & _source_ref
31 6180 )
32 6180 : name(_name)
33 6180 , type(_type)
34 6180 , parent(_parent)
35 6180 , source_ref(_source_ref)
36 6180 {}
37
38 6180 NS2Entity::~NS2Entity()
39 6180 {}
40
41 const std::string &
42 8542 NS2Entity::get_name() const
43 8542 { return name;}
44
45 std::string
46 6206 NS2Entity::get_fully_qualified_name() const
47 {
48 6206 std::string ret_string;
49
2/2
✓ Branch 0 taken 570 times.
✓ Branch 1 taken 5636 times.
6206 if (parent == nullptr) {
50 // If our parent is null, we are the root
51 // namespace, so our name is empty.
52 1140 return std::string("");
53 }
54
2/2
✓ Branch 0 taken 424 times.
✓ Branch 1 taken 5212 times.
5636 if (parent->parent != nullptr) {
55 // If our parent is not the root, we can ask for its name also.
56 424 return parent->get_fully_qualified_name() + NS2Context::NAMESPACE_DELIMITER + get_name();
57 }
58 else {
59 5212 return get_name();
60 }
61 6206 }
62
63
64 const NS2Entity::EntityType &
65 25920 NS2Entity::get_type() const
66 25920 { return type; }
67
68 const Gyoji::context::SourceReference &
69 NS2Entity::get_source_ref() const
70 { return source_ref; }
71
72 NS2Entity*
73 1938 NS2Entity::add_identifier(
74 std::string _name,
75 const Gyoji::context::SourceReference & _source_ref
76 )
77 {
78 1938 const auto & it = elements.find(_name);
79
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1938 times.
1938 if (it != elements.end()) {
80 fprintf(stderr, "Identifier already defined in namespace %s\n", _name.c_str());
81 return nullptr;
82 }
83
84 1938 Gyoji::owned<NS2Entity> entity = std::make_unique<NS2Entity>(_name, NS2Entity::ENTITY_TYPE_IDENTIFIER, this, _source_ref);
85 1938 NS2Entity *ret = entity.get();
86 1938 elements.insert(std::pair(_name, std::move(entity)));
87 1938 return ret;
88 1938 }
89
90 NS2Entity*
91 3696 NS2Entity::add_type(
92 std::string _name,
93 const Gyoji::context::SourceReference & _source_ref
94 )
95 {
96 3696 const auto & it = elements.find(_name);
97
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 3696 times.
3696 if (it != elements.end()) {
98 fprintf(stderr, "Identifier already defined in namespace %s\n", _name.c_str());
99 return nullptr;
100 }
101
102 3696 Gyoji::owned<NS2Entity> entity = std::make_unique<NS2Entity>(_name, NS2Entity::ENTITY_TYPE_TYPE, this, _source_ref);
103 3696 NS2Entity *ret = entity.get();
104 3696 elements.insert(std::pair(_name, std::move(entity)));
105 3696 return ret;
106 3696 }
107
108 NS2Entity*
109 140 NS2Entity::add_class(
110 std::string _name,
111 const Gyoji::context::SourceReference & _source_ref
112 )
113 {
114 140 const auto & it = elements.find(_name);
115
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 140 times.
140 if (it != elements.end()) {
116 fprintf(stderr, "Identifier already defined in namespace %s\n", _name.c_str());
117 return nullptr;
118 }
119 140 Gyoji::owned<NS2Entity> entity = std::make_unique<NS2Entity>(_name, NS2Entity::ENTITY_TYPE_CLASS, this, _source_ref);
120 140 NS2Entity *ret = entity.get();
121 140 elements.insert(std::pair(_name, std::move(entity)));
122 140 return ret;
123 140 }
124
125 NS2Entity*
126 118 NS2Entity::add_namespace(
127 std::string _namespace,
128 const Gyoji::context::SourceReference & _source_ref
129 )
130 {
131 118 Gyoji::owned<NS2Entity> entity = std::make_unique<NS2Entity>(_namespace, NS2Entity::ENTITY_TYPE_NAMESPACE, this, _source_ref);
132 118 NS2Entity *ret = entity.get();
133 118 elements.insert(std::pair(_namespace, std::move(entity)));
134 236 return ret;
135 118 }
136
137 NS2Entity *
138 22444 NS2Entity::get_entity(std::string _name) const
139 {
140 22444 const auto & it = elements.find(_name);
141
2/2
✓ Branch 2 taken 10816 times.
✓ Branch 3 taken 11628 times.
22444 if (it == elements.end()) {
142 10816 return nullptr;
143 }
144 11628 return it->second.get();
145 }
146
147 NS2Entity*
148 NS2Entity::add_entity(std::string _name, Gyoji::owned<NS2Entity> _entity)
149 {
150 NS2Entity *ret = _entity.get();
151 elements.insert(std::pair(_name, std::move(_entity)));
152 return ret;
153 }
154
155 NS2Entity *
156 1252 NS2Entity::get_parent() const
157 1252 { return parent; }
158
159 void
160 104 NS2Entity::dump(int indent) const
161 {
162 208 std::string pad(indent*8, ' ');
163 208 std::string pad2((indent+1)*8, ' ');
164 208 std::string start = pad + std::string("{");
165 104 std::string end = pad + std::string("}");
166 104 fprintf(stderr, "%s\n", start.c_str());
167
2/2
✓ Branch 5 taken 1028 times.
✓ Branch 6 taken 104 times.
1132 for (const auto & el : elements) {
168 1028 std::string name = el.first;
169 1028 const NS2Entity *entity = el.second.get();
170
171 1028 std::string typestr;
172
4/6
✓ Branch 1 taken 342 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 630 times.
✓ Branch 4 taken 30 times.
✓ Branch 5 taken 26 times.
✗ Branch 6 not taken.
1028 switch (entity->get_type()) {
173 342 case NS2Entity::ENTITY_TYPE_IDENTIFIER:
174 342 typestr = " identifier";
175 342 break;
176 case NS2Entity::ENTITY_TYPE_LABEL:
177 typestr = " label";
178 break;
179 630 case NS2Entity::ENTITY_TYPE_TYPE:
180 630 typestr = " type";
181 630 break;
182 30 case NS2Entity::ENTITY_TYPE_CLASS:
183 30 typestr = " class";
184 30 break;
185 26 case NS2Entity::ENTITY_TYPE_NAMESPACE:
186 26 typestr = " namespace";
187 26 break;
188 }
189 2056 std::string entity_desc = pad2 + name + typestr;
190 1028 fprintf(stderr, "%s\n", entity_desc.c_str());
191
4/4
✓ Branch 1 taken 998 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 56 times.
✓ Branch 4 taken 972 times.
2026 if (entity->get_type() == NS2Entity::ENTITY_TYPE_CLASS ||
192
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 972 times.
998 entity->get_type() == NS2Entity::ENTITY_TYPE_NAMESPACE) {
193 56 entity->dump(indent+1);
194 }
195 1028 }
196 104 fprintf(stderr, "%s\n", end.c_str());
197 104 }
198
199 ///////////////////////////////////////////////////
200 // NS2Context
201 ///////////////////////////////////////////////////
202 1260 NS2SearchPaths::NS2SearchPaths()
203 1260 {}
204
205 1260 NS2SearchPaths::~NS2SearchPaths()
206 1260 {}
207
208 void
209 46 NS2SearchPaths::add_using(std::string name, NS2Entity *alias)
210 {
211 46 aliases.push_back(std::pair(name, alias));
212 46 alias_map.insert(std::pair(name, alias));
213 46 }
214
215 NS2Entity *
216 NS2SearchPaths::get_name(std::string name)
217 {
218 const auto & it = alias_map.find(name);
219 if (it == alias_map.end()) {
220 return nullptr;
221 }
222 return it->second;
223 }
224
225 const std::vector<std::pair<std::string, NS2Entity*>> &
226 10018 NS2SearchPaths::get_aliases() const
227 10018 { return aliases; }
228
229 ///////////////////////////////////////////////////
230 // NS2Context
231 ///////////////////////////////////////////////////
232
233 static std::string internal_filename("builtin");
234 static const Gyoji::context::SourceReference zero_source_ref(internal_filename, 1, 0, 0);
235
236 288 NS2Context::NS2Context()
237 288 : root(std::make_unique<NS2Entity>(
238 "root",
239 576 NS2Entity::ENTITY_TYPE_NAMESPACE,
240 288 nullptr,
241 zero_source_ref
242 )
243 288 )
244 {
245 288 Gyoji::owned<NS2SearchPaths> search_paths = std::make_unique<NS2SearchPaths>();
246 288 stack.push_back(std::pair(root.get(), std::move(search_paths)));
247
248 576 root->add_type("i8", zero_source_ref);
249 576 root->add_type("i16", zero_source_ref);
250 576 root->add_type("i32", zero_source_ref);
251 576 root->add_type("i64", zero_source_ref);
252
253 576 root->add_type("u8", zero_source_ref);
254 576 root->add_type("u16", zero_source_ref);
255 576 root->add_type("u32", zero_source_ref);
256 576 root->add_type("u64", zero_source_ref);
257
258 576 root->add_type("f32", zero_source_ref);
259 576 root->add_type("f64", zero_source_ref);
260
261 576 root->add_type("void", zero_source_ref);
262 576 root->add_type("bool", zero_source_ref);
263 288 }
264
265 288 NS2Context::~NS2Context()
266 288 {}
267
268 // We're going to search for an entity in
269 // the current namespace context.
270 //void
271 //NS2Context::find_entity(std::string _name)
272 //{
273 //}
274 //
275 // We are going to define a type or identifier
276 // in the current namespace. This would be like 'class Foo' or 'typedef u8 bar'.
277 //NS2Context::new_entity();
278
279 // We are now adding a namespace to the list of
280 // namespaces we want to search.
281 void
282 46 NS2Context::namespace_using(std::string name, NS2Entity* alias)
283 {
284 46 const auto & b = stack.back();
285 46 b.second->add_using(name, alias);
286 46 }
287
288 NS2Entity*
289 22092 NS2Context::namespace_find_in(NS2Entity* current, std::string _name) const
290 {
291 22092 std::vector<std::string> name_components = Gyoji::misc::string_split(_name, NAMESPACE_DELIMITER);
292 // This is the point where we might insert
293 // our aliases to search for them under different
294 // names.
295 22092 return namespace_find_in(current, name_components);
296 22092 }
297
298 NS2Entity *
299 22092 NS2Context::namespace_find_in(NS2Entity* current, std::vector<std::string> names) const
300 {
301 22092 NS2Entity *entity_found = nullptr;
302
303 //dump();
304
305 // First, look in the current namespace
306 // for the first component of the name.
307 22092 size_t i = 0;
308 while (true) {
309 22326 entity_found = current->get_entity(names.at(i));
310
2/2
✓ Branch 1 taken 22002 times.
✓ Branch 2 taken 324 times.
22326 if (i == names.size()-1) {
311 22002 break;
312 }
313
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 234 times.
324 if (entity_found == nullptr) {
314 90 break;
315 }
316 // We have another parent to search, so try that.
317 234 current = entity_found;
318 234 i++;
319 }
320 22092 return entity_found;
321 }
322
323 NS2Entity*
324 17308 NS2Context::namespace_find(std::string name) const
325 {
326 // Iterate the stack in reverse order,
327 // searching at each level.
328
2/2
✓ Branch 1 taken 21332 times.
✓ Branch 2 taken 5914 times.
27246 for (size_t i = 0; i < stack.size(); i++) {
329 21332 const auto & it = stack.at(stack.size() - 1 - i);
330 // First, check to see if we can find it in the namespace
331 // we're currently operating on.
332 21332 NS2Entity *found = namespace_find_in(it.first, name);
333
2/2
✓ Branch 0 taken 11314 times.
✓ Branch 1 taken 10018 times.
21332 if (found != nullptr) {
334 11314 return found;
335 }
336
337 // Now, try finding the thing in
338 // any relevant alias namespaces from 'using' clauses.
339
2/2
✓ Branch 7 taken 760 times.
✓ Branch 8 taken 9938 times.
10698 for (const auto & alias : it.second->get_aliases()) {
340 760 std::string aliasname;
341
2/2
✓ Branch 1 taken 230 times.
✓ Branch 2 taken 530 times.
760 if (alias.first.size() > 0) {
342 460 aliasname = Gyoji::misc::string_replace_start(name, alias.first + NS2Context::NAMESPACE_DELIMITER, "");
343 }
344 else {
345 530 aliasname = name;
346 }
347 760 found = namespace_find_in(alias.second, aliasname);
348
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 680 times.
760 if (found != nullptr) {
349 80 return found;
350 }
351
2/2
✓ Branch 1 taken 680 times.
✓ Branch 2 taken 80 times.
760 }
352 }
353 5914 return nullptr;
354 }
355
356 NS2Entity *
357 2432 NS2Context::get_current() const
358 2432 { return stack.back().first; }
359
360 // Enter the given namespace
361 // so that new declarations will
362 // appear in this namespace
363 void
364 972 NS2Context::namespace_push(NS2Entity *ns)
365 {
366 // We are now in the context of this
367 // namespace with our own search path.
368 972 Gyoji::owned<NS2SearchPaths> search_paths = std::make_unique<NS2SearchPaths>();
369 972 stack.push_back(
370 1944 std::pair(
371 ns,
372 972 std::move(search_paths)
373 )
374 );
375 972 }
376
377 void
378 972 NS2Context::namespace_pop()
379 {
380 // Assertion to protect us from popping the end of
381 // the stack.
382
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 972 times.
972 if (stack.size() == 1) {
383 char *p = (char*)0;
384 *p = 0x02;
385 }
386 972 stack.pop_back();
387 972 }
388
389 void
390 48 NS2Context::dump() const
391 {
392 48 root->dump(0);
393 48 }
394
395
396
397 // Scenario:
398 // namespace foo::bar {
399 //
400 // In this scenario, we create foo and bar as namespaces
401 // inside the current namespace.
402 // We also push the namespace resolution context to resolve
403 // first from this new namespace 'bar' and then
404 // from the parents of that namespace.
405 // Any 'using namespace' statements add to the current resolution
406 // context.
407
408 // Scenario:
409 // }
410 //
411 // In this scenario, we pop the namespace 'bar' we just pushed
412 // and we also pop the 'using' statements. Future declarations
413 // will go back into the root namespace. Lookups will happen
414 // in the context of whatever 'using' were in effect before
415 // the push. Qualified resolutions will happen relative to
416 // the current namespace or 'root' if not found there.
417
418 // Scenario:
419 // void foo();
420 //
421 // We look for an existing declaration of 'foo' in the current namespace.
422 // If it already exists, we throw an error.
423
424 // Scenario:
425 // void foo() {
426 //
427 // 1) We look for an existing declaration 'foo' in the
428 // current namespace resolution context. If found, we resolve
429 // 'foo' to that name. If more than one found, we
430 // throw an error. If none found, we declare in current namespace.
431 //
432 // Scenario
433 // class Foo {
434 // void foo();
435 // }
436 //
437 // We define 'Foo' as a class in the current namespace.
438 // We enter that namespace context.
439 // We define 'foo' inside the 'Foo' namespace.
440
441 // Scenario:
442 // Foo::foo();
443 // We look for 'Foo' in the current namespace
444 // resolution context. If found as a NS, if it is a
445 // namespace, we look for 'Foo::foo()' declared
446 // inside it. If found as a class, we look for 'foo'
447 // declared inside it.
448
449