Appearance
| 1 | namespace Semantic is | |
| 2 | use IO.Std; | |
| 3 | ||
| 4 | use Logging; | |
| 5 | use Source; | |
| 6 | ||
| 7 | use Syntax.Trees; | |
| 8 | ||
| 9 | // This class manages entering and leaving namespace scopes: it merges all lexical | |
| 10 | // namespace blocks that reference the same namespace into a single aggregated | |
| 11 | // namespace symbol, so that symbols defined within any lexical namespace are visible | |
| 12 | // across all references to that namespace | |
| 13 | ||
| 14 | class NAMESPACES(_logger: Logger, _symbol_table: SYMBOL_TABLE): Collections.Iterable[Symbols.NAMESPACE] is | |
| 15 | _namespaces: Collections.MAP[string,Symbols.NAMESPACE]; | |
| 16 | _prefixes: Collections.STACK[string]; | |
| 17 | _current_prefix: string => _prefixes.peek(); | |
| 18 | ||
| 19 | iterator: Collections.Iterator[Symbols.NAMESPACE] => _namespaces.values.iterator; | |
| 20 | ||
| 21 | get_qualified_name(name: string) -> string => | |
| 22 | "{_current_prefix}.{name}"; | |
| 23 | ||
| 24 | init(..) is | |
| 25 | clear(); | |
| 26 | si | |
| 27 | ||
| 28 | dump_counts() is | |
| 29 | Std.error.write_line("namespaces: {_namespaces.count}"); | |
| 30 | Std.error.write_line("prefixes: {_prefixes.count}"); | |
| 31 | si | |
| 32 | ||
| 33 | clear() is | |
| 34 | _namespaces = Collections.MAP[string,Symbols.NAMESPACE](65521); | |
| 35 | _prefixes = Collections.STACK[string](); | |
| 36 | ||
| 37 | _prefixes.push(""); | |
| 38 | si | |
| 39 | ||
| 40 | mark_namespace_stack() -> int => _prefixes.count; | |
| 41 | ||
| 42 | release_namepace_stack(mark: int) is | |
| 43 | assert mark <= _prefixes.count; | |
| 44 | ||
| 45 | while _prefixes.count > mark do | |
| 46 | _prefixes.pop(); | |
| 47 | od | |
| 48 | si | |
| 49 | ||
| 50 | find_root_matches(results: Collections.MutableMap[string,Symbols.Symbol]) is | |
| 51 | for ns in _namespaces do | |
| 52 | let parts = ns.key.split(['.']); | |
| 53 | ||
| 54 | if parts.count == 2 then | |
| 55 | let name = parts[1]; | |
| 56 | ||
| 57 | if name.length > 0 /\ !results.contains_key(name) then | |
| 58 | results.add(name, ns.value); | |
| 59 | fi | |
| 60 | fi | |
| 61 | od | |
| 62 | si | |
| 63 | ||
| 64 | find_namespace_matches(namespace_name: string, results: Collections.MutableMap[string,Symbols.Symbol]) is | |
| 65 | let prefix = ".{namespace_name}."; | |
| 66 | ||
| 67 | for ns in _namespaces do | |
| 68 | if ns.key.starts_with(prefix) then | |
| 69 | let suffix = ns.key.substring(prefix.length); | |
| 70 | ||
| 71 | if !suffix.contains('.') /\ !results.contains_key(suffix) then | |
| 72 | results.add(suffix, ns.value); | |
| 73 | fi | |
| 74 | fi | |
| 75 | od | |
| 76 | si | |
| 77 | ||
| 78 | declare_and_enter_namespace( | |
| 79 | location: LOCATION, | |
| 80 | name: string, | |
| 81 | symbol_definition_listener: Semantic.SymbolDefinitionListener, | |
| 82 | is_compiler_generated: bool | |
| 83 | ) -> Symbols.NAMESPACE | |
| 84 | is | |
| 85 | let qualified_name = get_qualified_name(name); | |
| 86 | let ns = find_or_add_namespace(name, qualified_name, is_compiler_generated); | |
| 87 | let existing = _symbol_table.current_scope.find_direct(name); | |
| 88 | ||
| 89 | if existing == null then | |
| 90 | ||
| 91 | _symbol_table.current_namespace_context.declare_namespace(location, name, ns, symbol_definition_listener); | |
| 92 | elif existing != ns then | |
| 93 | _logger.error(location, "redefining symbol {name} as a namespace, originally defined at {existing.location}"); | |
| 94 | _logger.error(existing.location, "symbol {name} is redefined as namespace at {location}"); | |
| 95 | fi | |
| 96 | ||
| 97 | _prefixes.push(qualified_name); | |
| 98 | ||
| 99 | return ns; | |
| 100 | si | |
| 101 | ||
| 102 | pop_all_namespaces() is | |
| 103 | // leave global namespace on top | |
| 104 | while _prefixes.count > 1 do | |
| 105 | _prefixes.pop(); | |
| 106 | od | |
| 107 | si | |
| 108 | ||
| 109 | enter_namespace(location: LOCATION, name: string) -> Symbols.NAMESPACE is | |
| 110 | let qualified_name = get_qualified_name(name); | |
| 111 | let ns = find_namespace(qualified_name); | |
| 112 | ||
| 113 | _prefixes.push(qualified_name); | |
| 114 | ||
| 115 | return ns; | |
| 116 | si | |
| 117 | ||
| 118 | leave_namespace(location: LOCATION, name: string) is | |
| 119 | let ns = find_namespace(_current_prefix); | |
| 120 | ||
| 121 | _prefixes.pop(); | |
| 122 | si | |
| 123 | ||
| 124 | find_or_add_namespace(name: string, qualified_name: string, is_compiler_generated: bool) -> Symbols.NAMESPACE is | |
| 125 | if _namespaces.contains_key(qualified_name) then | |
| 126 | return _namespaces[qualified_name]; | |
| 127 | else | |
| 128 | ||
| 129 | let result = Symbols.NAMESPACE(LOCATION.internal, name, _symbol_table.current_scope, qualified_name, is_compiler_generated); | |
| 130 | _namespaces[qualified_name] = result; | |
| 131 | return result; | |
| 132 | fi | |
| 133 | si | |
| 134 | ||
| 135 | find_namespace(qualified_name: string) -> Symbols.NAMESPACE is | |
| 136 | assert _namespaces.contains_key(qualified_name) else "aggregate namespace {qualified_name} should already exist"; | |
| 137 | ||
| 138 | return _namespaces[qualified_name]; | |
| 139 | si | |
| 140 | ||
| 141 | try_find_namespace(qualified_name: string) -> Symbols.NAMESPACE? => | |
| 142 | if _namespaces.contains_key(qualified_name) then _namespaces[qualified_name] else null fi; | |
| 143 | si | |
| 144 | si |