@designliquido/delegua 0.71.0 → 0.72.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.
package/umd/delegua.js CHANGED
@@ -58,20 +58,18 @@ class AnalisadorSemanticoBase {
58
58
  return;
59
59
  }
60
60
  if (expressao instanceof construtos_1.Chamada) {
61
- if (expressao.entidadeChamada instanceof construtos_1.Variavel) {
62
- this.gerenciadorEscopos.marcarComoUsada(expressao.entidadeChamada.simbolo.lexema);
63
- }
64
- if (expressao.entidadeChamada instanceof construtos_1.AcessoMetodo) {
65
- this.marcarVariaveisUsadasEmExpressao(expressao.entidadeChamada.objeto);
66
- }
67
- if (expressao.entidadeChamada instanceof construtos_1.AcessoMetodoOuPropriedade) {
68
- this.marcarVariaveisUsadasEmExpressao(expressao.entidadeChamada.objeto);
69
- }
61
+ this.marcarVariaveisUsadasEmExpressao(expressao.entidadeChamada);
70
62
  for (const arg of expressao.argumentos) {
71
63
  this.marcarVariaveisUsadasEmExpressao(arg);
72
64
  }
73
65
  return;
74
66
  }
67
+ if (expressao instanceof construtos_1.AcessoMetodo ||
68
+ expressao instanceof construtos_1.AcessoMetodoOuPropriedade ||
69
+ expressao instanceof construtos_1.AcessoPropriedade) {
70
+ this.marcarVariaveisUsadasEmExpressao(expressao.objeto);
71
+ return;
72
+ }
75
73
  if (expressao instanceof construtos_1.Logico) {
76
74
  this.marcarVariaveisUsadasEmExpressao(expressao.esquerda);
77
75
  this.marcarVariaveisUsadasEmExpressao(expressao.direita);
@@ -661,6 +659,27 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
661
659
  // Verifica a condição (incluindo validação de tipos para operadores lógicos)
662
660
  return this.verificarCondicao(declaracao.condicao);
663
661
  }
662
+ /**
663
+ * Verifica uma expressão recursivamente, incluindo operações binárias
664
+ */
665
+ verificarExpressao(expressao) {
666
+ if (expressao instanceof construtos_1.Agrupamento) {
667
+ this.verificarExpressao(expressao.expressao);
668
+ return;
669
+ }
670
+ if (expressao instanceof construtos_1.Binario) {
671
+ this.verificarBinario(expressao);
672
+ return;
673
+ }
674
+ if (expressao instanceof construtos_1.Logico) {
675
+ this.verificarLogico(expressao);
676
+ return;
677
+ }
678
+ if (expressao instanceof construtos_1.Chamada) {
679
+ this.verificarChamada(expressao);
680
+ return;
681
+ }
682
+ }
664
683
  verificarCondicao(condicao) {
665
684
  if (condicao instanceof construtos_1.Agrupamento) {
666
685
  return this.verificarCondicao(condicao.expressao);
@@ -730,9 +749,34 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
730
749
  verificarTiposOperandos(binario) {
731
750
  const tipoEsquerda = this.obterTipoExpressao(binario.esquerda);
732
751
  const tipoDireita = this.obterTipoExpressao(binario.direita);
752
+ const tiposNumericos = ['inteiro', 'número', 'real'];
753
+ // Verifica se algum operando é do tipo texto em operação aritmética
754
+ if (tipoEsquerda === 'texto' || tipoDireita === 'texto') {
755
+ // Verifica se é uma operação que precisa de números (não concatenação)
756
+ const operadoresAritmeticos = ['SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO'];
757
+ if (operadoresAritmeticos.includes(binario.operador.tipo)) {
758
+ const ladoProblematico = tipoEsquerda === 'texto' ? 'esquerdo' : 'direito';
759
+ const expressaoProblematica = tipoEsquerda === 'texto' ? binario.esquerda : binario.direita;
760
+ // Verifica se a expressão problemática é um Leia ou uma variável inicializada com Leia
761
+ let mensagemAdicional = '';
762
+ if (expressaoProblematica instanceof construtos_1.Leia) {
763
+ mensagemAdicional = " Função 'leia()' retorna texto. Use 'inteiro(leia(...))' ou 'real(leia(...))' para converter.";
764
+ }
765
+ else if (expressaoProblematica instanceof construtos_1.Variavel) {
766
+ const variavel = this.gerenciadorEscopos.buscar(expressaoProblematica.simbolo.lexema);
767
+ if (variavel && variavel.valor instanceof construtos_1.Leia) {
768
+ mensagemAdicional = " A variável foi inicializada com 'leia()' que retorna texto. Use 'inteiro(leia(...))' ou 'real(leia(...))' para converter.";
769
+ }
770
+ else {
771
+ mensagemAdicional = " Use 'inteiro(...)' ou 'real(...)' para converter texto em número.";
772
+ }
773
+ }
774
+ this.erro(binario.operador, `Operação aritmética com tipo incompatível: operando ${ladoProblematico} é do tipo 'texto', mas a operação requer número.${mensagemAdicional}`);
775
+ return;
776
+ }
777
+ }
733
778
  if (tipoEsquerda && tipoDireita && tipoEsquerda !== tipoDireita) {
734
779
  // Verificar se são tipos numéricos compatíveis
735
- const tiposNumericos = ['inteiro', 'número', 'real'];
736
780
  const ambosNumericos = tiposNumericos.includes(tipoEsquerda) &&
737
781
  tiposNumericos.includes(tipoDireita);
738
782
  if (!ambosNumericos) {
@@ -819,7 +863,7 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
819
863
  }
820
864
  }
821
865
  /**
822
- * Obtém o tipo de uma expressão (pode ser Literal, Variavel, ou Binario)
866
+ * Obtém o tipo de uma expressão (pode ser Literal, Variavel, Binario, Leia, etc)
823
867
  */
824
868
  obterTipoExpressao(expressao) {
825
869
  if (expressao instanceof construtos_1.Literal) {
@@ -840,6 +884,10 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
840
884
  if (expressao instanceof construtos_1.Agrupamento) {
841
885
  return this.obterTipoExpressao(expressao.expressao);
842
886
  }
887
+ if (expressao instanceof construtos_1.Leia) {
888
+ // leia() sempre retorna texto
889
+ return 'texto';
890
+ }
843
891
  return null;
844
892
  }
845
893
  /**
@@ -895,8 +943,17 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
895
943
  switch (chamada.entidadeChamada.constructor) {
896
944
  case construtos_1.Variavel:
897
945
  let entidadeChamadaVariavel = chamada.entidadeChamada;
898
- if (!this.funcoes[entidadeChamadaVariavel.simbolo.lexema]) {
899
- this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${entidadeChamadaVariavel.simbolo.lexema}' não existe.`);
946
+ const nomeFuncao = entidadeChamadaVariavel.simbolo.lexema;
947
+ // Lista de funções built-in que não precisam ser declaradas
948
+ const funcoesBuiltIn = ['inteiro', 'real', 'número', 'texto', 'leia', 'escreva', 'tipo'];
949
+ // Classes/construtores geralmente começam com letra maiúscula
950
+ const pareceSerClasse = nomeFuncao[0] === nomeFuncao[0].toUpperCase();
951
+ // Só verifica se a função existe se não for built-in e não parecer ser classe
952
+ if (!funcoesBuiltIn.includes(nomeFuncao) &&
953
+ !pareceSerClasse &&
954
+ !this.funcoes[nomeFuncao] &&
955
+ !this.gerenciadorEscopos.buscar(nomeFuncao)) {
956
+ this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${nomeFuncao}' não existe.`);
900
957
  }
901
958
  break;
902
959
  }
@@ -999,6 +1056,8 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
999
1056
  this.verificarTipoAtribuido(declaracao);
1000
1057
  if (declaracao.inicializador) {
1001
1058
  this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
1059
+ // Verifica operações binárias no inicializador
1060
+ this.verificarExpressao(declaracao.inicializador);
1002
1061
  }
1003
1062
  const constanteCorrespondente = this.gerenciadorEscopos.buscarNoEscopoAtual(declaracao.simbolo.lexema);
1004
1063
  if (constanteCorrespondente) {
@@ -1022,6 +1081,8 @@ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemantic
1022
1081
  this.verificarTipoAtribuido(declaracao);
1023
1082
  if (declaracao.inicializador) {
1024
1083
  this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
1084
+ // Verifica operações binárias no inicializador
1085
+ this.verificarExpressao(declaracao.inicializador);
1025
1086
  switch (declaracao.inicializador.constructor) {
1026
1087
  case construtos_1.FuncaoConstruto:
1027
1088
  const funcaoConstruto = declaracao.inicializador;
@@ -2291,7 +2352,7 @@ class AvaliadorSintatico extends avaliador_sintatico_base_1.AvaliadorSintaticoBa
2291
2352
  return construtoChamada;
2292
2353
  }
2293
2354
  unario() {
2294
- if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.NAO, delegua_2.default.NEGACAO, delegua_2.default.SUBTRACAO, delegua_2.default.BIT_NOT, delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR)) {
2355
+ if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.NAO, delegua_2.default.NEGACAO, delegua_2.default.ADICAO, delegua_2.default.SUBTRACAO, delegua_2.default.BIT_NOT, delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR)) {
2295
2356
  const operador = this.simbolos[this.atual - 1];
2296
2357
  const direito = this.unario();
2297
2358
  return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES');
@@ -2312,7 +2373,32 @@ class AvaliadorSintatico extends avaliador_sintatico_base_1.AvaliadorSintaticoBa
2312
2373
  }
2313
2374
  return expressao;
2314
2375
  }
2376
+ /**
2377
+ * Verifica recursivamente se um construto é ou contém uma operação unária em um vetor.
2378
+ * Isso bloqueia padrões de ofuscação como !![] usado em operações aritméticas.
2379
+ */
2380
+ verificarOperacaoUnariaEmVetor(construto) {
2381
+ if (construto instanceof construtos_1.Unario) {
2382
+ const operando = construto.operando;
2383
+ // Verifica se o operando é um vetor
2384
+ if (operando instanceof construtos_1.Vetor || operando.tipo === 'vetor' || operando.tipo.endsWith('[]')) {
2385
+ return true;
2386
+ }
2387
+ // Verifica recursivamente para casos como !![]
2388
+ if (operando instanceof construtos_1.Unario) {
2389
+ return this.verificarOperacaoUnariaEmVetor(operando);
2390
+ }
2391
+ }
2392
+ return false;
2393
+ }
2315
2394
  verificacaoOperacoesBinariasIlegais(esquerdo, direito, operador) {
2395
+ // Bloquear operações aritméticas com operações unárias em vetores (padrão de ofuscação tipo !![] * 1)
2396
+ if (this.verificarOperacaoUnariaEmVetor(esquerdo)) {
2397
+ throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} com expressão unária aplicada a vetor.`);
2398
+ }
2399
+ if (this.verificarOperacaoUnariaEmVetor(direito)) {
2400
+ throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} com expressão unária aplicada a vetor.`);
2401
+ }
2316
2402
  if (esquerdo.tipo === 'vetor' || esquerdo.tipo.endsWith('[]')) {
2317
2403
  if (['dicionario', 'dicionário', 'nulo'].includes(direito.tipo)) {
2318
2404
  throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre vetor e ${direito.tipo}.`);
@@ -9913,7 +9999,41 @@ exports.default = {
9913
9999
  },{"../informacao-elemento-sintatico":135}],31:[function(require,module,exports){
9914
10000
  "use strict";
9915
10001
  Object.defineProperty(exports, "__esModule", { value: true });
10002
+ exports.implementacaoParticao = void 0;
9916
10003
  const informacao_elemento_sintatico_1 = require("../informacao-elemento-sintatico");
10004
+ const construtos_1 = require("../construtos");
10005
+ const excecoes_1 = require("../excecoes");
10006
+ const implementacaoParticao = (interpretador, nomePrimitiva, texto, separador, ...args) => {
10007
+ if (args.length > 0) {
10008
+ return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `A função "${nomePrimitiva}" aceita apenas um argumento.`, interpretador.linhaDeclaracaoAtual));
10009
+ }
10010
+ if (typeof texto !== 'string') {
10011
+ return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `A função "${nomePrimitiva}" só pode ser chamada em textos.`, interpretador.linhaDeclaracaoAtual));
10012
+ }
10013
+ if (separador === undefined) {
10014
+ return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, `A função "${nomePrimitiva}" requer um argumento separador.`, interpretador.linhaDeclaracaoAtual));
10015
+ }
10016
+ if (typeof separador !== 'string') {
10017
+ return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, 'O separador deve ser do tipo texto.', interpretador.linhaDeclaracaoAtual));
10018
+ }
10019
+ if (separador === '') {
10020
+ return Promise.reject(new excecoes_1.ErroEmTempoDeExecucao(null, 'O separador não pode ser uma string vazia.', interpretador.linhaDeclaracaoAtual));
10021
+ }
10022
+ const indice = texto.indexOf(separador);
10023
+ let partes;
10024
+ if (indice === -1) {
10025
+ partes = [texto, '', ''];
10026
+ }
10027
+ else {
10028
+ const antes = texto.substring(0, indice);
10029
+ const depois = texto.substring(indice + separador.length);
10030
+ partes = [antes, separador, depois];
10031
+ }
10032
+ const elementos = partes.map(p => new construtos_1.Literal(interpretador.hashArquivoDeclaracaoAtual, interpretador.linhaDeclaracaoAtual, p, 'texto'));
10033
+ const tupla = new construtos_1.TuplaN(interpretador.hashArquivoDeclaracaoAtual, interpretador.linhaDeclaracaoAtual, elementos);
10034
+ return Promise.resolve(tupla);
10035
+ };
10036
+ exports.implementacaoParticao = implementacaoParticao;
9917
10037
  exports.default = {
9918
10038
  aparar: {
9919
10039
  tipoRetorno: 'texto',
@@ -10088,6 +10208,30 @@ exports.default = {
10088
10208
  '\n\n ### Formas de uso \n',
10089
10209
  exemploCodigo: 'texto.minusculo()',
10090
10210
  },
10211
+ particao: {
10212
+ tipoRetorno: 'tupla',
10213
+ argumentos: [
10214
+ new informacao_elemento_sintatico_1.InformacaoElementoSintatico('separador', 'texto', true, [], 'O separador usado para partir o texto.'),
10215
+ ],
10216
+ implementacao: exports.implementacaoParticao,
10217
+ assinaturaFormato: 'texto.particao(separador: texto)',
10218
+ documentacao: '# `texto.particao(separador)` \n \n' +
10219
+ 'Divide o texto na primeira ocorrência do separador e retorna uma tupla com: ' +
10220
+ 'o que vem antes, o separador e o que vem depois.',
10221
+ exemploCodigo: 'texto.particao(" ")',
10222
+ },
10223
+ partição: {
10224
+ tipoRetorno: 'tupla',
10225
+ argumentos: [
10226
+ new informacao_elemento_sintatico_1.InformacaoElementoSintatico('separador', 'texto', true, [], 'O separador usado para partir o texto.'),
10227
+ ],
10228
+ implementacao: exports.implementacaoParticao,
10229
+ assinaturaFormato: 'texto.partição(separador: texto)',
10230
+ documentacao: '# `texto.partição(separador)` \n \n' +
10231
+ 'Divide o texto na primeira ocorrência do separador e retorna uma tupla com: ' +
10232
+ 'o que vem antes, o separador e o que vem depois.',
10233
+ exemploCodigo: 'texto.partição(" ")',
10234
+ },
10091
10235
  substituir: {
10092
10236
  tipoRetorno: 'texto',
10093
10237
  argumentos: [
@@ -10182,7 +10326,7 @@ exports.default = {
10182
10326
  },
10183
10327
  };
10184
10328
 
10185
- },{"../informacao-elemento-sintatico":135}],32:[function(require,module,exports){
10329
+ },{"../construtos":62,"../excecoes":128,"../informacao-elemento-sintatico":135}],32:[function(require,module,exports){
10186
10330
  "use strict";
10187
10331
  Object.defineProperty(exports, "__esModule", { value: true });
10188
10332
  const informacao_elemento_sintatico_1 = require("../informacao-elemento-sintatico");
@@ -16486,6 +16630,9 @@ class InterpretadorBase {
16486
16630
  const operando = await this.avaliar(expressao.operando);
16487
16631
  let valor = this.resolverValor(operando);
16488
16632
  switch (expressao.operador.tipo) {
16633
+ case delegua_1.default.ADICAO:
16634
+ this.verificarOperandoNumero(expressao.operador, valor);
16635
+ return +valor;
16489
16636
  case delegua_1.default.SUBTRACAO:
16490
16637
  this.verificarOperandoNumero(expressao.operador, valor);
16491
16638
  return -valor;