lucidaflow 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lucidaflow might be problematic. Click here for more details.

@@ -0,0 +1,1043 @@
1
+ # Copie e cole TODO este conteúdo em seu arquivo lucida_analyzer.py
2
+
3
+ from lucida_ast import *
4
+ from lucida_errors import LucidaSemanticError
5
+ from lucida_stdlib import NATIVE_MODULES_SEMANTICS
6
+ from lucida_symbols import (
7
+ Symbol, VarSymbol, BuiltInTypeSymbol, BuiltInFunctionSymbol,
8
+ ProcessSymbol, TypeSymbol, ModuleSymbol, ScopedSymbolTable,
9
+ ListTypeSymbol, DictTypeSymbol, EnumSymbol, EnumMemberSymbol,
10
+ FunctionTypeSymbol, TupleTypeSymbol # <--- ADICIONE AQUI
11
+ )
12
+ from lucida_lexer import (
13
+ Lexer, T_PLUS, T_MINUS, T_MUL, T_DIV, T_POW, T_MOD, T_EQ, T_NE, T_LT,
14
+ T_GT, T_LTE, T_GTE, T_AMPERSAND, T_PIPE, T_CARET, T_LSHIFT, T_RSHIFT,
15
+ T_IDENTIFIER, Token # <-- ADICIONE 'Token' AQUI
16
+ )
17
+ from lucida_parser import Parser
18
+ import importlib.util
19
+ import sys
20
+ from pathlib import Path
21
+
22
+ class NodeVisitor:
23
+ def visit(self, node):
24
+ method_name = f'visit_{type(node).__name__}'
25
+ visitor = getattr(self, method_name, self.generic_visit)
26
+ return visitor(node)
27
+
28
+ def generic_visit(self, node):
29
+ raise Exception(f'Nenhum método visit_{type(node).__name__} encontrado na classe {type(self).__name__}')
30
+
31
+ class SemanticAnalyzer(NodeVisitor):
32
+ def __init__(self):
33
+ self.current_scope = None
34
+ self.loop_level = 0
35
+ self.current_process_symbol = None
36
+ self.current_class_symbol = None
37
+ # Adicione este dicionário para guardar os "modelos" de métodos
38
+ self._native_type_method_templates = {}
39
+
40
+ def _initialize_native_type_methods(self):
41
+ # Este método cria os "templates" de métodos para cada tipo nativo.
42
+
43
+ # Primeiro, pegamos referências aos tipos básicos que usaremos nas assinaturas
44
+ string_type = self.current_scope.lookup('string')
45
+ int_type = self.current_scope.lookup('int')
46
+ bool_type = self.current_scope.lookup('bool')
47
+ any_type = self.current_scope.lookup('any')
48
+ null_type = self.current_scope.lookup('null')
49
+
50
+ # Template de métodos para STRING
51
+ string_methods = {
52
+ 'to_upper': BuiltInFunctionSymbol(name='to_upper', return_type=string_type),
53
+ 'to_lower': BuiltInFunctionSymbol(name='to_lower', return_type=string_type),
54
+ 'trim': BuiltInFunctionSymbol(name='trim', return_type=string_type),
55
+ 'split': BuiltInFunctionSymbol(name='split', params=[VarSymbol('sep', string_type)], return_type=ListTypeSymbol(string_type)),
56
+ 'replace': BuiltInFunctionSymbol(name='replace', params=[VarSymbol('old', string_type), VarSymbol('new', string_type)], return_type=string_type),
57
+ 'contains': BuiltInFunctionSymbol(name='contains', params=[VarSymbol('substring', string_type)], return_type=bool_type),
58
+ 'starts_with': BuiltInFunctionSymbol(name='starts_with', params=[VarSymbol('prefix', string_type)], return_type=bool_type),
59
+ 'ends_with': BuiltInFunctionSymbol(name='ends_with', params=[VarSymbol('suffix', string_type)], return_type=bool_type),
60
+ 'slice': BuiltInFunctionSymbol(name='slice', params=[VarSymbol('start', int_type), VarSymbol('end', int_type)], return_type=string_type),
61
+ 'length': BuiltInFunctionSymbol(name='length', params=[], return_type=int_type),
62
+ }
63
+ self._native_type_method_templates['string'] = string_methods
64
+
65
+ # Template de métodos para LISTA
66
+ list_methods = {
67
+ 'append': BuiltInFunctionSymbol(name='append', params=[VarSymbol('item', any_type)], return_type=null_type),
68
+ 'pop': BuiltInFunctionSymbol(name='pop', params=[], return_type=any_type),
69
+ 'length': BuiltInFunctionSymbol(name='length', params=[], return_type=int_type),
70
+ }
71
+ self._native_type_method_templates['list'] = list_methods
72
+
73
+ # Template de métodos para DICIONÁRIO
74
+ dict_methods = {
75
+ 'keys': BuiltInFunctionSymbol(name='keys', params=[], return_type=ListTypeSymbol(any_type)),
76
+ 'values': BuiltInFunctionSymbol(name='values', params=[], return_type=ListTypeSymbol(any_type)),
77
+ # Esta é a definição semântica correta que o analisador usará
78
+ 'get': BuiltInFunctionSymbol(
79
+ name='get',
80
+ params=[
81
+ VarSymbol('key', any_type),
82
+ VarSymbol('default', any_type, is_optional=True)
83
+ ],
84
+ return_type=any_type
85
+ ),
86
+ }
87
+ self._native_type_method_templates['dict'] = dict_methods
88
+
89
+ def check_type_compatibility(self, type1, type2, error_message, node):
90
+ # type1 é o tipo esperado (ex: 'list')
91
+ # type2 é o tipo real que foi passado (ex: 'list[int]')
92
+
93
+ if not type1 or not type2 or type1.name == 'any' or type2.name == 'any':
94
+ return True
95
+
96
+ if type1.name == type2.name:
97
+ return True
98
+
99
+ # --- LÓGICA DE SUBTIPO ADICIONADA AQUI ---
100
+ # Regra: Qualquer 'list[T]' é compatível com o tipo base 'list'.
101
+ if isinstance(type2, ListTypeSymbol) and type1.name == 'list':
102
+ return True
103
+ # Regra: Qualquer 'dict[K, V]' é compatível com o tipo base 'dict'.
104
+ if isinstance(type2, DictTypeSymbol) and type1.name == 'dict':
105
+ return True
106
+ # Regra: Qualquer 'tuple[...]' é compatível com o tipo base 'tuple'.
107
+ if isinstance(type2, TupleTypeSymbol) and type1.name == 'tuple':
108
+ return True
109
+ # --- FIM DA LÓGICA DE SUBTIPO ---
110
+
111
+ # Regra especial para números
112
+ if type1.name == 'float' and type2.name == 'int':
113
+ return True
114
+
115
+ # Se nenhuma regra de compatibilidade for atendida, lança o erro.
116
+ self.error(error_message, node)
117
+ def error(self, message, node):
118
+ # Esta definição aceita o 'node' e o usa para criar o erro com linha e coluna.
119
+ raise LucidaSemanticError(message, node.line, node.col)
120
+
121
+ def visit_EnumNode(self, node):
122
+ enum_name = node.name_token.value
123
+ if self.current_scope.lookup(enum_name, current_scope_only=True):
124
+ self.error(f"Símbolo '{enum_name}' já declarado neste escopo.", node.name_token)
125
+
126
+ enum_symbol = EnumSymbol(enum_name)
127
+ self.current_scope.define(enum_symbol)
128
+
129
+ for member_token in node.member_tokens:
130
+ member_name = member_token.value
131
+ if member_name in enum_symbol.members:
132
+ self.error(f"Membro de enum duplicado: '{member_name}'", member_token)
133
+
134
+ # O tipo do membro é o próprio enum
135
+ member_symbol = EnumMemberSymbol(name=member_name, enum_type=enum_symbol)
136
+ enum_symbol.members[member_name] = member_symbol
137
+
138
+ def visit_TryCatchNode(self, node):
139
+ # 1. Visita o bloco 'try' normalmente
140
+ self.visit(node.try_block)
141
+
142
+ # 2. Visita cada uma das cláusulas 'catch'
143
+ for catch_clause in node.catch_clauses:
144
+ self.visit(catch_clause)
145
+
146
+ # 3. Se houver um bloco 'finally', visita-o
147
+ if node.finally_block:
148
+ self.visit(node.finally_block)
149
+
150
+ def visit_CatchNode(self, node):
151
+ # Entra em um novo escopo para o bloco catch
152
+ catch_scope = ScopedSymbolTable(
153
+ 'catch',
154
+ self.current_scope.scope_level + 1,
155
+ self.current_scope
156
+ )
157
+ self.current_scope = catch_scope
158
+
159
+ # Pega o nome do tipo do erro (ex: 'FileNotFoundError')
160
+ error_type_name = node.type_node.name
161
+ # Procura por este tipo no escopo
162
+ error_type_symbol = self.current_scope.lookup(error_type_name)
163
+
164
+ # Validação: O tipo de erro existe e é um tipo?
165
+ if not error_type_symbol or not isinstance(error_type_symbol, TypeSymbol):
166
+ self.error(f"O tipo de exceção '{error_type_name}' não foi definido.", node.type_node)
167
+
168
+ # Pega o nome da variável (ex: 'e')
169
+ var_name = node.var_token.value
170
+ # Cria o símbolo da variável com o tipo de erro correto
171
+ var_symbol = VarSymbol(var_name, type=error_type_symbol)
172
+
173
+ # Define a variável de erro no escopo do catch
174
+ self.current_scope.define(var_symbol)
175
+
176
+ # Agora, analisa o corpo do bloco 'catch' dentro deste novo escopo
177
+ self.visit(node.body_block)
178
+
179
+ # Sai do escopo do catch, restaurando o escopo anterior
180
+ self.current_scope = self.current_scope.enclosing_scope
181
+
182
+ def visit_ProgramNode(self, node):
183
+ print("--- Análise Semântica Iniciada ---")
184
+ global_scope = ScopedSymbolTable('global', 1)
185
+ self.current_scope = global_scope
186
+
187
+ # =================================================================
188
+ # PASSO 1: Definir todos os tipos nativos que a linguagem conhece.
189
+ # =================================================================
190
+
191
+ # A ScopedSymbolTable já define: int, float, string, bool, null.
192
+ # Nós adicionamos o que falta:
193
+ global_scope.define(BuiltInTypeSymbol('any'))
194
+
195
+ # Para anotações de tipo, pegamos referências a estes símbolos.
196
+ any_type = global_scope.lookup('any')
197
+ string_type = global_scope.lookup('string')
198
+ int_type = global_scope.lookup('int')
199
+ float_type = global_scope.lookup('float')
200
+ bool_type = global_scope.lookup('bool')
201
+ null_type = global_scope.lookup('null')
202
+
203
+ # Define os tipos de coleção base para serem usados em anotações como '-> list'
204
+ list_type = ListTypeSymbol(any_type)
205
+ list_type.name = 'list' # Força o nome para ser simples e encontrável
206
+ global_scope.define(list_type)
207
+
208
+ dict_type = DictTypeSymbol(any_type, any_type)
209
+ dict_type.name = 'dict'
210
+ global_scope.define(dict_type)
211
+
212
+ tuple_type = TupleTypeSymbol([any_type]) # Exemplo, pode ser melhorado
213
+ tuple_type.name = 'tuple'
214
+ global_scope.define(tuple_type)
215
+
216
+ # Define os tipos de erro para as cláusulas 'catch'
217
+ global_scope.define(TypeSymbol('Exception'))
218
+ global_scope.define(TypeSymbol('FileNotFoundError'))
219
+ global_scope.define(TypeSymbol('ValueError'))
220
+ global_scope.define(TypeSymbol('TypeError'))
221
+ global_scope.define(TypeSymbol('ArithmeticError'))
222
+ global_scope.define(TypeSymbol('IndexError'))
223
+
224
+ # =================================================================
225
+ # PASSO 2: Anexar métodos aos tipos nativos
226
+ # =================================================================
227
+
228
+ self._initialize_native_type_methods()
229
+
230
+ global_scope.lookup('string').methods = self._native_type_method_templates.get('string', {})
231
+ global_scope.lookup('list').methods = self._native_type_method_templates.get('list', {})
232
+ global_scope.lookup('dict').methods = self._native_type_method_templates.get('dict', {})
233
+
234
+ # =================================================================
235
+ # PASSO 3: Definir todas as funções globais nativas com suas assinaturas
236
+ # =================================================================
237
+
238
+ global_scope.define(BuiltInFunctionSymbol(name='print', params=[], return_type=null_type))
239
+ global_scope.define(BuiltInFunctionSymbol(name='read', params=[VarSymbol('prompt', string_type)], return_type=string_type))
240
+ global_scope.define(BuiltInFunctionSymbol(name='to_int', params=[VarSymbol('value', any_type)], return_type=int_type))
241
+ global_scope.define(BuiltInFunctionSymbol(name='to_float', params=[VarSymbol('value', any_type)], return_type=float_type))
242
+ global_scope.define(BuiltInFunctionSymbol(name='typeof', params=[VarSymbol('value', any_type)], return_type=string_type))
243
+ global_scope.define(BuiltInFunctionSymbol(name='len', params=[VarSymbol('value', any_type)], return_type=int_type))
244
+ global_scope.define(BuiltInFunctionSymbol(name='abs', params=[VarSymbol('number', any_type)], return_type=any_type))
245
+ global_scope.define(BuiltInFunctionSymbol(name='round', params=[VarSymbol('number', float_type)], return_type=int_type))
246
+ global_scope.define(BuiltInFunctionSymbol(name='sum', params=[VarSymbol('list', list_type)], return_type=any_type))
247
+ global_scope.define(BuiltInFunctionSymbol(name='max', params=[VarSymbol('list', list_type)], return_type=any_type))
248
+ global_scope.define(BuiltInFunctionSymbol(name='min', params=[VarSymbol('list', list_type)], return_type=any_type))
249
+
250
+ # =================================================================
251
+ # PASSO 4: Iniciar a análise do programa do usuário
252
+ # =================================================================
253
+
254
+ for child in node.statements:
255
+ self.visit(child)
256
+
257
+ print("--- Análise Semântica Concluída com Sucesso ---")
258
+
259
+ def visit_BlockNode(self, node):
260
+ # 1. Cria um novo escopo aninhado ao escopo atual
261
+ block_scope = ScopedSymbolTable(
262
+ scope_name='block',
263
+ scope_level=self.current_scope.scope_level + 1,
264
+ enclosing_scope=self.current_scope
265
+ )
266
+ # 2. Entra no novo escopo
267
+ self.current_scope = block_scope
268
+
269
+ # Visita todas as declarações dentro do novo escopo
270
+ for statement in node.statements:
271
+ self.visit(statement)
272
+
273
+ # 3. Sai do escopo do bloco, restaurando o escopo anterior
274
+ self.current_scope = self.current_scope.enclosing_scope
275
+
276
+ def visit_VarDeclNode(self, node):
277
+ var_name = node.var_name_token.value
278
+ if self.current_scope.lookup(var_name, current_scope_only=True):
279
+ self.error(f"Variável '{var_name}' já declarada neste escopo.", node.var_name_token) # Corrigido aqui também para ser mais preciso
280
+
281
+ right_type = self.visit(node.value_node)
282
+
283
+ final_type = right_type
284
+ if node.type_hint_node:
285
+ hint_type_name = node.type_hint_node.name
286
+ hint_type_symbol = self.current_scope.lookup(hint_type_name)
287
+ if not hint_type_symbol:
288
+ self.error(f"Tipo '{hint_type_name}' desconhecido.", node.type_hint_node)
289
+
290
+ error_msg = f"Não se pode atribuir o tipo '{right_type.name if right_type else 'desconhecido'}' a uma variável do tipo '{hint_type_symbol.name}'."
291
+
292
+ # --- A CORREÇÃO ESTÁ AQUI ---
293
+ # Adicione 'node.value_node' como o último argumento
294
+ self.check_type_compatibility(hint_type_symbol, right_type, error_msg, node.value_node)
295
+ # --- FIM DA CORREÇÃO ---
296
+
297
+ final_type = hint_type_symbol
298
+
299
+ var_symbol = VarSymbol(var_name, type=final_type, is_const=node.is_const)
300
+ self.current_scope.define(var_symbol)
301
+
302
+ def visit_AssignNode(self, node):
303
+ right_type = self.visit(node.value_node)
304
+
305
+ # --- LÓGICA ATUALIZADA PARA CRIAR CAMPOS DINAMICAMENTE ---
306
+ if isinstance(node.left_node, AttributeAccessNode):
307
+ # O lado esquerdo é algo como 'self.x'
308
+ attr_access_node = node.left_node
309
+ object_type = self.visit(attr_access_node.object_node)
310
+
311
+ if not isinstance(object_type, TypeSymbol):
312
+ self.error("Acesso a atributo só é permitido em instâncias de tipos.", attr_access_node)
313
+
314
+ attr_name = attr_access_node.attribute_token.value
315
+ member_symbol = object_type.lookup_member(attr_name)
316
+
317
+ # Se o membro (campo) não existe, nós o criamos!
318
+ if not member_symbol:
319
+ new_field = VarSymbol(attr_name, type=right_type)
320
+ object_type.fields[attr_name] = new_field
321
+ return # A atribuição é válida, pois está criando o campo.
322
+ # Se o membro já existe, continuamos com a checagem de tipo normal...
323
+ # --- FIM DA LÓGICA ATUALIZADA ---
324
+
325
+ # Lógica antiga para variáveis normais e campos existentes
326
+ left_type = self.visit(node.left_node)
327
+
328
+ if isinstance(node.left_node, VarAccessNode):
329
+ var_symbol = self.current_scope.lookup(node.left_node.var_name)
330
+ if var_symbol and var_symbol.is_const:
331
+ self.error(f"Não é possível atribuir a uma constante '{var_symbol.name}'.", node)
332
+
333
+ error_msg = f"Não se pode atribuir o tipo '{right_type.name}' a uma variável do tipo '{left_type.name}'."
334
+ self.check_type_compatibility(left_type, right_type, error_msg, node.value_node)
335
+
336
+ def visit_NumberNode(self, node):
337
+ return self.current_scope.lookup('int' if node.token.type == 'INT' else 'float')
338
+
339
+ def visit_StringNode(self, node):
340
+ return self.current_scope.lookup('string')
341
+
342
+ def visit_BoolNode(self, node):
343
+ return self.current_scope.lookup('bool')
344
+
345
+ def visit_NullNode(self, node):
346
+ return self.current_scope.lookup('null')
347
+
348
+ def visit_VarAccessNode(self, node):
349
+ var_name = node.var_name
350
+ var_symbol = self.current_scope.lookup(var_name)
351
+ if not var_symbol:
352
+ self.error(f"Variável '{var_name}' não foi declarada.", node)
353
+
354
+ # --- LÓGICA CORRIGIDA ---
355
+ # Se o símbolo é um tipo, módulo ou enum, ele é o próprio "tipo".
356
+ if isinstance(var_symbol, (TypeSymbol, ModuleSymbol, EnumSymbol)):
357
+ return var_symbol
358
+
359
+ # Se for uma variável comum, retornamos o tipo da variável.
360
+ return var_symbol.type
361
+
362
+ # --- FUNÇÃO CORRIGIDA ---
363
+ def visit_BinOpNode(self, node):
364
+ left_type = self.visit(node.left_node)
365
+ right_type = self.visit(node.right_node)
366
+ op = node.op_token
367
+
368
+ if op.type == 'PLUS' and (left_type.name == 'string' or right_type.name == 'string'):
369
+ return self.current_scope.lookup('string')
370
+
371
+ if not left_type or not right_type:
372
+ # Se um dos lados não tem tipo, o resultado é desconhecido
373
+ return self.current_scope.lookup('any')
374
+
375
+ # --- LÓGICA REFINADA PARA O TIPO 'any' ---
376
+ if left_type.name == 'any' or right_type.name == 'any':
377
+ # Operações de comparação e lógicas sempre resultam em 'bool'
378
+ if op.type in (T_EQ, T_NE, T_LT, T_GT, T_LTE, T_GTE) or op.value in ('and', 'or'):
379
+ return self.current_scope.lookup('bool')
380
+
381
+ # Concatenação de string com '+' resulta em string
382
+ if op.type == T_PLUS and (left_type.name == 'string' or right_type.name == 'string'):
383
+ return self.current_scope.lookup('string')
384
+
385
+ # Para todas as outras operações com 'any' (ex: any + int, any * any),
386
+ # não podemos saber o resultado estaticamente, então ele é 'any'.
387
+ return self.current_scope.lookup('any')
388
+
389
+ # --- O resto da lógica para tipos estritos continua igual ---
390
+ if op.type in (T_PLUS, T_MINUS, T_MUL, T_DIV, T_POW, T_MOD):
391
+ if not (left_type.name in ('int', 'float') and right_type.name in ('int', 'float')):
392
+ self.error(f"Operador '{op.value}' inválido para os tipos '{left_type.name}' e '{right_type.name}'.", node)
393
+ if left_type.name == 'float' or right_type.name == 'float':
394
+ return self.current_scope.lookup('float')
395
+ return self.current_scope.lookup('int')
396
+
397
+ if op.type in (T_EQ, T_NE, T_LT, T_GT, T_LTE, T_GTE) or op.value in ('and', 'or'):
398
+ return self.current_scope.lookup('bool')
399
+
400
+ if op.type in (T_AMPERSAND, T_PIPE, T_CARET, T_LSHIFT, T_RSHIFT):
401
+ if not (left_type.name == 'int' and right_type.name == 'int'):
402
+ self.error(f"Operadores bitwise só podem ser usados com o tipo 'int'.", node)
403
+ return self.current_scope.lookup('int')
404
+
405
+ self.error(f"Operador binário '{op.value}' não suportado para os tipos '{left_type.name}' e '{right_type.name}'.", node)
406
+
407
+ def visit_UnaryOpNode(self, node):
408
+ return self.visit(node.node)
409
+
410
+ def visit_ProcessDeclNode(self, node):
411
+ proc_name = node.name
412
+ if self.current_scope.lookup(proc_name, current_scope_only=True):
413
+ self.error(f"Símbolo '{proc_name}' já declarado neste escopo.", node)
414
+
415
+ # Lógica para tipo de retorno (a mesma de antes)
416
+ return_type_symbol = None
417
+ if node.return_type_node:
418
+ return_type_name = node.return_type_node.name
419
+ return_type_symbol = self.current_scope.lookup(return_type_name)
420
+ if not return_type_symbol:
421
+ self.error(f"Tipo de retorno '{return_type_name}' não definido.", node.return_type_node)
422
+
423
+ # Cria o símbolo do processo e o define no escopo ATUAL
424
+ proc_symbol = ProcessSymbol(proc_name, return_type=return_type_symbol)
425
+ self.current_scope.define(proc_symbol)
426
+
427
+ # Guarda o estado atual do analisador
428
+ previous_process = self.current_process_symbol
429
+ self.current_process_symbol = proc_symbol
430
+
431
+ # Cria o ESCOPO INTERNO da função
432
+ proc_scope = ScopedSymbolTable(proc_name, self.current_scope.scope_level + 1, self.current_scope)
433
+ self.current_scope = proc_scope
434
+
435
+ # --- LÓGICA SIMPLIFICADA (UMA PASSAGEM) ---
436
+ param_types = []
437
+ for param_node in node.params:
438
+ # 1. Analisa o nó do parâmetro (isso o define no proc_scope)
439
+ self.visit(param_node)
440
+ # 2. Pega o símbolo que acabou de ser definido
441
+ param_symbol = self.current_scope.lookup(param_node.var_name_token.value)
442
+ # 3. Adiciona o símbolo à lista de parâmetros do ProcessSymbol
443
+ proc_symbol.params.append(param_symbol)
444
+ # 4. Adiciona o TIPO do símbolo à lista de tipos para o FunctionTypeSymbol
445
+ param_types.append(param_symbol.type)
446
+ # --- FIM DA LÓGICA SIMPLIFICADA ---
447
+
448
+ # Agora, com a lista de tipos pronta, cria o TIPO FUNÇÃO e anexa ao símbolo
449
+ proc_symbol.type = FunctionTypeSymbol(
450
+ name=proc_name,
451
+ param_types=param_types,
452
+ return_type=return_type_symbol
453
+ )
454
+
455
+ # Analisa o corpo da função
456
+ self.visit(node.body_node)
457
+
458
+ # Restaura o escopo e o estado do analisador
459
+ self.current_scope = self.current_scope.enclosing_scope
460
+ self.current_process_symbol = previous_process
461
+
462
+ def visit_ProcessCallNode(self, node):
463
+ callable_symbol = self.visit(node.node_to_call)
464
+
465
+ if callable_symbol is None:
466
+ self.error("Tentativa de chamar um valor nulo ou 'void'.", node)
467
+
468
+ # --- LÓGICA DE INSTANCIAÇÃO CORRIGIDA ---
469
+ if isinstance(callable_symbol, TypeSymbol):
470
+ # É uma instanciação, como Carro(...)
471
+ init_method = callable_symbol.lookup_member('__init__')
472
+
473
+ if init_method:
474
+ # Nós visitamos a chamada ao construtor para VALIDAR os argumentos,
475
+ # mas ignoramos o seu valor de retorno (que é 'null').
476
+ self.visit(MethodCallNode(
477
+ node.node_to_call,
478
+ Token(T_IDENTIFIER, '__init__'), # Token de mentira para o nome do método
479
+ node.arg_nodes
480
+ ))
481
+
482
+ # O tipo da expressão de instanciação é SEMPRE o próprio tipo da classe.
483
+ return callable_symbol
484
+ # --- FIM DA CORREÇÃO ---
485
+
486
+ # Validação para funções normais (continua igual)
487
+ if not isinstance(callable_symbol, (ProcessSymbol, BuiltInFunctionSymbol)):
488
+ self.error(f"O símbolo '{callable_symbol.name}' não é chamável.", node)
489
+
490
+ params = callable_symbol.params
491
+ arg_types = [self.visit(arg) for arg in node.arg_nodes]
492
+
493
+ min_args = sum(1 for p in params if not p.is_optional)
494
+ max_args = len(params)
495
+ num_provided_args = len(arg_types)
496
+
497
+ if not (min_args <= num_provided_args <= max_args):
498
+ expected_str = f"{min_args}" if min_args == max_args else f"de {min_args} a {max_args}"
499
+ self.error(f"Função '{callable_symbol.name}' espera {expected_str} argumentos, mas recebeu {num_provided_args}.", node)
500
+
501
+ for i, param_symbol in enumerate(params[:num_provided_args]):
502
+ arg_type = arg_types[i]
503
+ error_msg = f"Argumento {i+1}: tipo '{arg_type.name}' incompatível com o parâmetro tipo '{param_symbol.type.name}'."
504
+ self.check_type_compatibility(param_symbol.type, arg_type, error_msg, node.arg_nodes[i])
505
+
506
+ return callable_symbol.type.return_type
507
+
508
+ def visit_WhenNode(self, node):
509
+ condition_type = self.visit(node.condition_node)
510
+ if condition_type and condition_type.name != 'bool':
511
+ # A CORREÇÃO É ADICIONAR ', node' AQUI
512
+ self.error("Condição de 'when' deve ser um booleano.", node)
513
+
514
+ self.visit(node.then_block)
515
+ if node.else_block:
516
+ self.visit(node.else_block)
517
+
518
+ def visit_WhileNode(self, node):
519
+ self.loop_level += 1
520
+ condition_type = self.visit(node.condition_node)
521
+ if condition_type and condition_type.name != 'bool':
522
+ self.error("Condição de 'while' deve ser um booleano.")
523
+ self.visit(node.body_node)
524
+ self.loop_level -= 1
525
+
526
+ def visit_BreakNode(self, node):
527
+ if self.loop_level == 0:
528
+ self.error("'break' só pode ser usado dentro de um loop.")
529
+
530
+ def visit_ContinueNode(self, node):
531
+ # A regra para 'continue' é a mesma que para 'break':
532
+ # só pode ser usado dentro de um loop.
533
+ if self.loop_level == 0:
534
+ self.error("'continue' só pode ser usado dentro de um loop.", node)
535
+
536
+ def visit_ReturnNode(self, node):
537
+ if self.current_process_symbol is None:
538
+ self.error("'return' só pode ser usado dentro de um processo.", node)
539
+
540
+ expected_type = self.current_process_symbol.return_type
541
+
542
+ # Se o processo não deveria retornar nada (void)
543
+ if expected_type is None:
544
+ if node.node_to_return: # Se 'return' tem um valor
545
+ self.error(f"O processo '{self.current_process_symbol.name}' não deveria retornar um valor.", node)
546
+ return # OK, return vazio
547
+
548
+ # Se o processo deveria retornar um valor
549
+ if not node.node_to_return: # Se 'return' está vazio
550
+ self.error(f"O processo '{self.current_process_symbol.name}' deve retornar um valor do tipo '{expected_type.name}'.", node)
551
+
552
+ actual_type = self.visit(node.node_to_return)
553
+
554
+ # Usamos nossa função de checagem de compatibilidade
555
+ error_msg = (
556
+ f"Tipo de retorno incompatível. O processo '{self.current_process_symbol.name}' "
557
+ f"espera '{expected_type.name}', mas recebeu '{actual_type.name}'."
558
+ )
559
+ self.check_type_compatibility(expected_type, actual_type, error_msg, node.node_to_return)
560
+
561
+ def visit_ForEachNode(self, node):
562
+ self.loop_level += 1
563
+ iterable_type = self.visit(node.iterable_node)
564
+
565
+ # --- LÓGICA CORRIGIDA ---
566
+ # Permite tipos de coleção conhecidos OU o tipo 'any'.
567
+ # Só lança um erro se for um tipo que DEFINITIVAMENTE não é iterável (ex: int, bool).
568
+ if (
569
+ not isinstance(iterable_type, (ListTypeSymbol, TupleTypeSymbol, DictTypeSymbol))
570
+ and iterable_type.name != 'any'
571
+ ):
572
+ self.error(f"O tipo '{iterable_type.name}' não é iterável e não pode ser usado em um loop 'for each'.", node.iterable_node)
573
+ # --- FIM DA CORREÇÃO ---
574
+
575
+ # Cria um novo escopo para o loop
576
+ loop_scope = ScopedSymbolTable(
577
+ 'foreach_loop',
578
+ self.current_scope.scope_level + 1,
579
+ self.current_scope
580
+ )
581
+ self.current_scope = loop_scope
582
+
583
+ # Define a variável do loop com o tipo correto
584
+ element_type = self.current_scope.lookup('any') # Padrão para o caso de a coleção ser 'any'
585
+ if isinstance(iterable_type, (ListTypeSymbol, TupleTypeSymbol)):
586
+ element_type = iterable_type.element_type
587
+ elif isinstance(iterable_type, DictTypeSymbol):
588
+ # Em um 'for each' em um dicionário, tradicionalmente iteramos sobre as chaves.
589
+ element_type = iterable_type.key_type
590
+
591
+ var_name = node.var_name_token.value
592
+ var_symbol = VarSymbol(var_name, type=element_type)
593
+ self.current_scope.define(var_symbol)
594
+
595
+ # Analisa o corpo do loop
596
+ self.visit(node.body_node)
597
+
598
+ self.current_scope = self.current_scope.enclosing_scope
599
+ self.loop_level -= 1
600
+
601
+ def visit_TernaryOpNode(self, node):
602
+ self.visit(node.true_expr)
603
+ self.visit(node.condition_node)
604
+ self.visit(node.false_expr)
605
+
606
+ def visit_ListNode(self, node):
607
+ if not node.element_nodes:
608
+ element_type = self.current_scope.lookup('any')
609
+ else:
610
+ element_types = [self.visit(e) for e in node.element_nodes]
611
+ element_type = element_types[0]
612
+
613
+ # 1. Cria o tipo específico da lista, ex: 'list[int]'
614
+ list_type = ListTypeSymbol(element_type)
615
+
616
+ # 2. Copia os métodos do nosso "modelo" de lista para este novo tipo
617
+ if 'list' in self._native_type_method_templates:
618
+ list_type.methods = self._native_type_method_templates['list']
619
+
620
+ return list_type
621
+
622
+ def visit_TupleNode(self, node):
623
+ # Visita cada elemento para obter seus tipos
624
+ element_types = [self.visit(e) for e in node.element_nodes]
625
+
626
+ # Cria e retorna o tipo da tupla
627
+ tuple_type = TupleTypeSymbol(element_types)
628
+
629
+ # Futuramente, você pode adicionar métodos nativos para tuplas aqui
630
+ # if 'tuple' in self._native_type_method_templates:
631
+ #     tuple_type.methods = self._native_type_method_templates['tuple']
632
+
633
+ return tuple_type
634
+
635
+ def visit_DictNode(self, node):
636
+ any_type = self.current_scope.lookup('any')
637
+
638
+ # Se o dicionário estiver vazio, o tipo é dict[any, any]
639
+ if not node.pairs:
640
+ dict_type = DictTypeSymbol(any_type, any_type)
641
+ else:
642
+ # Visita todas as chaves e valores primeiro
643
+ key_types = [self.visit(k) for k, v in node.pairs]
644
+ value_types = [self.visit(v) for k, v in node.pairs]
645
+
646
+ # --- LÓGICA DE INFERÊNCIA APRIMORADA ---
647
+ # Verifica se todos os tipos de chave são iguais
648
+ final_key_type = key_types[0]
649
+ for kt in key_types[1:]:
650
+ if kt.name != final_key_type.name:
651
+ final_key_type = any_type # Se misturados, o tipo da chave vira 'any'
652
+ break
653
+
654
+ # Verifica se todos os tipos de valor são iguais
655
+ final_value_type = value_types[0]
656
+ for vt in value_types[1:]:
657
+ if vt.name != final_value_type.name:
658
+ final_value_type = any_type # Se misturados, o tipo do valor vira 'any'
659
+ break
660
+ # --- FIM DA LÓGICA APRIMORADA ---
661
+
662
+ dict_type = DictTypeSymbol(final_key_type, final_value_type)
663
+
664
+ # Anexa os métodos nativos de dicionário (ex: .keys())
665
+ if 'dict' in self._native_type_method_templates:
666
+ dict_type.methods = self._native_type_method_templates['dict']
667
+
668
+ return dict_type
669
+
670
+ def visit_AttributeAccessNode(self, node):
671
+ attr_name = node.attribute_token.value
672
+
673
+ # --- Lógica para obter o símbolo à esquerda do '.' ---
674
+ left_symbol = None
675
+ if isinstance(node.object_node, VarAccessNode):
676
+ var_name = node.object_node.var_name
677
+ left_symbol = self.current_scope.lookup(var_name)
678
+ if not left_symbol:
679
+ self.error(f"O símbolo '{var_name}' não foi declarado.", node.object_node)
680
+ elif isinstance(node.object_node, SuperNode):
681
+ # O caso 'super' retorna o símbolo do membro diretamente, o que está correto
682
+ parent_type = self.visit(node.object_node)
683
+ member_symbol = parent_type.lookup_member(attr_name)
684
+ if not member_symbol:
685
+ self.error(f"O membro '{attr_name}' não foi encontrado na cadeia de herança de '{parent_type.name}'.", node.attribute_token)
686
+ return member_symbol
687
+ else:
688
+ left_symbol = self.visit(node.object_node)
689
+
690
+ # --- Lógica para encontrar o membro e retornar a coisa certa (TIPO ou SÍMBOLO) ---
691
+ member_symbol = None
692
+
693
+ if isinstance(left_symbol, ModuleSymbol):
694
+ member_symbol = left_symbol.symbol_table.lookup(attr_name)
695
+ if not member_symbol:
696
+ self.error(f"O módulo '{left_symbol.name}' não possui um membro chamado '{attr_name}'.", node.attribute_token)
697
+
698
+ elif isinstance(left_symbol, EnumSymbol):
699
+ member_symbol = left_symbol.members.get(attr_name)
700
+ if not member_symbol:
701
+ self.error(f"O enum '{left_symbol.name}' não possui um membro chamado '{attr_name}'.", node.attribute_token)
702
+
703
+ elif isinstance(left_symbol, TypeSymbol):
704
+ member_symbol = left_symbol.lookup_member(attr_name)
705
+ if not member_symbol:
706
+ self.error(f"O tipo '{left_symbol.name}' não possui um atributo ou método chamado '{attr_name}'.", node.attribute_token)
707
+
708
+ else:
709
+ type_name = left_symbol.type.name if hasattr(left_symbol, 'type') and left_symbol.type else 'desconhecido'
710
+ self.error(f"Não é possível acessar o atributo '{attr_name}' em um valor do tipo '{type_name}'.", node.object_node)
711
+
712
+ # --- REGRA FINAL: O que retornar? ---
713
+ # Se o membro é um valor (variável, constante, membro de enum), retorne seu TIPO.
714
+ if isinstance(member_symbol, (VarSymbol, EnumMemberSymbol)):
715
+ return member_symbol.type
716
+
717
+ # Se o membro é algo chamável (função, método), retorne o SÍMBOLO em si.
718
+ elif isinstance(member_symbol, (ProcessSymbol, BuiltInFunctionSymbol)):
719
+ return member_symbol
720
+
721
+ # Fallback para outros tipos de símbolo que possam existir
722
+ return member_symbol
723
+
724
+ def visit_MethodCallNode(self, node):
725
+ object_or_module = self.visit(node.object_node)
726
+ method_name = node.method_token.value
727
+
728
+ # --- LÓGICA DE DESPACHO APRIMORADA ---
729
+
730
+ # Caso 1: A chamada é em um Módulo (ex: math.sqrt())
731
+ if isinstance(object_or_module, ModuleSymbol):
732
+ # Procura a função dentro da tabela de símbolos do módulo
733
+ func_symbol = object_or_module.symbol_table.lookup(method_name)
734
+ if not func_symbol or not isinstance(func_symbol, BuiltInFunctionSymbol):
735
+ self.error(f"O módulo '{object_or_module.name}' não possui a função '{method_name}'.", node.method_token)
736
+
737
+ # Reutiliza a lógica de validação de chamada de função que já temos
738
+ # (Esta parte é idêntica à de visit_ProcessCallNode)
739
+ params = func_symbol.params
740
+ arg_types = [self.visit(arg) for arg in node.arg_nodes]
741
+ min_args = sum(1 for p in params if not p.is_optional)
742
+ max_args = len(params)
743
+ if not (min_args <= len(arg_types) <= max_args):
744
+ expected_str = f"{min_args}" if min_args == max_args else f"de {min_args} a {max_args}"
745
+ self.error(f"Função '{func_symbol.name}' espera {expected_str} argumentos, mas recebeu {len(arg_types)}.", node)
746
+ for i, param_symbol in enumerate(params[:len(arg_types)]):
747
+ self.check_type_compatibility(param_symbol.type, arg_types[i], f"Argumento {i+1} da função '{func_symbol.name}': tipo '{arg_types[i].name}' incompatível com o tipo esperado '{param_symbol.type.name}'.", node.arg_nodes[i])
748
+ return func_symbol.type.return_type
749
+
750
+ # Caso 2: A chamada é em um Tipo de Classe (continua como antes)
751
+ elif isinstance(object_or_module, TypeSymbol):
752
+ method_symbol = object_or_module.lookup_member(method_name)
753
+ if not method_symbol or not isinstance(method_symbol, (ProcessSymbol, BuiltInFunctionSymbol)):
754
+ self.error(f"O tipo '{object_or_module.name}' não possui um método chamado '{method_name}'.", node.method_token)
755
+
756
+ callable_type = method_symbol.type
757
+ arg_types = [self.visit(arg_node) for arg_node in node.arg_nodes]
758
+
759
+ params_to_check = method_symbol.params[1:] if isinstance(method_symbol, ProcessSymbol) else method_symbol.params
760
+ min_args = sum(1 for p in params_to_check if not p.is_optional)
761
+ max_args = len(params_to_check)
762
+
763
+ if not (min_args <= len(arg_types) <= max_args):
764
+ expected_str = f"{min_args}" if min_args == max_args else f"de {min_args} a {max_args}"
765
+ self.error(f"Método '{method_name}' espera {expected_str} argumentos, mas recebeu {len(arg_types)}.", node)
766
+
767
+ for i, param_symbol in enumerate(params_to_check[:len(arg_types)]):
768
+ self.check_type_compatibility(param_symbol.type, arg_types[i], f"Argumento {i+1} do método '{method_name}': tipo '{arg_types[i].name}' incompatível com tipo '{param_symbol.type.name}'.", node.arg_nodes[i])
769
+
770
+ return callable_type.return_type
771
+
772
+ else:
773
+ self.error(f"Não é possível chamar métodos em um valor do tipo '{object_or_module.name if object_or_module else 'desconhecido'}'.", node.object_node)
774
+
775
+ def visit_IndexAccessNode(self, node):
776
+ collection_type = self.visit(node.object_node)
777
+ index_type = self.visit(node.index_node)
778
+
779
+ if collection_type.name == 'any':
780
+ return self.current_scope.lookup('any')
781
+
782
+ elif isinstance(collection_type, self.current_scope.lookup('string').__class__):
783
+ if index_type.name != 'int':
784
+ self.error("O índice de uma string deve ser um inteiro.", node.index_node)
785
+ # O resultado de aceder a um caractere de uma string é outra string
786
+ return self.current_scope.lookup('string')
787
+
788
+ if isinstance(collection_type, ListTypeSymbol):
789
+ if index_type.name != 'int':
790
+ self.error("Índice de lista deve ser um inteiro.", node.index_node)
791
+ return collection_type.element_type
792
+
793
+ elif isinstance(collection_type, DictTypeSymbol):
794
+ error_msg = f"Tipo de chave incompatível para o dicionário. Esperado '{collection_type.key_type.name}', mas recebeu '{index_type.name}'."
795
+ # --- CORREÇÃO AQUI: Adiciona 'node.index_node' como último argumento ---
796
+ self.check_type_compatibility(
797
+ collection_type.key_type,
798
+ index_type,
799
+ error_msg,
800
+ node.index_node
801
+ )
802
+ return collection_type.value_type
803
+
804
+ else:
805
+ self.error(f"O tipo '{collection_type.name}' não suporta acesso por índice '[]'.", node.object_node)
806
+
807
+ def visit_TypeDeclNode(self, node):
808
+ type_name = node.name_token.value
809
+ parent_symbol = None
810
+
811
+ if node.parent_name_node:
812
+ parent_name = node.parent_name_node.name
813
+ parent_symbol = self.current_scope.lookup(parent_name)
814
+ if not parent_symbol or not isinstance(parent_symbol, TypeSymbol):
815
+ self.error(f"O tipo pai '{parent_name}' é inválido.", node.parent_name_node)
816
+ if parent_symbol.name == type_name:
817
+ self.error("Um tipo não pode herdar de si mesmo.", node.name_token)
818
+
819
+ # Cria o símbolo do tipo, passando o pai
820
+ type_symbol = TypeSymbol(type_name, parent_type_symbol=parent_symbol)
821
+ self.current_scope.define(type_symbol)
822
+
823
+ # Define o estado da classe atual para que os métodos saibam a quem pertencem
824
+ previous_class_symbol = self.current_class_symbol
825
+ self.current_class_symbol = type_symbol
826
+
827
+ # Entra em um novo escopo para os membros da classe
828
+ type_scope = ScopedSymbolTable(type_name, self.current_scope.scope_level + 1, self.current_scope)
829
+ self.current_scope = type_scope
830
+
831
+ # --- LÓGICA DE REGISTRO ADICIONADA AQUI ---
832
+ # Visita e REGISTRA todos os campos e métodos no TypeSymbol
833
+ for field_node in node.fields:
834
+ self.visit(field_node)
835
+ # Pega o símbolo que foi definido no escopo e o adiciona ao dicionário do tipo
836
+ field_symbol = self.current_scope.lookup(field_node.var_name_token.value)
837
+ type_symbol.fields[field_symbol.name] = field_symbol
838
+
839
+ for method_node in node.methods:
840
+ self.visit(method_node)
841
+ # Pega o símbolo do método e o adiciona ao dicionário do tipo
842
+ method_symbol = self.current_scope.lookup(method_node.name)
843
+ type_symbol.methods[method_symbol.name] = method_symbol
844
+ # --- FIM DA LÓGICA DE REGISTRO ---
845
+
846
+ # Sai do escopo da classe
847
+ self.current_scope = self.current_scope.enclosing_scope
848
+
849
+ # Restaura o estado de classe anterior
850
+ self.current_class_symbol = previous_class_symbol
851
+
852
+ def visit_LambdaNode(self, node):
853
+ # Guarda o processo anterior
854
+ previous_process = self.current_process_symbol
855
+
856
+ # --- A CORREÇÃO PRINCIPAL ESTÁ AQUI ---
857
+ # Primeiro, pegamos o tipo 'any' para usar como retorno padrão
858
+ any_type = self.current_scope.lookup('any')
859
+ # Agora, criamos o símbolo do processo JÁ COM o tipo de retorno
860
+ lambda_symbol = ProcessSymbol('<lambda>', return_type=any_type)
861
+ self.current_process_symbol = lambda_symbol
862
+ # --- FIM DA CORREÇÃO ---
863
+
864
+ # Cria o escopo da lambda
865
+ lambda_scope = ScopedSymbolTable(
866
+ 'lambda',
867
+ self.current_scope.scope_level + 1,
868
+ self.current_scope
869
+ )
870
+ self.current_scope = lambda_scope
871
+
872
+ # Analisa os parâmetros e coleta seus tipos
873
+ param_types = []
874
+ for param_node in node.params_node:
875
+ self.visit(param_node)
876
+ param_symbol = self.current_scope.lookup(param_node.var_name_token.value)
877
+ param_types.append(param_symbol.type)
878
+
879
+ # Analisa o corpo da lambda
880
+ self.visit(node.body_node)
881
+
882
+ # Restaura o escopo e o processo anterior
883
+ self.current_scope = self.current_scope.enclosing_scope
884
+ self.current_process_symbol = previous_process
885
+
886
+ # Retorna um tipo função que descreve a lambda
887
+ func_type_name = f"lambda_({','.join([t.name for t in param_types])})"
888
+ return FunctionTypeSymbol(func_type_name, param_types, any_type)
889
+
890
+ def visit_TypeNode(self, node):
891
+ pass
892
+
893
+ def visit_ParamNode(self, node):
894
+ param_name = node.var_name_token.value
895
+ type_symbol = None
896
+
897
+ # --- LÓGICA ADICIONADA PARA TRATAR 'self' ---
898
+ # Se o parâmetro é 'self' E estamos dentro da análise de uma classe...
899
+ if param_name == 'self' and self.current_class_symbol:
900
+ # O tipo de 'self' é a própria classe!
901
+ type_symbol = self.current_class_symbol
902
+ # Um erro comum é anotar o tipo de self, o que é redundante.
903
+ if node.type_node:
904
+ self.error("O parâmetro 'self' não pode ter uma anotação de tipo.", node.type_node)
905
+ # --- FIM DA LÓGICA 'self' ---
906
+
907
+ # Se não for 'self', a lógica continua como antes
908
+ elif node.type_node:
909
+ type_name = node.type_node.name
910
+ type_symbol = self.current_scope.lookup(type_name)
911
+ if not type_symbol:
912
+ self.error(f"O tipo '{type_name}' do parâmetro '{param_name}' não foi definido.", node.type_node)
913
+ else:
914
+ type_symbol = self.current_scope.lookup('any')
915
+
916
+ if self.current_scope.lookup(param_name, current_scope_only=True):
917
+ self.error(f"Parâmetro '{param_name}' já foi definido.", node)
918
+
919
+ param_symbol = VarSymbol(param_name, type=type_symbol)
920
+ self.current_scope.define(param_symbol)
921
+
922
+ def visit_FieldDeclNode(self, node):
923
+ pass
924
+
925
+ def visit_SuperNode(self, node):
926
+ # Regra 1: 'super' deve estar dentro de um método.
927
+ if not self.current_process_symbol:
928
+ self.error("'super' só pode ser usado dentro de um método.", node)
929
+
930
+ # Regra 2: O método deve pertencer a uma classe.
931
+ if not self.current_class_symbol:
932
+ self.error("'super' só pode ser usado dentro de um método de uma classe.", node)
933
+
934
+ # Regra 3: A classe deve ter uma classe pai.
935
+ if not self.current_class_symbol.parent_type_symbol:
936
+ self.error(f"'super' usado na classe base '{self.current_class_symbol.name}', que não tem pai.", node)
937
+
938
+ # Se todas as regras passaram, o 'valor' de 'super' é o tipo da classe pai.
939
+ return self.current_class_symbol.parent_type_symbol
940
+
941
+ # Em lucida_analyzer.py, na classe SemanticAnalyzer
942
+
943
+ def visit_ImportNode(self, node):
944
+ module_name = node.filepath_node.value
945
+ namespace = node.namespace_token.value
946
+
947
+ if self.current_scope.lookup(namespace, current_scope_only=True):
948
+ self.error(f"O nome '{namespace}' já está em uso neste escopo.", node.namespace_token)
949
+
950
+ semantic_scope = None
951
+
952
+ # 1. Procura primeiro nos módulos nativos INTERNOS
953
+ if module_name in NATIVE_MODULES_SEMANTICS:
954
+ semantic_scope = NATIVE_MODULES_SEMANTICS[module_name]
955
+ else:
956
+ # 2. Se não achar, procura por um arquivo .py na pasta 'lib'
957
+ try:
958
+ plugin_path = Path('lib') / f'{module_name}.py'
959
+ if not plugin_path.exists():
960
+ self.error(f"Módulo '{module_name}' não encontrado.", node)
961
+
962
+ spec = importlib.util.spec_from_file_location(module_name, plugin_path)
963
+ python_module = importlib.util.module_from_spec(spec)
964
+ spec.loader.exec_module(python_module)
965
+
966
+ # 3. Chama a função 'register_semantics' para pegar a descrição
967
+ if hasattr(python_module, 'register_semantics'):
968
+ semantic_scope = python_module.register_semantics()
969
+ else:
970
+ self.error(f"O módulo externo '{module_name}' não possui a função 'register_semantics()'.", node)
971
+ except Exception as e:
972
+ self.error(f"Falha ao carregar a semântica do módulo externo '{module_name}': {e}", node)
973
+
974
+ # O resto do processo é o mesmo
975
+ if semantic_scope:
976
+ module_symbol = ModuleSymbol(namespace, symbol_table=semantic_scope)
977
+ self.current_scope.define(module_symbol)
978
+
979
+ def visit_InterpolatedStringNode(self, node):
980
+ # Percorre todas as partes da string interpolada
981
+ for part in node.parts:
982
+ # Visita cada parte. Se for um StringNode, não faz nada.
983
+ # Se for um nó de expressão, ele será validado.
984
+ self.visit(part)
985
+
986
+ # O tipo de uma f-string é sempre 'string'
987
+ return self.current_scope.lookup('string')
988
+
989
+ def visit_ListComprehensionNode(self, node):
990
+ # Esta função transforma a AST de [expr for each var in iterable]
991
+ # na AST para o seguinte código Lucida:
992
+ #
993
+ # {
994
+ # let __comp_list = [];
995
+ # for each var in iterable {
996
+ # __comp_list.append(expr);
997
+ # }
998
+ # __comp_list;
999
+ # }
1000
+
1001
+ # 1. Cria um nome único para a lista temporária
1002
+ temp_list_name = f"__comp_list_{node.line}_{node.col}"
1003
+ temp_list_token = Token(T_IDENTIFIER, temp_list_name, node.line, node.col)
1004
+
1005
+ # 2. Cria o nó para a declaração da lista: let __comp_list = []
1006
+ list_decl_node = VarDeclNode(
1007
+ var_name_token=temp_list_token,
1008
+ value_node=ListNode(node.token, []),
1009
+ is_const=False,
1010
+ type_hint_node=None
1011
+ )
1012
+
1013
+ # 3. Cria o nó para o corpo do loop: __comp_list.append(expr)
1014
+ append_call = MethodCallNode(
1015
+ object_node=VarAccessNode(temp_list_token),
1016
+ method_token=Token(T_IDENTIFIER, 'append', node.expression_node.line, node.expression_node.col),
1017
+ arg_nodes=[node.expression_node]
1018
+ )
1019
+ loop_body = BlockNode(node.token, [append_call])
1020
+
1021
+ # 4. Cria o nó do loop 'for each'
1022
+ for_each_node = ForEachNode(
1023
+ for_token=node.token,
1024
+ var_name_token=node.var_name_token,
1025
+ iterable_node=node.iterable_node,
1026
+ body_node=loop_body
1027
+ )
1028
+
1029
+ # 5. Cria o nó de acesso final à lista, que será o valor de retorno do bloco
1030
+ final_access_node = VarAccessNode(temp_list_token)
1031
+
1032
+ # 6. Junta tudo em um único Bloco de código
1033
+ comprehension_block = BlockNode(
1034
+ node.token,
1035
+ [list_decl_node, for_each_node, final_access_node]
1036
+ )
1037
+
1038
+ # --- A CORREÇÃO MÁGICA ---
1039
+ # Em vez de retornar um tipo, nós efetivamente substituímos este nó
1040
+ # pelo bloco que acabamos de criar, visitando-o imediatamente.
1041
+ # O analisador continuará a partir daqui como se o programador
1042
+ # tivesse escrito o 'for' loop manualmente.
1043
+ return self.visit(comprehension_block)