UNPKG

@designliquido/delegua

Version:

Linguagem de programação simples e moderna usando português estruturado.

1,119 lines (1,118 loc) 3.55 MB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Delegua = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AnalisadorSemanticoBase = void 0; const construtos_1 = require("../construtos"); const declaracoes_1 = require("../declaracoes"); const interfaces_1 = require("../interfaces"); const quebras_1 = require("../quebras"); const gerenciador_escopos_1 = require("./gerenciador-escopos"); const tabela_diagnosticos_semanticos_1 = require("./tabela-diagnosticos-semanticos"); /** * Essa classe só existe para eliminar redundância entre todos os analisadores * semânticos. Por padrão, quando um método não é implementado, ao invés de dar erro, * simplesmente passa por ele (`return Promise.resolve()`). */ class AnalisadorSemanticoBase { constructor() { this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos(); this.diagnosticos = []; } diagnosticoJaExiste(simbolo, mensagem) { return this.diagnosticos.some((d) => d.linha === simbolo.linha && d.mensagem === mensagem && d.simbolo?.lexema === simbolo.lexema); } erro(simbolo, mensagem, codigoDiagnostico, simboloRelacionado) { if (this.diagnosticoJaExiste(simbolo, mensagem)) { return; } const severidade = interfaces_1.DiagnosticoSeveridade.ERRO; this.diagnosticos.push({ simbolo: simbolo, simboloRelacionado: simboloRelacionado ?? simbolo, mensagem: mensagem, codigoDiagnostico: codigoDiagnostico ?? (0, tabela_diagnosticos_semanticos_1.inferirCodigoDiagnosticoSemantico)(mensagem, severidade), hashArquivo: simbolo.hashArquivo, linha: simbolo.linha, severidade, }); } aviso(simbolo, mensagem, codigoDiagnostico, simboloRelacionado) { if (this.diagnosticoJaExiste(simbolo, mensagem)) { return; } const severidade = interfaces_1.DiagnosticoSeveridade.AVISO; this.diagnosticos.push({ simbolo: simbolo, simboloRelacionado: simboloRelacionado ?? simbolo, mensagem: mensagem, codigoDiagnostico: codigoDiagnostico ?? (0, tabela_diagnosticos_semanticos_1.inferirCodigoDiagnosticoSemantico)(mensagem, severidade), hashArquivo: simbolo.hashArquivo, linha: simbolo.linha, severidade, }); } sugestao(simbolo, mensagem, correcoes, codigoDiagnostico, simboloRelacionado) { if (this.diagnosticoJaExiste(simbolo, mensagem)) { return; } const severidade = interfaces_1.DiagnosticoSeveridade.SUGESTAO; this.diagnosticos.push({ simbolo: simbolo, simboloRelacionado: simboloRelacionado ?? simbolo, mensagem: mensagem, codigoDiagnostico: codigoDiagnostico ?? (0, tabela_diagnosticos_semanticos_1.inferirCodigoDiagnosticoSemantico)(mensagem, severidade), hashArquivo: simbolo.hashArquivo, linha: simbolo.linha, severidade, colunaInicio: correcoes[0]?.colunaInicio, colunaFim: correcoes[0]?.colunaFim, correcoes: correcoes, }); } comparacaoArgumentosContraParametrosFuncao(simboloFuncao, parametros, argumentos) { if (parametros.length !== argumentos.length) { this.erro(simboloFuncao, `Função '${simboloFuncao.lexema}' espera ${parametros.length} parâmetros. Atual: ${argumentos.length}.`); } for (let [indice, parametro] of parametros.entries()) { const argumento = argumentos[indice]; if (argumento) { // Usando `obterTipoExpressao` para resolver adequadamente o tipo do argumento, // independentemente de ser um `Literal` (tipo já resolvido), `Variavel` (tipo inferido do // escopo), `Binario`, `Agrupamento`, ou qualquer outro construto (retorna `null` quando // o tipo não pode ser determinado em tempo de compilação). const tipoArgumento = this.obterTipoExpressao(argumento); // Validar apenas quando ambos os lados têm um tipo específico e determinável. // Ignorar quando `tipoArgumento` é nulo (por exemplo, resultado de `Chamada`) ou `qualquer`, // evitando falsos positivos para expressões cujo tipo é desconhecido em tempo de compilação. if (tipoArgumento && tipoArgumento !== 'qualquer' && parametro.tipoDado) { if (parametro.tipoDado === 'texto' && tipoArgumento !== 'texto') { this.erro(simboloFuncao, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${tipoArgumento}).`); } else if (['inteiro', 'número', 'real'].includes(parametro.tipoDado)) { // Delegua suporta conversões implícitas entre tipos numéricos, mas não // entre texto e número. if (!['inteiro', 'número', 'real'].includes(tipoArgumento)) { this.erro(simboloFuncao, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${tipoArgumento}).`); } } } } } } /** * Obtém o tipo de uma expressão (pode ser Literal, Variavel, Binario, Leia, etc) */ obterTipoExpressao(expressao) { if (expressao instanceof construtos_1.Literal) { return expressao.tipo; } if (expressao instanceof construtos_1.Variavel) { const variavel = this.gerenciadorEscopos.buscar(expressao.simbolo.lexema); return variavel?.tipo || null; } if (expressao instanceof construtos_1.Binario) { // Para binários, tentamos inferir o tipo baseado nos operandos return this.inferirTipoBinario(expressao); } if (expressao instanceof construtos_1.Logico) { // Operadores lógicos sempre retornam tipo lógico return 'lógico'; } if (expressao instanceof construtos_1.Agrupamento) { return this.obterTipoExpressao(expressao.expressao); } if (expressao instanceof construtos_1.Leia) { // leia() sempre retorna texto return 'texto'; } return null; } /** * Infere o tipo de resultado de uma operação binária */ inferirTipoBinario(binario) { const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO']; const operadoresComparacao = [ 'MAIOR', 'MAIOR_IGUAL', 'MENOR', 'MENOR_IGUAL', 'IGUAL', 'DIFERENTE', ]; // Operadores de comparação sempre retornam lógico if (operadoresComparacao.includes(binario.operador.tipo)) { return 'lógico'; } const tipoEsquerda = this.obterTipoExpressao(binario.esquerda); const tipoDireita = this.obterTipoExpressao(binario.direita); if (!tipoEsquerda || !tipoDireita) { return null; } if (operadoresMatematicos.includes(binario.operador.tipo)) { const tiposNumericos = ['inteiro', 'número', 'real']; if (tiposNumericos.includes(tipoEsquerda) && tiposNumericos.includes(tipoDireita)) { // Se um dos lados é 'real', o resultado é 'real' if (tipoEsquerda === 'real' || tipoDireita === 'real') { return 'real'; } return 'número'; } // Concatenação de textos if (tipoEsquerda === 'texto' || tipoDireita === 'texto') { return 'texto'; } } return 'qualquer'; } /** * Marca as variáveis usadas em uma expressão. */ marcarVariaveisUsadasEmExpressao(expressao) { if (expressao instanceof declaracoes_1.Expressao) { this.marcarVariaveisUsadasEmExpressao(expressao.expressao); return; } if (expressao instanceof construtos_1.Variavel) { this.gerenciadorEscopos.marcarComoUsada(expressao.simbolo.lexema); return; } if (expressao instanceof construtos_1.Binario) { this.marcarVariaveisUsadasEmExpressao(expressao.esquerda); this.marcarVariaveisUsadasEmExpressao(expressao.direita); return; } if (expressao instanceof construtos_1.Agrupamento) { this.marcarVariaveisUsadasEmExpressao(expressao.expressao); return; } if (expressao instanceof construtos_1.Chamada) { this.marcarVariaveisUsadasEmExpressao(expressao.entidadeChamada); for (const arg of expressao.argumentos) { this.marcarVariaveisUsadasEmExpressao(arg); } return; } if (expressao instanceof construtos_1.AcessoMetodo || expressao instanceof construtos_1.AcessoMetodoOuPropriedade || expressao instanceof construtos_1.AcessoPropriedade) { this.marcarVariaveisUsadasEmExpressao(expressao.objeto); return; } if (expressao instanceof construtos_1.Logico) { this.marcarVariaveisUsadasEmExpressao(expressao.esquerda); this.marcarVariaveisUsadasEmExpressao(expressao.direita); return; } if (expressao instanceof construtos_1.Unario) { this.marcarVariaveisUsadasEmExpressao(expressao.operando); return; } if (expressao instanceof construtos_1.AcessoIndiceVariavel) { this.marcarVariaveisUsadasEmExpressao(expressao.entidadeChamada); this.marcarVariaveisUsadasEmExpressao(expressao.indice); return; } if (expressao instanceof construtos_1.AjudaComoConstruto) { if (expressao.valor) { this.marcarVariaveisUsadasEmExpressao(expressao.valor); } return; } if (expressao instanceof construtos_1.Literal && typeof expressao.valor === 'string') { this.verificarInterpolacaoTexto(expressao.valor, expressao); return; } // TODO: Adicionar outros tipos de expressões conforme necessário. } /** * Stub para ser sobrescrito por subclasses que implementam análise de interpolações. */ verificarInterpolacaoTexto(_texto, _literal) { // implementado nas subclasses } /** * Analisa se todos os caminhos retornam * @returns true se todos os caminhos retornam, false caso contrário */ todosOsCaminhosRetornam(declaracoes) { return this.verificarBlocoRetorna(declaracoes); } verificarBlocoRetorna(declaracoes) { for (let i = 0; i < declaracoes.length; i++) { const declaracao = declaracoes[i]; if (declaracao instanceof declaracoes_1.Retorna) { return true; } if (declaracao instanceof declaracoes_1.Se) { const todosOsCaminhosSe = this.verificarSeRetorna(declaracao); if (todosOsCaminhosSe) { return true; } } if (declaracao instanceof declaracoes_1.Escolha) { const todosOsCaminhosEscolha = this.verificarEscolhaRetorna(declaracao); if (todosOsCaminhosEscolha) { return true; } } } return false; } verificarSeRetorna(declaracaoSe) { const caminhoEntaoResolvido = declaracaoSe.caminhoEntao; const entaoRetorna = this.verificarBlocoRetorna(caminhoEntaoResolvido.declaracoes); const caminhoSenaoResolvido = declaracaoSe.caminhoSenao; if (!caminhoSenaoResolvido || caminhoSenaoResolvido.declaracoes?.length === 0) { return false; } if (caminhoSenaoResolvido instanceof declaracoes_1.Se && caminhoSenaoResolvido.caminhoEntao.declaracoes?.length === 1) { const senaoSeRetorna = this.verificarSeRetorna(caminhoSenaoResolvido); return entaoRetorna && senaoSeRetorna; } const senaoRetorna = this.verificarBlocoRetorna(declaracaoSe.caminhoSenao.declaracoes); return entaoRetorna && senaoRetorna; } verificarEscolhaRetorna(declaracaoEscolha) { let temPadrao = false; // Verifica se todos os caminhos retornam for (let caminho of declaracaoEscolha.caminhos) { const caminhoRetorna = this.verificarBlocoRetorna(caminho.declaracoes); if (!caminhoRetorna) { return false; } // Verifica se há um caso padrão if (caminho.condicoes.length === 0) { temPadrao = true; } } // Se não há caso padrão, não podemos garantir que todos os caminhos retornam return temPadrao; } visitarDeclaracaoTextoDocumentacao(declaracao) { return Promise.resolve(); } visitarExpressaoAcessoIntervaloVariavel(expressao) { return Promise.resolve(); } visitarExpressaoTuplaN(expressao) { return Promise.resolve(); } visitarExpressaoComentario(expressao) { // Comentários não afetam a análise semântica, então não faz nada. return Promise.resolve(); } visitarExpressaoSeparador(expressao) { // Separadores não afetam a análise semântica, então não faz nada. return Promise.resolve(); } adicionarDiagnostico(simbolo, mensagem, severidade = interfaces_1.DiagnosticoSeveridade.AVISO, codigoDiagnostico, simboloRelacionado) { this.diagnosticos.push({ simbolo: simbolo, simboloRelacionado: simboloRelacionado ?? simbolo, mensagem: mensagem, codigoDiagnostico: codigoDiagnostico ?? (0, tabela_diagnosticos_semanticos_1.inferirCodigoDiagnosticoSemantico)(mensagem, severidade), hashArquivo: simbolo.hashArquivo, linha: simbolo.linha, severidade: severidade, }); } visitarExpressaoArgumentoReferenciaFuncao(expressao) { return Promise.resolve(); } visitarExpressaoReferenciaFuncao(expressao) { return Promise.resolve(); } visitarExpressaoAcessoMetodo(expressao) { return Promise.resolve(); } visitarExpressaoAcessoPropriedade(expressao) { return Promise.resolve(); } visitarDeclaracaoCabecalhoPrograma(declaracao) { return Promise.resolve(); } visitarDeclaracaoClasse(declaracao) { return Promise.resolve(); } visitarDeclaracaoComentario(declaracao) { return Promise.resolve(); } visitarDeclaracaoConst(declaracao) { return Promise.resolve(); } visitarDeclaracaoConstMultiplo(declaracao) { return Promise.resolve(); } visitarExpressaoDeAtribuicao(expressao) { return Promise.resolve(); } visitarDeclaracaoDeExpressao(declaracao) { return Promise.resolve(); } visitarDeclaracaoDefinicaoFuncao(declaracao) { return Promise.resolve(); } visitarDeclaracaoEnquanto(declaracao) { return Promise.resolve(); } visitarDeclaracaoEscolha(declaracao) { return Promise.resolve(); } visitarDeclaracaoEscreva(declaracao) { return Promise.resolve(); } visitarDeclaracaoFazer(declaracao) { return Promise.resolve(); } visitarDeclaracaoImportar(declaracao) { return Promise.resolve(); } visitarDeclaracaoInicioAlgoritmo(declaracao) { return Promise.resolve(); } visitarDeclaracaoPara(declaracao) { return Promise.resolve(); } visitarDeclaracaoParaCada(declaracao) { return Promise.resolve(); } visitarDeclaracaoSe(declaracao) { return Promise.resolve(); } visitarDeclaracaoTendoComo(declaracao) { return Promise.resolve(); } visitarDeclaracaoTente(declaracao) { return Promise.resolve(); } visitarDeclaracaoVar(declaracao) { return Promise.resolve(); } visitarDeclaracaoVarMultiplo(declaracao) { return Promise.resolve(); } visitarExpressaoAcessoIndiceVariavel(expressao) { return Promise.resolve(); } visitarExpressaoAcessoElementoMatriz(expressao) { return Promise.resolve(); } visitarExpressaoAcessoMetodoOuPropriedade(expressao) { return Promise.resolve(); } visitarExpressaoAgrupamento(expressao) { return Promise.resolve(); } visitarExpressaoAtribuicaoPorIndice(expressao) { return Promise.resolve(); } visitarExpressaoAtribuicaoPorIndicesMatriz(expressao) { return Promise.resolve(); } visitarExpressaoBinaria(expressao) { return Promise.resolve(); } visitarExpressaoBloco(declaracao) { return Promise.resolve(); } visitarExpressaoContinua(declaracao) { return new quebras_1.ContinuarQuebra(); } visitarExpressaoDeChamada(expressao) { return Promise.resolve(); } visitarExpressaoDefinirValor(expressao) { return Promise.resolve(); } visitarExpressaoFuncaoConstruto(expressao) { return Promise.resolve(); } visitarExpressaoDeVariavel(expressao) { return Promise.resolve(); } visitarExpressaoDicionario(expressao) { return Promise.resolve(); } visitarExpressaoExpressaoRegular(expressao) { return Promise.resolve(new RegExp('')); } visitarDeclaracaoEscrevaMesmaLinha(declaracao) { return Promise.resolve(); } visitarExpressaoFalhar(expressao) { return Promise.resolve(); } visitarExpressaoFimPara(declaracao) { return Promise.resolve(); } visitarExpressaoFormatacaoEscrita(declaracao) { return Promise.resolve(); } visitarExpressaoIsto(expressao) { return Promise.resolve(); } visitarExpressaoLeia(expressao) { return Promise.resolve(); } visitarExpressaoLiteral(expressao) { return Promise.resolve(); } visitarExpressaoLogica(expressao) { return Promise.resolve(); } visitarExpressaoRetornar(declaracao) { return Promise.resolve(new quebras_1.RetornoQuebra(null)); } visitarExpressaoSuper(expressao) { return Promise.resolve(); } visitarExpressaoSustar(declaracao) { return new quebras_1.SustarQuebra(); } visitarExpressaoTupla(expressao) { return Promise.resolve(); } visitarExpressaoTipoDe(expressao) { return Promise.resolve(); } visitarExpressaoUnaria(expressao) { return Promise.resolve(); } visitarExpressaoVetor(expressao) { return Promise.resolve(); } visitarExpressaoMorsa(expressao) { return Promise.resolve(); } visitarExpressaoBote(expressao) { return Promise.resolve(); } } exports.AnalisadorSemanticoBase = AnalisadorSemanticoBase; },{"../construtos":67,"../declaracoes":116,"../interfaces":176,"../quebras":247,"./gerenciador-escopos":5,"./tabela-diagnosticos-semanticos":8}],2:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AnalisadorSemantico = void 0; const construtos_1 = require("../construtos"); const declaracoes_1 = require("../declaracoes"); const erros_1 = require("../interfaces/erros"); const comum_1 = require("../avaliador-sintatico/comum"); const micro_avaliador_sintatico_1 = require("../avaliador-sintatico/micro-avaliador-sintatico"); const micro_lexador_1 = require("../lexador/micro-lexador"); const analisador_semantico_base_1 = require("./analisador-semantico-base"); const gerenciador_escopos_1 = require("./gerenciador-escopos"); const pilha_variaveis_1 = require("./pilha-variaveis"); /** * O Analisador Semântico de Delégua. */ class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemanticoBase { constructor() { super(); this.microLexador = new micro_lexador_1.MicroLexador(); this.microAvaliadorSintatico = new micro_avaliador_sintatico_1.MicroAvaliadorSintatico(); this.pilhaVariaveis = new pilha_variaveis_1.PilhaVariaveis(); this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos(); this.funcoes = {}; this.classesDeclaradas = new Set(); this.classesRegistradas = new Map(); this.classesExternasRegistradas = new Map(); this.classeAtualEmAnalise = null; this.atual = 0; this.diagnosticos = []; } registrarClassesExternas(classes) { for (const classe of classes) { this.classesExternasRegistradas.set(classe.simbolo.lexema, classe); } } verificarTipoAtribuido(declaracao) { if (declaracao.tipo) { if (['vetor', 'qualquer[]', 'inteiro[]', 'texto[]'].includes(declaracao.tipo)) { if (declaracao.inicializador instanceof construtos_1.Vetor) { const vetor = declaracao.inicializador; const vetorSemSeparadores = vetor.elementos; if (declaracao.tipo === 'inteiro[]') { const apenasValores = vetorSemSeparadores.find((v) => typeof v?.valor !== 'number'); if (apenasValores) { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo vetor de inteiro ou real. Atual: ${vetor.tipo}.`); } } if (declaracao.tipo === 'texto[]') { const apenasValores = vetorSemSeparadores.find((v) => typeof v?.valor !== 'string'); if (apenasValores) { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo vetor de texto. Atual: ${vetor.tipo}.`); } } } else if (declaracao.inicializador instanceof construtos_1.Chamada) { // Chamadas de método/função podem retornar vetores. // A validação detalhada é feita em tempo de execução. } else { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um vetor de elementos.`); } } if (declaracao.inicializador instanceof construtos_1.Literal) { const literal = declaracao.inicializador; if (declaracao.tipo === 'texto' && literal.tipo !== 'texto') { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo texto. Atual: ${literal.tipo}.`); } if (['inteiro', 'número', 'real'].includes(declaracao.tipo) && !['inteiro', 'número', 'real'].includes(literal.tipo)) { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}': é esperado um valor do tipo número. Atual: ${literal.tipo}.`); } } if (declaracao.inicializador instanceof construtos_1.Leia) { if (!['qualquer', 'texto'].includes(declaracao.tipo)) { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', Função 'leia()' sempre retorna 'texto'.`); } } } } visitarExpressaoTipoDe(expressao) { return this.verificarTipoDe(expressao.valor); } /** * Método recursivo para verificar o tipo de um construto, usado principalmente para validar * o uso de `tipoDe` e `falhar()`. * @param {ConstrutoInterface} valor O construto a ser avaliado. * @returns {Promise<any>} O tipo do construto, ou `Promise.resolve()` se o tipo não puder ser * determinado neste estágio da análise. */ verificarTipoDe(valor) { switch (valor.constructor) { case construtos_1.Agrupamento: const valorAgrupamento = valor; return this.verificarTipoDe(valorAgrupamento.expressao); case construtos_1.Binario: const valorBinario = valor; this.verificarTipoDe(valorBinario.direita); this.verificarTipoDe(valorBinario.esquerda); break; case construtos_1.Variavel: const valorVariavel = valor; return this.verificarVariavel(valorVariavel); } return Promise.resolve(); } visitarExpressaoFalhar(expressao) { return this.verificarFalhar(expressao.explicacao); } /** * Método recursivo para verificar se um construto passado para `falhar()` é válido, ou seja, se é * do tipo texto ou pode ser avaliado como texto. * @param {ConstrutoInterface} valor O construto a ser avaliado. * @returns {Promise<any>} O tipo do construto, ou `Promise.resolve()` se o tipo não puder ser * determinado neste estágio da análise. */ verificarFalhar(valor) { if (valor instanceof construtos_1.Binario) { this.verificarFalhar(valor.direita); this.verificarFalhar(valor.esquerda); } if (valor instanceof construtos_1.Agrupamento) { return this.verificarFalhar(valor.expressao); } if (valor instanceof construtos_1.Variavel) { return this.verificarVariavel(valor); } return Promise.resolve(); } comparacaoArgumentosContraParametrosFuncao(simboloFuncao, parametros, argumentos) { if (parametros.length !== argumentos.length) { this.erro(simboloFuncao, `Função '${simboloFuncao.lexema}' espera ${parametros.length} parâmetros. Atual: ${argumentos.length}.`); } for (let [indice, parametro] of parametros.entries()) { const argumento = argumentos[indice]; if (!argumento) continue; const tipoArgumento = argumento.tipo ?? 'qualquer'; if (tipoArgumento === 'qualquer') continue; if (parametro.tipoDado === 'texto' && tipoArgumento !== 'texto') { this.erro(simboloFuncao, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${tipoArgumento}).`); } else if (['inteiro', 'número', 'real'].includes(parametro.tipoDado)) { // Delégua pode trabalhar com conversões implícitas entre tipos numéricos. if (!['inteiro', 'número', 'real'].includes(tipoArgumento)) { this.erro(simboloFuncao, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${tipoArgumento}).`); } } } } visitarChamadaPorArgumentoReferenciaFuncao(argumentoReferenciaFuncao, argumentos) { const variavelCorrespondente = this.gerenciadorEscopos.buscar(argumentoReferenciaFuncao.simboloFuncao.lexema)?.valor; if (!variavelCorrespondente) { return; } this.comparacaoArgumentosContraParametrosFuncao(argumentoReferenciaFuncao.simboloFuncao, variavelCorrespondente.parametros, argumentos); } visitarChamadaPorReferenciaFuncao(referenciaFuncao, argumentos) { const funcaoCorrespondente = this.funcoes[referenciaFuncao.simboloFuncao.lexema]; if (!funcaoCorrespondente) { return; } this.comparacaoArgumentosContraParametrosFuncao(referenciaFuncao.simboloFuncao, funcaoCorrespondente.valor.parametros, argumentos); } visitarChamadaPorVariavel(entidadeChamadaVariavel, argumentos) { const variavel = entidadeChamadaVariavel; const nomeFuncao = variavel.simbolo.lexema; const funcoesNativas = [ 'aleatorio', 'aleatorioEntre', 'algum', 'arredondar', 'clonar', 'encontrar', 'encontrarIndice', 'encontrarUltimo', 'encontrarUltimoIndice', 'escreva', 'filtrarPor', 'incluido', 'inteiro', 'intervalo', 'leia', 'longo', 'mapear', 'maximo', 'minimo', 'numero', 'número', 'ordenar', 'paraCada', 'primeiroEmCondicao', 'real', 'reduzir', 'somar', 'tamanho', 'texto', 'tipo', 'todos', 'todosEmCondicao', 'tupla', 'vetor', ]; const pareceSerClasse = nomeFuncao[0] === nomeFuncao[0].toUpperCase(); if (funcoesNativas.includes(nomeFuncao) || pareceSerClasse) { return Promise.resolve(); } const funcaoChamada = this.gerenciadorEscopos.buscar(nomeFuncao) || this.funcoes[nomeFuncao]; if (!funcaoChamada) { this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${nomeFuncao}' não existe.`); return Promise.resolve(); } const funcao = funcaoChamada.valor; this.comparacaoArgumentosContraParametrosFuncao(entidadeChamadaVariavel.simbolo, funcao.parametros, argumentos); } async visitarExpressaoDeChamada(expressao) { for (const argumento of expressao.argumentos) { if (argumento instanceof construtos_1.Variavel) { this.gerenciadorEscopos.marcarComoUsada(argumento.simbolo.lexema); } } switch (expressao.entidadeChamada.constructor) { case construtos_1.AcessoMetodo: // Marca o objeto como usado quando seus métodos são chamados (ex: thor.corre()) const entidadeChamadaAcessoMetodo = expressao.entidadeChamada; this.marcarVariaveisUsadasEmExpressao(entidadeChamadaAcessoMetodo.objeto); break; case construtos_1.AcessoMetodoOuPropriedade: // Marca o objeto como usado quando seus métodos/propriedades são acessados (ex: thor.corre()) // e verifica acesso a membros privados/protegidos const entidadeChamadaAcessoMetodoOuPropriedade = expressao.entidadeChamada; this.marcarVariaveisUsadasEmExpressao(entidadeChamadaAcessoMetodoOuPropriedade.objeto); await expressao.entidadeChamada.aceitar(this); break; case construtos_1.ArgumentoReferenciaFuncao: const entidadeChamadaArgumentoReferenciaFuncao = expressao.entidadeChamada; this.visitarChamadaPorArgumentoReferenciaFuncao(entidadeChamadaArgumentoReferenciaFuncao, expressao.argumentos); break; case construtos_1.ReferenciaFuncao: const entidadeChamadaReferenciaFuncao = expressao.entidadeChamada; this.visitarChamadaPorReferenciaFuncao(entidadeChamadaReferenciaFuncao, expressao.argumentos); break; case construtos_1.Variavel: const entidadeChamadaVariavel = expressao.entidadeChamada; this.visitarChamadaPorVariavel(entidadeChamadaVariavel, expressao.argumentos); break; } return Promise.resolve(); } visitarExpressaoDeAtribuicao(expressao) { let simboloAlvo; switch (expressao.alvo.constructor) { case construtos_1.Variavel: const alvoVariavel = expressao.alvo; simboloAlvo = alvoVariavel.simbolo; break; default: return Promise.resolve(); } const variavel = this.gerenciadorEscopos.buscar(simboloAlvo.lexema); if (!variavel) { this.erro(simboloAlvo, `Variável '${simboloAlvo.lexema}' ainda não foi declarada até este ponto.`); return Promise.resolve(); } if (variavel.imutavel) { this.erro(simboloAlvo, `Constante '${simboloAlvo.lexema}' não pode ser modificada.`); return Promise.resolve(); } // Marca como inicializada após atribuição this.gerenciadorEscopos.marcarComoInicializada(simboloAlvo.lexema, expressao.valor); // Marca variáveis usadas no valor atribuído (ex: idade = ano - inteiro(leia(...))). this.marcarVariaveisUsadasEmExpressao(expressao.valor); // Atualiza tipo se a variável não foi tipada explicitamente if (variavel.tipo === 'qualquer') { const tipoInferido = this.obterTipoExpressao(expressao.valor); if (tipoInferido && tipoInferido !== 'qualquer') { variavel.tipo = tipoInferido; } } // TODO: Readaptar para trabalhar com `expressao.alvo` sendo um construto. switch (expressao.alvo.constructor) { case construtos_1.Variavel: const alvoVariavel = expressao.alvo; simboloAlvo = alvoVariavel.simbolo; break; default: // throw new Error(`Implementar atribuição para ${expressao.alvo.constructor}.`); return Promise.resolve(); } let valor = this.gerenciadorEscopos.buscar(simboloAlvo.lexema); if (!valor) { this.erro(simboloAlvo, `Variável ${simboloAlvo.lexema} ainda não foi declarada até este ponto.`); return Promise.resolve(); } if (valor.tipo) { if (expressao.valor instanceof construtos_1.Literal && valor.tipo.includes('[]')) { this.erro(simboloAlvo, `Atribuição inválida, esperado tipo '${valor.tipo}' na atribuição.`); return Promise.resolve(); } if (expressao.valor instanceof construtos_1.Vetor && !valor.tipo.includes('[]')) { this.erro(simboloAlvo, `Atribuição inválida, esperado tipo '${valor.tipo}' na atribuição.`); return Promise.resolve(); } if (expressao.valor instanceof construtos_1.Literal) { let valorLiteral = typeof expressao.valor.valor; if (!['qualquer'].includes(valor.tipo)) { if (valorLiteral === 'string') { if (valor.tipo != 'texto') { this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`); return Promise.resolve(); } } if (valorLiteral === 'number') { if (!['inteiro', 'número', 'real'].includes(valor.tipo)) { this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`); return Promise.resolve(); } } } } if (expressao.valor instanceof construtos_1.Vetor) { let valoresSemSeparador = expressao.valor.elementos; if (!['qualquer[]'].includes(valor.tipo)) { if (valor.tipo === 'texto[]') { if (!valoresSemSeparador.every((v) => typeof v.valor === 'string')) { this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`); return Promise.resolve(); } } if (['inteiro[]', 'numero[]'].includes(valor.tipo)) { if (!valoresSemSeparador.every((v) => typeof v.valor === 'number')) { this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`); return Promise.resolve(); } } } } } return Promise.resolve(); } async visitarDeclaracaoDeExpressao(declaracao) { return await declaracao.expressao.aceitar(this); } async visitarExpressaoBloco(declaracao) { this.gerenciadorEscopos.empilharEscopo(); try { for (const declaracaoBloco of declaracao.declaracoes) { await declaracaoBloco.aceitar(this); } } finally { this.gerenciadorEscopos.desempilharEscopo(); } return Promise.resolve(); } visitarExpressaoAcessoIndiceVariavel(expressao) { this.marcarVariaveisUsadasEmExpressao(expressao.entidadeChamada); this.marcarVariaveisUsadasEmExpressao(expressao.indice); return Promise.resolve(); } visitarExpressaoAtribuicaoPorIndice(expressao) { this.marcarVariaveisUsadasEmExpressao(expressao.objeto); this.marcarVariaveisUsadasEmExpressao(expressao.indice); this.marcarVariaveisUsadasEmExpressao(expressao.valor); return Promise.resolve(); } visitarDeclaracaoAjuda(declaracao) { if (declaracao.elemento) { this.marcarVariaveisUsadasEmExpressao(declaracao.elemento); } return Promise.resolve(); } visitarExpressaoAjuda(expressao) { if (expressao.valor) { this.marcarVariaveisUsadasEmExpressao(expressao.valor); } return Promise.resolve(); } async visitarDeclaracaoEscolha(declaracao) { const identificadorOuLiteral = declaracao.identificadorOuLiteral; const tipo = identificadorOuLiteral.tipo || 'qualquer'; const tiposLiteraisCasos = []; this.marcarVariaveisUsadasEmExpressao(identificadorOuLiteral); for (let caminho of declaracao.caminhos) { for (const declaracao of caminho.declaracoes) { await declaracao.aceitar(this); } for (let condicao of caminho.condicoes) { switch (condicao.constructor) { case construtos_1.Literal: const condicaoLiteral = condicao; const tiposNumericos = ['inteiro', 'número', 'real']; const ambosSaoNumericos = tiposNumericos.includes(condicaoLiteral.tipo) && tiposNumericos.includes(tipo); if (condicaoLiteral.tipo !== tipo && !ambosSaoNumericos && tipo !== 'qualquer') { this.erro({ lexema: condicaoLiteral.valor, tipo: condicaoLiteral.tipo, linha: condicaoLiteral.linha, hashArquivo: condicaoLiteral.hashArquivo, }, `'caso ${condicaoLiteral.valor}:' não é do mesmo tipo esperado em 'escolha' (esperado: ${tipo}, atual: ${condicaoLiteral.tipo}).`); } tiposLiteraisCasos.push(condicaoLiteral.tipo); break; case construtos_1.Variavel: const condicaoVariavel = condicao; this.verificarVariavel(condicaoVariavel); const variavelHipotetica = this.gerenciadorEscopos.buscar(condicaoVariavel.simbolo.lexema); if (variavelHipotetica && typeof variavelHipotetica.valor !== tipo) { this.erro(condicaoVariavel.simbolo, `'caso ${condicaoVariavel.simbolo.lexema}:' não é do mesmo tipo esperado em 'escolha'`); } break; } } } if (tipo === 'qualquer' && tiposLiteraisCasos.length > 0 && identificadorOuLiteral instanceof construtos_1.Variavel) { const tiposUnicos = [...new Set(tiposLiteraisCasos)]; if (tiposUnicos.length === 1) { const tipoInferido = tiposUnicos[0]; const variavelEscopo = this.gerenciadorEscopos.buscar(identificadorOuLiteral.simbolo.lexema); if (variavelEscopo) { this.sugestao(identificadorOuLiteral.simbolo, `Um tipo melhor pode ser inferido para '${identificadorOuLiteral.simbolo.lexema}': '${tipoInferido}'`, [ { titulo: `Alterar tipo de '${identificadorOuLiteral.simbolo.lexema}' para '${tipoInferido}'`, textoOriginal: 'qualquer', textoSubstituto: tipoInferido, linha: variavelEscopo.linha, colunaInicio: 0, colunaFim: 0, }, ]); } } } if (declaracao.caminhoPadrao) { for (const decl of declaracao.caminhoPadrao.declaracoes) { await decl.aceitar(this); } } return Promise.resolve(); } async visitarDeclaracaoEnquanto(declaracao) { // Marca variáveis usadas na condição. this.marcarVariaveisUsadasEmExpressao(declaracao.condicao); // Verifica a condição (incluindo validações de tipo para operadores lógicos). await this.verificarCondicao(declaracao.condicao); // Visita corpo para que usos/atribuições dentro do laço sejam analisados. for (const declaracaoCorpo of declaracao.corpo.declaracoes) { await declaracaoCorpo.aceitar(this); } return Promise.resolve(); } async visitarDeclaracaoFazer(declaracao) { // Visita corpo para que usos/atribuições dentro do bloco sejam analisados. for (const declaracaoCorpo of declaracao.caminhoFazer.declaracoes) { await declaracaoCorpo.aceitar(this); } // Marca variáveis usadas na condição this.marcarVariaveisUsadasEmExpressao(declaracao.condicaoEnquanto); // Verifica a condição return this.verificarCondicao(declaracao.condicaoEnquanto); } async visitarDeclaracaoPara(declaracao) { this.gerenciadorEscopos.empilharEscopo(); try { if (Array.isArray(declaracao.inicializador)) { for (const inicializador of declaracao.inicializador) { await inicializador.aceitar(this); } } else if (declaracao.inicializador) { await declaracao.inicializador.aceitar(this); } // O laço precisa visitar condição/incremento/corpo para registrar usos de variáveis. if (declaracao.condicao) { this.marcarVariaveisUsadasEmExpressao(declaracao.condicao); await this.verificarCondicao(declaracao.condicao); } if (declaracao.incrementar) { this.marcarVariaveisUsadasEmExpressao(declaracao.incrementar); this.verificarExpressao(declaracao.incrementar); } for (const declaracaoCorpo of declaracao.corpo.declaracoes) { await declaracaoCorpo.aceitar(this); } } finally { this.gerenciadorEscopos.desempilharEscopo(); } return Promise.resolve(); } async visitarDeclaracaoParaCada(declaracao) { this.marcarVariaveisUsadasEmExpressao(declaracao.vetorOuDicionario); this.gerenciadorEscopos.empilharEscopo(); try { if (declaracao.variavelIteracao instanceof construtos_1.Variavel) { const nome = declaracao.variavelIteracao.simbolo.lexema; this.gerenciadorEscopos.declarar(nome, { nome, tipo: 'qualquer', imutavel: false, valor: null, inicializada: true, usada: false, hashArquivo: declaracao.hashArquivo, linha: declaracao.linha, }); } else if (declaracao.variavelIteracao instanceof construtos_1.Dupla) { const dupla = declaracao.variavelIteracao; for (const parte of [dupla.primeiro, dupla.segundo]) { if (parte instanceof construtos_1.Variavel) { const nome = parte.simbolo.lexema; this.gerenciadorEscopos.declarar(nome, { nome, tipo: 'qualquer', imutavel: false, valor: null, inicializada: true, usada: false, hashArquivo: declaracao.hashArquivo, linha: declaracao.linha, }); } } } if (declaracao.corpo) { await declaracao.corpo.aceitar(this); } } finally { this.gerenciadorEscopos.desempilharEscopo(); } return Promise.resolve(); } async visitarDeclaracaoSe(declaracao) { // Marca variáveis usadas na condição this.marcarVariaveisUsadasEmExpressao(declaracao.condicao); // Verifica a condição (incluindo validação de tipos para operadores lógicos) await this.verificarCondicao(declaracao.condicao); if (declaracao.caminhoEntao) { await declaracao.caminhoEntao.aceitar(this); } if (declaracao.caminhosSeSenao && declaracao.caminhosSeSenao.length > 0) { for (const caminhoSeSenao of declaracao.caminhosSeSenao) { this.marcarVariaveisUsadasEmExpressao(caminhoSeSenao.condicao); await this.verificarCondicao(caminhoSeSenao.condicao); await caminhoSeSenao.caminho.aceitar(this); } } if (declaracao.caminhoSenao) { await declaracao.caminhoSenao.aceitar(this); } return Promise.resolve(); } /** * Verifica uma expressão recursivamente, incluindo operações binárias */ verificarExpressao(expressao) { if (expressao instanceof construtos_1.Agrupamento) { this.verificarExpressao(expressao.expressao); return; } if (expressao instanceof construtos_1.Binario) { this.verificarBinario(expressao); return; } if (expressao instanceof construtos_1.Logico) { this.verificarLogico(expressao); return; } if (expressao instanceof construtos_1.Chamada) { this.verificarChamada(expressao); return; } } verificarCondicao(condicao) { if (condicao instanceof construtos_1.Agrupamento) { return this.verificarCondicao(condicao.expressao); } if (condicao instanceof construtos_1.Variavel) { return this.verificarVariavelBinaria(condicao); } if (condicao instanceof construtos_1.Binario) { return this.verificarBinario(condicao); } if (condicao instanceof construtos_1.Logico) { return this.verificarLogico(condicao); } if (condicao instanceof construtos_1.Chamada) { return this.verificarChamada(condicao); } return Promise.resolve(); } verificarVariavelBinaria(variavel) { this.verificarVariavel(variavel); const variavelHipotetica = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema); if (variavelHipotetica && !(variavelHipotetica.valor instanceof construtos_1.Binario) && typeof variavelHipotetica.valor !== 'boolean') { this.erro(variavel.simbolo, `Esperado tipo 'lógico' na condição do 'enquanto'.`); } return Promise.resolve(); } verificarVariavel(variavel) { const variavelEscopo = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema); if (!variavelEscopo) { if (this.funcoes[variavel.simbolo.lexema]) { return Promise.resolve(); } this.erro(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' ainda não foi declarada até este ponto.`); return Promise.resolve(); } // Marca como usada this.gerenciadorEscopos.marcarComoUsada(variavel.simbolo.lexema); // Verifica se foi inicializada if (!variavelEscopo.inicializada) { this.aviso(variavel.simbolo, `Variá