@yakuzaa/jade-compiler 0.1.0

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.
Files changed (65) hide show
  1. package/dist/ast/nodes.d.ts +219 -0
  2. package/dist/ast/nodes.d.ts.map +1 -0
  3. package/dist/ast/nodes.js +2 -0
  4. package/dist/ast/nodes.js.map +1 -0
  5. package/dist/cli.d.ts +14 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +108 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/codegen/ir_generator.d.ts +42 -0
  10. package/dist/codegen/ir_generator.d.ts.map +1 -0
  11. package/dist/codegen/ir_generator.js +691 -0
  12. package/dist/codegen/ir_generator.js.map +1 -0
  13. package/dist/codegen/ir_nodes.d.ts +143 -0
  14. package/dist/codegen/ir_nodes.d.ts.map +1 -0
  15. package/dist/codegen/ir_nodes.js +3 -0
  16. package/dist/codegen/ir_nodes.js.map +1 -0
  17. package/dist/codegen/ir_printer.d.ts +10 -0
  18. package/dist/codegen/ir_printer.d.ts.map +1 -0
  19. package/dist/codegen/ir_printer.js +131 -0
  20. package/dist/codegen/ir_printer.js.map +1 -0
  21. package/dist/codegen/wasm_generator.d.ts +12 -0
  22. package/dist/codegen/wasm_generator.d.ts.map +1 -0
  23. package/dist/codegen/wasm_generator.js +48 -0
  24. package/dist/codegen/wasm_generator.js.map +1 -0
  25. package/dist/codegen/wat_generator.d.ts +29 -0
  26. package/dist/codegen/wat_generator.d.ts.map +1 -0
  27. package/dist/codegen/wat_generator.js +345 -0
  28. package/dist/codegen/wat_generator.js.map +1 -0
  29. package/dist/index.d.ts +61 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +79 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lexer/lexer.d.ts +31 -0
  34. package/dist/lexer/lexer.d.ts.map +1 -0
  35. package/dist/lexer/lexer.js +475 -0
  36. package/dist/lexer/lexer.js.map +1 -0
  37. package/dist/lexer/token.d.ts +8 -0
  38. package/dist/lexer/token.d.ts.map +1 -0
  39. package/dist/lexer/token.js +2 -0
  40. package/dist/lexer/token.js.map +1 -0
  41. package/dist/lexer/token_type.d.ts +80 -0
  42. package/dist/lexer/token_type.d.ts.map +1 -0
  43. package/dist/lexer/token_type.js +98 -0
  44. package/dist/lexer/token_type.js.map +1 -0
  45. package/dist/parser/parse_result.d.ts +12 -0
  46. package/dist/parser/parse_result.d.ts.map +1 -0
  47. package/dist/parser/parse_result.js +2 -0
  48. package/dist/parser/parse_result.js.map +1 -0
  49. package/dist/parser/parser.d.ts +57 -0
  50. package/dist/parser/parser.d.ts.map +1 -0
  51. package/dist/parser/parser.js +1094 -0
  52. package/dist/parser/parser.js.map +1 -0
  53. package/dist/semantic/semantic_analyzer.d.ts +10 -0
  54. package/dist/semantic/semantic_analyzer.d.ts.map +1 -0
  55. package/dist/semantic/semantic_analyzer.js +16 -0
  56. package/dist/semantic/semantic_analyzer.js.map +1 -0
  57. package/dist/semantic/symbol_table.d.ts +33 -0
  58. package/dist/semantic/symbol_table.d.ts.map +1 -0
  59. package/dist/semantic/symbol_table.js +105 -0
  60. package/dist/semantic/symbol_table.js.map +1 -0
  61. package/dist/semantic/type_checker.d.ts +66 -0
  62. package/dist/semantic/type_checker.d.ts.map +1 -0
  63. package/dist/semantic/type_checker.js +963 -0
  64. package/dist/semantic/type_checker.js.map +1 -0
  65. package/package.json +84 -0
@@ -0,0 +1,1094 @@
1
+ import { TokenType } from '../lexer/token_type.js';
2
+ export class Parser {
3
+ tokens;
4
+ current = 0;
5
+ errors = [];
6
+ constructor(tokens) {
7
+ this.tokens = tokens;
8
+ }
9
+ parse() {
10
+ const declaracoes = [];
11
+ while (!this.isAtEnd()) {
12
+ try {
13
+ const declaracao = this.parseDeclaracao();
14
+ if (declaracao) {
15
+ declaracoes.push(declaracao);
16
+ }
17
+ }
18
+ catch (error) {
19
+ this.errors.push({
20
+ message: error.message || 'Erro de sintaxe',
21
+ line: this.peek().line,
22
+ column: this.peek().column
23
+ });
24
+ this.synchronize();
25
+ }
26
+ }
27
+ const program = {
28
+ kind: 'Programa',
29
+ line: 1,
30
+ column: 1,
31
+ declaracoes
32
+ };
33
+ return {
34
+ program,
35
+ errors: this.errors,
36
+ success: this.errors.length === 0
37
+ };
38
+ }
39
+ // ── Declarações ───────────────────────────────────────────
40
+ parseDeclaracao() {
41
+ try {
42
+ if (this.match(TokenType.MODULO))
43
+ return this.parseModulo();
44
+ if (this.match(TokenType.CLASSE))
45
+ return this.parseClasse();
46
+ if (this.match(TokenType.ENTIDADE))
47
+ return this.parseEntidade();
48
+ if (this.match(TokenType.SERVICO))
49
+ return this.parseServico();
50
+ if (this.match(TokenType.FUNCAO))
51
+ return this.parseFuncao();
52
+ if (this.match(TokenType.EVENTO))
53
+ return this.parseEvento();
54
+ if (this.match(TokenType.REGRA))
55
+ return this.parseRegra();
56
+ if (this.match(TokenType.INTERFACE))
57
+ return this.parseInterface();
58
+ if (this.match(TokenType.ENUM))
59
+ return this.parseEnum();
60
+ if (this.match(TokenType.IMPORTAR))
61
+ return this.parseImportacao();
62
+ if (this.match(TokenType.TELA))
63
+ return this.parseTela();
64
+ return null;
65
+ }
66
+ catch (error) {
67
+ throw error;
68
+ }
69
+ }
70
+ parseModulo() {
71
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do módulo");
72
+ const nome = nomeToken.value;
73
+ const declaracoes = [];
74
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
75
+ const declaracao = this.parseDeclaracao();
76
+ if (declaracao)
77
+ declaracoes.push(declaracao);
78
+ }
79
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar módulo");
80
+ return {
81
+ kind: 'Modulo',
82
+ line: nomeToken.line,
83
+ column: nomeToken.column,
84
+ nome,
85
+ declaracoes
86
+ };
87
+ }
88
+ parseClasse() {
89
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da classe");
90
+ const nome = nomeToken.value;
91
+ let superClasse;
92
+ const interfaces = [];
93
+ if (this.match(TokenType.EXTENDS)) {
94
+ superClasse = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da superclasse").value;
95
+ }
96
+ if (this.match(TokenType.IMPLEMENTS)) {
97
+ do {
98
+ interfaces.push(this.consume(TokenType.IDENTIFICADOR, "Esperado nome da interface").value);
99
+ } while (this.match(TokenType.VIRGULA));
100
+ }
101
+ const campos = [];
102
+ const metodos = [];
103
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
104
+ if (this.match(TokenType.FUNCAO)) {
105
+ metodos.push(this.parseFuncao());
106
+ }
107
+ else {
108
+ campos.push(this.parseCampo());
109
+ }
110
+ }
111
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar classe");
112
+ return {
113
+ kind: 'Classe',
114
+ line: nomeToken.line,
115
+ column: nomeToken.column,
116
+ nome,
117
+ superClasse,
118
+ interfaces,
119
+ campos,
120
+ metodos
121
+ };
122
+ }
123
+ parseEntidade() {
124
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da entidade");
125
+ const nome = nomeToken.value;
126
+ const campos = [];
127
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
128
+ campos.push(this.parseCampo());
129
+ }
130
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar entidade");
131
+ return {
132
+ kind: 'Entidade',
133
+ line: nomeToken.line,
134
+ column: nomeToken.column,
135
+ nome,
136
+ campos
137
+ };
138
+ }
139
+ parseServico() {
140
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do serviço");
141
+ const nome = nomeToken.value;
142
+ const metodos = [];
143
+ const ouvintes = [];
144
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
145
+ if (this.match(TokenType.FUNCAO)) {
146
+ metodos.push(this.parseFuncao());
147
+ }
148
+ else if (this.match(TokenType.ESCUTAR)) {
149
+ ouvintes.push(this.parseOuvinte());
150
+ }
151
+ else {
152
+ throw this.error("Esperado 'funcao' ou 'escutar' no serviço");
153
+ }
154
+ }
155
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar serviço");
156
+ return {
157
+ kind: 'Servico',
158
+ line: nomeToken.line,
159
+ column: nomeToken.column,
160
+ nome,
161
+ metodos,
162
+ ouvintes
163
+ };
164
+ }
165
+ parseFuncao() {
166
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da função");
167
+ const nome = nomeToken.value;
168
+ this.consume(TokenType.ABRE_PAREN, "Esperado '('");
169
+ const parametros = [];
170
+ if (!this.check(TokenType.FECHA_PAREN)) {
171
+ do {
172
+ parametros.push(this.parseParametro());
173
+ } while (this.match(TokenType.VIRGULA));
174
+ }
175
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')'");
176
+ let tipoRetorno;
177
+ if (this.match(TokenType.SETA)) {
178
+ tipoRetorno = this.parseTipo();
179
+ }
180
+ const corpo = this.parseBloco();
181
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar função");
182
+ return {
183
+ kind: 'Funcao',
184
+ line: nomeToken.line,
185
+ column: nomeToken.column,
186
+ nome,
187
+ parametros,
188
+ tipoRetorno,
189
+ corpo
190
+ };
191
+ }
192
+ parseEvento() {
193
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do evento");
194
+ const nome = nomeToken.value;
195
+ const campos = [];
196
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
197
+ campos.push(this.parseCampo());
198
+ }
199
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar evento");
200
+ return {
201
+ kind: 'Evento',
202
+ line: nomeToken.line,
203
+ column: nomeToken.column,
204
+ nome,
205
+ campos
206
+ };
207
+ }
208
+ parseRegra() {
209
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da regra");
210
+ const nome = nomeToken.value;
211
+ this.consume(TokenType.QUANDO, "Esperado 'quando'");
212
+ const condicao = this.parseExpressao();
213
+ this.consume(TokenType.ENTAO, "Esperado 'entao'");
214
+ const entao = this.parseBloco();
215
+ let senao;
216
+ if (this.match(TokenType.SENAO)) {
217
+ senao = this.parseBloco();
218
+ }
219
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar regra");
220
+ return {
221
+ kind: 'Regra',
222
+ line: nomeToken.line,
223
+ column: nomeToken.column,
224
+ nome,
225
+ condicao,
226
+ entao,
227
+ senao
228
+ };
229
+ }
230
+ parseInterface() {
231
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da interface");
232
+ const nome = nomeToken.value;
233
+ const assinaturas = [];
234
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
235
+ this.consume(TokenType.FUNCAO, "Esperado 'funcao' na interface");
236
+ const nomeAssinaturaToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da assinatura");
237
+ const nomeAssinatura = nomeAssinaturaToken.value;
238
+ this.consume(TokenType.ABRE_PAREN, "Esperado '('");
239
+ const parametros = [];
240
+ if (!this.check(TokenType.FECHA_PAREN)) {
241
+ do {
242
+ parametros.push(this.parseParametro());
243
+ } while (this.match(TokenType.VIRGULA));
244
+ }
245
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')'");
246
+ this.consume(TokenType.SETA, "Esperado '->'");
247
+ const tipoRetorno = this.parseTipo();
248
+ assinaturas.push({
249
+ kind: 'Assinatura',
250
+ line: nomeAssinaturaToken.line,
251
+ column: nomeAssinaturaToken.column,
252
+ nome: nomeAssinatura,
253
+ parametros,
254
+ tipoRetorno
255
+ });
256
+ }
257
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar interface");
258
+ return {
259
+ kind: 'Interface',
260
+ line: nomeToken.line,
261
+ column: nomeToken.column,
262
+ nome,
263
+ assinaturas
264
+ };
265
+ }
266
+ parseEnum() {
267
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do enum");
268
+ const nome = nomeToken.value;
269
+ const valores = [];
270
+ do {
271
+ const valor = this.consume(TokenType.IDENTIFICADOR, "Esperado valor do enum").value;
272
+ valores.push(valor);
273
+ } while (!this.check(TokenType.FIM) && !this.isAtEnd());
274
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar enum");
275
+ return {
276
+ kind: 'Enum',
277
+ line: nomeToken.line,
278
+ column: nomeToken.column,
279
+ nome,
280
+ valores
281
+ };
282
+ }
283
+ parseImportacao() {
284
+ const moduloToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do módulo");
285
+ const modulo = moduloToken.value;
286
+ let item;
287
+ let wildcard = false;
288
+ let alias;
289
+ if (this.match(TokenType.PONTO)) {
290
+ if (this.match(TokenType.ASTERISCO)) {
291
+ wildcard = true;
292
+ }
293
+ else {
294
+ item = this.consume(TokenType.IDENTIFICADOR, "Esperado item importado").value;
295
+ }
296
+ }
297
+ else if (this.match(TokenType.COMO)) {
298
+ alias = this.consume(TokenType.IDENTIFICADOR, "Esperado alias").value;
299
+ }
300
+ return {
301
+ kind: 'Importacao',
302
+ line: moduloToken.line,
303
+ column: moduloToken.column,
304
+ modulo,
305
+ item,
306
+ wildcard,
307
+ alias
308
+ };
309
+ }
310
+ parseTela() {
311
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da tela");
312
+ const nome = nomeToken.value;
313
+ const tituloToken = this.consume(TokenType.LITERAL_TEXTO, "Esperado título da tela entre aspas");
314
+ const titulo = tituloToken.value;
315
+ const elementos = [];
316
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
317
+ elementos.push(this.parseTelaElemento());
318
+ }
319
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar tela");
320
+ return {
321
+ kind: 'Tela',
322
+ line: nomeToken.line,
323
+ column: nomeToken.column,
324
+ nome,
325
+ titulo,
326
+ elementos
327
+ };
328
+ }
329
+ parseTelaElemento() {
330
+ const tipoToken = this.consume(TokenType.IDENTIFICADOR, "Esperado tipo do elemento (tabela, formulario, botao, card)");
331
+ const tipo = tipoToken.value;
332
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do elemento");
333
+ const nome = nomeToken.value;
334
+ const propriedades = [];
335
+ while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
336
+ // Chaves de propriedade podem ser keywords como 'entidade'
337
+ const chaveToken = this.consumeAny([
338
+ TokenType.IDENTIFICADOR,
339
+ TokenType.ENTIDADE,
340
+ ], "Esperado nome da propriedade");
341
+ const chave = chaveToken.value;
342
+ this.consume(TokenType.DOIS_PONTOS, "Esperado ':' após nome da propriedade");
343
+ let valor;
344
+ if (this.check(TokenType.LITERAL_TEXTO)) {
345
+ valor = this.advance().value;
346
+ }
347
+ else if (this.check(TokenType.VERDADEIRO)) {
348
+ this.advance();
349
+ valor = 'verdadeiro';
350
+ }
351
+ else if (this.check(TokenType.FALSO)) {
352
+ this.advance();
353
+ valor = 'falso';
354
+ }
355
+ else if (this.checkAny([
356
+ TokenType.IDENTIFICADOR,
357
+ TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO, TokenType.TIPO_DECIMAL,
358
+ TokenType.TIPO_BOOLEANO, TokenType.TIPO_DATA, TokenType.TIPO_HORA,
359
+ TokenType.TIPO_ID, TokenType.TIPO_LISTA, TokenType.TIPO_MAPA
360
+ ])) {
361
+ // Pode ser: nome simples, nome() chamada, ou lista: a, b, c
362
+ // Aceita palavras-chave de tipo (data, texto, etc.) como valores
363
+ const first = this.advance().value;
364
+ if (this.check(TokenType.ABRE_PAREN)) {
365
+ // acao: funcao()
366
+ this.advance(); // (
367
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')'");
368
+ valor = first + '()';
369
+ }
370
+ else if (this.check(TokenType.VIRGULA)) {
371
+ // lista: a, b, c (pode incluir palavras-chave de tipo como 'data')
372
+ const lista = [first];
373
+ while (this.match(TokenType.VIRGULA)) {
374
+ lista.push(this.consumeAny([
375
+ TokenType.IDENTIFICADOR,
376
+ TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO, TokenType.TIPO_DECIMAL,
377
+ TokenType.TIPO_BOOLEANO, TokenType.TIPO_DATA, TokenType.TIPO_HORA,
378
+ TokenType.TIPO_ID
379
+ ], "Esperado identificador").value);
380
+ }
381
+ valor = lista;
382
+ }
383
+ else {
384
+ valor = first;
385
+ }
386
+ }
387
+ else {
388
+ throw this.error("Esperado valor para a propriedade");
389
+ }
390
+ propriedades.push({ chave, valor });
391
+ }
392
+ this.consume(TokenType.FIM, `Esperado 'fim' para fechar elemento '${tipo}'`);
393
+ return {
394
+ kind: 'TelaElemento',
395
+ line: tipoToken.line,
396
+ column: tipoToken.column,
397
+ tipo,
398
+ nome,
399
+ propriedades
400
+ };
401
+ }
402
+ parseCampo() {
403
+ const nomeToken = this.consumeAny([
404
+ TokenType.IDENTIFICADOR,
405
+ TokenType.TIPO_ID,
406
+ TokenType.TIPO_TEXTO,
407
+ TokenType.TIPO_NUMERO,
408
+ TokenType.TIPO_DECIMAL,
409
+ TokenType.TIPO_BOOLEANO,
410
+ TokenType.TIPO_DATA,
411
+ TokenType.TIPO_HORA,
412
+ TokenType.TIPO_LISTA,
413
+ TokenType.TIPO_MAPA,
414
+ TokenType.TIPO_OBJETO
415
+ ], "Esperado nome do campo");
416
+ const nome = nomeToken.value;
417
+ this.consume(TokenType.DOIS_PONTOS, "Esperado ':'");
418
+ const tipo = this.parseTipo();
419
+ return {
420
+ kind: 'Campo',
421
+ line: nomeToken.line,
422
+ column: nomeToken.column,
423
+ nome,
424
+ tipo
425
+ };
426
+ }
427
+ parseParametro() {
428
+ const nomeToken = this.consumeAny([
429
+ TokenType.IDENTIFICADOR,
430
+ TokenType.TIPO_ID,
431
+ TokenType.TIPO_TEXTO,
432
+ TokenType.TIPO_NUMERO,
433
+ TokenType.TIPO_DECIMAL,
434
+ TokenType.TIPO_BOOLEANO,
435
+ TokenType.TIPO_DATA,
436
+ TokenType.TIPO_HORA,
437
+ TokenType.TIPO_LISTA,
438
+ TokenType.TIPO_MAPA,
439
+ TokenType.TIPO_OBJETO
440
+ ], "Esperado nome do parâmetro");
441
+ const nome = nomeToken.value;
442
+ this.consume(TokenType.DOIS_PONTOS, "Esperado ':'");
443
+ const tipo = this.parseTipo();
444
+ return {
445
+ kind: 'Parametro',
446
+ line: nomeToken.line,
447
+ column: nomeToken.column,
448
+ nome,
449
+ tipo
450
+ };
451
+ }
452
+ parseTipo() {
453
+ if (this.match(TokenType.TIPO_LISTA)) {
454
+ this.consume(TokenType.MENOR, "Esperado '<'");
455
+ const elementoTipo = this.parseTipo();
456
+ this.consume(TokenType.MAIOR, "Esperado '>'");
457
+ return {
458
+ kind: 'TipoLista',
459
+ line: elementoTipo.line,
460
+ column: elementoTipo.column,
461
+ elementoTipo
462
+ };
463
+ }
464
+ if (this.match(TokenType.TIPO_MAPA)) {
465
+ this.consume(TokenType.MENOR, "Esperado '<'");
466
+ const chaveTipo = this.parseTipo();
467
+ this.consume(TokenType.VIRGULA, "Esperado ','");
468
+ const valorTipo = this.parseTipo();
469
+ this.consume(TokenType.MAIOR, "Esperado '>'");
470
+ return {
471
+ kind: 'TipoMapa',
472
+ line: chaveTipo.line,
473
+ column: chaveTipo.column,
474
+ chaveTipo,
475
+ valorTipo
476
+ };
477
+ }
478
+ const token = this.consumeAny([
479
+ TokenType.IDENTIFICADOR,
480
+ TokenType.TIPO_ID,
481
+ TokenType.TIPO_TEXTO,
482
+ TokenType.TIPO_NUMERO,
483
+ TokenType.TIPO_DECIMAL,
484
+ TokenType.TIPO_BOOLEANO,
485
+ TokenType.TIPO_DATA,
486
+ TokenType.TIPO_HORA,
487
+ TokenType.TIPO_LISTA,
488
+ TokenType.TIPO_MAPA,
489
+ TokenType.TIPO_OBJETO
490
+ ], "Esperado tipo");
491
+ let opcional = false;
492
+ let obrigatorio = false;
493
+ if (this.match(TokenType.INTERROGACAO)) {
494
+ opcional = true;
495
+ }
496
+ else if (this.match(TokenType.EXCLAMACAO)) {
497
+ obrigatorio = true;
498
+ }
499
+ return {
500
+ kind: 'TipoSimples',
501
+ line: token.line,
502
+ column: token.column,
503
+ nome: token.value,
504
+ opcional,
505
+ obrigatorio
506
+ };
507
+ }
508
+ parseOuvinte() {
509
+ const eventoToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do evento");
510
+ const evento = eventoToken.value;
511
+ const corpo = this.parseBloco();
512
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar ouvinte");
513
+ return {
514
+ kind: 'Ouvinte',
515
+ line: eventoToken.line,
516
+ column: eventoToken.column,
517
+ evento,
518
+ corpo
519
+ };
520
+ }
521
+ parseBloco() {
522
+ const instrucoes = [];
523
+ while (!this.check(TokenType.FIM) && !this.check(TokenType.SENAO) && !this.isAtEnd()) {
524
+ const before = this.current;
525
+ const instrucao = this.parseInstrucao();
526
+ if (instrucao) {
527
+ instrucoes.push(instrucao);
528
+ }
529
+ // Evita loop infinito - se nenhum token foi consumido, avança ou lança erro
530
+ if (this.current === before) {
531
+ if (this.isAtEnd()) {
532
+ break;
533
+ }
534
+ throw this.error(`Parser travado: token inesperado ${this.peek().type}='${this.peek().value}'`);
535
+ }
536
+ }
537
+ return {
538
+ kind: 'Bloco',
539
+ line: 1,
540
+ column: 1,
541
+ instrucoes
542
+ };
543
+ }
544
+ parseInstrucao() {
545
+ if (this.match(TokenType.VARIAVEL))
546
+ return this.parseVariavel();
547
+ if (this.match(TokenType.RETORNAR))
548
+ return this.parseRetorno();
549
+ if (this.match(TokenType.SE))
550
+ return this.parseCondicional();
551
+ if (this.match(TokenType.ENQUANTO))
552
+ return this.parseEnquanto();
553
+ if (this.match(TokenType.PARA))
554
+ return this.parsePara();
555
+ if (this.match(TokenType.EMITIR))
556
+ return this.parseEmissaoEvento();
557
+ if (this.match(TokenType.ERRO))
558
+ return this.parseErro();
559
+ // Atribuição ou chamada de função
560
+ return this.parseAtribuicaoOuChamada();
561
+ }
562
+ parseVariavel() {
563
+ const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da variável");
564
+ const nome = nomeToken.value;
565
+ let tipo;
566
+ if (this.match(TokenType.DOIS_PONTOS)) {
567
+ tipo = this.parseTipo();
568
+ }
569
+ let inicializador;
570
+ if (this.match(TokenType.IGUAL)) {
571
+ inicializador = this.parseExpressao();
572
+ }
573
+ return {
574
+ kind: 'Variavel',
575
+ line: nomeToken.line,
576
+ column: nomeToken.column,
577
+ nome,
578
+ tipo,
579
+ inicializador
580
+ };
581
+ }
582
+ parseAtribuicaoOuChamada() {
583
+ // Só age se o token atual for IDENTIFICADOR
584
+ if (!this.check(TokenType.IDENTIFICADOR)) {
585
+ return null;
586
+ }
587
+ const nomeToken = this.advance();
588
+ const nome = nomeToken.value;
589
+ // Caso: nome.membro... (acesso a membro — pode ser atribuição ou chamada)
590
+ if (this.check(TokenType.PONTO)) {
591
+ // Constrói a cadeia de acesso: objeto.membro1.membro2...
592
+ let objeto = {
593
+ kind: 'Identificador',
594
+ nome,
595
+ line: nomeToken.line,
596
+ column: nomeToken.column
597
+ };
598
+ while (this.match(TokenType.PONTO)) {
599
+ const membroToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do membro após '.'");
600
+ const membro = membroToken.value;
601
+ // produto.metodo(...) — chamada de método
602
+ if (this.check(TokenType.ABRE_PAREN)) {
603
+ this.advance(); // consome '('
604
+ const argumentos = [];
605
+ if (!this.check(TokenType.FECHA_PAREN)) {
606
+ do {
607
+ argumentos.push(this.parseExpressao());
608
+ } while (this.match(TokenType.VIRGULA));
609
+ }
610
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')'");
611
+ objeto = {
612
+ kind: 'AcessoMembro',
613
+ objeto,
614
+ membro,
615
+ chamada: argumentos,
616
+ line: membroToken.line,
617
+ column: membroToken.column
618
+ };
619
+ // Após chamada de método como instrução, retorna
620
+ if (!this.check(TokenType.PONTO)) {
621
+ return objeto;
622
+ }
623
+ }
624
+ else {
625
+ objeto = {
626
+ kind: 'AcessoMembro',
627
+ objeto,
628
+ membro,
629
+ line: membroToken.line,
630
+ column: membroToken.column
631
+ };
632
+ }
633
+ }
634
+ // produto.campo = valor — atribuição de membro
635
+ if (this.match(TokenType.IGUAL)) {
636
+ const valor = this.parseExpressao();
637
+ return {
638
+ kind: 'Atribuicao',
639
+ line: nomeToken.line,
640
+ column: nomeToken.column,
641
+ alvo: objeto,
642
+ valor
643
+ };
644
+ }
645
+ // Se chegou aqui sem atribuição, é uma expressão usada como instrução
646
+ return objeto;
647
+ }
648
+ // Caso: nome = valor — atribuição simples
649
+ if (this.match(TokenType.IGUAL)) {
650
+ const valor = this.parseExpressao();
651
+ return {
652
+ kind: 'Atribuicao',
653
+ line: nomeToken.line,
654
+ column: nomeToken.column,
655
+ alvo: nome,
656
+ valor
657
+ };
658
+ }
659
+ // Caso: nome(...) — chamada de função simples
660
+ if (this.match(TokenType.ABRE_PAREN)) {
661
+ const argumentos = [];
662
+ if (!this.check(TokenType.FECHA_PAREN)) {
663
+ do {
664
+ argumentos.push(this.parseExpressao());
665
+ } while (this.match(TokenType.VIRGULA));
666
+ }
667
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')'");
668
+ return {
669
+ kind: 'ChamadaFuncao',
670
+ line: nomeToken.line,
671
+ column: nomeToken.column,
672
+ nome,
673
+ argumentos
674
+ };
675
+ }
676
+ // Token consumido mas não reconhecido como instrução válida
677
+ // Volta o token para não perder (usando current--)
678
+ this.current--;
679
+ return null;
680
+ }
681
+ parseRetorno() {
682
+ let valor;
683
+ if (!this.check(TokenType.FIM) && !this.check(TokenType.EOF)) {
684
+ valor = this.parseExpressao();
685
+ }
686
+ return {
687
+ kind: 'Retorno',
688
+ line: 1,
689
+ column: 1,
690
+ valor
691
+ };
692
+ }
693
+ parseCondicional() {
694
+ const condicao = this.parseExpressao();
695
+ const entao = this.parseBloco();
696
+ let senao;
697
+ if (this.match(TokenType.SENAO)) {
698
+ if (this.match(TokenType.SE)) {
699
+ // senao se — encadeia novo condicional sem fim próprio
700
+ const inner = this.parseCondicionalSemFim();
701
+ senao = {
702
+ kind: 'Bloco',
703
+ line: inner.line,
704
+ column: inner.column,
705
+ instrucoes: [inner]
706
+ };
707
+ }
708
+ else {
709
+ senao = this.parseBloco();
710
+ }
711
+ }
712
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar condicional");
713
+ return {
714
+ kind: 'Condicional',
715
+ line: condicao.line,
716
+ column: condicao.column,
717
+ condicao,
718
+ entao,
719
+ senao
720
+ };
721
+ }
722
+ parseCondicionalSemFim() {
723
+ const condicao = this.parseExpressao();
724
+ const entao = this.parseBloco();
725
+ let senao;
726
+ if (this.match(TokenType.SENAO)) {
727
+ if (this.match(TokenType.SE)) {
728
+ const inner = this.parseCondicionalSemFim();
729
+ senao = {
730
+ kind: 'Bloco',
731
+ line: inner.line,
732
+ column: inner.column,
733
+ instrucoes: [inner]
734
+ };
735
+ }
736
+ else {
737
+ senao = this.parseBloco();
738
+ }
739
+ }
740
+ return {
741
+ kind: 'Condicional',
742
+ line: condicao.line,
743
+ column: condicao.column,
744
+ condicao,
745
+ entao,
746
+ senao
747
+ };
748
+ }
749
+ parseEnquanto() {
750
+ const condicao = this.parseExpressao();
751
+ const corpo = this.parseBloco();
752
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar enquanto");
753
+ return {
754
+ kind: 'Enquanto',
755
+ line: condicao.line,
756
+ column: condicao.column,
757
+ condicao,
758
+ corpo
759
+ };
760
+ }
761
+ parsePara() {
762
+ const variavelToken = this.consume(TokenType.IDENTIFICADOR, "Esperado variável");
763
+ const variavel = variavelToken.value;
764
+ this.consume(TokenType.EM, "Esperado 'em' após variável do 'para'");
765
+ const iteravel = this.parseExpressao();
766
+ const corpo = this.parseBloco();
767
+ this.consume(TokenType.FIM, "Esperado 'fim' para fechar para");
768
+ return {
769
+ kind: 'Para',
770
+ line: variavelToken.line,
771
+ column: variavelToken.column,
772
+ variavel,
773
+ iteravel,
774
+ corpo
775
+ };
776
+ }
777
+ parseEmissaoEvento() {
778
+ const eventoToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome do evento");
779
+ const evento = eventoToken.value;
780
+ this.consume(TokenType.ABRE_PAREN, "Esperado '('");
781
+ const argumentos = [];
782
+ if (!this.check(TokenType.FECHA_PAREN)) {
783
+ do {
784
+ argumentos.push(this.parseExpressao());
785
+ } while (this.match(TokenType.VIRGULA));
786
+ }
787
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')'");
788
+ return {
789
+ kind: 'EmissaoEvento',
790
+ line: eventoToken.line,
791
+ column: eventoToken.column,
792
+ evento,
793
+ argumentos
794
+ };
795
+ }
796
+ parseErro() {
797
+ const mensagem = this.parseExpressao();
798
+ return {
799
+ kind: 'Erro',
800
+ line: mensagem.line,
801
+ column: mensagem.column,
802
+ mensagem
803
+ };
804
+ }
805
+ parseExpressao() {
806
+ return this.parseExpressaoLogica();
807
+ }
808
+ parseExpressaoLogica() {
809
+ let expr = this.parseExpressaoComparacao();
810
+ while (this.match(TokenType.OU) || this.match(TokenType.E)) {
811
+ const operador = this.previous().value;
812
+ const direita = this.parseExpressaoComparacao();
813
+ expr = {
814
+ kind: 'Binario',
815
+ line: expr.line,
816
+ column: expr.column,
817
+ esquerda: expr,
818
+ operador,
819
+ direita
820
+ };
821
+ }
822
+ return expr;
823
+ }
824
+ // O lexer salva apenas o último caractere do token para operadores duplos (==, !=, <=, >=)
825
+ // então derivamos o operador correto pelo tipo do token
826
+ tipoParaOperador(type) {
827
+ switch (type) {
828
+ case TokenType.IGUAL_IGUAL: return '==';
829
+ case TokenType.DIFERENTE: return '!=';
830
+ case TokenType.MENOR_IGUAL: return '<=';
831
+ case TokenType.MAIOR_IGUAL: return '>=';
832
+ case TokenType.MENOR: return '<';
833
+ case TokenType.MAIOR: return '>';
834
+ default: return this.previous().value;
835
+ }
836
+ }
837
+ parseExpressaoComparacao() {
838
+ let expr = this.parseExpressaoAritmetica();
839
+ while (this.match(TokenType.IGUAL_IGUAL) || this.match(TokenType.DIFERENTE) ||
840
+ this.match(TokenType.MENOR) || this.match(TokenType.MENOR_IGUAL) ||
841
+ this.match(TokenType.MAIOR) || this.match(TokenType.MAIOR_IGUAL)) {
842
+ const operador = this.tipoParaOperador(this.previous().type);
843
+ const direita = this.parseExpressaoAritmetica();
844
+ expr = {
845
+ kind: 'Binario',
846
+ line: expr.line,
847
+ column: expr.column,
848
+ esquerda: expr,
849
+ operador,
850
+ direita
851
+ };
852
+ }
853
+ return expr;
854
+ }
855
+ parseExpressaoAritmetica() {
856
+ let expr = this.parseTermo();
857
+ while (this.match(TokenType.MAIS) || this.match(TokenType.MENOS)) {
858
+ const operador = this.previous().value;
859
+ const direita = this.parseTermo();
860
+ expr = {
861
+ kind: 'Binario',
862
+ line: expr.line,
863
+ column: expr.column,
864
+ esquerda: expr,
865
+ operador,
866
+ direita
867
+ };
868
+ }
869
+ return expr;
870
+ }
871
+ parseTermo() {
872
+ let expr = this.parseFator();
873
+ while (this.match(TokenType.ASTERISCO) || this.match(TokenType.BARRA)) {
874
+ const operador = this.previous().value;
875
+ const direita = this.parseFator();
876
+ expr = {
877
+ kind: 'Binario',
878
+ line: expr.line,
879
+ column: expr.column,
880
+ esquerda: expr,
881
+ operador,
882
+ direita
883
+ };
884
+ }
885
+ return expr;
886
+ }
887
+ parseFator() {
888
+ if (this.match(TokenType.MENOS) || this.match(TokenType.NAO)) {
889
+ const operador = this.previous().value;
890
+ const operando = this.parseFator();
891
+ return {
892
+ kind: 'Unario',
893
+ line: operando.line,
894
+ column: operando.column,
895
+ operador,
896
+ operando
897
+ };
898
+ }
899
+ return this.parsePrimario();
900
+ }
901
+ parsePrimario() {
902
+ if (this.match(TokenType.LITERAL_NUMERO)) {
903
+ const token = this.previous();
904
+ return {
905
+ kind: 'Literal',
906
+ line: token.line,
907
+ column: token.column,
908
+ valor: Number(token.value),
909
+ tipoLiteral: 'numero'
910
+ };
911
+ }
912
+ if (this.match(TokenType.LITERAL_TEXTO)) {
913
+ const token = this.previous();
914
+ return {
915
+ kind: 'Literal',
916
+ line: token.line,
917
+ column: token.column,
918
+ valor: token.value,
919
+ tipoLiteral: 'texto'
920
+ };
921
+ }
922
+ if (this.match(TokenType.VERDADEIRO)) {
923
+ const token = this.previous();
924
+ return {
925
+ kind: 'Literal',
926
+ line: token.line,
927
+ column: token.column,
928
+ valor: true,
929
+ tipoLiteral: 'booleano'
930
+ };
931
+ }
932
+ if (this.match(TokenType.FALSO)) {
933
+ const token = this.previous();
934
+ return {
935
+ kind: 'Literal',
936
+ line: token.line,
937
+ column: token.column,
938
+ valor: false,
939
+ tipoLiteral: 'booleano'
940
+ };
941
+ }
942
+ if (this.match(TokenType.ABRE_PAREN)) {
943
+ const expr = this.parseExpressao();
944
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')' após expressão");
945
+ return expr;
946
+ }
947
+ if (this.match(TokenType.LITERAL_DECIMAL)) {
948
+ const token = this.previous();
949
+ return {
950
+ kind: 'Literal',
951
+ line: token.line,
952
+ column: token.column,
953
+ valor: Number(token.value),
954
+ tipoLiteral: 'decimal'
955
+ };
956
+ }
957
+ if (this.match(TokenType.IDENTIFICADOR)) {
958
+ const token = this.previous();
959
+ // nome(...) — chamada de função em expressão
960
+ if (this.match(TokenType.ABRE_PAREN)) {
961
+ const argumentos = [];
962
+ if (!this.check(TokenType.FECHA_PAREN)) {
963
+ do {
964
+ argumentos.push(this.parseExpressao());
965
+ } while (this.match(TokenType.VIRGULA));
966
+ }
967
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')' após argumentos");
968
+ return {
969
+ kind: 'ChamadaFuncao',
970
+ line: token.line,
971
+ column: token.column,
972
+ nome: token.value,
973
+ argumentos
974
+ };
975
+ }
976
+ // nome.membro... — acesso a membro (encadeado)
977
+ if (this.check(TokenType.PONTO)) {
978
+ let objeto = {
979
+ kind: 'Identificador',
980
+ line: token.line,
981
+ column: token.column,
982
+ nome: token.value
983
+ };
984
+ while (this.match(TokenType.PONTO)) {
985
+ const membroToken = this.consumeAny([
986
+ TokenType.IDENTIFICADOR,
987
+ TokenType.TIPO_ID, TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO,
988
+ TokenType.TIPO_DECIMAL, TokenType.TIPO_BOOLEANO, TokenType.TIPO_DATA,
989
+ TokenType.TIPO_HORA, TokenType.TIPO_LISTA, TokenType.TIPO_MAPA, TokenType.TIPO_OBJETO
990
+ ], "Esperado nome do membro");
991
+ if (this.match(TokenType.ABRE_PAREN)) {
992
+ const argumentos = [];
993
+ if (!this.check(TokenType.FECHA_PAREN)) {
994
+ do {
995
+ argumentos.push(this.parseExpressao());
996
+ } while (this.match(TokenType.VIRGULA));
997
+ }
998
+ this.consume(TokenType.FECHA_PAREN, "Esperado ')'");
999
+ objeto = { kind: 'AcessoMembro', line: membroToken.line, column: membroToken.column, objeto, membro: membroToken.value, chamada: argumentos };
1000
+ }
1001
+ else {
1002
+ objeto = { kind: 'AcessoMembro', line: membroToken.line, column: membroToken.column, objeto, membro: membroToken.value };
1003
+ }
1004
+ }
1005
+ return objeto;
1006
+ }
1007
+ return {
1008
+ kind: 'Identificador',
1009
+ line: token.line,
1010
+ column: token.column,
1011
+ nome: token.value
1012
+ };
1013
+ }
1014
+ throw this.error(`Expressão inesperada: ${this.peek().value}`);
1015
+ }
1016
+ // ── Utilitários ───────────────────────────────────────────
1017
+ peek() {
1018
+ return this.tokens[this.current];
1019
+ }
1020
+ previous() {
1021
+ return this.tokens[this.current - 1];
1022
+ }
1023
+ advance() {
1024
+ if (!this.isAtEnd())
1025
+ this.current++;
1026
+ return this.previous();
1027
+ }
1028
+ checkAny(types) {
1029
+ for (const type of types) {
1030
+ if (this.check(type))
1031
+ return true;
1032
+ }
1033
+ return false;
1034
+ }
1035
+ check(type) {
1036
+ if (this.isAtEnd())
1037
+ return false;
1038
+ return this.peek().type === type;
1039
+ }
1040
+ match(...types) {
1041
+ for (const type of types) {
1042
+ if (this.check(type)) {
1043
+ this.advance();
1044
+ return true;
1045
+ }
1046
+ }
1047
+ return false;
1048
+ }
1049
+ consume(type, message) {
1050
+ if (this.check(type))
1051
+ return this.advance();
1052
+ throw this.error(message);
1053
+ }
1054
+ consumeAny(types, message) {
1055
+ for (const type of types) {
1056
+ if (this.check(type))
1057
+ return this.advance();
1058
+ }
1059
+ throw this.error(message);
1060
+ }
1061
+ isAtEnd() {
1062
+ return this.peek().type === TokenType.EOF;
1063
+ }
1064
+ error(message, token) {
1065
+ const errorToken = token || this.peek();
1066
+ return {
1067
+ message,
1068
+ line: errorToken.line,
1069
+ column: errorToken.column
1070
+ };
1071
+ }
1072
+ synchronize() {
1073
+ this.advance();
1074
+ while (!this.isAtEnd()) {
1075
+ if (this.previous().type === TokenType.FIM)
1076
+ return;
1077
+ switch (this.peek().type) {
1078
+ case TokenType.CLASSE:
1079
+ case TokenType.ENTIDADE:
1080
+ case TokenType.SERVICO:
1081
+ case TokenType.FUNCAO:
1082
+ case TokenType.EVENTO:
1083
+ case TokenType.REGRA:
1084
+ case TokenType.INTERFACE:
1085
+ case TokenType.ENUM:
1086
+ case TokenType.TELA:
1087
+ case TokenType.MODULO:
1088
+ return;
1089
+ }
1090
+ this.advance();
1091
+ }
1092
+ }
1093
+ }
1094
+ //# sourceMappingURL=parser.js.map