@semacode/cli 1.5.28 → 1.5.30

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 (125) hide show
  1. package/README.md +144 -144
  2. package/dist/bridge.d.ts +52 -0
  3. package/dist/bridge.js +318 -0
  4. package/dist/bridge.js.map +1 -0
  5. package/dist/comandos.d.ts +11 -0
  6. package/dist/comandos.js +146 -0
  7. package/dist/comandos.js.map +1 -0
  8. package/dist/contexto.d.ts +34 -0
  9. package/dist/contexto.js +197 -0
  10. package/dist/contexto.js.map +1 -0
  11. package/dist/drift.d.ts +1 -1
  12. package/dist/drift.js +32 -5
  13. package/dist/drift.js.map +1 -1
  14. package/dist/guard.d.ts +35 -0
  15. package/dist/guard.js +164 -0
  16. package/dist/guard.js.map +1 -0
  17. package/dist/index.js +391 -64
  18. package/dist/index.js.map +1 -1
  19. package/dist/lua-symbols.d.ts +0 -6
  20. package/dist/lua-symbols.js +11 -78
  21. package/dist/lua-symbols.js.map +1 -1
  22. package/dist/projeto.js +6 -0
  23. package/dist/projeto.js.map +1 -1
  24. package/dist/tipos.d.ts +1 -1
  25. package/exemplos/.prepack-generated +1 -0
  26. package/exemplos/author_obra_comum.sema +294 -294
  27. package/exemplos/author_tema_sensivel.sema +264 -264
  28. package/exemplos/profile_game.sema +114 -114
  29. package/exemplos/profile_legal.sema +105 -105
  30. package/exemplos/profile_ops.sema +110 -110
  31. package/exemplos/profile_research.sema +104 -104
  32. package/exemplos/profile_software.sema +123 -123
  33. package/exemplos/profile_workflow_n8n.sema +99 -99
  34. package/node_modules/@sema/gerador-css/package.json +14 -7
  35. package/node_modules/@sema/gerador-css/src/index.ts +605 -0
  36. package/node_modules/@sema/gerador-css/tsconfig.json +13 -0
  37. package/node_modules/@sema/gerador-css/tsconfig.tsbuildinfo +1 -0
  38. package/node_modules/@sema/gerador-dart/package.json +14 -7
  39. package/node_modules/@sema/gerador-dart/src/index.ts +52 -0
  40. package/node_modules/@sema/gerador-dart/tsconfig.json +13 -0
  41. package/node_modules/@sema/gerador-dart/tsconfig.tsbuildinfo +1 -0
  42. package/node_modules/@sema/gerador-html/package.json +14 -7
  43. package/node_modules/@sema/gerador-html/src/index.ts +185 -0
  44. package/node_modules/@sema/gerador-html/tsconfig.json +13 -0
  45. package/node_modules/@sema/gerador-html/tsconfig.tsbuildinfo +1 -0
  46. package/node_modules/@sema/gerador-javascript/package.json +14 -7
  47. package/node_modules/@sema/gerador-javascript/src/index.ts +461 -0
  48. package/node_modules/@sema/gerador-javascript/tsconfig.json +13 -0
  49. package/node_modules/@sema/gerador-javascript/tsconfig.tsbuildinfo +1 -0
  50. package/node_modules/@sema/gerador-lua/package.json +14 -7
  51. package/node_modules/@sema/gerador-lua/src/index.ts +359 -0
  52. package/node_modules/@sema/gerador-lua/tsconfig.json +13 -0
  53. package/node_modules/@sema/gerador-lua/tsconfig.tsbuildinfo +1 -0
  54. package/node_modules/@sema/gerador-python/package.json +14 -7
  55. package/node_modules/@sema/gerador-python/src/index.ts +850 -0
  56. package/node_modules/@sema/gerador-python/tsconfig.json +13 -0
  57. package/node_modules/@sema/gerador-python/tsconfig.tsbuildinfo +1 -0
  58. package/node_modules/@sema/gerador-typescript/package.json +14 -7
  59. package/node_modules/@sema/gerador-typescript/src/index.ts +876 -0
  60. package/node_modules/@sema/gerador-typescript/tsconfig.json +13 -0
  61. package/node_modules/@sema/gerador-typescript/tsconfig.tsbuildinfo +1 -0
  62. package/node_modules/@sema/nucleo/dist/ast/tipos.d.ts +1 -1
  63. package/node_modules/@sema/nucleo/dist/index.d.ts +17 -0
  64. package/node_modules/@sema/nucleo/dist/index.js +28 -0
  65. package/node_modules/@sema/nucleo/dist/index.js.map +1 -1
  66. package/node_modules/@sema/nucleo/dist/ir/conversor.js +4 -0
  67. package/node_modules/@sema/nucleo/dist/ir/conversor.js.map +1 -1
  68. package/node_modules/@sema/nucleo/dist/ir/modelos.d.ts +3 -3
  69. package/node_modules/@sema/nucleo/dist/parser/parser.js +2 -0
  70. package/node_modules/@sema/nucleo/dist/parser/parser.js.map +1 -1
  71. package/node_modules/@sema/nucleo/dist/semantico/analisador.d.ts +2 -2
  72. package/node_modules/@sema/nucleo/dist/semantico/analisador.js +3 -1
  73. package/node_modules/@sema/nucleo/dist/semantico/analisador.js.map +1 -1
  74. package/node_modules/@sema/nucleo/package.json +10 -7
  75. package/node_modules/@sema/nucleo/src/ast/tipos.ts +207 -0
  76. package/node_modules/@sema/nucleo/src/diagnosticos/index.ts +43 -0
  77. package/node_modules/@sema/nucleo/src/formatador/index.ts +530 -0
  78. package/node_modules/@sema/nucleo/src/index.ts +183 -0
  79. package/node_modules/@sema/nucleo/src/ir/conversor.ts +1037 -0
  80. package/node_modules/@sema/nucleo/src/ir/modelos.ts +403 -0
  81. package/node_modules/@sema/nucleo/src/lexer/lexer.ts +166 -0
  82. package/node_modules/@sema/nucleo/src/lexer/tokens.ts +79 -0
  83. package/node_modules/@sema/nucleo/src/parser/gramatica.ebnf +41 -0
  84. package/node_modules/@sema/nucleo/src/parser/parser.ts +936 -0
  85. package/node_modules/@sema/nucleo/src/persistencia/contratos.ts +379 -0
  86. package/node_modules/@sema/nucleo/src/semantico/analisador.ts +3126 -0
  87. package/node_modules/@sema/nucleo/src/semantico/estruturas.ts +665 -0
  88. package/node_modules/@sema/nucleo/src/semantico/seguranca.ts +362 -0
  89. package/node_modules/@sema/nucleo/src/util/arquivos.ts +28 -0
  90. package/node_modules/@sema/nucleo/tsconfig.json +9 -0
  91. package/node_modules/@sema/nucleo/tsconfig.tsbuildinfo +1 -0
  92. package/node_modules/@sema/padroes/package.json +10 -7
  93. package/node_modules/@sema/padroes/src/index.ts +382 -0
  94. package/node_modules/@sema/padroes/tsconfig.json +9 -0
  95. package/node_modules/@sema/padroes/tsconfig.tsbuildinfo +1 -0
  96. package/package.json +74 -74
  97. package/AGENTS.md +0 -280
  98. package/LICENSE +0 -22
  99. package/SEMA_BRIEF.curto.txt +0 -11
  100. package/SEMA_BRIEF.md +0 -112
  101. package/SEMA_BRIEF.micro.txt +0 -9
  102. package/SEMA_INDEX.json +0 -1534
  103. package/dist/php-symbols.d.ts +0 -24
  104. package/dist/php-symbols.js +0 -375
  105. package/dist/php-symbols.js.map +0 -1
  106. package/docs/AGENT_STARTER.md +0 -109
  107. package/docs/cli.md +0 -175
  108. package/docs/como-ensinar-a-sema-para-ia.md +0 -155
  109. package/docs/deploy.md +0 -93
  110. package/docs/documentacao.md +0 -88
  111. package/docs/env.md +0 -105
  112. package/docs/extensao-vscode.md +0 -53
  113. package/docs/fluxo-pratico-ia-sema.md +0 -187
  114. package/docs/instalacao-e-primeiro-uso.md +0 -134
  115. package/docs/integracao-com-ia.md +0 -110
  116. package/docs/mcp.md +0 -292
  117. package/docs/pagamento-ponta-a-ponta.md +0 -171
  118. package/docs/persistencia-vendor-first.md +0 -151
  119. package/docs/prompt-base-ia-sema.md +0 -111
  120. package/docs/repositories.md +0 -54
  121. package/docs/rollback.md +0 -49
  122. package/docs/seguranca.md +0 -126
  123. package/docs/sintaxe.md +0 -218
  124. package/llms-full.txt +0 -34
  125. package/llms.txt +0 -17
@@ -0,0 +1,936 @@
1
+ import {
2
+ criarDiagnostico,
3
+ type Diagnostico,
4
+ type IntervaloFonte,
5
+ } from "../diagnosticos/index.js";
6
+ import type {
7
+ BlocoAst,
8
+ BlocoCasoTesteAst,
9
+ BlocoGenericoAst,
10
+ CampoAst,
11
+ EntityAst,
12
+ EnumAst,
13
+ FlowAst,
14
+ ModuloAst,
15
+ RouteAst,
16
+ StateAst,
17
+ TaskAst,
18
+ TypeAst,
19
+ UseAst,
20
+ } from "../ast/tipos.js";
21
+ import type { Token } from "../lexer/tokens.js";
22
+
23
+ interface ResultadoParser {
24
+ modulo?: ModuloAst;
25
+ diagnosticos: Diagnostico[];
26
+ }
27
+
28
+ type PalavraBloco =
29
+ | "database"
30
+ | "docs"
31
+ | "comments"
32
+ | "table"
33
+ | "view"
34
+ | "query"
35
+ | "transaction"
36
+ | "index"
37
+ | "constraint"
38
+ | "relationship"
39
+ | "collection"
40
+ | "document"
41
+ | "keyspace"
42
+ | "stream"
43
+ | "lock"
44
+ | "retention"
45
+ | "replication"
46
+ | "fields"
47
+ | "invariants"
48
+ | "transitions"
49
+ | "input"
50
+ | "output"
51
+ | "rules"
52
+ | "effects"
53
+ | "impl"
54
+ | "vinculos"
55
+ | "execucao"
56
+ | "auth"
57
+ | "authz"
58
+ | "dados"
59
+ | "audit"
60
+ | "segredos"
61
+ | "forbidden"
62
+ | "guarantees"
63
+ | "state"
64
+ | "tests"
65
+ | "error"
66
+ | "flow"
67
+ | "route"
68
+ | "worker"
69
+ | "evento"
70
+ | "fila"
71
+ | "cron"
72
+ | "webhook"
73
+ | "cache"
74
+ | "storage"
75
+ | "policy"
76
+ | "when"
77
+ | "given"
78
+ | "expect";
79
+
80
+ const PALAVRAS_BLOCO_NOMEADO_LIVRE = new Set<PalavraBloco>([
81
+ "table",
82
+ "view",
83
+ "query",
84
+ "transaction",
85
+ "index",
86
+ "constraint",
87
+ "relationship",
88
+ "collection",
89
+ "document",
90
+ "keyspace",
91
+ "stream",
92
+ "lock",
93
+ "retention",
94
+ "replication",
95
+ ]);
96
+
97
+ function decodificarTextoLiteral(valor: string): string {
98
+ let resultado = "";
99
+
100
+ for (let indice = 0; indice < valor.length; indice += 1) {
101
+ const atual = valor[indice];
102
+ if (atual !== "\\") {
103
+ resultado += atual;
104
+ continue;
105
+ }
106
+
107
+ const proximo = valor[indice + 1];
108
+ if (!proximo) {
109
+ resultado += atual;
110
+ continue;
111
+ }
112
+
113
+ switch (proximo) {
114
+ case "\"":
115
+ resultado += "\"";
116
+ indice += 1;
117
+ break;
118
+ case "\\":
119
+ resultado += "\\";
120
+ indice += 1;
121
+ break;
122
+ case "n":
123
+ resultado += "\n";
124
+ indice += 1;
125
+ break;
126
+ case "r":
127
+ resultado += "\r";
128
+ indice += 1;
129
+ break;
130
+ case "t":
131
+ resultado += "\t";
132
+ indice += 1;
133
+ break;
134
+ default:
135
+ resultado += `${atual}${proximo}`;
136
+ indice += 1;
137
+ break;
138
+ }
139
+ }
140
+
141
+ return resultado;
142
+ }
143
+
144
+ class Parser {
145
+ private indice = 0;
146
+ private diagnosticos: Diagnostico[] = [];
147
+
148
+ public constructor(private readonly tokens: Token[]) {}
149
+
150
+ public analisar(): ResultadoParser {
151
+ this.ignorarRuido();
152
+ const modulo = this.parseModulo();
153
+ return { modulo, diagnosticos: this.diagnosticos };
154
+ }
155
+
156
+ private atual(): Token {
157
+ return this.tokens[this.indice]!;
158
+ }
159
+
160
+ private anterior(): Token {
161
+ return this.tokens[Math.max(0, this.indice - 1)]!;
162
+ }
163
+
164
+ private avancar(): Token {
165
+ const token = this.atual();
166
+ if (this.indice < this.tokens.length - 1) {
167
+ this.indice += 1;
168
+ }
169
+ return token;
170
+ }
171
+
172
+ private ignorarRuido(): void {
173
+ while (["nova_linha", "comentario"].includes(this.atual().tipo)) {
174
+ this.avancar();
175
+ }
176
+ }
177
+
178
+ private tokenNaFrente(distancia = 1): Token | undefined {
179
+ return this.tokens[this.indice + distancia];
180
+ }
181
+
182
+ private iniciaBlocoSimples(keyword: string): boolean {
183
+ if (this.atual().valor !== keyword) {
184
+ return false;
185
+ }
186
+ return this.tokenNaFrente()?.valor === "{";
187
+ }
188
+
189
+ private iniciaBlocoComNomeObrigatorio(keyword: string): boolean {
190
+ if (this.atual().valor !== keyword) {
191
+ return false;
192
+ }
193
+ return this.tokenNaFrente()?.tipo === "identificador" && this.tokenNaFrente(2)?.valor === "{";
194
+ }
195
+
196
+ private iniciaBlocoState(): boolean {
197
+ if (this.atual().valor !== "state") {
198
+ return false;
199
+ }
200
+ return this.tokenNaFrente()?.valor === "{"
201
+ || (this.tokenNaFrente()?.tipo === "identificador" && this.tokenNaFrente(2)?.valor === "{");
202
+ }
203
+
204
+ private iniciaSubblocoConhecido(): boolean {
205
+ if (this.atual().tipo !== "palavra_chave") {
206
+ return false;
207
+ }
208
+
209
+ if (["state"].includes(this.atual().valor)) {
210
+ return this.iniciaBlocoState();
211
+ }
212
+
213
+ return [
214
+ "docs",
215
+ "comments",
216
+ "fields",
217
+ "invariants",
218
+ "transitions",
219
+ "input",
220
+ "output",
221
+ "rules",
222
+ "effects",
223
+ "impl",
224
+ "vinculos",
225
+ "execucao",
226
+ "auth",
227
+ "authz",
228
+ "dados",
229
+ "audit",
230
+ "segredos",
231
+ "forbidden",
232
+ "guarantees",
233
+ "tests",
234
+ "error",
235
+ "given",
236
+ "when",
237
+ "expect",
238
+ ].includes(this.atual().valor) && this.iniciaBlocoSimples(this.atual().valor);
239
+ }
240
+
241
+ private consumirValor(valor: string, mensagem: string): Token {
242
+ const token = this.atual();
243
+ if (token.valor === valor) {
244
+ this.avancar();
245
+ return token;
246
+ }
247
+ this.registrarErro("PAR001", mensagem, token.intervalo, `Esperado "${valor}", recebido "${token.valor}".`);
248
+ return token;
249
+ }
250
+
251
+ private consumirTipo(tipo: Token["tipo"], mensagem: string): Token {
252
+ const token = this.atual();
253
+ if (token.tipo === tipo) {
254
+ this.avancar();
255
+ return token;
256
+ }
257
+ this.registrarErro("PAR002", mensagem, token.intervalo, `Esperado token do tipo "${tipo}".`);
258
+ return token;
259
+ }
260
+
261
+ private registrarErro(codigo: string, mensagem: string, intervalo?: IntervaloFonte, contexto?: string): void {
262
+ this.diagnosticos.push(
263
+ criarDiagnostico(
264
+ codigo,
265
+ mensagem,
266
+ "erro",
267
+ intervalo,
268
+ "Revise a sintaxe do bloco e a ordem das declaracoes.",
269
+ contexto,
270
+ ),
271
+ );
272
+ }
273
+
274
+ private parseModulo(): ModuloAst | undefined {
275
+ if (this.atual().valor !== "module") {
276
+ this.registrarErro("PAR003", "Arquivo .sema deve iniciar com um bloco module.", this.atual().intervalo);
277
+ return undefined;
278
+ }
279
+
280
+ const inicio = this.avancar().intervalo.inicio;
281
+ const nome = this.consumirTipo("identificador", "Era esperado o nome do modulo.").valor;
282
+ this.consumirValor("{", "Era esperado abrir o corpo do modulo com {.");
283
+
284
+ const uses: UseAst[] = [];
285
+ let vinculos: BlocoGenericoAst | undefined;
286
+ const databases: BlocoGenericoAst[] = [];
287
+ const types: TypeAst[] = [];
288
+ const entities: EntityAst[] = [];
289
+ const enums: EnumAst[] = [];
290
+ const tasks: TaskAst[] = [];
291
+ const flows: FlowAst[] = [];
292
+ const routes: RouteAst[] = [];
293
+ const workers: BlocoGenericoAst[] = [];
294
+ const eventos: BlocoGenericoAst[] = [];
295
+ const filas: BlocoGenericoAst[] = [];
296
+ const crons: BlocoGenericoAst[] = [];
297
+ const webhooks: BlocoGenericoAst[] = [];
298
+ const caches: BlocoGenericoAst[] = [];
299
+ const storages: BlocoGenericoAst[] = [];
300
+ const policies: BlocoGenericoAst[] = [];
301
+ const states: StateAst[] = [];
302
+ const extras: BlocoGenericoAst[] = [];
303
+ let docs: BlocoGenericoAst | undefined;
304
+ let comments: BlocoGenericoAst | undefined;
305
+ let tests: BlocoGenericoAst | undefined;
306
+
307
+ while (this.atual().tipo !== "fim_arquivo" && this.atual().valor !== "}") {
308
+ this.ignorarRuido();
309
+ const token = this.atual();
310
+ if (token.valor === "}") {
311
+ break;
312
+ }
313
+
314
+ switch (token.valor) {
315
+ case "use":
316
+ if (this.tokenNaFrente()?.tipo === "identificador") {
317
+ uses.push(this.parseUse());
318
+ break;
319
+ }
320
+ extras.push(this.parseBlocoGenerico("desconhecido"));
321
+ break;
322
+ case "docs":
323
+ if (this.iniciaBlocoSimples("docs")) {
324
+ docs = this.parseBlocoGenerico("docs");
325
+ break;
326
+ }
327
+ extras.push(this.parseBlocoGenerico("desconhecido"));
328
+ break;
329
+ case "comments":
330
+ if (this.iniciaBlocoSimples("comments")) {
331
+ comments = this.parseBlocoGenerico("comments");
332
+ break;
333
+ }
334
+ extras.push(this.parseBlocoGenerico("desconhecido"));
335
+ break;
336
+ case "vinculos":
337
+ if (this.iniciaBlocoSimples("vinculos")) {
338
+ vinculos = this.parseBlocoGenerico("vinculos");
339
+ break;
340
+ }
341
+ extras.push(this.parseBlocoGenerico("desconhecido"));
342
+ break;
343
+ case "database":
344
+ if (this.iniciaBlocoComNomeObrigatorio("database")) {
345
+ databases.push(this.parseBlocoGenerico("database"));
346
+ break;
347
+ }
348
+ extras.push(this.parseBlocoGenerico("desconhecido"));
349
+ break;
350
+ case "type":
351
+ if (this.iniciaBlocoComNomeObrigatorio("type")) {
352
+ types.push(this.parseType());
353
+ break;
354
+ }
355
+ extras.push(this.parseBlocoGenerico("desconhecido"));
356
+ break;
357
+ case "entity":
358
+ if (this.iniciaBlocoComNomeObrigatorio("entity")) {
359
+ entities.push(this.parseEntity());
360
+ break;
361
+ }
362
+ extras.push(this.parseBlocoGenerico("desconhecido"));
363
+ break;
364
+ case "enum":
365
+ if (this.iniciaBlocoComNomeObrigatorio("enum")) {
366
+ enums.push(this.parseEnum());
367
+ break;
368
+ }
369
+ extras.push(this.parseBlocoGenerico("desconhecido"));
370
+ break;
371
+ case "task":
372
+ if (this.iniciaBlocoComNomeObrigatorio("task")) {
373
+ tasks.push(this.parseTask());
374
+ break;
375
+ }
376
+ extras.push(this.parseBlocoGenerico("desconhecido"));
377
+ break;
378
+ case "flow":
379
+ if (this.iniciaBlocoComNomeObrigatorio("flow")) {
380
+ flows.push(this.parseFlow());
381
+ break;
382
+ }
383
+ extras.push(this.parseBlocoGenerico("desconhecido"));
384
+ break;
385
+ case "route":
386
+ if (this.iniciaBlocoComNomeObrigatorio("route")) {
387
+ routes.push(this.parseRoute());
388
+ break;
389
+ }
390
+ extras.push(this.parseBlocoGenerico("desconhecido"));
391
+ break;
392
+ case "worker":
393
+ if (this.iniciaBlocoComNomeObrigatorio("worker")) {
394
+ workers.push(this.parseBlocoGenerico("worker"));
395
+ break;
396
+ }
397
+ extras.push(this.parseBlocoGenerico("desconhecido"));
398
+ break;
399
+ case "evento":
400
+ if (this.iniciaBlocoComNomeObrigatorio("evento")) {
401
+ eventos.push(this.parseBlocoGenerico("evento"));
402
+ break;
403
+ }
404
+ extras.push(this.parseBlocoGenerico("desconhecido"));
405
+ break;
406
+ case "fila":
407
+ if (this.iniciaBlocoComNomeObrigatorio("fila")) {
408
+ filas.push(this.parseBlocoGenerico("fila"));
409
+ break;
410
+ }
411
+ extras.push(this.parseBlocoGenerico("desconhecido"));
412
+ break;
413
+ case "cron":
414
+ if (this.iniciaBlocoComNomeObrigatorio("cron")) {
415
+ crons.push(this.parseBlocoGenerico("cron"));
416
+ break;
417
+ }
418
+ extras.push(this.parseBlocoGenerico("desconhecido"));
419
+ break;
420
+ case "webhook":
421
+ if (this.iniciaBlocoComNomeObrigatorio("webhook")) {
422
+ webhooks.push(this.parseBlocoGenerico("webhook"));
423
+ break;
424
+ }
425
+ extras.push(this.parseBlocoGenerico("desconhecido"));
426
+ break;
427
+ case "cache":
428
+ if (this.iniciaBlocoComNomeObrigatorio("cache")) {
429
+ caches.push(this.parseBlocoGenerico("cache"));
430
+ break;
431
+ }
432
+ extras.push(this.parseBlocoGenerico("desconhecido"));
433
+ break;
434
+ case "storage":
435
+ if (this.iniciaBlocoComNomeObrigatorio("storage")) {
436
+ storages.push(this.parseBlocoGenerico("storage"));
437
+ break;
438
+ }
439
+ extras.push(this.parseBlocoGenerico("desconhecido"));
440
+ break;
441
+ case "policy":
442
+ if (this.iniciaBlocoComNomeObrigatorio("policy")) {
443
+ policies.push(this.parseBlocoGenerico("policy"));
444
+ break;
445
+ }
446
+ extras.push(this.parseBlocoGenerico("desconhecido"));
447
+ break;
448
+ case "state":
449
+ if (this.iniciaBlocoState()) {
450
+ states.push(this.parseState());
451
+ break;
452
+ }
453
+ extras.push(this.parseBlocoGenerico("desconhecido"));
454
+ break;
455
+ case "tests":
456
+ if (this.iniciaBlocoSimples("tests")) {
457
+ tests = this.parseBlocoGenerico("tests");
458
+ break;
459
+ }
460
+ extras.push(this.parseBlocoGenerico("desconhecido"));
461
+ break;
462
+ default:
463
+ extras.push(this.parseBlocoGenerico("desconhecido"));
464
+ break;
465
+ }
466
+ this.ignorarRuido();
467
+ }
468
+
469
+ const fim = this.consumirValor("}", "Era esperado fechar o bloco module com }.").intervalo.fim;
470
+
471
+ return {
472
+ tipo: "module",
473
+ nome,
474
+ uses,
475
+ vinculos,
476
+ docs,
477
+ comments,
478
+ databases,
479
+ types,
480
+ entities,
481
+ enums,
482
+ tasks,
483
+ flows,
484
+ routes,
485
+ workers,
486
+ eventos,
487
+ filas,
488
+ crons,
489
+ webhooks,
490
+ caches,
491
+ storages,
492
+ policies,
493
+ states,
494
+ tests,
495
+ extras,
496
+ intervalo: { inicio, fim },
497
+ };
498
+ }
499
+
500
+ private parseUse(): UseAst {
501
+ const inicio = this.avancar().intervalo.inicio;
502
+ const primeiro = this.consumirTipo("identificador", "Era esperado o caminho do use.");
503
+ let origem: UseAst["origem"] = "sema";
504
+ let caminho = primeiro.valor;
505
+
506
+ const origemNormalizada = normalizarOrigemUse(primeiro.valor);
507
+ if (origemNormalizada && this.atual().tipo === "identificador") {
508
+ origem = origemNormalizada;
509
+ caminho = this.avancar().valor;
510
+ }
511
+
512
+ const fim = this.anterior().intervalo.fim;
513
+ return { tipo: "use", origem, caminho, intervalo: { inicio, fim } };
514
+ }
515
+
516
+ private parseType(): TypeAst {
517
+ const inicio = this.avancar().intervalo.inicio;
518
+ const nome = this.consumirTipo("identificador", "Era esperado o nome do type.").valor;
519
+ const corpo = this.parseBlocoComNomeOpcional("type");
520
+ return { tipo: "type", nome, corpo, intervalo: { inicio, fim: corpo.intervalo?.fim ?? this.anterior().intervalo.fim } };
521
+ }
522
+
523
+ private parseEntity(): EntityAst {
524
+ const inicio = this.avancar().intervalo.inicio;
525
+ const nome = this.consumirTipo("identificador", "Era esperado o nome da entity.").valor;
526
+ const corpo = this.parseBlocoComNomeOpcional("entity");
527
+ return { tipo: "entity", nome, corpo, intervalo: { inicio, fim: corpo.intervalo?.fim ?? this.anterior().intervalo.fim } };
528
+ }
529
+
530
+ private parseEnum(): EnumAst {
531
+ const inicio = this.avancar().intervalo.inicio;
532
+ const nome = this.consumirTipo("identificador", "Era esperado o nome do enum.").valor;
533
+ this.consumirValor("{", "Era esperado abrir o corpo do enum.");
534
+ const valores: string[] = [];
535
+ let docs: BlocoGenericoAst | undefined;
536
+ while (this.atual().tipo !== "fim_arquivo" && this.atual().valor !== "}") {
537
+ this.ignorarRuido();
538
+ if (this.atual().valor === "}") {
539
+ break;
540
+ }
541
+ if (this.atual().valor === "docs") {
542
+ docs = this.parseBlocoGenerico("docs");
543
+ continue;
544
+ }
545
+ if (["identificador", "palavra_chave"].includes(this.atual().tipo)) {
546
+ valores.push(this.avancar().valor);
547
+ if (this.atual().valor === ",") {
548
+ this.avancar();
549
+ }
550
+ continue;
551
+ }
552
+ this.avancar();
553
+ }
554
+ const fim = this.consumirValor("}", "Era esperado fechar o enum.").intervalo.fim;
555
+ return { tipo: "enum", nome, valores, docs, intervalo: { inicio, fim } };
556
+ }
557
+
558
+ private parseTask(): TaskAst {
559
+ const inicio = this.avancar().intervalo.inicio;
560
+ const nome = this.consumirTipo("identificador", "Era esperado o nome da task.").valor;
561
+ const corpo = this.parseBlocoComNomeOpcional("task");
562
+
563
+ const localizar = (palavraChave: PalavraBloco): BlocoGenericoAst | undefined =>
564
+ corpo.blocos.find((bloco): bloco is BlocoGenericoAst => bloco.tipo === "bloco_generico" && bloco.palavraChave === palavraChave);
565
+
566
+ return {
567
+ tipo: "task",
568
+ nome,
569
+ corpo,
570
+ input: localizar("input"),
571
+ output: localizar("output"),
572
+ rules: localizar("rules"),
573
+ effects: localizar("effects"),
574
+ impl: localizar("impl"),
575
+ vinculos: localizar("vinculos"),
576
+ execucao: localizar("execucao"),
577
+ auth: localizar("auth"),
578
+ authz: localizar("authz"),
579
+ dados: localizar("dados"),
580
+ audit: localizar("audit"),
581
+ segredos: localizar("segredos"),
582
+ forbidden: localizar("forbidden"),
583
+ guarantees: localizar("guarantees"),
584
+ state: localizar("state"),
585
+ tests: localizar("tests"),
586
+ error: localizar("error"),
587
+ docs: localizar("docs"),
588
+ comments: localizar("comments"),
589
+ intervalo: { inicio, fim: corpo.intervalo?.fim ?? this.anterior().intervalo.fim },
590
+ };
591
+ }
592
+
593
+ private parseFlow(): FlowAst {
594
+ const inicio = this.avancar().intervalo.inicio;
595
+ const nome = this.consumirTipo("identificador", "Era esperado o nome do flow.").valor;
596
+ const corpo = this.parseBlocoComNomeOpcional("flow");
597
+ const vinculos = corpo.blocos.find((bloco): bloco is BlocoGenericoAst => bloco.tipo === "bloco_generico" && bloco.palavraChave === "vinculos");
598
+ return { tipo: "flow", nome, corpo, vinculos, intervalo: { inicio, fim: corpo.intervalo?.fim ?? this.anterior().intervalo.fim } };
599
+ }
600
+
601
+ private parseRoute(): RouteAst {
602
+ const inicio = this.avancar().intervalo.inicio;
603
+ const nome = this.consumirTipo("identificador", "Era esperado o nome da route.").valor;
604
+ const corpo = this.parseBlocoComNomeOpcional("route");
605
+ const vinculos = corpo.blocos.find((bloco): bloco is BlocoGenericoAst => bloco.tipo === "bloco_generico" && bloco.palavraChave === "vinculos");
606
+ return { tipo: "route", nome, corpo, vinculos, intervalo: { inicio, fim: corpo.intervalo?.fim ?? this.anterior().intervalo.fim } };
607
+ }
608
+
609
+ private parseState(): StateAst {
610
+ const inicioToken = this.avancar();
611
+ let nome: string | undefined;
612
+ if (this.atual().tipo === "identificador") {
613
+ nome = this.avancar().valor;
614
+ }
615
+ this.consumirValor("{", "Era esperado abrir o bloco state.");
616
+ const corpo = this.parseCorpoBloco("state", nome, inicioToken.intervalo.inicio);
617
+ return {
618
+ tipo: "state",
619
+ nome,
620
+ corpo,
621
+ intervalo: { inicio: inicioToken.intervalo.inicio, fim: corpo.intervalo?.fim ?? this.anterior().intervalo.fim },
622
+ };
623
+ }
624
+
625
+ private parseBlocoComNomeOpcional(nomeBloco: string): BlocoGenericoAst {
626
+ this.consumirValor("{", `Era esperado abrir o bloco ${nomeBloco}.`);
627
+ return this.parseCorpoBloco(nomeBloco as PalavraBloco | "type" | "entity" | "task" | "flow" | "route");
628
+ }
629
+
630
+ private parseBlocoGenerico(palavraChave: PalavraBloco | "desconhecido"): BlocoGenericoAst {
631
+ const inicioToken = this.avancar();
632
+ let nome: string | undefined;
633
+ if (this.atual().tipo === "identificador") {
634
+ nome = this.avancar().valor;
635
+ }
636
+ this.consumirValor("{", `Era esperado abrir o bloco ${inicioToken.valor}.`);
637
+ return this.parseCorpoBloco((palavraChave === "desconhecido" ? "desconhecido" : inicioToken.valor) as BlocoGenericoAst["palavraChave"], nome, inicioToken.intervalo.inicio);
638
+ }
639
+
640
+ private parseBlocoNomeadoLivre(): BlocoGenericoAst {
641
+ const inicioToken = this.avancar();
642
+ const nome = inicioToken.valor;
643
+ this.consumirValor("{", `Era esperado abrir o bloco ${nome}.`);
644
+ return this.parseCorpoBloco("desconhecido", nome, inicioToken.intervalo.inicio);
645
+ }
646
+
647
+ private parseBlocoNomeadoComPalavraChaveLivre(): BlocoGenericoAst {
648
+ const palavraChaveToken = this.avancar();
649
+ const nomeToken = this.avancar();
650
+ this.consumirValor("{", `Era esperado abrir o bloco ${palavraChaveToken.valor} ${nomeToken.valor}.`);
651
+ return this.parseCorpoBloco(
652
+ palavraChaveToken.valor as PalavraBloco,
653
+ nomeToken.valor,
654
+ palavraChaveToken.intervalo.inicio,
655
+ );
656
+ }
657
+
658
+ private parseCorpoBloco(
659
+ palavraChave: BlocoGenericoAst["palavraChave"],
660
+ nome?: string,
661
+ inicioManual?: IntervaloFonte["inicio"],
662
+ ): BlocoGenericoAst {
663
+ const inicio = inicioManual ?? this.anterior().intervalo.inicio;
664
+ const campos: CampoAst[] = [];
665
+ const linhas: BlocoGenericoAst["linhas"] = [];
666
+ const blocos: BlocoAst[] = [];
667
+
668
+ while (this.atual().tipo !== "fim_arquivo" && this.atual().valor !== "}") {
669
+ this.ignorarRuido();
670
+ if (this.atual().valor === "}") {
671
+ break;
672
+ }
673
+
674
+ if (palavraChave === "tests" && this.atual().valor === "caso") {
675
+ blocos.push(this.parseCasoTeste());
676
+ continue;
677
+ }
678
+
679
+ if (this.iniciaSubblocoConhecido()) {
680
+ blocos.push(this.parseBlocoGenerico(this.atual().valor as PalavraBloco));
681
+ continue;
682
+ }
683
+
684
+ if (
685
+ ["identificador", "palavra_chave"].includes(this.atual().tipo)
686
+ && PALAVRAS_BLOCO_NOMEADO_LIVRE.has(this.atual().valor as PalavraBloco)
687
+ && ["identificador", "palavra_chave"].includes(this.tokens[this.indice + 1]?.tipo ?? "")
688
+ && this.tokens[this.indice + 2]?.valor === "{"
689
+ ) {
690
+ blocos.push(this.parseBlocoNomeadoComPalavraChaveLivre());
691
+ continue;
692
+ }
693
+
694
+ if (["identificador", "palavra_chave"].includes(this.atual().tipo) && this.tokens[this.indice + 1]?.valor === "{") {
695
+ blocos.push(this.parseBlocoNomeadoLivre());
696
+ continue;
697
+ }
698
+
699
+ if (["identificador", "palavra_chave"].includes(this.atual().tipo) && this.tokens[this.indice + 1]?.valor === ":") {
700
+ campos.push(this.parseCampo());
701
+ continue;
702
+ }
703
+
704
+ const linha = this.parseLinhaDeclarativa();
705
+ if (linha.conteudo.trim().length > 0) {
706
+ linhas.push(linha);
707
+ }
708
+ }
709
+
710
+ const fim = this.consumirValor("}", "Era esperado fechar o bloco com }.").intervalo.fim;
711
+ return {
712
+ tipo: "bloco_generico",
713
+ palavraChave,
714
+ nome,
715
+ campos,
716
+ linhas,
717
+ blocos,
718
+ intervalo: { inicio, fim },
719
+ };
720
+ }
721
+
722
+ private parseCampo(): CampoAst {
723
+ const inicio = this.atual().intervalo.inicio;
724
+ const nome = this.avancar().valor;
725
+ this.consumirValor(":", "Era esperado ':' depois do nome do campo.");
726
+ const partes: Token[] = [];
727
+ let profundidade = 0;
728
+ while (this.atual().tipo !== "fim_arquivo" && this.atual().tipo !== "nova_linha" && this.atual().valor !== "}") {
729
+ const proximoToken = this.tokens[this.indice + 1];
730
+ const iniciaNovoCampo =
731
+ profundidade === 0
732
+ && partes.length > 0
733
+ && ["identificador", "palavra_chave"].includes(this.atual().tipo)
734
+ && proximoToken?.valor === ":";
735
+
736
+ if (iniciaNovoCampo) {
737
+ break;
738
+ }
739
+
740
+ const token = this.avancar();
741
+ partes.push(token);
742
+ if (["<", "[", "("].includes(token.valor)) {
743
+ profundidade += 1;
744
+ } else if ([">", "]", ")"].includes(token.valor)) {
745
+ profundidade = Math.max(0, profundidade - 1);
746
+ }
747
+ }
748
+ const segmentos = partes.filter((token) => token.valor.length > 0);
749
+ const possuiTextoLiteral = segmentos.some((token) => token.tipo === "texto");
750
+
751
+ if (possuiTextoLiteral) {
752
+ const valorLiteral = segmentos.map((token) => token.tipo === "texto" ? decodificarTextoLiteral(token.valor) : token.valor).join(" ");
753
+ if (this.atual().tipo === "nova_linha") {
754
+ this.avancar();
755
+ }
756
+ return {
757
+ tipo: "campo",
758
+ nome,
759
+ valor: valorLiteral,
760
+ modificadores: [],
761
+ intervalo: { inicio, fim: this.anterior().intervalo.fim },
762
+ };
763
+ }
764
+
765
+ const tipoTokens: string[] = [];
766
+ const modificadores: string[] = [];
767
+ let profundidadeTipo = 0;
768
+ let iniciouModificadores = false;
769
+
770
+ for (const token of segmentos) {
771
+ const segmento = token.valor;
772
+ if (!iniciouModificadores) {
773
+ if (["<", "[", "("].includes(segmento)) {
774
+ profundidadeTipo += 1;
775
+ tipoTokens.push(segmento);
776
+ continue;
777
+ }
778
+ if ([">", "]", ")"].includes(segmento)) {
779
+ profundidadeTipo = Math.max(0, profundidadeTipo - 1);
780
+ tipoTokens.push(segmento);
781
+ continue;
782
+ }
783
+
784
+ const pareceModificador =
785
+ profundidadeTipo === 0
786
+ && tipoTokens.length > 0
787
+ && /^[a-z_][a-z0-9_]*$/u.test(segmento);
788
+
789
+ if (pareceModificador) {
790
+ iniciouModificadores = true;
791
+ modificadores.push(segmento);
792
+ continue;
793
+ }
794
+
795
+ tipoTokens.push(segmento);
796
+ continue;
797
+ }
798
+
799
+ modificadores.push(segmento);
800
+ }
801
+
802
+ let valor = tipoTokens
803
+ .join(" ")
804
+ .replace(/\s*([<>\[\](),|?])\s*/g, "$1")
805
+ .trim();
806
+ if (valor.includes("/")) {
807
+ valor = valor.replace(/\s*\/\s*/g, "/");
808
+ }
809
+ if (this.atual().tipo === "nova_linha") {
810
+ this.avancar();
811
+ }
812
+ return {
813
+ tipo: "campo",
814
+ nome,
815
+ valor,
816
+ modificadores,
817
+ intervalo: { inicio, fim: this.anterior().intervalo.fim },
818
+ };
819
+ }
820
+
821
+ private parseLinhaDeclarativa() {
822
+ const inicio = this.atual().intervalo.inicio;
823
+ const partes: string[] = [];
824
+ while (this.atual().tipo !== "fim_arquivo" && this.atual().tipo !== "nova_linha" && this.atual().valor !== "}") {
825
+ partes.push(this.avancar().valor);
826
+ }
827
+ if (this.atual().tipo === "nova_linha") {
828
+ this.avancar();
829
+ }
830
+ return {
831
+ tipo: "linha_declarativa" as const,
832
+ conteudo: partes.join(" ").trim(),
833
+ intervalo: { inicio, fim: this.anterior().intervalo.fim },
834
+ };
835
+ }
836
+
837
+ private parseCasoTeste(): BlocoCasoTesteAst {
838
+ const inicio = this.avancar().intervalo.inicio;
839
+ const nomeToken = this.atual();
840
+ const nome =
841
+ nomeToken.tipo === "texto"
842
+ ? this.avancar().valor
843
+ : this.consumirTipo("identificador", "Era esperado o nome textual do caso de teste.").valor;
844
+ this.consumirValor("{", "Era esperado abrir o bloco do caso de teste.");
845
+ let given: BlocoGenericoAst | undefined;
846
+ let when: BlocoGenericoAst | undefined;
847
+ let expect: BlocoGenericoAst | undefined;
848
+ let error: BlocoGenericoAst | undefined;
849
+ let docs: BlocoGenericoAst | undefined;
850
+ let comments: BlocoGenericoAst | undefined;
851
+
852
+ while (this.atual().tipo !== "fim_arquivo" && this.atual().valor !== "}") {
853
+ this.ignorarRuido();
854
+ if (this.atual().valor === "}") {
855
+ break;
856
+ }
857
+ switch (this.atual().valor) {
858
+ case "given":
859
+ given = this.parseBlocoGenerico("given");
860
+ break;
861
+ case "when":
862
+ when = this.parseBlocoGenerico("when");
863
+ break;
864
+ case "expect":
865
+ expect = this.parseBlocoGenerico("expect");
866
+ break;
867
+ case "error":
868
+ error = this.parseBlocoGenerico("error");
869
+ break;
870
+ case "docs":
871
+ docs = this.parseBlocoGenerico("docs");
872
+ break;
873
+ case "comments":
874
+ comments = this.parseBlocoGenerico("comments");
875
+ break;
876
+ default:
877
+ this.avancar();
878
+ break;
879
+ }
880
+ }
881
+
882
+ const fim = this.consumirValor("}", "Era esperado fechar o caso de teste.").intervalo.fim;
883
+ return {
884
+ tipo: "caso_teste",
885
+ nome,
886
+ given,
887
+ when,
888
+ expect,
889
+ error,
890
+ docs,
891
+ comments,
892
+ intervalo: { inicio, fim },
893
+ };
894
+ }
895
+ }
896
+
897
+ function normalizarOrigemUse(valor: string): UseAst["origem"] | undefined {
898
+ switch (valor.toLowerCase()) {
899
+ case "sema":
900
+ return "sema";
901
+ case "ts":
902
+ case "typescript":
903
+ return "ts";
904
+ case "py":
905
+ case "python":
906
+ return "py";
907
+ case "dart":
908
+ return "dart";
909
+ case "lua":
910
+ return "lua";
911
+ case "cs":
912
+ case "csharp":
913
+ case "dotnet":
914
+ return "cs";
915
+ case "java":
916
+ return "java";
917
+ case "go":
918
+ case "golang":
919
+ return "go";
920
+ case "rust":
921
+ case "rs":
922
+ return "rust";
923
+ case "cpp":
924
+ case "cxx":
925
+ case "cc":
926
+ case "c++":
927
+ return "cpp";
928
+ default:
929
+ return undefined;
930
+ }
931
+ }
932
+
933
+ export function parsear(tokens: Token[]): ResultadoParser {
934
+ const parser = new Parser(tokens);
935
+ return parser.analisar();
936
+ }