Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Compiler_Sema.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
51#ifndef COMPILER_SEMA_H
52#define COMPILER_SEMA_H
53
54#include <string>
55
56#include <Compiler_AST.H>
58#include <ah-diagnostics.H>
59#include <tpl_dynMapTree.H>
60
61namespace Aleph {
68
71{
72 bool allow_shadowing = true;
73};
74
77{
84 size_t function_depth = 0;
85 size_t loop_depth = 0;
86
87 void emit_error(const Source_Span &span,
88 const std::string &message,
89 const std::string &code,
90 const std::string &note = "",
91 const std::string &help = "") const
92 {
93 if (diagnostics == nullptr)
94 return;
95
96 auto builder = diagnostics->error(span, message).code(code);
97 if (not note.empty())
98 builder.note(note);
99 if (not help.empty())
100 builder.help(help);
101 builder.emit();
102 }
103
105 const std::string &name,
106 const Source_Span &span)
107 {
108 const auto result = value_bindings.declare(kind, name, span);
109 if (result.duplicate_local())
110 {
111 emit_error(span, "duplicate declaration of '" + name + "'", "SEM001");
112 return result.symbol_id;
113 }
114
115 if (result.shadowing() and not options.allow_shadowing)
116 emit_error(span, "shadowing declaration of '" + name + "' is not allowed", "SEM007");
117
118 return result.symbol_id;
119 }
120
121 void record_resolution(const Compiler_Expr *expr, const Compiler_Symbol_Id symbol_id)
122 {
123 if (expr == nullptr)
124 return;
125
126 const auto *entry = resolution_index.search(expr);
127 if (entry != nullptr)
128 {
129 resolutions.access(entry->second).symbol_id = symbol_id;
130 return;
131 }
132
133 const size_t slot = resolutions.size();
134 resolutions.append({expr, symbol_id});
135 resolution_index[expr] = slot;
136 }
137
138 void declare_type_name(const std::string &name, const Source_Span &span)
139 {
140 const auto result = type_bindings.declare(Compiler_Symbol_Kind::Type, name, span);
141 if (result.duplicate_local())
142 {
143 emit_error(span, "duplicate type declaration of '" + name + "'", "SEM008");
144 return;
145 }
146 }
147
148 void analyze_imports(const Compiler_Module *module) const
149 {
150 if (module == nullptr)
151 return;
152
154 for (size_t i = 0; i < module->imports.size(); ++i)
155 {
156 const auto *decl = module->imports.access(i);
157 if (decl == nullptr or decl->kind != Compiler_Import_Decl_Kind::Import)
158 continue;
159
160 if (not module->source_name.empty() and decl->module_name == module->source_name)
161 emit_error(decl->module_name_span,
162 "module '" + module->source_name + "' cannot import itself",
163 "SEM010");
164
165 bool duplicate = false;
166 for (size_t j = 0; j < imported_modules.size(); ++j)
167 if (imported_modules.access(j) == decl->module_name)
168 {
169 duplicate = true;
170 break;
171 }
172
173 if (duplicate)
174 emit_error(decl->module_name_span,
175 "duplicate import of '" + decl->module_name + "'",
176 "SEM009");
177 else
178 imported_modules.append(decl->module_name);
179 }
180 }
181
182 void analyze_expr(const Compiler_Expr *expr)
183 {
184 if (expr == nullptr)
185 return;
186
187 switch (expr->kind)
188 {
194 return;
195
197 {
198 const auto *node = static_cast<const Compiler_Identifier_Expr *>(expr);
199 const auto *symbol_id = value_bindings.lookup(node->name);
200 if (symbol_id == nullptr)
201 {
202 emit_error(node->span, "use of undeclared identifier '" + node->name + "'", "SEM002");
203 return;
204 }
205
206 record_resolution(expr, *symbol_id);
207 return;
208 }
209
211 analyze_expr(static_cast<const Compiler_Grouping_Expr *>(expr)->inner);
212 return;
213
215 analyze_expr(static_cast<const Compiler_Unary_Expr *>(expr)->operand);
216 return;
217
219 {
220 const auto *node = static_cast<const Compiler_Binary_Expr *>(expr);
221 analyze_expr(node->left);
222 analyze_expr(node->right);
223 return;
224 }
225
227 {
228 const auto *node = static_cast<const Compiler_Call_Expr *>(expr);
229 analyze_expr(node->callee);
230 for (size_t i = 0; i < node->arguments.size(); ++i)
231 analyze_expr(node->arguments.access(i));
232 return;
233 }
234 }
235 }
236
238 {
239 if (block == nullptr)
240 return;
241
243 for (size_t i = 0; i < block->statements.size(); ++i)
244 analyze_stmt(block->statements.access(i));
246 }
247
248 void analyze_stmt(const Compiler_Stmt *stmt)
249 {
250 if (stmt == nullptr)
251 return;
252
253 switch (stmt->kind)
254 {
256 return;
257
259 analyze_expr(static_cast<const Compiler_Expr_Stmt *>(stmt)->expr);
260 return;
261
263 {
264 const auto *node = static_cast<const Compiler_Let_Stmt *>(stmt);
265 analyze_expr(node->initializer);
266 const auto kind
268 (void) declare_symbol(kind, node->name, node->name_span);
269 return;
270 }
271
273 {
274 const auto *node = static_cast<const Compiler_Return_Stmt *>(stmt);
275 if (function_depth == 0)
276 emit_error(node->keyword_span, "'return' is only valid inside a function", "SEM006");
277 analyze_expr(node->value);
278 return;
279 }
280
282 analyze_block(static_cast<const Compiler_Block_Stmt *>(stmt));
283 return;
284
286 {
287 const auto *node = static_cast<const Compiler_If_Stmt *>(stmt);
288 analyze_expr(node->condition);
289 analyze_stmt(node->then_branch);
290 analyze_stmt(node->else_branch);
291 return;
292 }
293
295 {
296 const auto *node = static_cast<const Compiler_While_Stmt *>(stmt);
297 analyze_expr(node->condition);
298 ++loop_depth;
299 analyze_stmt(node->body);
300 --loop_depth;
301 return;
302 }
303
305 {
306 const auto *node = static_cast<const Compiler_Break_Stmt *>(stmt);
307 if (loop_depth == 0)
308 emit_error(node->keyword_span, "'break' is only valid inside a loop", "SEM003");
309 return;
310 }
311
313 {
314 const auto *node = static_cast<const Compiler_Continue_Stmt *>(stmt);
315 if (loop_depth == 0)
316 emit_error(node->keyword_span, "'continue' is only valid inside a loop", "SEM004");
317 return;
318 }
319 }
320 }
321
323 {
324 if (fn == nullptr)
325 return;
326
329 for (size_t i = 0; i < fn->parameters.size(); ++i)
330 {
331 const auto &param = fn->parameters.access(i);
333 }
334 analyze_block(fn->body);
337 }
338
339public:
347 : diagnostics(dx), options(opts)
348 {}
349
352 {
353 resolutions.clear();
354 resolution_index.clear();
357 function_depth = 0;
358 loop_depth = 0;
359 }
360
367 void analyze_module(const Compiler_Module *module)
368 {
369 clear();
370 if (module == nullptr)
371 return;
372
375
376 analyze_imports(module);
377
378 for (size_t i = 0; i < module->type_declarations.size(); ++i)
379 {
380 const auto *decl = module->type_declarations.access(i);
381 if (decl != nullptr)
382 declare_type_name(decl->name, decl->name_span);
383 }
384
385 for (size_t i = 0; i < module->functions.size(); ++i)
386 {
387 const auto *fn = module->functions.access(i);
388 if (fn != nullptr)
390 }
391
392 for (size_t i = 0; i < module->statements.size(); ++i)
393 analyze_stmt(module->statements.access(i));
394
395 for (size_t i = 0; i < module->functions.size(); ++i)
396 analyze_function(module->functions.access(i));
397
400 }
401
404 {
406 }
407
410 {
411 return resolutions.size();
412 }
413
421 {
422 return value_bindings.symbol(id);
423 }
424
431 const Compiler_Name_Resolution &resolution(const size_t i) const
432 {
434 << "Compiler_Semantic_Analyzer::resolution(): invalid index " << i;
435 return resolutions.access(i);
436 }
437
443 const Compiler_Symbol *resolved_symbol(const Compiler_Expr *expr) const noexcept
444 {
445 if (expr == nullptr)
446 return nullptr;
447
448 const auto *entry = resolution_index.search(expr);
449 if (entry == nullptr)
450 return nullptr;
451
452 const auto id = resolutions.access(entry->second).symbol_id;
453 if (id == 0 or id > value_bindings.symbol_count())
454 return nullptr;
455
456 return &value_bindings.symbol(id);
457 }
458
460 std::string dump_symbols() const
461 {
463 }
464};
465} // namespace Aleph
466
467#endif // COMPILER_SEMA_H
Arena-backed AST node model for the compiler-support MVP.
Reusable lexical bindings and symbol records for frontends.
Plain-text diagnostic engine for compiler-style tooling.
#define ah_out_of_range_error_unless(C)
Throws std::out_of_range if condition does NOT hold.
Definition ah-errors.H:600
Name-resolution and basic semantic checker for the MVP AST.
const Compiler_Symbol * resolved_symbol(const Compiler_Expr *expr) const noexcept
Returns the resolved symbol for an identifier expression.
Compiler_Semantic_Options options
Compiler_Symbol_Bindings value_bindings
void analyze_imports(const Compiler_Module *module) const
Compiler_Symbol_Bindings type_bindings
DynArray< Compiler_Name_Resolution > resolutions
std::string dump_symbols() const
Dumps the current symbol table in a deterministic text format.
void declare_type_name(const std::string &name, const Source_Span &span)
const Compiler_Name_Resolution & resolution(const size_t i) const
Returns resolution record i.
size_t resolution_count() const noexcept
Returns the number of identifier resolutions.
void analyze_function(const Compiler_Function_Decl *fn)
Compiler_Symbol_Id declare_symbol(const Compiler_Symbol_Kind kind, const std::string &name, const Source_Span &span)
const Compiler_Symbol & symbol(const Compiler_Symbol_Id id) const
Returns symbol id.
void analyze_expr(const Compiler_Expr *expr)
void record_resolution(const Compiler_Expr *expr, const Compiler_Symbol_Id symbol_id)
size_t symbol_count() const noexcept
Returns the number of collected symbols.
void clear() noexcept
Clears previously collected semantic state.
Compiler_Semantic_Analyzer(Diagnostic_Engine *dx=nullptr, const Compiler_Semantic_Options &opts={})
Constructs a semantic analyzer.
void emit_error(const Source_Span &span, const std::string &message, const std::string &code, const std::string &note="", const std::string &help="") const
void analyze_block(const Compiler_Block_Stmt *block)
DynMapTree< const Compiler_Expr *, size_t > resolution_index
void analyze_module(const Compiler_Module *module)
Analyzes a parsed module.
void analyze_stmt(const Compiler_Stmt *stmt)
Reusable lexical symbol table with stable ids and scope tracking.
Compiler_Symbol_Declare_Result declare(const Compiler_Symbol_Kind kind, const std::string &name, const Source_Span &span)
Declares one symbol in the current scope.
void clear() noexcept
Clears all scopes and previously recorded symbols.
const Compiler_Symbol & symbol(const Compiler_Symbol_Id id) const
Returns one symbol by its stable 1-based identifier.
std::string dump_symbols() const
Dumps the current symbol table in deterministic text form.
const Compiler_Symbol_Id * lookup(const std::string &name) const noexcept
Looks up one visible symbol id by name.
size_t symbol_count() const noexcept
Returns the number of recorded symbols.
void leave_scope()
Leaves the current lexical scope.
size_t enter_scope()
Enters one nested lexical scope.
Diagnostic_Builder & code(const std::string &value)
Sets the stable diagnostic code.
size_t emit() const noexcept
Finalizes the builder and returns the diagnostic index.
Diagnostic_Builder & help(const std::string &msg)
Appends a help line.
Diagnostic_Builder & note(const std::string &msg)
Appends a note line.
Diagnostic accumulator and renderer.
Diagnostic_Builder error(const Source_Span &span, const std::string &msg)
Starts an error diagnostic.
Generic key-value map implemented on top of a binary search tree.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
@ Grouping
Parenthesized expression.
@ Unary
Prefix unary operation (e.g., -x, !p).
@ Identifier
Variable or function name reference.
@ Integer_Literal
Numeric integer constant.
@ Invalid
Placeholder for malformed expressions.
@ Char_Literal
Character constant.
@ Binary
Infix binary operation (e.g., x + y).
@ Call
Function or method call.
@ String_Literal
String constant.
@ Bool_Literal
Boolean constant (true/false).
void message(const char *file, int line, const char *format,...)
Print an informational message with file and line info.
Definition ahDefs.C:95
@ Import
One concrete import "name"; declaration.
Compiler_Symbol_Kind
Generic symbol kinds reusable across multiple frontends.
@ While
Loop while a condition is true.
@ Invalid
Placeholder for malformed statements.
@ Expr
Expression evaluated for side effects (e.g., assignment, call).
@ If
Conditional execution.
@ Return
Exit current function with an optional value.
@ Continue
Skip to the next iteration of the innermost loop.
@ Let
Variable declaration and optional initialization.
@ Block
Scoped sequence of statements.
@ Break
Immediate exit from the innermost loop.
and
Check uniqueness with explicit hash + equality functors.
std::string code(Node *root)
Compute a string with the Lukasiewicz`s word of a tree.
size_t Compiler_Symbol_Id
Node representing an infix binary operation.
Node representing a braced sequence of statements.
Node representing a loop break.
Node representing a function or method call.
Node representing a loop continuation.
Node representing an expression evaluated as a statement.
Abstract base class for expression nodes.
Compiler_Expr_Kind kind
Specific expression type.
Node representing a top-level function declaration.
Node representing an expression explicitly wrapped in parentheses.
Node representing a named identifier reference.
Node representing a conditional branch.
Node representing a local variable binding.
Node representing a complete translation unit or module.
std::string source_name
User-facing source name that produced the module.
DynArray< Compiler_Stmt * > statements
Optional top-level code.
DynArray< Compiler_Function_Decl * > functions
Top-level function definitions.
Mapping from an identifier expression to its resolved symbol.
const Compiler_Expr * expr
Identifier expression node.
Compiler_Symbol_Id symbol_id
Resolved symbol id.
Node representing a return from the current function.
Options controlling semantic analyzer behavior.
bool allow_shadowing
If false, nested redefinitions report SEM007.
Abstract base class for statement nodes.
Compiler_Stmt_Kind kind
Specific statement type.
One reusable symbol record tracked by lexical bindings.
Node representing a prefix unary operation.
Node representing a while-loop.
Half-open byte range inside a source file.
Definition ah-source.H:100
Dynamic key-value map based on balanced binary search trees.