VapourSynth-llvmexpr
Loading...
Searching...
No Matches
infix2postfix::SemanticAnalyzer Class Reference

#include <llvmexpr/frontend/infix2postfix/SemanticAnalyzer.hpp>

Classes

class  ScopeGuard

Public Member Functions

 SemanticAnalyzer (Mode mode, int num_inputs, int num_intermediate_inputs=0, int library_line_count=0)
bool analyze (const Program *program)
const std::vector< Diagnostic > & getDiagnostics () const
bool hasErrors () const
const std::map< std::string, std::vector< FunctionSignature > > & getFunctionSignatures () const
const std::map< std::string, std::vector< FunctionDef * > > & getFunctionDefs () const
const std::map< std::string, std::pair< std::string, Range > > & getWrittenProperties () const

Detailed Description

Definition at line 36 of file SemanticAnalyzer.hpp.

Constructor & Destructor Documentation

◆ SemanticAnalyzer()

infix2postfix::SemanticAnalyzer::SemanticAnalyzer ( Mode mode,
int num_inputs,
int num_intermediate_inputs = 0,
int library_line_count = 0 )

Definition at line 32 of file SemanticAnalyzer.cpp.

35 : mode(mode), num_inputs(num_inputs),
36 num_intermediate_inputs(num_intermediate_inputs),
37 library_line_count(library_line_count),
38 global_scope(std::make_unique<SymbolTable>()),
39 current_scope(global_scope.get()) {}

Referenced by infix2postfix::SemanticAnalyzer::ScopeGuard::operator=(), and infix2postfix::SemanticAnalyzer::ScopeGuard::ScopeGuard().

Member Function Documentation

◆ analyze()

bool infix2postfix::SemanticAnalyzer::analyze ( const Program * program)

Definition at line 41 of file SemanticAnalyzer.cpp.

41 {
42 if (program == nullptr) {
43 reportError("Program AST is null", Range{});
44 return false;
45 }
46
47 current_pending_gotos.clear();
48 current_labels_seen.clear();
49
50 // Collect global labels
51 for (const auto& stmt : program->statements) {
52 if (auto* label_def = get_if<LabelStmt>(stmt.get())) {
53 if (global_labels.contains(label_def->name.value)) {
54 reportError(std::format("Duplicate label '{}' in global scope",
55 label_def->name.value),
56 label_def->range);
57 }
58 global_labels.insert(label_def->name.value);
59 }
60 }
61
62 // Collect function signatures and build function symbols
63 for (const auto& stmt : program->statements) {
64 if (auto* func_def = get_if<FunctionDef>(stmt.get())) {
65 if (function_signatures.contains(func_def->name.value)) {
66 for (const auto& existing_sig :
67 function_signatures.at(func_def->name.value)) {
68 if (existing_sig.params.size() == func_def->params.size()) {
69 bool same = true;
70 for (size_t i = 0; i < func_def->params.size(); ++i) {
71 if (existing_sig.params[i].type !=
72 func_def->params[i].type) {
73 same = false;
74 break;
75 }
76 }
77 if (same) {
78 reportError(
79 std::format(
80 "Duplicate function signature for '{}'",
81 func_def->name.value),
82 func_def->range);
83 }
84 }
85 }
86 }
87
88 FunctionSignature sig;
89 sig.name = func_def->name.value;
90 for (const auto& p : func_def->params) {
91 sig.params.push_back({p.name.value, p.type});
92 }
93 sig.range = func_def->range;
94
95 sig.has_return = false;
96 std::optional<bool> returns_value_opt;
97
98 std::function<void(Stmt*)> find_returns = [&](Stmt* s) {
99 if (!s) {
100 return;
101 }
102
103 if (auto* ret = get_if<ReturnStmt>(s)) {
104 sig.has_return = true;
105 bool current_has_value = (ret->value != nullptr);
106 if (!returns_value_opt.has_value()) {
107 returns_value_opt = current_has_value;
108 } else if (returns_value_opt.value() != current_has_value) {
109 reportError(
110 std::format(
111 "Function '{}' has inconsistent return "
112 "statements (some with values, some without).",
113 func_def->name.value),
114 ret->range);
115 }
116 } else if (auto* block = get_if<BlockStmt>(s)) {
117 for (const auto& inner_s : block->statements) {
118 find_returns(inner_s.get());
119 }
120 } else if (auto* if_s = get_if<IfStmt>(s)) {
121 find_returns(if_s->then_branch.get());
122 if (if_s->else_branch) {
123 find_returns(if_s->else_branch.get());
124 }
125 } else if (auto* while_s = get_if<WhileStmt>(s)) {
126 find_returns(while_s->body.get());
127 }
128 };
129 for (const auto& s : func_def->body->statements) {
130 find_returns(s.get());
131 }
132 sig.returns_value = returns_value_opt.value_or(false);
133
134 if (func_def->global_decl) {
135 sig.global_mode = func_def->global_decl->mode;
136 for (const auto& g : func_def->global_decl->globals) {
137 sig.specific_globals.insert(g.value);
138 }
139 }
140
141 std::set<std::string> local_vars;
142 for (const auto& p : func_def->params) {
143 local_vars.insert(p.name.value);
144 }
145
146 std::function<void(Stmt*)> collect_local_defs = [&](Stmt* s) {
147 if (!s) {
148 return;
149 }
150 if (auto* assign = get_if<AssignStmt>(s)) {
151 local_vars.insert(assign->name.value);
152 } else if (auto* block = get_if<BlockStmt>(s)) {
153 for (const auto& stmt : block->statements) {
154 collect_local_defs(stmt.get());
155 }
156 } else if (auto* if_stmt = get_if<IfStmt>(s)) {
157 collect_local_defs(if_stmt->then_branch.get());
158 if (if_stmt->else_branch) {
159 collect_local_defs(if_stmt->else_branch.get());
160 }
161 } else if (auto* while_stmt = get_if<WhileStmt>(s)) {
162 collect_local_defs(while_stmt->body.get());
163 }
164 };
165 for (const auto& s : func_def->body->statements) {
166 collect_local_defs(s.get());
167 }
168
169 for (const auto& s : func_def->body->statements) {
170 collectUsedGlobalsInStmt(s.get(), sig.used_globals);
171 }
172
173 for (const auto& local_var : local_vars) {
174 sig.used_globals.erase(local_var);
175 }
176
177 function_signatures[sig.name].push_back(sig);
178 function_defs[sig.name].push_back(func_def);
179
180 auto func_symbol = defineSymbol(SymbolKind::Function, sig.name,
181 Type::Value, sig.range);
182 func_symbol->signature = &function_signatures[sig.name].back();
183 func_def->symbol = func_symbol;
184 }
185 }
186
187 // Analyze all statements
188 for (const auto& stmt : program->statements) {
189 analyzeStmt(stmt.get());
190
191 validateGlobalDependencies(stmt.get());
192
193 if (auto* assign = get_if<AssignStmt>(stmt.get())) {
194 if (current_scope == global_scope.get()) {
195 defined_global_vars.insert(assign->name.value);
196 }
197 }
198 }
199
200 // Check for recursion in function call graph
201 std::set<std::string> visited;
202 for (const auto& [func_name, _] : function_call_graph) {
203 std::set<std::string> visiting;
204 std::vector<std::string> cycle_path;
205
206 if (detectCycleInCallGraph(func_name, visiting, visited, cycle_path)) {
207
208 std::string cycle_str;
209 for (size_t i = 0; i < cycle_path.size(); ++i) {
210 cycle_str += cycle_path[i];
211 if (i < cycle_path.size() - 1) {
212 cycle_str += " -> ";
213 }
214 }
215 cycle_str += " -> " + cycle_path[0];
216
217 Range first_range;
218 if (function_signatures.contains(cycle_path[0])) {
219 first_range = function_signatures.at(cycle_path[0])[0].range;
220 }
221
222 reportError(std::format("Recursion is not allowed: {}", cycle_str),
223 first_range);
224
225 for (const auto& fn : cycle_path) {
226 if (function_signatures.contains(fn)) {
227 const auto& sig = function_signatures.at(fn)[0];
228 reportError(
229 std::format(
230 " Function '{}' is part of the recursion cycle",
231 fn),
232 sig.range);
233 }
234 }
235
236 break;
237 }
238 }
239
240 // Check for unused global variables and functions
241 for (const auto& [name, symbol] : global_scope->getSymbols()) {
242 if (!symbol->is_used) {
243 if (symbol->name == "RESULT" || symbol->name == "_") {
244 continue;
245 }
246
247 // Skip warnings for symbols defined in standard library code
248 if (symbol->definition_range.start.line <= library_line_count) {
249 continue;
250 }
251
252 reportWarning(std::format("Unused symbol '{}'", name),
253 symbol->definition_range);
254 }
255 }
256
257 // Check if RESULT is defined in Expr/VkExpr mode
258 if ((mode == Mode::Expr || mode == Mode::VkExpr) && !has_result) {
259 reportError("Final result must be assigned to variable 'RESULT'!",
260 Range{});
261 }
262
263 if ((mode == Mode::Expr || mode == Mode::VkExpr) &&
264 !result_defined_in_global_scope) {
265 reportError(
266 "'RESULT' must be defined in the global scope in Expr mode.",
267 Range{});
268 }
269
270 return !hasErrors();
271}
auto get_if(Wrapper *wrapper) -> decltype(std::get_if< T >(&wrapper->value))
Definition AST.hpp:442

References infix2postfix::Expr, infix2postfix::Function, infix2postfix::get_if(), infix2postfix::FunctionSignature::global_mode, infix2postfix::FunctionSignature::has_return, hasErrors(), infix2postfix::FunctionSignature::name, infix2postfix::FunctionSignature::params, infix2postfix::FunctionSignature::range, infix2postfix::FunctionSignature::returns_value, infix2postfix::FunctionSignature::specific_globals, infix2postfix::Program::statements, infix2postfix::FunctionSignature::used_globals, infix2postfix::Value, and infix2postfix::VkExpr.

◆ getDiagnostics()

const std::vector< Diagnostic > & infix2postfix::SemanticAnalyzer::getDiagnostics ( ) const
inlinenodiscard

Definition at line 58 of file SemanticAnalyzer.hpp.

58 {
59 return diagnostics;
60 }

◆ getFunctionDefs()

const std::map< std::string, std::vector< FunctionDef * > > & infix2postfix::SemanticAnalyzer::getFunctionDefs ( ) const
inlinenodiscard

Definition at line 70 of file SemanticAnalyzer.hpp.

70 {
71 return function_defs;
72 }

◆ getFunctionSignatures()

const std::map< std::string, std::vector< FunctionSignature > > & infix2postfix::SemanticAnalyzer::getFunctionSignatures ( ) const
inlinenodiscard

Definition at line 65 of file SemanticAnalyzer.hpp.

65 {
66 return function_signatures;
67 }

◆ getWrittenProperties()

const std::map< std::string, std::pair< std::string, Range > > & infix2postfix::SemanticAnalyzer::getWrittenProperties ( ) const
inlinenodiscard

Definition at line 75 of file SemanticAnalyzer.hpp.

75 {
76 return written_properties;
77 }

◆ hasErrors()

bool infix2postfix::SemanticAnalyzer::hasErrors ( ) const
nodiscard

Definition at line 273 of file SemanticAnalyzer.cpp.

273 {
274 return std::ranges::any_of(diagnostics, [](const auto& diag) {
275 return diag.severity == DiagnosticSeverity::Error;
276 });
277}

References infix2postfix::Error.

Referenced by analyze().


The documentation for this class was generated from the following files: