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.
- lucidaflow/__init__.py +0 -0
- lucidaflow/lucida_analyzer.py +1043 -0
- lucidaflow/lucida_ast.py +321 -0
- lucidaflow/lucida_errors.py +26 -0
- lucidaflow/lucida_interpreter.py +821 -0
- lucidaflow/lucida_lexer.py +248 -0
- lucidaflow/lucida_parser.py +584 -0
- lucidaflow/lucida_stdlib.py +249 -0
- lucidaflow/lucida_symbols.py +176 -0
- lucidaflow-1.0.0.dist-info/METADATA +1567 -0
- lucidaflow-1.0.0.dist-info/RECORD +14 -0
- lucidaflow-1.0.0.dist-info/WHEEL +5 -0
- lucidaflow-1.0.0.dist-info/licenses/LICENSE +21 -0
- lucidaflow-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -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)
|