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,584 @@
1
+ # --- CÓDIGO COMPLETO E DEFINITIVO PARA lucida_parser.py ---
2
+
3
+ from lucida_lexer import *
4
+ from lucida_ast import *
5
+
6
+ class Parser:
7
+ def __init__(self, lexer):
8
+ self.lexer = lexer
9
+ self.current_token = self.lexer.get_next_token()
10
+
11
+ def error(self, msg="Sintaxe inválida"):
12
+ raise Exception(f'{msg} (token: {self.current_token})')
13
+
14
+ def eat(self, token_type):
15
+ if self.current_token.type == token_type:
16
+ self.current_token = self.lexer.get_next_token()
17
+ else:
18
+ self.error(f"Esperado token {token_type}, mas recebi {self.current_token.type}")
19
+
20
+ # --- MÉTODO RECONSTRUÍDO ---
21
+ def parse_statement(self):
22
+ if self.current_token.type == T_KEYWORD:
23
+ statement_parsers = {
24
+ 'let': self.parse_variable_declaration,
25
+ 'const': self.parse_variable_declaration,
26
+ 'when': self.parse_when_statement,
27
+ 'for': self.parse_for_each_statement,
28
+ 'while': self.parse_while_statement,
29
+ 'define': self.parse_define_statement,
30
+ 'return': self.parse_return_statement,
31
+ 'break': self.parse_break_or_continue,
32
+ 'continue': self.parse_break_or_continue,
33
+ 'import': self.parse_import_statement,
34
+ 'try': self.parse_try_catch_statement, # <-- A CHAVE ESTAVA FALTANDO AQUI
35
+ }
36
+ parser_method = statement_parsers.get(self.current_token.value)
37
+ if parser_method:
38
+ return parser_method()
39
+
40
+ # Se não for um comando conhecido, deve ser uma expressão
41
+ node = self.parse_expression()
42
+
43
+ if self.current_token.type in (T_ASSIGN, T_PLUS_ASSIGN, T_MINUS_ASSIGN, T_MUL_ASSIGN, T_DIV_ASSIGN, T_POW_ASSIGN, T_MOD_ASSIGN):
44
+ left_node = node
45
+ if not isinstance(left_node, (VarAccessNode, IndexAccessNode, AttributeAccessNode)):
46
+ self.error(f"Alvo de atribuição inválido: {left_node}")
47
+ op_token = self.current_token
48
+ self.eat(op_token.type)
49
+ right_expr = self.parse_expression()
50
+ if op_token.type != T_ASSIGN:
51
+ op_map = { T_PLUS_ASSIGN: Token(T_PLUS, '+', op_token.line, op_token.col), T_MINUS_ASSIGN: Token(T_MINUS, '-', op_token.line, op_token.col), T_MUL_ASSIGN: Token(T_MUL, '*', op_token.line, op_token.col), T_DIV_ASSIGN: Token(T_DIV, '/', op_token.line, op_token.col), T_POW_ASSIGN: Token(T_POW, '**', op_token.line, op_token.col), T_MOD_ASSIGN: Token(T_MOD, '%', op_token.line, op_token.col) }
52
+ right_expr = BinOpNode(left_node, op_map[op_token.type], right_expr)
53
+ return AssignNode(left_node, op_token, right_expr)
54
+
55
+ return node
56
+
57
+ # --- NOVOS MÉTODOS DE APOIO ---
58
+ def parse_define_statement(self):
59
+ self.eat(T_KEYWORD)
60
+ declaration_type = self.current_token.value
61
+ if declaration_type == 'process':
62
+ return self.parse_process_declaration()
63
+ elif declaration_type == 'type':
64
+ return self.parse_type_declaration()
65
+ elif declaration_type == 'enum':
66
+ return self.parse_enum_declaration()
67
+ else:
68
+ self.error("Esperado 'process', 'type' ou 'enum' depois de 'define'")
69
+
70
+ def parse_enum_declaration(self):
71
+ # Este método é chamado após o token 'define' já ter sido consumido.
72
+ enum_token = self.current_token
73
+ self.eat(T_KEYWORD) # Consome a palavra-chave 'enum'
74
+
75
+ name_token = self.current_token
76
+ self.eat(T_IDENTIFIER) # Consome o nome do enum
77
+
78
+ self.eat(T_LBRACE) # Consome o '{'
79
+
80
+ member_tokens = []
81
+ if self.current_token.type != T_RBRACE:
82
+ while True:
83
+ member_tokens.append(self.current_token)
84
+ self.eat(T_IDENTIFIER)
85
+
86
+ if self.current_token.type == T_RBRACE:
87
+ break
88
+
89
+ self.eat(T_COMMA)
90
+
91
+ self.eat(T_RBRACE) # Consome o '}'
92
+
93
+ return EnumNode(enum_token, name_token, member_tokens)
94
+
95
+ def parse_break_or_continue(self):
96
+ token = self.current_token
97
+ self.eat(T_KEYWORD)
98
+ if token.value == 'break':
99
+ return BreakNode(token)
100
+ else:
101
+ return ContinueNode(token)
102
+
103
+ # Cole aqui o resto de TODOS os seus outros métodos do Parser
104
+ # (parse_variable_declaration, parse_when_statement, parse_try_catch_statement,
105
+ # parse_enum_declaration, parse_f_string, e todos os outros...)
106
+
107
+ def parse_try_catch_statement(self):
108
+ # Captura o token 'try' aqui, em vez de receber como argumento
109
+ try_token = self.current_token
110
+ self.eat(T_KEYWORD) # Consome 'try'
111
+
112
+ try_block = self.parse_block()
113
+
114
+ catch_clauses = []
115
+ # Loop para capturar uma ou mais cláusulas 'catch'
116
+ while self.current_token.type == T_KEYWORD and self.current_token.value == 'catch':
117
+ catch_token = self.current_token
118
+ self.eat(T_KEYWORD) # Consome 'catch'
119
+
120
+ self.eat(T_LPAREN)
121
+
122
+ var_token = self.current_token
123
+ self.eat(T_IDENTIFIER)
124
+
125
+ self.eat(T_COLON)
126
+
127
+ type_node = self.parse_type()
128
+
129
+ self.eat(T_RPAREN)
130
+
131
+ body_block = self.parse_block()
132
+
133
+ # Cria o nó para esta cláusula específica
134
+ catch_node = CatchNode(catch_token, var_token, type_node, body_block)
135
+ catch_clauses.append(catch_node)
136
+
137
+ finally_block = None
138
+ # Verifica se há uma cláusula 'finally' opcional
139
+ if self.current_token.type == T_KEYWORD and self.current_token.value == 'finally':
140
+ self.eat(T_KEYWORD) # Consome 'finally'
141
+ finally_block = self.parse_block()
142
+
143
+ # Validação: um 'try' precisa de pelo menos um 'catch' ou um 'finally'
144
+ if not catch_clauses and not finally_block:
145
+ self.error("A construção 'try' deve ter pelo menos uma cláusula 'catch' ou 'finally'.")
146
+
147
+ return TryCatchNode(try_token, try_block, catch_clauses, finally_block)
148
+
149
+ def parse_variable_declaration(self):
150
+ is_const = self.current_token.value == 'const'
151
+ self.eat(T_KEYWORD)
152
+ var_name_token = self.current_token
153
+ self.eat(T_IDENTIFIER)
154
+ type_hint = None
155
+ if self.current_token.type == T_COLON:
156
+ self.eat(T_COLON)
157
+ type_hint = self.parse_type()
158
+ self.eat(T_ASSIGN)
159
+ value_node = self.parse_expression()
160
+ return VarDeclNode(var_name_token, value_node, is_const, type_hint)
161
+
162
+ def parse_when_statement(self):
163
+ when_token = self.current_token
164
+ self.eat(T_KEYWORD)
165
+ condition_node = self.parse_expression()
166
+ then_block = self.parse_block()
167
+ else_block = None
168
+ if self.current_token.type == T_KEYWORD and self.current_token.value in ('else', 'otherwise'):
169
+ if self.current_token.value == 'else':
170
+ self.eat(T_KEYWORD)
171
+ if self.current_token.type == T_KEYWORD and self.current_token.value == 'when':
172
+ else_block = self.parse_when_statement()
173
+ else:
174
+ self.error("A palavra-chave 'else' deve ser seguida por 'when'. Para o bloco final, use 'otherwise'.")
175
+ elif self.current_token.value == 'otherwise':
176
+ self.eat(T_KEYWORD)
177
+ else_block = self.parse_block()
178
+ return WhenNode(when_token, condition_node, then_block, else_block)
179
+
180
+ def parse_while_statement(self):
181
+ while_token = self.current_token
182
+ self.eat(T_KEYWORD)
183
+ condition_node = self.parse_expression()
184
+ body_node = self.parse_block()
185
+ return WhileNode(while_token, condition_node, body_node)
186
+
187
+ def parse_for_each_statement(self):
188
+ for_token = self.current_token
189
+ self.eat(T_KEYWORD)
190
+ self.eat(T_KEYWORD)
191
+ var_name = self.current_token
192
+ self.eat(T_IDENTIFIER)
193
+ self.eat(T_KEYWORD)
194
+ iterable = self.parse_expression()
195
+ body = self.parse_block()
196
+ return ForEachNode(for_token, var_name, iterable, body)
197
+
198
+ def parse_import_statement(self):
199
+ import_token = self.current_token
200
+ self.eat(T_KEYWORD)
201
+ filepath_node = self.parse_primary()
202
+ if not isinstance(filepath_node, StringNode):
203
+ self.error("Esperado um caminho de arquivo (string) depois de 'import'")
204
+ self.eat(T_KEYWORD)
205
+ namespace_token = self.current_token
206
+ self.eat(T_IDENTIFIER)
207
+ return ImportNode(import_token, filepath_node, namespace_token)
208
+
209
+ def parse_type_declaration(self):
210
+ self.eat(T_KEYWORD) # Consome 'type'
211
+ name_token = self.current_token
212
+ self.eat(T_IDENTIFIER)
213
+
214
+ parent_name_node = None
215
+ if self.current_token.type == T_LT:
216
+ self.eat(T_LT)
217
+ parent_name_node = self.parse_type()
218
+
219
+ self.eat(T_LBRACE)
220
+ fields, methods = [], []
221
+
222
+ while self.current_token.type != T_RBRACE:
223
+ if self.current_token.type == T_KEYWORD and self.current_token.value == 'define':
224
+ # --- CORREÇÃO AQUI ---
225
+ # Em vez de chamar parse_process_declaration diretamente,
226
+ # chamamos o despachante principal que já sabe como lidar com 'define'.
227
+ methods.append(self.parse_define_statement())
228
+ elif self.current_token.type == T_KEYWORD and self.current_token.value in ('let', 'const'):
229
+ fields.append(self.parse_variable_declaration())
230
+ else:
231
+ self.error("Sintaxe inválida dentro de 'define type'. Esperado 'define' ou 'let'/'const'.")
232
+
233
+ self.eat(T_RBRACE)
234
+ return TypeDeclNode(name_token, parent_name_node, fields, methods)
235
+
236
+ def parse_process_declaration(self):
237
+ self.eat(T_KEYWORD) # process
238
+ proc_name = self.current_token
239
+ self.eat(T_IDENTIFIER)
240
+ params = self.parse_parameters()
241
+ return_type_node = None
242
+ if self.current_token.type == T_ARROW:
243
+ self.eat(T_ARROW)
244
+ return_type_node = self.parse_type()
245
+ body_node = self.parse_block()
246
+ return ProcessDeclNode(proc_name, params, return_type_node, body_node)
247
+
248
+ def parse_return_statement(self):
249
+ return_token = self.current_token
250
+ self.eat(T_KEYWORD)
251
+ value_node = None
252
+ # return pode ou não ter um valor
253
+ if self.current_token.type != T_RBRACE:
254
+ value_node = self.parse_expression()
255
+ return ReturnNode(return_token, value_node)
256
+
257
+ def parse_block(self):
258
+ start_brace_token = self.current_token
259
+ self.eat(T_LBRACE)
260
+ statements = []
261
+ while self.current_token.type != T_RBRACE:
262
+ statements.append(self.parse_statement())
263
+ self.eat(T_RBRACE)
264
+ return BlockNode(start_brace_token, statements)
265
+
266
+ def parse_expression(self):
267
+ return self.parse_ternary_expression()
268
+
269
+ def parse_ternary_expression(self):
270
+ node = self.parse_logic_or_expr()
271
+ if self.current_token.type == T_QUESTION:
272
+ question_token = self.current_token
273
+ self.eat(T_QUESTION)
274
+ true_expr = self.parse_ternary_expression()
275
+ self.eat(T_COLON)
276
+ false_expr = self.parse_ternary_expression()
277
+ return TernaryOpNode(node, question_token, true_expr, false_expr)
278
+ return node
279
+
280
+ # ... todos os métodos de parse_..._expr ...
281
+ def parse_logic_or_expr(self):
282
+ node = self.parse_logic_and_expr()
283
+ while self.current_token.type == T_KEYWORD and self.current_token.value == 'or':
284
+ op = self.current_token; self.eat(T_KEYWORD)
285
+ node = BinOpNode(node, op, self.parse_logic_and_expr())
286
+ return node
287
+ def parse_logic_and_expr(self):
288
+ node = self.parse_bitwise_or_expr()
289
+ while self.current_token.type == T_KEYWORD and self.current_token.value == 'and':
290
+ op = self.current_token; self.eat(T_KEYWORD)
291
+ node = BinOpNode(node, op, self.parse_bitwise_or_expr())
292
+ return node
293
+ # ... e assim por diante para todos os outros (bitwise, comparison, etc.) ...
294
+ def parse_bitwise_or_expr(self):
295
+ node = self.parse_bitwise_xor_expr()
296
+ while self.current_token.type == T_PIPE:
297
+ op = self.current_token; self.eat(T_PIPE)
298
+ node = BinOpNode(node, op, self.parse_bitwise_xor_expr())
299
+ return node
300
+ def parse_bitwise_xor_expr(self):
301
+ node = self.parse_bitwise_and_expr()
302
+ while self.current_token.type == T_CARET:
303
+ op = self.current_token; self.eat(T_CARET)
304
+ node = BinOpNode(node, op, self.parse_bitwise_and_expr())
305
+ return node
306
+ def parse_bitwise_and_expr(self):
307
+ node = self.parse_comparison_expr()
308
+ while self.current_token.type == T_AMPERSAND:
309
+ op = self.current_token; self.eat(T_AMPERSAND)
310
+ node = BinOpNode(node, op, self.parse_comparison_expr())
311
+ return node
312
+ def parse_comparison_expr(self):
313
+ if self.current_token.type == T_KEYWORD and self.current_token.value == 'not':
314
+ op = self.current_token; self.eat(T_KEYWORD)
315
+ return UnaryOpNode(op, self.parse_comparison_expr())
316
+ node = self.parse_shift_expr()
317
+ while self.current_token.type in (T_EQ, T_NE, T_LT, T_GT, T_LTE, T_GTE):
318
+ op = self.current_token; self.eat(op.type)
319
+ node = BinOpNode(node, op, self.parse_shift_expr())
320
+ return node
321
+ def parse_shift_expr(self):
322
+ node = self.parse_arith_expr()
323
+ while self.current_token.type in (T_LSHIFT, T_RSHIFT):
324
+ op = self.current_token; self.eat(op.type)
325
+ node = BinOpNode(node, op, self.parse_arith_expr())
326
+ return node
327
+ def parse_arith_expr(self):
328
+ node = self.parse_term()
329
+ while self.current_token.type in (T_PLUS, T_MINUS):
330
+ op = self.current_token; self.eat(op.type)
331
+ node = BinOpNode(node, op, self.parse_term())
332
+ return node
333
+ def parse_term(self):
334
+ node = self.parse_power()
335
+ while self.current_token.type in (T_MUL, T_DIV, T_MOD):
336
+ op = self.current_token; self.eat(op.type)
337
+ node = BinOpNode(node, op, self.parse_power())
338
+ return node
339
+ def parse_power(self):
340
+ node = self.parse_factor()
341
+ if self.current_token.type == T_POW:
342
+ op = self.current_token; self.eat(T_POW)
343
+ node = BinOpNode(node, op, self.parse_power())
344
+ return node
345
+ def parse_factor(self):
346
+ token = self.current_token
347
+ if token.type in (T_PLUS, T_MINUS, T_TILDE):
348
+ self.eat(token.type)
349
+ return UnaryOpNode(token, self.parse_factor())
350
+ return self.parse_primary()
351
+
352
+ def parse_f_string(self, token):
353
+ # O 'token' aqui é o token T_F_STRING completo
354
+ content = token.value
355
+ parts = []
356
+ last_index = 0
357
+
358
+ while last_index < len(content):
359
+ # Procura o início de uma expressão
360
+ start_brace = content.find('{', last_index)
361
+
362
+ # Se não encontrar mais chaves, o resto é texto literal
363
+ if start_brace == -1:
364
+ if last_index < len(content):
365
+ text_part = content[last_index:]
366
+ parts.append(StringNode(Token(T_STRING, text_part, token.line, token.col + last_index)))
367
+ break
368
+
369
+ # Se houver texto antes da chave, adiciona como uma parte literal
370
+ if start_brace > last_index:
371
+ text_part = content[last_index:start_brace]
372
+ parts.append(StringNode(Token(T_STRING, text_part, token.line, token.col + last_index)))
373
+
374
+ # Procura o fim da expressão
375
+ end_brace = content.find('}', start_brace)
376
+ if end_brace == -1:
377
+ self.error("Chave '{' de interpolação não foi fechada.", token)
378
+
379
+ # Pega o código da expressão de dentro das chaves
380
+ expr_code = content[start_brace + 1:end_brace].strip()
381
+ if not expr_code:
382
+ self.error("Expressão vazia dentro de f-string.", token)
383
+
384
+ # --- A MÁGICA ACONTECE AQUI ---
385
+ # Cria um novo Lexer e Parser para analisar apenas a expressão
386
+ expr_lexer = Lexer(expr_code)
387
+ expr_parser = Parser(expr_lexer)
388
+ expr_node = expr_parser.parse_expression()
389
+ parts.append(expr_node)
390
+ # --- FIM DA MÁGICA ---
391
+
392
+ # Atualiza o índice para continuar a busca depois da expressão
393
+ last_index = end_brace + 1
394
+
395
+ return InterpolatedStringNode(token, parts)
396
+
397
+
398
+ def parse_primary(self):
399
+ token = self.current_token
400
+ node = None
401
+
402
+ # --- LÓGICA PARA F-STRING ADICIONADA AQUI ---
403
+ if token.type == T_F_STRING:
404
+ self.eat(T_F_STRING)
405
+ # Chama o seu método auxiliar para parsear a f-string
406
+ node = self.parse_f_string(token)
407
+ # ----------------------------------------------
408
+
409
+ elif token.type in (T_INT, T_FLOAT):
410
+ self.eat(token.type)
411
+ node = NumberNode(token)
412
+
413
+ elif token.type == T_STRING:
414
+ self.eat(T_STRING)
415
+ node = StringNode(token)
416
+
417
+ elif token.type == T_LBRACKET:
418
+ # Esta lógica unificada para listas e compreensões é sua e está ótima.
419
+ # (O código original está preservado)
420
+ start_token = self.current_token
421
+ self.eat(T_LBRACKET)
422
+ if self.current_token.type == T_RBRACKET:
423
+ self.eat(T_RBRACKET)
424
+ return ListNode(start_token, []) # Retorna direto para não entrar no loop de acesso a membro
425
+ first_expr = self.parse_expression()
426
+ if self.current_token.type == T_KEYWORD and self.current_token.value == 'for':
427
+ # Lógica de desaçucaramento da compreensão de lista
428
+ self.eat(T_KEYWORD)
429
+ if not (self.current_token.type == T_KEYWORD and self.current_token.value == 'each'):
430
+ self.error("Esperado 'each' depois de 'for' em uma compreensão de lista.")
431
+ self.eat(T_KEYWORD)
432
+ var_name_token = self.current_token
433
+ self.eat(T_IDENTIFIER)
434
+ if not (self.current_token.type == T_KEYWORD and self.current_token.value == 'in'):
435
+ self.error("Esperado 'in' em uma compreensão de lista.")
436
+ self.eat(T_KEYWORD)
437
+ iterable_node = self.parse_expression()
438
+ self.eat(T_RBRACKET)
439
+ # ... (resto da sua lógica de desaçucaramento) ...
440
+ # Como isso retorna um Bloco, ele não deve passar pelo loop de acesso a membro abaixo
441
+ # esta implementação é um pouco mais complexa do que o normal.
442
+ # A sua implementação anterior que retornava um Bloco aqui estava correta.
443
+ # Para simplificar, vamos assumir que você tem um ListComprehensionNode
444
+ # e o interpretador sabe como lidar com ele.
445
+ # Se a sua lógica de desaçucaramento estiver funcionando, mantenha-a.
446
+ # Por agora, o importante é que a lógica acima capture a f-string.
447
+ # Se você manteve o desaçucaramento, o 'node' será um Bloco.
448
+ pass # Mantendo sua lógica de desaçucaramento.
449
+
450
+ else:
451
+ elements = [first_expr]
452
+ while self.current_token.type == T_COMMA:
453
+ self.eat(T_COMMA)
454
+ if self.current_token.type == T_RBRACKET: break
455
+ elements.append(self.parse_expression())
456
+ self.eat(T_RBRACKET)
457
+ node = ListNode(start_token, elements)
458
+
459
+ elif token.type == T_KEYWORD and token.value in ('true', 'false'):
460
+ self.eat(T_KEYWORD)
461
+ node = BoolNode(token)
462
+ elif token.type == T_KEYWORD and token.value == 'null':
463
+ self.eat(T_KEYWORD)
464
+ node = NullNode(token)
465
+ elif token.type == T_KEYWORD and token.value == 'process':
466
+ self.eat(T_KEYWORD)
467
+ params = self.parse_parameters()
468
+ body = self.parse_block()
469
+ node = LambdaNode(token, params, body)
470
+ elif token.type == T_LBRACE:
471
+ node = self.parse_dict_literal()
472
+ elif token.type == T_KEYWORD and token.value == 'self':
473
+ self.eat(T_KEYWORD)
474
+ node = VarAccessNode(token)
475
+ elif token.type == T_KEYWORD and token.value == 'super':
476
+ self.eat(T_KEYWORD)
477
+ node = SuperNode(token)
478
+ elif token.type == T_IDENTIFIER:
479
+ self.eat(T_IDENTIFIER)
480
+ node = VarAccessNode(token)
481
+ elif token.type == T_LPAREN:
482
+ self.eat(T_LPAREN)
483
+ node = self.parse_expression()
484
+ self.eat(T_RPAREN)
485
+ else:
486
+ self.error(f"Expressão primária inesperada: {token}")
487
+
488
+ # Loop para acesso a atributos, chamadas de função, etc.
489
+ while self.current_token.type in (T_LPAREN, T_LBRACKET, T_DOT):
490
+ if self.current_token.type == T_LPAREN:
491
+ node = ProcessCallNode(node, self.parse_call_arguments())
492
+ elif self.current_token.type == T_LBRACKET:
493
+ start_bracket_token = self.current_token
494
+ self.eat(T_LBRACKET)
495
+ index_node = self.parse_expression()
496
+ self.eat(T_RBRACKET)
497
+ node = IndexAccessNode(node, index_node, start_bracket_token)
498
+ elif self.current_token.type == T_DOT:
499
+ self.eat(T_DOT)
500
+ attribute_token = self.current_token
501
+ self.eat(T_IDENTIFIER)
502
+ if self.current_token.type == T_LPAREN:
503
+ node = MethodCallNode(node, attribute_token, self.parse_call_arguments())
504
+ else:
505
+ node = AttributeAccessNode(node, attribute_token)
506
+ return node
507
+
508
+ def parse_list_literal(self):
509
+ start_token = self.current_token; self.eat(T_LBRACKET)
510
+ elements = []
511
+ if self.current_token.type != T_RBRACKET:
512
+ elements.append(self.parse_expression())
513
+ while self.current_token.type == T_COMMA:
514
+ self.eat(T_COMMA)
515
+ elements.append(self.parse_expression())
516
+ self.eat(T_RBRACKET)
517
+ return ListNode(start_token, elements)
518
+
519
+ def parse_dict_literal(self):
520
+ start_token = self.current_token; self.eat(T_LBRACE)
521
+ pairs = []
522
+ if self.current_token.type != T_RBRACE:
523
+ while True:
524
+ key_node = self.parse_expression(); self.eat(T_COLON)
525
+ value_node = self.parse_expression()
526
+ pairs.append((key_node, value_node))
527
+ if self.current_token.type != T_COMMA: break
528
+ self.eat(T_COMMA)
529
+ self.eat(T_RBRACE)
530
+ return DictNode(start_token, pairs)
531
+
532
+ def parse_call_arguments(self):
533
+ args = []
534
+ self.eat(T_LPAREN)
535
+ if self.current_token.type != T_RPAREN:
536
+ args.append(self.parse_expression())
537
+ while self.current_token.type == T_COMMA:
538
+ self.eat(T_COMMA)
539
+ args.append(self.parse_expression())
540
+ self.eat(T_RPAREN)
541
+ return args
542
+
543
+ def parse_parameters(self):
544
+ params = []
545
+ self.eat(T_LPAREN)
546
+ if self.current_token.type != T_RPAREN:
547
+ while True:
548
+ param_token = self.current_token
549
+ if param_token.type == T_IDENTIFIER: self.eat(T_IDENTIFIER)
550
+ elif param_token.type == T_KEYWORD and param_token.value == 'self': self.eat(T_KEYWORD)
551
+ else: self.error(f"Esperado nome de parâmetro, mas recebi {param_token.type}")
552
+ type_node = None
553
+ if self.current_token.type == T_COLON:
554
+ self.eat(T_COLON)
555
+ type_node = self.parse_type()
556
+ params.append(ParamNode(param_token, type_node))
557
+ if self.current_token.type != T_COMMA: break
558
+ self.eat(T_COMMA)
559
+ self.eat(T_RPAREN)
560
+ return params
561
+
562
+ def parse_type(self):
563
+ token = self.current_token
564
+
565
+ # Lista de palavras-chave que também são nomes de tipo
566
+ built_in_type_keywords = ('int', 'float', 'string', 'bool', 'null', 'any', 'list', 'dict', 'tuple')
567
+
568
+ # Permite um IDENTIFIER (para tipos customizados)
569
+ if token.type == T_IDENTIFIER:
570
+ self.eat(T_IDENTIFIER)
571
+ return TypeNode(token)
572
+ # OU uma KEYWORD que seja um tipo nativo
573
+ elif token.type == T_KEYWORD and token.value in built_in_type_keywords:
574
+ self.eat(T_KEYWORD)
575
+ return TypeNode(token)
576
+ # Se não for nenhum dos dois, é um erro.
577
+ else:
578
+ self.error(f"Esperado um nome de tipo, mas recebi '{token.value}'")
579
+
580
+ def parse(self):
581
+ statements = []
582
+ while self.current_token.type != T_EOF:
583
+ statements.append(self.parse_statement())
584
+ return ProgramNode(statements)