@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
949 lines • 44.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnalisadorSemanticoPitugues = 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 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 Pituguês.
*/
class AnalisadorSemanticoPitugues extends analisador_semantico_base_1.AnalisadorSemanticoBase {
constructor() {
super();
this.pilhaVariaveis = new pilha_variaveis_1.PilhaVariaveis();
this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos();
this.funcoes = {};
this.atual = 0;
this.diagnosticos = [];
}
/**
* Marca as variáveis usadas em uma expressão.
* Versão estendida da classe base com tratamento adicional de construtos Pituguês.
*/
marcarVariaveisUsadasEmExpressao(expressao) {
if (expressao instanceof construtos_1.Variavel) {
this.gerenciadorEscopos.marcarComoUsada(expressao.simbolo.lexema);
return;
}
if (expressao instanceof construtos_1.Atribuir) {
// Em atribuições, marca variáveis usadas no valor (lado direito)
this.marcarVariaveisUsadasEmExpressao(expressao.valor);
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.Logico) {
this.marcarVariaveisUsadasEmExpressao(expressao.esquerda);
this.marcarVariaveisUsadasEmExpressao(expressao.direita);
return;
}
if (expressao instanceof construtos_1.Chamada) {
// Marca a entidade sendo chamada (pode ser Variavel, AcessoMetodo, etc.)
this.marcarVariaveisUsadasEmExpressao(expressao.entidadeChamada);
// Marca todos os argumentos
for (const argumento of expressao.argumentos) {
this.marcarVariaveisUsadasEmExpressao(argumento);
}
return;
}
if (expressao instanceof construtos_1.AcessoMetodo ||
expressao instanceof construtos_1.AcessoMetodoOuPropriedade ||
expressao instanceof construtos_1.AcessoPropriedade) {
this.marcarVariaveisUsadasEmExpressao(expressao.objeto);
return;
}
// Chama a classe base para outros tipos (Unario, AcessoIndiceVariavel, etc.)
super.marcarVariaveisUsadasEmExpressao(expressao);
}
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 === null || v === void 0 ? void 0 : 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 === null || v === void 0 ? void 0 : 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, então não geramos erro
// A verificação de tipo será 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);
}
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);
}
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();
}
visitarChamadaPorArgumentoReferenciaFuncao(argumentoReferenciaFuncao, argumentos) {
var _a;
const variavelCorrespondente = (_a = this.gerenciadorEscopos.buscar(argumentoReferenciaFuncao.simboloFuncao.lexema)) === null || _a === void 0 ? void 0 : _a.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 funcaoChamada = this.gerenciadorEscopos.buscar(variavel.simbolo.lexema) ||
this.funcoes[variavel.simbolo.lexema];
if (!funcaoChamada) {
this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${entidadeChamadaVariavel.simbolo.lexema}' não existe.`);
return Promise.resolve();
}
const funcao = funcaoChamada.valor;
this.comparacaoArgumentosContraParametrosFuncao(entidadeChamadaVariavel.simbolo, funcao.parametros, argumentos);
}
visitarExpressaoDeChamada(expressao) {
// Garante que toda a árvore de argumentos seja validada e marcada como usada.
for (const argumento of expressao.argumentos) {
this.marcarVariaveisUsadasEmExpressao(argumento);
this.verificarExpressao(argumento);
}
switch (expressao.entidadeChamada.constructor) {
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();
}
resolverSimboloAlvoAtribuicao(alvo) {
let atual = alvo;
while (atual) {
if (atual instanceof construtos_1.Variavel) {
return atual.simbolo;
}
if (atual instanceof construtos_1.Agrupamento) {
atual = atual.expressao;
continue;
}
if (atual instanceof construtos_1.AcessoIndiceVariavel) {
atual = atual.entidadeChamada;
continue;
}
if (atual instanceof construtos_1.AcessoMetodo ||
atual instanceof construtos_1.AcessoMetodoOuPropriedade ||
atual instanceof construtos_1.AcessoPropriedade) {
atual = atual.objeto;
continue;
}
const simbolo = atual === null || atual === void 0 ? void 0 : atual.simbolo;
if (simbolo === null || simbolo === void 0 ? void 0 : simbolo.lexema) {
return simbolo;
}
return undefined;
}
return undefined;
}
visitarExpressaoDeAtribuicao(expressao) {
const simboloAlvo = this.resolverSimboloAlvoAtribuicao(expressao.alvo);
if (!simboloAlvo) {
return Promise.resolve();
}
// Marca variáveis usadas no valor da atribuição
this.marcarVariaveisUsadasEmExpressao(expressao.valor);
let variavel = this.gerenciadorEscopos.buscar(simboloAlvo.lexema);
if (!variavel) {
// Em Pituguês (como Python), atribuições criam variáveis se não existirem
let valorInicializador = undefined;
if (expressao.valor.hasOwnProperty('valor')) {
valorInicializador = expressao.valor.valor;
}
else {
valorInicializador = expressao.valor;
}
this.gerenciadorEscopos.declarar(simboloAlvo.lexema, {
nome: simboloAlvo.lexema,
tipo: 'qualquer',
imutavel: false,
valor: valorInicializador,
inicializada: true,
usada: false,
hashArquivo: simboloAlvo.hashArquivo,
linha: simboloAlvo.linha,
});
return Promise.resolve();
}
// Marca como inicializada após atribuição
this.gerenciadorEscopos.marcarComoInicializada(simboloAlvo.lexema, expressao.valor);
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();
}
}
}
}
}
}
async visitarDeclaracaoDeExpressao(declaracao) {
return await declaracao.expressao.aceitar(this);
}
visitarDeclaracaoEscolha(declaracao) {
const identificadorOuLiteral = declaracao.identificadorOuLiteral;
const tipo = identificadorOuLiteral.tipo;
for (let caminho of declaracao.caminhos) {
for (let condicao of caminho.condicoes) {
switch (condicao.constructor) {
case construtos_1.Literal:
const condicaoLiteral = condicao;
if (condicaoLiteral.tipo !== tipo) {
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}).`);
}
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;
}
}
}
return Promise.resolve();
}
visitarDeclaracaoEnquanto(declaracao) {
return this.verificarCondicao(declaracao.condicao);
}
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) {
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ável '${variavel.simbolo.lexema}' pode não ter sido inicializada antes do uso.`);
}
return Promise.resolve();
}
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;
}
if (expressao instanceof construtos_1.Variavel) {
this.verificarVariavel(expressao);
return;
}
}
verificarBinario(binario) {
this.verificarExistenciaConstruto(binario.direita);
this.verificarExistenciaConstruto(binario.esquerda);
this.verificarOperadorBinario(binario);
return Promise.resolve();
}
verificarOperadorBinario(binario) {
if (binario.esquerda instanceof construtos_1.Binario) {
this.verificarOperadorBinario(binario.esquerda);
}
if (binario.direita instanceof construtos_1.Binario) {
this.verificarOperadorBinario(binario.direita);
}
const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO'];
if (operadoresMatematicos.includes(binario.operador.tipo)) {
this.verificarTiposOperandos(binario);
}
if (binario.operador.tipo === 'DIVISAO') {
this.verificarDivisaoPorZero(binario);
}
}
/**
* Verifica se os tipos dos operandos são compatíveis
*/
verificarTiposOperandos(binario) {
const tipoEsquerda = this.obterTipoExpressao(binario.esquerda);
const tipoDireita = this.obterTipoExpressao(binario.direita);
if (tipoEsquerda && tipoDireita && tipoEsquerda !== tipoDireita) {
// Verificar se são tipos numéricos compatíveis
const tiposNumericos = ['inteiro', 'número', 'real'];
const ambosNumericos = tiposNumericos.includes(tipoEsquerda) && tiposNumericos.includes(tipoDireita);
if (!ambosNumericos) {
this.aviso(binario.operador, `Operação entre tipos diferentes: tipo esquerdo '${tipoEsquerda}' e tipo direito '${tipoDireita}'. O resultado será resolvido implicitamente.`);
}
}
}
/**
* Verifica divisão por zero recursivamente
*/
verificarDivisaoPorZero(binario) {
const valorDireita = this.avaliarExpressaoConstante(binario.direita);
if (valorDireita === 0) {
this.erro(binario.operador, `Divisão por zero.`);
}
}
/**
* Tenta avaliar uma expressão em tempo de compilação para detectar valores constantes
* Retorna o valor se puder ser determinado, ou null caso contrário
*/
avaliarExpressaoConstante(expressao) {
if (expressao instanceof construtos_1.Literal) {
return expressao.valor;
}
if (expressao instanceof construtos_1.Variavel) {
const variavel = this.gerenciadorEscopos.buscar(expressao.simbolo.lexema);
if (!variavel) {
return null;
}
if (variavel.imutavel && variavel.inicializada) {
return variavel.valor;
}
if (variavel.inicializada && variavel.valor !== undefined) {
return variavel.valor;
}
return null;
}
if (expressao instanceof construtos_1.Binario) {
const esquerda = this.avaliarExpressaoConstante(expressao.esquerda);
const direita = this.avaliarExpressaoConstante(expressao.direita);
if (esquerda !== null && direita !== null) {
return this.calcularOperacaoBinaria(expressao.operador.tipo, esquerda, direita);
}
}
if (expressao instanceof construtos_1.Agrupamento) {
return this.avaliarExpressaoConstante(expressao.expressao);
}
return null;
}
/**
* Calcula o resultado de uma operação binária em tempo de compilação
*/
calcularOperacaoBinaria(operador, esquerda, direita) {
try {
switch (operador) {
case 'ADICAO':
return esquerda + direita;
case 'SUBTRACAO':
return esquerda - direita;
case 'MULTIPLICACAO':
return esquerda * direita;
case 'DIVISAO':
return esquerda / direita;
case 'MODULO':
return esquerda % direita;
case 'MAIOR':
return esquerda > direita;
case 'MAIOR_IGUAL':
return esquerda >= direita;
case 'MENOR':
return esquerda < direita;
case 'MENOR_IGUAL':
return esquerda <= direita;
case 'IGUAL':
return esquerda === direita;
case 'DIFERENTE':
return esquerda !== direita;
default:
return null;
}
}
catch (e) {
return null;
}
}
/**
* Obtém o tipo de uma expressão (pode ser Literal, Variavel, ou Binario)
*/
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 === null || variavel === void 0 ? void 0 : 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.Agrupamento) {
return this.obterTipoExpressao(expressao.expressao);
}
return null;
}
/**
* Infere o tipo de resultado de uma operação binária
*/
inferirTipoBinario(binario) {
const tipoEsquerda = this.obterTipoExpressao(binario.esquerda);
const tipoDireita = this.obterTipoExpressao(binario.direita);
if (!tipoEsquerda || !tipoDireita) {
return null;
}
const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO'];
const operadoresComparacao = [
'MAIOR',
'MAIOR_IGUAL',
'MENOR',
'MENOR_IGUAL',
'IGUAL',
'DIFERENTE',
];
if (operadoresComparacao.includes(binario.operador.tipo)) {
return 'lógico';
}
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';
}
verificarExistenciaConstruto(construto) {
if (construto instanceof construtos_1.Variavel) {
if (!this.gerenciadorEscopos.buscar(construto.simbolo.lexema)) {
this.erro(construto.simbolo, `Variável ${construto.simbolo.lexema} ainda não foi declarada até este ponto.`);
return;
}
this.gerenciadorEscopos.marcarComoUsada(construto.simbolo.lexema);
return;
}
if (construto instanceof construtos_1.Binario) {
this.verificarBinario(construto);
}
}
verificarLogico(logico) {
this.verificarLadoLogico(logico.direita);
this.verificarLadoLogico(logico.esquerda);
return Promise.resolve();
}
verificarChamada(chamada) {
switch (chamada.entidadeChamada.constructor) {
case construtos_1.Variavel:
let entidadeChamadaVariavel = chamada.entidadeChamada;
const nomeFuncao = entidadeChamadaVariavel.simbolo.lexema;
const funcoesBuiltIn = [
'inteiro',
'real',
'número',
'texto',
'leia',
'escreva',
'tipo',
];
const pareceSerClasse = nomeFuncao[0] === nomeFuncao[0].toUpperCase();
if (!funcoesBuiltIn.includes(nomeFuncao) &&
!pareceSerClasse &&
!this.funcoes[nomeFuncao] &&
!this.gerenciadorEscopos.buscar(nomeFuncao)) {
this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${nomeFuncao}' não existe.`);
}
break;
}
return Promise.resolve();
}
verificarLadoLogico(lado) {
if (lado instanceof construtos_1.Variavel) {
let variavel = lado;
this.verificarVariavelBinaria(variavel);
}
}
/**
* Verifica interpolações de texto e marca variáveis como usadas
*/
verificarInterpolacaoTexto(texto, literal) {
// Regex para encontrar ${identificador}
const regexInterpolacao = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
let match;
while ((match = regexInterpolacao.exec(texto)) !== null) {
const nomeVariavel = match[1];
// Verifica se a variável existe
const variavel = this.gerenciadorEscopos.buscar(nomeVariavel);
const funcao = this.funcoes[nomeVariavel];
if (!variavel && !funcao) {
this.erro({
lexema: nomeVariavel,
tipo: 'IDENTIFICADOR',
linha: literal.linha,
hashArquivo: literal.hashArquivo,
literal: null,
}, `Variável ou função '${nomeVariavel}' usada em interpolação não existe.`);
}
else if (variavel) {
// Marca como usada
this.gerenciadorEscopos.marcarComoUsada(nomeVariavel);
// Verifica se foi inicializada
if (!variavel.inicializada) {
this.aviso({
lexema: nomeVariavel,
tipo: 'IDENTIFICADOR',
linha: literal.linha,
hashArquivo: literal.hashArquivo,
literal: null,
}, `Variável '${nomeVariavel}' usada em interpolação pode não ter sido inicializada.`);
}
}
}
}
visitarDeclaracaoEscreva(declaracao) {
if (declaracao.argumentos.length === 0) {
const { linha, hashArquivo } = declaracao;
const simbolo = {
literal: '',
tipo: '',
lexema: 'escreva',
linha,
hashArquivo,
};
this.erro(simbolo, `É preciso ter um ou mais parametros para 'escreva(...)'`);
return Promise.resolve();
}
for (const argumento of declaracao.argumentos) {
this.marcarVariaveisUsadasEmExpressao(argumento);
if (argumento instanceof construtos_1.Literal && argumento.tipo === 'texto') {
this.verificarInterpolacaoTexto(String(argumento.valor), argumento);
}
if (argumento instanceof construtos_1.Variavel) {
const possivelVariavel = this.gerenciadorEscopos.buscar(argumento.simbolo.lexema);
const possivelFuncao = this.funcoes[argumento.simbolo.lexema];
if (!possivelVariavel && !possivelFuncao) {
this.erro(argumento.simbolo, `Variável ou função '${argumento.simbolo.lexema}' não existe.`);
continue;
}
if (possivelVariavel && possivelVariavel.valor === undefined) {
this.aviso(argumento.simbolo, `Variável '${argumento.simbolo.lexema}' não foi inicializada.`);
}
}
}
return Promise.resolve();
}
visitarDeclaracaoConst(declaracao) {
var _a;
this.verificarTipoAtribuido(declaracao);
if (declaracao.inicializador) {
this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
this.verificarExpressao(declaracao.inicializador);
}
const constanteCorrespondente = this.gerenciadorEscopos.buscarNoEscopoAtual(declaracao.simbolo.lexema);
if (constanteCorrespondente) {
this.erro(declaracao.simbolo, 'Declaração de constante já feita.');
return Promise.resolve();
}
this.gerenciadorEscopos.declarar(declaracao.simbolo.lexema, {
nome: declaracao.simbolo.lexema,
tipo: declaracao.tipo || 'qualquer',
imutavel: true,
valor: (_a = declaracao.inicializador) === null || _a === void 0 ? void 0 : _a.valor,
inicializada: true,
usada: false,
hashArquivo: declaracao.simbolo.hashArquivo,
linha: declaracao.simbolo.linha,
});
// TODO: Verificar inicializador.
return Promise.resolve();
}
visitarDeclaracaoVar(declaracao) {
this.verificarTipoAtribuido(declaracao);
if (declaracao.inicializador) {
this.marcarVariaveisUsadasEmExpressao(declaracao.inicializador);
this.verificarExpressao(declaracao.inicializador);
switch (declaracao.inicializador.constructor) {
case construtos_1.FuncaoConstruto:
const funcaoConstruto = declaracao.inicializador;
if (funcaoConstruto.parametros.length >= 255) {
this.erro(declaracao.simbolo, 'Função não pode ter mais de 255 parâmetros.');
}
if (funcaoConstruto.tipo) {
const tipoRetornoFuncao = funcaoConstruto.tipo;
if (!['vazio', 'qualquer'].includes(tipoRetornoFuncao)) {
const todosOsCaminhosRetornam = this.todosOsCaminhosRetornam(funcaoConstruto.corpo);
if (!todosOsCaminhosRetornam) {
this.erro(declaracao.simbolo, `Função '${declaracao.simbolo.lexema}' deve retornar '${tipoRetornoFuncao}' em todos os caminhos de execução.`);
}
const funcaoContemRetorno = funcaoConstruto.corpo.find((c) => c instanceof declaracoes_1.Retorna);
if (funcaoContemRetorno && funcaoContemRetorno.valor) {
const tipoValor = typeof funcaoContemRetorno.valor.valor;
if (!['qualquer'].includes(tipoRetornoFuncao)) {
if (tipoValor === 'string' && tipoRetornoFuncao !== 'texto') {
this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
}
if (tipoValor === 'number' &&
!['inteiro', 'real', 'número'].includes(tipoRetornoFuncao)) {
this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
}
}
}
}
}
break;
}
}
let valorInicializador = undefined;
if (declaracao.inicializador) {
if (declaracao.inicializador.hasOwnProperty('valor')) {
valorInicializador = declaracao.inicializador.valor;
}
else {
valorInicializador = declaracao.inicializador;
}
}
let tipoInferido = declaracao.tipo;
// Se o tipo é o padrão implícito 'qualquer', tenta inferir um tipo mais específico
// a partir do inicializador. Para 'qualquer' explícito, apenas sugerimos mais abaixo.
if (tipoInferido === 'qualquer' && !declaracao.tipoExplicito && declaracao.inicializador) {
tipoInferido = this.obterTipoExpressao(declaracao.inicializador);
}
if (declaracao.tipoExplicito &&
declaracao.tipoOriginal === 'qualquer' &&
declaracao.inicializador) {
const tipoMelhor = this.obterTipoExpressao(declaracao.inicializador);
if (tipoMelhor && tipoMelhor !== 'qualquer') {
this.sugestao(declaracao.simbolo, 'Um tipo melhor pode ser inferido.', [
{
titulo: `Alterar tipo para '${tipoMelhor}'`,
textoOriginal: 'qualquer',
textoSubstituto: tipoMelhor,
linha: declaracao.simbolo.linha,
colunaInicio: declaracao.simbolo.colunaInicio,
colunaFim: declaracao.simbolo.colunaFim,
},
]);
}
}
const variavel = {
nome: declaracao.simbolo.lexema,
tipo: tipoInferido || 'qualquer',
imutavel: false,
valor: valorInicializador,
inicializada: declaracao.inicializador !== null && declaracao.inicializador !== undefined,
usada: false,
hashArquivo: declaracao.simbolo.hashArquivo,
linha: declaracao.simbolo.linha,
};
const declaradaComSucesso = this.gerenciadorEscopos.declarar(declaracao.simbolo.lexema, variavel);
if (!declaradaComSucesso) {
const variavelExistente = this.gerenciadorEscopos.buscarNoEscopoAtual(declaracao.simbolo.lexema);
this.aviso(declaracao.simbolo, `Variável '${declaracao.simbolo.lexema}' já foi declarada na linha ${variavelExistente === null || variavelExistente === void 0 ? void 0 : variavelExistente.linha}.`);
}
return Promise.resolve();
}
visitarExpressaoFormatacaoEscrita(expressao) {
if (expressao.expressao) {
/* for (const expressao of expressao.expressoes) {
if (expressao instanceof Variavel) {
this.gerenciadorEscopos.marcarComoUsada(expressao.simbolo.lexema);
}
// Recursively check for variables in more complex expressions
this.marcarVariaveisUsadasEmExpressao(expressao);
} */
}
return Promise.resolve();
}
verificarBinarioEmExpressao(expressao) {
if (expressao instanceof construtos_1.Agrupamento) {
this.verificarBinarioEmExpressao(expressao.expressao);
return;
}
if (expressao instanceof construtos_1.Binario) {
this.verificarOperadorBinario(expressao);
}
}
visitarExpressaoRetornar(declaracao) {
if (declaracao.valor) {
this.verificarBinarioEmExpressao(declaracao.valor);
}
return Promise.resolve(null);
}
visitarExpressaoDeVariavel(expressao) {
if (expressao instanceof construtos_1.Variavel) {
return this.verificarVariavel(expressao);
}
return Promise.resolve();
}
async visitarExpressaoAcessoIntervaloVariavel(expressao) {
const expressaoIntervalo = expressao;
await expressaoIntervalo.objeto.aceitar(this);
if (expressaoIntervalo.inicio) {
await expressaoIntervalo.inicio.aceitar(this);
}
if (expressaoIntervalo.fim) {
await expressaoIntervalo.fim.aceitar(this);
}
return Promise.resolve();
}
async visitarExpressaoTuplaN(expressao) {
for (let i = 0; i < expressao.elementos.length; i++) {
await expressao.elementos[i].aceitar(this);
}
return Promise.resolve();
}
visitarDeclaracaoDefinicaoFuncao(declaracao) {
var _a;
if (declaracao.funcao.parametros.length >= 255) {
this.erro(declaracao.simbolo, 'Função não pode ter mais de 255 parâmetros.');
}
const todosRetornos = declaracao.funcao.corpo.flatMap((c) => (0, comum_1.buscarRetornos)(c));
for (const instrucao of todosRetornos) {
if (instrucao.valor) {
this.verificarBinarioEmExpressao(instrucao.valor);
}
}
let tipoRetornoFuncao = declaracao.funcao.tipo;
if (tipoRetornoFuncao) {
if (!['vazio', 'qualquer'].includes(tipoRetornoFuncao)) {
const todosOsCaminhosRetornam = this.todosOsCaminhosRetornam(declaracao.funcao.corpo);
if (!todosOsCaminhosRetornam) {
this.erro(declaracao.simbolo, `Função '${declaracao.simbolo.lexema}' deve retornar '${tipoRetornoFuncao}' em todos os caminhos de execução.`);
}
}
const retornos = declaracao.funcao.corpo.flatMap((c) => (0, comum_1.buscarRetornos)(c));
// Filtra retornos com tipo 'qualquer' (não determinado em tempo de análise sintática)
const retornosComTipoIndeterminado = retornos.filter((retorno) => retorno.valor !== null &&
retorno.valor !== undefined &&
retorno.tipo === 'qualquer');
// Se a função é 'vazio' e há retornos com tipo indeterminado,
// tenta inferir o tipo e fornece mensagem útil ao desenvolvedor
if (tipoRetornoFuncao === 'vazio' &&
declaracao.funcao.tipoExplicito &&
retornosComTipoIndeterminado.length > 0) {
const retornoComValor = retornosComTipoIndeterminado[0];
const tipoInferido = this.obterTipoExpressao(retornoComValor.valor);
if (tipoInferido && tipoInferido !== 'qualquer') {
this.erro(declaracao.simbolo, `A função não pode ter nenhum tipo de retorno. Tipo inferido do retorno: '${tipoInferido}'.`);
}
else {
this.erro(declaracao.simbolo, `A função não pode ter nenhum tipo de retorno.`);
}
}
else {
// Verifica tipos de retorno para funções não-vazio
const retornoComValor = retornos.find((retorno) => retorno.valor !== null && retorno.valor !== undefined);
if (retornoComValor) {
const tipoValor = typeof ((_a = retornoComValor.valor) === null || _a === void 0 ? void 0 : _a.valor);
if (!['qualquer'].includes(tipoRetornoFuncao)) {
if (tipoValor === 'string' && tipoRetornoFuncao !== 'texto') {
this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
}
if (tipoValor === 'number' &&
!['inteiro', 'real', 'número'].includes(tipoRetornoFuncao)) {
this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`);
}
}
}
}
}
this.funcoes[declaracao.simbolo.lexema] = {
valor: declaracao.funcao,
};
return Promise.resolve();
}
verificarVariaveisNaoUsadas() {
const naoUsadas = this.gerenciadorEscopos.obterVariaveisNaoUsadas();
for (let variavel of naoUsadas) {
// Verifica se já existe um erro associado à variável.
const temErro = this.diagnosticos.some((d) => d.severidade === erros_1.DiagnosticoSeveridade.ERRO &&
d.simbolo.lexema === variavel.nome);
// Se a variável já tem um erro associado, não emitir aviso de não usada.
if (temErro) {
continue;
}
this.aviso({
lexema: variavel.nome,
linha: variavel.linha,
tipo: variavel.tipo,
hashArquivo: variavel.hashArquivo,
}, `Variável '${variavel.nome}' foi declarada mas nunca usada.`);
}
}
async analisar(declaracoes) {
this.gerenciadorEscopos = new gerenciador_escopos_1.GerenciadorEscopos();
this.funcoes = {};
this.atual = 0;
this.diagnosticos = [];
while (this.atual < declaracoes.length) {
await declaracoes[this.atual].aceitar(this);
this.atual++;
}
// Verifica variáveis não usadas ao final
this.verificarVariaveisNaoUsadas();
return {
diagnosticos: this.diagnosticos,
};
}
}
exports.AnalisadorSemanticoPitugues = AnalisadorSemanticoPitugues;
//# sourceMappingURL=analisador-semantico-pitugues.js.map