@yakuzaa/jade-compiler 0.1.1 → 0.1.3
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.
- package/dist/cli.js +198 -29
- package/dist/cli.js.map +1 -1
- package/dist/codegen/ir_generator.d.ts +13 -0
- package/dist/codegen/ir_generator.d.ts.map +1 -1
- package/dist/codegen/ir_generator.js +167 -6
- package/dist/codegen/ir_generator.js.map +1 -1
- package/dist/codegen/ir_nodes.d.ts +19 -0
- package/dist/codegen/ir_nodes.d.ts.map +1 -1
- package/dist/codegen/wasm_generator.d.ts.map +1 -1
- package/dist/codegen/wasm_generator.js +0 -1
- package/dist/codegen/wasm_generator.js.map +1 -1
- package/dist/formatter/formatter.d.ts +48 -0
- package/dist/formatter/formatter.d.ts.map +1 -0
- package/dist/formatter/formatter.js +280 -0
- package/dist/formatter/formatter.js.map +1 -0
- package/dist/import_resolver.d.ts +15 -0
- package/dist/import_resolver.d.ts.map +1 -0
- package/dist/import_resolver.js +88 -0
- package/dist/import_resolver.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +116 -1
- package/dist/index.js.map +1 -1
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +15 -7
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/lexer/token_type.d.ts +3 -1
- package/dist/lexer/token_type.d.ts.map +1 -1
- package/dist/lexer/token_type.js +2 -0
- package/dist/lexer/token_type.js.map +1 -1
- package/dist/linter/linter.d.ts +46 -0
- package/dist/linter/linter.d.ts.map +1 -0
- package/dist/linter/linter.js +166 -0
- package/dist/linter/linter.js.map +1 -0
- package/dist/parser/parse_result.d.ts +2 -0
- package/dist/parser/parse_result.d.ts.map +1 -1
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +117 -21
- package/dist/parser/parser.js.map +1 -1
- package/dist/semantic/symbol_table.d.ts +1 -0
- package/dist/semantic/symbol_table.d.ts.map +1 -1
- package/dist/semantic/symbol_table.js +11 -0
- package/dist/semantic/symbol_table.js.map +1 -1
- package/dist/semantic/type_checker.d.ts +7 -0
- package/dist/semantic/type_checker.d.ts.map +1 -1
- package/dist/semantic/type_checker.js +187 -72
- package/dist/semantic/type_checker.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linter JADE — analisa o AST e reporta problemas de estilo e qualidade.
|
|
3
|
+
*
|
|
4
|
+
* Regras implementadas:
|
|
5
|
+
* NOME001 Entidade/Classe/Evento/Interface deve ser PascalCase
|
|
6
|
+
* NOME002 Função/Serviço deve iniciar com letra minúscula
|
|
7
|
+
* NOME003 Valores de enum devem ser UPPER_CASE
|
|
8
|
+
* NOME004 Variável deve iniciar com letra minúscula
|
|
9
|
+
* VAZIO001 Função com corpo vazio
|
|
10
|
+
* VAZIO002 Entidade sem campos
|
|
11
|
+
* VAZIO003 Serviço sem métodos nem ouvintes
|
|
12
|
+
* PARAM001 Função com mais de 5 parâmetros
|
|
13
|
+
* VAR001 Variável declarada sem tipo nem inicializador
|
|
14
|
+
* MORT001 Código morto — instrução após retornar
|
|
15
|
+
*/
|
|
16
|
+
export class Linter {
|
|
17
|
+
warnings = [];
|
|
18
|
+
lint(program) {
|
|
19
|
+
this.warnings = [];
|
|
20
|
+
for (const decl of program.declaracoes) {
|
|
21
|
+
this.checkDeclaracao(decl);
|
|
22
|
+
}
|
|
23
|
+
return this.warnings;
|
|
24
|
+
}
|
|
25
|
+
// ── Declarações ──────────────────────────────────────────────────────────
|
|
26
|
+
checkDeclaracao(node) {
|
|
27
|
+
switch (node.kind) {
|
|
28
|
+
case 'Entidade':
|
|
29
|
+
this.checkEntidade(node);
|
|
30
|
+
break;
|
|
31
|
+
case 'Classe':
|
|
32
|
+
this.checkClasse(node);
|
|
33
|
+
break;
|
|
34
|
+
case 'Servico':
|
|
35
|
+
this.checkServico(node);
|
|
36
|
+
break;
|
|
37
|
+
case 'Funcao':
|
|
38
|
+
this.checkFuncao(node);
|
|
39
|
+
break;
|
|
40
|
+
case 'Evento':
|
|
41
|
+
this.checkEvento(node);
|
|
42
|
+
break;
|
|
43
|
+
case 'Interface':
|
|
44
|
+
this.checkInterface(node);
|
|
45
|
+
break;
|
|
46
|
+
case 'Enum':
|
|
47
|
+
this.checkEnum(node);
|
|
48
|
+
break;
|
|
49
|
+
case 'Modulo':
|
|
50
|
+
for (const d of node.declaracoes)
|
|
51
|
+
this.checkDeclaracao(d);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
checkEntidade(node) {
|
|
56
|
+
this.assertPascalCase(node.nome, 'NOME001', 'Entidade', node.line, node.column);
|
|
57
|
+
if (node.campos.length === 0) {
|
|
58
|
+
this.warn('VAZIO002', `Entidade '${node.nome}' não tem campos`, node.line, node.column);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
checkClasse(node) {
|
|
62
|
+
this.assertPascalCase(node.nome, 'NOME001', 'Classe', node.line, node.column);
|
|
63
|
+
for (const metodo of node.metodos)
|
|
64
|
+
this.checkFuncao(metodo);
|
|
65
|
+
}
|
|
66
|
+
checkServico(node) {
|
|
67
|
+
this.assertCamelCase(node.nome, 'NOME002', 'Serviço', node.line, node.column);
|
|
68
|
+
if (node.metodos.length === 0 && node.ouvintes.length === 0) {
|
|
69
|
+
this.warn('VAZIO003', `Serviço '${node.nome}' não tem métodos nem ouvintes`, node.line, node.column);
|
|
70
|
+
}
|
|
71
|
+
for (const metodo of node.metodos)
|
|
72
|
+
this.checkFuncao(metodo);
|
|
73
|
+
for (const ouvinte of node.ouvintes)
|
|
74
|
+
this.checkBloco(ouvinte.corpo);
|
|
75
|
+
}
|
|
76
|
+
checkFuncao(node) {
|
|
77
|
+
this.assertCamelCase(node.nome, 'NOME002', 'Função', node.line, node.column);
|
|
78
|
+
if (node.parametros.length > 5) {
|
|
79
|
+
this.warn('PARAM001', `Função '${node.nome}' tem ${node.parametros.length} parâmetros — considere agrupar em uma entidade`, node.line, node.column);
|
|
80
|
+
}
|
|
81
|
+
if (node.corpo.instrucoes.length === 0) {
|
|
82
|
+
this.warn('VAZIO001', `Função '${node.nome}' tem corpo vazio`, node.line, node.column);
|
|
83
|
+
}
|
|
84
|
+
this.checkBloco(node.corpo);
|
|
85
|
+
}
|
|
86
|
+
checkEvento(node) {
|
|
87
|
+
this.assertPascalCase(node.nome, 'NOME001', 'Evento', node.line, node.column);
|
|
88
|
+
}
|
|
89
|
+
checkInterface(node) {
|
|
90
|
+
this.assertPascalCase(node.nome, 'NOME001', 'Interface', node.line, node.column);
|
|
91
|
+
}
|
|
92
|
+
checkEnum(node) {
|
|
93
|
+
this.assertPascalCase(node.nome, 'NOME001', 'Enum', node.line, node.column);
|
|
94
|
+
for (const valor of node.valores) {
|
|
95
|
+
if (!this.isUpperCase(valor)) {
|
|
96
|
+
this.warn('NOME003', `Valor de enum '${valor}' deve ser UPPER_CASE (ex: ${valor.toUpperCase()})`, node.line, node.column);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// ── Bloco e instruções ───────────────────────────────────────────────────
|
|
101
|
+
checkBloco(bloco) {
|
|
102
|
+
const instrucoes = bloco.instrucoes;
|
|
103
|
+
for (let i = 0; i < instrucoes.length; i++) {
|
|
104
|
+
const instr = instrucoes[i];
|
|
105
|
+
this.checkInstrucao(instr);
|
|
106
|
+
// MORT001 — código morto após retornar
|
|
107
|
+
if (instr.kind === 'Retorno' && i < instrucoes.length - 1) {
|
|
108
|
+
const proxima = instrucoes[i + 1];
|
|
109
|
+
this.warn('MORT001', 'Código morto — instrução após retornar nunca será executada', proxima.line, proxima.column, 'error');
|
|
110
|
+
break; // não reporta múltiplos por bloco
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
checkInstrucao(node) {
|
|
115
|
+
switch (node.kind) {
|
|
116
|
+
case 'Variavel':
|
|
117
|
+
this.assertCamelCase(node.nome, 'NOME004', 'Variável', node.line, node.column);
|
|
118
|
+
if (!node.tipo && !node.inicializador) {
|
|
119
|
+
this.warn('VAR001', `Variável '${node.nome}' declarada sem tipo nem valor inicial`, node.line, node.column);
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
case 'Condicional':
|
|
123
|
+
this.checkBloco(node.entao);
|
|
124
|
+
if (node.senao)
|
|
125
|
+
this.checkBloco(node.senao);
|
|
126
|
+
break;
|
|
127
|
+
case 'Enquanto':
|
|
128
|
+
this.checkBloco(node.corpo);
|
|
129
|
+
break;
|
|
130
|
+
case 'Para':
|
|
131
|
+
this.checkBloco(node.corpo);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// ── Helpers de nomenclatura ──────────────────────────────────────────────
|
|
136
|
+
assertPascalCase(nome, code, tipo, line, col) {
|
|
137
|
+
if (!this.isPascalCase(nome)) {
|
|
138
|
+
this.warn(code, `${tipo} '${nome}' deve usar PascalCase (ex: ${this.toPascalCase(nome)})`, line, col);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
assertCamelCase(nome, code, tipo, line, col) {
|
|
142
|
+
if (!this.isCamelCase(nome)) {
|
|
143
|
+
this.warn(code, `${tipo} '${nome}' deve iniciar com letra minúscula (ex: ${this.toCamelCase(nome)})`, line, col);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
isPascalCase(nome) {
|
|
147
|
+
return /^[A-Z][a-zA-Z0-9]*$/.test(nome);
|
|
148
|
+
}
|
|
149
|
+
isCamelCase(nome) {
|
|
150
|
+
return /^[a-z][a-zA-Z0-9]*$/.test(nome);
|
|
151
|
+
}
|
|
152
|
+
isUpperCase(nome) {
|
|
153
|
+
return /^[A-Z][A-Z0-9_]*$/.test(nome);
|
|
154
|
+
}
|
|
155
|
+
toPascalCase(nome) {
|
|
156
|
+
return nome.charAt(0).toUpperCase() + nome.slice(1);
|
|
157
|
+
}
|
|
158
|
+
toCamelCase(nome) {
|
|
159
|
+
return nome.charAt(0).toLowerCase() + nome.slice(1);
|
|
160
|
+
}
|
|
161
|
+
// ── Emissão ──────────────────────────────────────────────────────────────
|
|
162
|
+
warn(code, message, line, column, severity = 'warning') {
|
|
163
|
+
this.warnings.push({ code, message, line, column, severity });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=linter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linter.js","sourceRoot":"","sources":["../../linter/linter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse_result.d.ts","sourceRoot":"","sources":["../../parser/parse_result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"parse_result.d.ts","sourceRoot":"","sources":["../../parser/parse_result.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../parser/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../parser/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAc,MAAM,mBAAmB,CAAC;AA8G5D,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,MAAM,CAAoB;gBAEtB,MAAM,EAAE,KAAK,EAAE;IAI3B,KAAK,IAAI,WAAW;IAgDpB,OAAO,CAAC,eAAe;IAoBvB,OAAO,CAAC,WAAW;IA+BnB,OAAO,CAAC,WAAW;IAyCnB,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,WAAW;IAkCnB,OAAO,CAAC,WAAW;IAwBnB,OAAO,CAAC,UAAU;IA0BlB,OAAO,CAAC,cAAc;IA2CtB,OAAO,CAAC,SAAS;IAqBjB,OAAO,CAAC,eAAe;IA6BvB,OAAO,CAAC,SAAS;IA4BjB,OAAO,CAAC,iBAAiB;IA2EzB,OAAO,CAAC,UAAU;IA4BlB,OAAO,CAAC,cAAc;IA4BtB,OAAO,CAAC,SAAS;IA+DjB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,UAAU;IAgClB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,aAAa;IAwBrB,OAAO,CAAC,wBAAwB;IA4GhC,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,gBAAgB;IAgCxB,OAAO,CAAC,sBAAsB;IA6B9B,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,SAAS;IAkBjB,OAAO,CAAC,kBAAkB;IAsB1B,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,oBAAoB;IAsB5B,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,wBAAwB;IAsBhC,OAAO,CAAC,wBAAwB;IAoBhC,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,UAAU;IAiBlB,OAAO,CAAC,aAAa;IAkIrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,WAAW;CAuBpB"}
|
package/dist/parser/parser.js
CHANGED
|
@@ -1,4 +1,60 @@
|
|
|
1
1
|
import { TokenType } from '../lexer/token_type.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tabela de dicas educativas para erros de sintaxe.
|
|
4
|
+
* Mapeamento: mensagem de erro → dica de como corrigir.
|
|
5
|
+
*/
|
|
6
|
+
const DICAS_PARSER = {
|
|
7
|
+
"Esperado nome do módulo": "Dê um nome ao módulo logo após a palavra 'modulo', ex: `modulo estoque`",
|
|
8
|
+
"Esperado 'fim' para fechar módulo": "Todo bloco 'modulo' precisa terminar com 'fim'",
|
|
9
|
+
"Esperado nome da classe": "Dê um nome à classe, ex: `classe Funcionario`",
|
|
10
|
+
"Esperado nome da superclasse": "Informe o nome da classe pai após 'extends', ex: `classe Gerente extends Funcionario`",
|
|
11
|
+
"Esperado nome da interface": "Informe o nome da interface após 'implements', ex: `classe Produto implements Estocavel`",
|
|
12
|
+
"Esperado 'fim' para fechar classe": "Todo bloco 'classe' precisa terminar com 'fim'",
|
|
13
|
+
"Esperado nome da entidade": "Dê um nome à entidade, ex: `entidade Produto`",
|
|
14
|
+
"Esperado 'fim' para fechar entidade": "Todo bloco 'entidade' precisa terminar com 'fim'",
|
|
15
|
+
"Esperado nome do serviço": "Dê um nome ao serviço, ex: `servico EstoqueService`",
|
|
16
|
+
"Esperado 'funcao' ou 'escutar' no serviço": "Dentro de um serviço só pode haver funções (`funcao nome()`) e ouvintes de evento (`escutar NomeEvento`)",
|
|
17
|
+
"Esperado 'fim' para fechar serviço": "Todo bloco 'servico' precisa terminar com 'fim'",
|
|
18
|
+
"Esperado nome da função": "Dê um nome à função, ex: `funcao calcularTotal()`",
|
|
19
|
+
"Esperado '('": "A declaração de função precisa de parênteses, mesmo sem parâmetros: `funcao nome()`",
|
|
20
|
+
"Esperado ')'": "Feche os parênteses — pode estar faltando ')' após os parâmetros ou argumentos",
|
|
21
|
+
"Esperado 'fim' para fechar função": "Todo bloco 'funcao' precisa terminar com 'fim'",
|
|
22
|
+
"Esperado nome do evento": "Dê um nome ao evento, ex: `evento PedidoCriado`",
|
|
23
|
+
"Esperado 'fim' para fechar evento": "Todo bloco 'evento' precisa terminar com 'fim'",
|
|
24
|
+
"Esperado nome da regra": "Dê um nome à regra, ex: `regra reposicaoAutomatica`",
|
|
25
|
+
"Esperado 'quando'": "A regra precisa de uma condição após o nome: `regra nome quando produto.estoque < 10`",
|
|
26
|
+
"Esperado 'entao'": "Após a condição do 'quando', use 'entao' para definir o que acontece: `quando x > 0 entao`",
|
|
27
|
+
"Esperado 'fim' para fechar regra": "Todo bloco 'regra' precisa terminar com 'fim'",
|
|
28
|
+
"Esperado 'funcao' na interface": "Interfaces só podem declarar assinaturas de função: `funcao nome(param: tipo) -> tipo`",
|
|
29
|
+
"Esperado nome da assinatura": "Dê um nome à assinatura da função na interface, ex: `funcao calcular(x: numero) -> numero`",
|
|
30
|
+
"Esperado '->'": "Informe o tipo de retorno após '->', ex: `funcao calcular() -> numero`",
|
|
31
|
+
"Esperado 'fim' para fechar interface": "Todo bloco 'interface' precisa terminar com 'fim'",
|
|
32
|
+
"Esperado nome do enum": "Dê um nome ao enum, ex: `enum StatusPedido`",
|
|
33
|
+
"Esperado valor do enum": "Liste os valores do enum, um por linha, em letras maiúsculas, ex: PENDENTE, CONFIRMADO, CANCELADO",
|
|
34
|
+
"Esperado 'fim' para fechar enum": "Todo bloco 'enum' precisa terminar com 'fim'",
|
|
35
|
+
"Esperado item importado": "Informe o item do módulo: `importar estoque.Produto`, ou use alias: `importar estoque como est`",
|
|
36
|
+
"Esperado alias": "Informe o nome alternativo após 'como', ex: `importar estoque como est`",
|
|
37
|
+
"Esperado nome da tela": "Dê um nome à tela, ex: `tela ListaProdutos \"Produtos\"`",
|
|
38
|
+
"Esperado título da tela entre aspas": "Informe o título da tela entre aspas logo após o nome: `tela ListaProdutos \"Lista de Produtos\"`",
|
|
39
|
+
"Esperado 'fim' para fechar tela": "Todo bloco 'tela' precisa terminar com 'fim'",
|
|
40
|
+
"Esperado tipo do elemento (tabela, formulario, botao, card)": "Use um dos tipos de elemento: tabela, formulario, botao, card, modal ou grafico",
|
|
41
|
+
"Esperado nome do elemento": "Dê um nome ao elemento da tela, ex: `tabela listagem`",
|
|
42
|
+
"Esperado nome da propriedade": "Informe o nome da propriedade seguido de ':', ex: `entidade: Produto`",
|
|
43
|
+
"Esperado ':' após nome da propriedade": "Use ':' para separar nome e valor da propriedade, ex: `titulo: \"Meu Título\"`",
|
|
44
|
+
"Esperado identificador": "Informe um nome válido (começa com letra, sem espaços)",
|
|
45
|
+
"Esperado valor para a propriedade": "O valor pode ser texto entre aspas, um identificador ou verdadeiro/falso",
|
|
46
|
+
"Esperado nome do campo": "O campo precisa de um nome seguido de ':', ex: `nome: texto`",
|
|
47
|
+
"Esperado ':'": "Use ':' para declarar o tipo do campo, ex: `preco: decimal`",
|
|
48
|
+
"Esperado '<'": "Especifique o tipo do elemento entre '<' e '>', ex: `lista<texto>` ou `lista<Produto>`",
|
|
49
|
+
"Esperado '>'": "Feche o tipo genérico com '>', ex: `lista<texto>` ou `mapa<texto, numero>`",
|
|
50
|
+
"Esperado tipo": "Informe um tipo válido: texto, numero, decimal, booleano, data, hora, id, lista<T> ou mapa<K,V>",
|
|
51
|
+
"Esperado nome da variável": "Dê um nome à variável, ex: `variavel total: numero` ou `variavel nome = \"João\"`",
|
|
52
|
+
"Esperado nome do membro após '.'": "Informe o nome do campo ou método após o ponto, ex: `produto.preco`",
|
|
53
|
+
"Esperado ')' após expressão": "Feche os parênteses da expressão",
|
|
54
|
+
"Esperado ')' após argumentos": "Feche os parênteses dos argumentos da função",
|
|
55
|
+
"Esperado nome do membro": "Informe o nome do campo ou método após o ponto",
|
|
56
|
+
"Esperado ','": "Separe os tipos com vírgula, ex: `mapa<texto, numero>`",
|
|
57
|
+
};
|
|
2
58
|
export class Parser {
|
|
3
59
|
tokens;
|
|
4
60
|
current = 0;
|
|
@@ -10,17 +66,33 @@ export class Parser {
|
|
|
10
66
|
const declaracoes = [];
|
|
11
67
|
while (!this.isAtEnd()) {
|
|
12
68
|
try {
|
|
69
|
+
const before = this.current;
|
|
13
70
|
const declaracao = this.parseDeclaracao();
|
|
14
71
|
if (declaracao) {
|
|
15
72
|
declaracoes.push(declaracao);
|
|
16
73
|
}
|
|
74
|
+
else if (this.current === before) {
|
|
75
|
+
// Token não reconhecido como declaração — registra erro e avança para não travar
|
|
76
|
+
this.errors.push({
|
|
77
|
+
message: `Token inesperado '${this.peek().value}' — esperado início de declaração (modulo, entidade, funcao, servico, etc.)`,
|
|
78
|
+
line: this.peek().line,
|
|
79
|
+
column: this.peek().column
|
|
80
|
+
});
|
|
81
|
+
this.advance();
|
|
82
|
+
}
|
|
17
83
|
}
|
|
18
84
|
catch (error) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
85
|
+
// ParseError thrown by consume/error() already has line, column and dica
|
|
86
|
+
if (error && typeof error.line === 'number' && typeof error.column === 'number') {
|
|
87
|
+
this.errors.push(error);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
this.errors.push({
|
|
91
|
+
message: error.message || 'Erro de sintaxe',
|
|
92
|
+
line: this.peek().line,
|
|
93
|
+
column: this.peek().column
|
|
94
|
+
});
|
|
95
|
+
}
|
|
24
96
|
this.synchronize();
|
|
25
97
|
}
|
|
26
98
|
}
|
|
@@ -72,9 +144,15 @@ export class Parser {
|
|
|
72
144
|
const nome = nomeToken.value;
|
|
73
145
|
const declaracoes = [];
|
|
74
146
|
while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
|
|
147
|
+
const before = this.current;
|
|
75
148
|
const declaracao = this.parseDeclaracao();
|
|
76
|
-
if (declaracao)
|
|
149
|
+
if (declaracao) {
|
|
77
150
|
declaracoes.push(declaracao);
|
|
151
|
+
}
|
|
152
|
+
else if (this.current === before) {
|
|
153
|
+
// Token não reconhecido dentro do módulo — lança erro para sair do loop
|
|
154
|
+
throw this.error(`Token inesperado '${this.peek().value}' dentro do módulo`, undefined, "Verifique se uma declaração está incompleta ou se há 'fim' faltando");
|
|
155
|
+
}
|
|
78
156
|
}
|
|
79
157
|
this.consume(TokenType.FIM, "Esperado 'fim' para fechar módulo");
|
|
80
158
|
return {
|
|
@@ -125,7 +203,11 @@ export class Parser {
|
|
|
125
203
|
const nome = nomeToken.value;
|
|
126
204
|
const campos = [];
|
|
127
205
|
while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
|
|
206
|
+
const before = this.current;
|
|
128
207
|
campos.push(this.parseCampo());
|
|
208
|
+
if (this.current === before) {
|
|
209
|
+
throw this.error(`Token inesperado '${this.peek().value}' na entidade — esperado nome de campo`);
|
|
210
|
+
}
|
|
129
211
|
}
|
|
130
212
|
this.consume(TokenType.FIM, "Esperado 'fim' para fechar entidade");
|
|
131
213
|
return {
|
|
@@ -194,7 +276,11 @@ export class Parser {
|
|
|
194
276
|
const nome = nomeToken.value;
|
|
195
277
|
const campos = [];
|
|
196
278
|
while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
|
|
279
|
+
const before = this.current;
|
|
197
280
|
campos.push(this.parseCampo());
|
|
281
|
+
if (this.current === before) {
|
|
282
|
+
throw this.error(`Token inesperado '${this.peek().value}' no evento — esperado nome de campo`);
|
|
283
|
+
}
|
|
198
284
|
}
|
|
199
285
|
this.consume(TokenType.FIM, "Esperado 'fim' para fechar evento");
|
|
200
286
|
return {
|
|
@@ -311,10 +397,14 @@ export class Parser {
|
|
|
311
397
|
const nomeToken = this.consume(TokenType.IDENTIFICADOR, "Esperado nome da tela");
|
|
312
398
|
const nome = nomeToken.value;
|
|
313
399
|
const tituloToken = this.consume(TokenType.LITERAL_TEXTO, "Esperado título da tela entre aspas");
|
|
314
|
-
const titulo = tituloToken.value;
|
|
400
|
+
const titulo = tituloToken.value.slice(1, -1);
|
|
315
401
|
const elementos = [];
|
|
316
402
|
while (!this.check(TokenType.FIM) && !this.isAtEnd()) {
|
|
403
|
+
const before = this.current;
|
|
317
404
|
elementos.push(this.parseTelaElemento());
|
|
405
|
+
if (this.current === before) {
|
|
406
|
+
throw this.error(`Token inesperado '${this.peek().value}' na tela — esperado elemento (tabela, formulario, botao, card)`);
|
|
407
|
+
}
|
|
318
408
|
}
|
|
319
409
|
this.consume(TokenType.FIM, "Esperado 'fim' para fechar tela");
|
|
320
410
|
return {
|
|
@@ -354,7 +444,7 @@ export class Parser {
|
|
|
354
444
|
}
|
|
355
445
|
else if (this.checkAny([
|
|
356
446
|
TokenType.IDENTIFICADOR,
|
|
357
|
-
TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO, TokenType.TIPO_DECIMAL,
|
|
447
|
+
TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO, TokenType.TIPO_DECIMAL, TokenType.TIPO_MOEDA,
|
|
358
448
|
TokenType.TIPO_BOOLEANO, TokenType.TIPO_DATA, TokenType.TIPO_HORA,
|
|
359
449
|
TokenType.TIPO_ID, TokenType.TIPO_LISTA, TokenType.TIPO_MAPA
|
|
360
450
|
])) {
|
|
@@ -373,7 +463,7 @@ export class Parser {
|
|
|
373
463
|
while (this.match(TokenType.VIRGULA)) {
|
|
374
464
|
lista.push(this.consumeAny([
|
|
375
465
|
TokenType.IDENTIFICADOR,
|
|
376
|
-
TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO, TokenType.TIPO_DECIMAL,
|
|
466
|
+
TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO, TokenType.TIPO_DECIMAL, TokenType.TIPO_MOEDA,
|
|
377
467
|
TokenType.TIPO_BOOLEANO, TokenType.TIPO_DATA, TokenType.TIPO_HORA,
|
|
378
468
|
TokenType.TIPO_ID
|
|
379
469
|
], "Esperado identificador").value);
|
|
@@ -406,6 +496,7 @@ export class Parser {
|
|
|
406
496
|
TokenType.TIPO_TEXTO,
|
|
407
497
|
TokenType.TIPO_NUMERO,
|
|
408
498
|
TokenType.TIPO_DECIMAL,
|
|
499
|
+
TokenType.TIPO_MOEDA,
|
|
409
500
|
TokenType.TIPO_BOOLEANO,
|
|
410
501
|
TokenType.TIPO_DATA,
|
|
411
502
|
TokenType.TIPO_HORA,
|
|
@@ -431,6 +522,7 @@ export class Parser {
|
|
|
431
522
|
TokenType.TIPO_TEXTO,
|
|
432
523
|
TokenType.TIPO_NUMERO,
|
|
433
524
|
TokenType.TIPO_DECIMAL,
|
|
525
|
+
TokenType.TIPO_MOEDA,
|
|
434
526
|
TokenType.TIPO_BOOLEANO,
|
|
435
527
|
TokenType.TIPO_DATA,
|
|
436
528
|
TokenType.TIPO_HORA,
|
|
@@ -481,6 +573,7 @@ export class Parser {
|
|
|
481
573
|
TokenType.TIPO_TEXTO,
|
|
482
574
|
TokenType.TIPO_NUMERO,
|
|
483
575
|
TokenType.TIPO_DECIMAL,
|
|
576
|
+
TokenType.TIPO_MOEDA,
|
|
484
577
|
TokenType.TIPO_BOOLEANO,
|
|
485
578
|
TokenType.TIPO_DATA,
|
|
486
579
|
TokenType.TIPO_HORA,
|
|
@@ -531,7 +624,7 @@ export class Parser {
|
|
|
531
624
|
if (this.isAtEnd()) {
|
|
532
625
|
break;
|
|
533
626
|
}
|
|
534
|
-
throw this.error(`
|
|
627
|
+
throw this.error(`Token inesperado '${this.peek().value}' — verifique se um 'fim' está faltando acima desta linha`, undefined, "Erros desse tipo costumam acontecer quando um bloco (entidade, funcao, servico, se...) não foi fechado com 'fim'");
|
|
535
628
|
}
|
|
536
629
|
}
|
|
537
630
|
return {
|
|
@@ -675,18 +768,20 @@ export class Parser {
|
|
|
675
768
|
}
|
|
676
769
|
// Token consumido mas não reconhecido como instrução válida
|
|
677
770
|
// Volta o token para não perder (usando current--)
|
|
678
|
-
this.current
|
|
771
|
+
if (this.current > 0)
|
|
772
|
+
this.current--;
|
|
679
773
|
return null;
|
|
680
774
|
}
|
|
681
775
|
parseRetorno() {
|
|
776
|
+
const retornarToken = this.previous(); // 'retornar' já consumido por match()
|
|
682
777
|
let valor;
|
|
683
778
|
if (!this.check(TokenType.FIM) && !this.check(TokenType.EOF)) {
|
|
684
779
|
valor = this.parseExpressao();
|
|
685
780
|
}
|
|
686
781
|
return {
|
|
687
782
|
kind: 'Retorno',
|
|
688
|
-
line:
|
|
689
|
-
column:
|
|
783
|
+
line: retornarToken.line,
|
|
784
|
+
column: retornarToken.column,
|
|
690
785
|
valor
|
|
691
786
|
};
|
|
692
787
|
}
|
|
@@ -985,7 +1080,7 @@ export class Parser {
|
|
|
985
1080
|
const membroToken = this.consumeAny([
|
|
986
1081
|
TokenType.IDENTIFICADOR,
|
|
987
1082
|
TokenType.TIPO_ID, TokenType.TIPO_TEXTO, TokenType.TIPO_NUMERO,
|
|
988
|
-
TokenType.TIPO_DECIMAL, TokenType.TIPO_BOOLEANO, TokenType.TIPO_DATA,
|
|
1083
|
+
TokenType.TIPO_DECIMAL, TokenType.TIPO_MOEDA, TokenType.TIPO_BOOLEANO, TokenType.TIPO_DATA,
|
|
989
1084
|
TokenType.TIPO_HORA, TokenType.TIPO_LISTA, TokenType.TIPO_MAPA, TokenType.TIPO_OBJETO
|
|
990
1085
|
], "Esperado nome do membro");
|
|
991
1086
|
if (this.match(TokenType.ABRE_PAREN)) {
|
|
@@ -1011,7 +1106,7 @@ export class Parser {
|
|
|
1011
1106
|
nome: token.value
|
|
1012
1107
|
};
|
|
1013
1108
|
}
|
|
1014
|
-
throw this.error(`Expressão inesperada: ${this.peek().value}
|
|
1109
|
+
throw this.error(`Expressão inesperada: '${this.peek().value}'`, undefined, "Use um valor (número, texto, verdadeiro/falso), uma variável, chamada de função ou expressão entre parênteses");
|
|
1015
1110
|
}
|
|
1016
1111
|
// ── Utilitários ───────────────────────────────────────────
|
|
1017
1112
|
peek() {
|
|
@@ -1046,27 +1141,28 @@ export class Parser {
|
|
|
1046
1141
|
}
|
|
1047
1142
|
return false;
|
|
1048
1143
|
}
|
|
1049
|
-
consume(type, message) {
|
|
1144
|
+
consume(type, message, dica) {
|
|
1050
1145
|
if (this.check(type))
|
|
1051
1146
|
return this.advance();
|
|
1052
|
-
throw this.error(message);
|
|
1147
|
+
throw this.error(message, undefined, dica);
|
|
1053
1148
|
}
|
|
1054
|
-
consumeAny(types, message) {
|
|
1149
|
+
consumeAny(types, message, dica) {
|
|
1055
1150
|
for (const type of types) {
|
|
1056
1151
|
if (this.check(type))
|
|
1057
1152
|
return this.advance();
|
|
1058
1153
|
}
|
|
1059
|
-
throw this.error(message);
|
|
1154
|
+
throw this.error(message, undefined, dica);
|
|
1060
1155
|
}
|
|
1061
1156
|
isAtEnd() {
|
|
1062
1157
|
return this.peek().type === TokenType.EOF;
|
|
1063
1158
|
}
|
|
1064
|
-
error(message, token) {
|
|
1159
|
+
error(message, token, dica) {
|
|
1065
1160
|
const errorToken = token || this.peek();
|
|
1066
1161
|
return {
|
|
1067
1162
|
message,
|
|
1068
1163
|
line: errorToken.line,
|
|
1069
|
-
column: errorToken.column
|
|
1164
|
+
column: errorToken.column,
|
|
1165
|
+
dica: dica ?? DICAS_PARSER[message]
|
|
1070
1166
|
};
|
|
1071
1167
|
}
|
|
1072
1168
|
synchronize() {
|