UNPKG

@designliquido/delegua

Version:

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

451 lines 21.9 kB
"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 analisador_semantico_base_1 = require("./analisador-semantico-base"); const pilha_variaveis_1 = require("./pilha-variaveis"); class AnalisadorSemantico extends analisador_semantico_base_1.AnalisadorSemanticoBase { constructor() { super(); this.pilhaVariaveis = new pilha_variaveis_1.PilhaVariaveis(); this.variaveis = {}; this.funcoes = {}; this.atual = 0; this.diagnosticos = []; } erro(simbolo, mensagem) { this.diagnosticos.push({ simbolo: simbolo, mensagem: mensagem, hashArquivo: simbolo.hashArquivo, linha: simbolo.linha, severidade: erros_1.DiagnosticoSeveridade.ERRO, }); } aviso(simbolo, mensagem) { this.diagnosticos.push({ simbolo: simbolo, mensagem: mensagem, hashArquivo: simbolo.hashArquivo, linha: simbolo.linha, severidade: erros_1.DiagnosticoSeveridade.AVISO, }); } verificarTipoAtribuido(declaracao) { if (declaracao.tipo) { if (['vetor', 'qualquer[]', 'inteiro[]', 'texto[]'].includes(declaracao.tipo)) { if (declaracao.inicializador instanceof construtos_1.Vetor) { const vetor = declaracao.inicializador; if (declaracao.tipo === 'inteiro[]') { const v = vetor.valores.find((v) => typeof (v === null || v === void 0 ? void 0 : v.valor) !== 'number'); if (v) { 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 v = vetor.valores.find((v) => typeof (v === null || v === void 0 ? void 0 : v.valor) !== 'string'); if (v) { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', é esperado um valor do tipo vetor de texto. Atual: ${vetor.tipo}.`); } } } 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}.`); } } // TODO: Estudar remoção. if (declaracao.inicializador instanceof declaracoes_1.Leia) { if (!['qualquer', 'texto'].includes(declaracao.tipo)) { this.erro(declaracao.simbolo, `Atribuição inválida para '${declaracao.simbolo.lexema}', Leia só pode receber tipo 'texto'.`); } } } } visitarExpressaoTipoDe(expressao) { return this.verificarTipoDe(expressao.valor); } verificarTipoDe(valor) { if (valor instanceof construtos_1.Binario) { this.verificarTipoDe(valor.direita); this.verificarTipoDe(valor.esquerda); } if (valor instanceof construtos_1.Agrupamento) { return this.verificarTipoDe(valor.expressao); } if (valor instanceof construtos_1.Variavel) { return this.verificarVariavel(valor); } 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(); } visitarExpressaoDeChamada(expressao) { if (expressao.entidadeChamada instanceof construtos_1.Variavel) { const variavel = expressao.entidadeChamada; const funcaoChamada = this.variaveis[variavel.simbolo.lexema] || this.funcoes[variavel.simbolo.lexema]; if (!funcaoChamada) { this.erro(expressao.entidadeChamada.simbolo, `Chamada da função '${expressao.entidadeChamada.simbolo.lexema}' não existe.`); return Promise.resolve(); } const funcao = funcaoChamada.valor; if (funcao.parametros.length !== expressao.argumentos.length) { this.erro(expressao.entidadeChamada.simbolo, `Função '${expressao.entidadeChamada.simbolo.lexema}' espera ${funcao.parametros.length} parâmetros. Atual: ${expressao.argumentos.length}.`); } for (let [indice, parametro] of funcao.parametros.entries()) { // TODO: `argumento` pode ser Literal (tipo já resolvido) ou variável (tipo inferido em outra etapa). const argumento = expressao.argumentos[indice]; if (argumento) { if (parametro.tipoDado === 'texto' && argumento.tipo !== 'texto') { this.erro(expressao.entidadeChamada.simbolo, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${argumento.tipo}).`); } else if (['inteiro', 'número', 'real'].includes(parametro.tipoDado)) { // Aqui, se houver diferença entre os tipos do parâmetro e do argumento, não há erro, // porque Delégua pode trabalhar com conversões implícitas. // Isso pode ou não mudar no futuro. if (!['inteiro', 'número', 'real'].includes(argumento.tipo)) { this.erro(expressao.entidadeChamada.simbolo, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${argumento.tipo}).`); } } } } } return Promise.resolve(); } visitarExpressaoDeAtribuicao(expressao) { // TODO: Readaptar para trabalhar com `expressao.alvo` sendo um construto. let simboloAlvo; switch (expressao.alvo.constructor.name) { case 'Variavel': const alvoVariavel = expressao.alvo; simboloAlvo = alvoVariavel.simbolo; break; default: // throw new Error(`Implementar atribuição para ${expressao.alvo.constructor.name}.`); return Promise.resolve(); } let valor = this.variaveis[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 valores = expressao.valor.valores; if (!['qualquer[]'].includes(valor.tipo)) { if (valor.tipo === 'texto[]') { if (!valores.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 (!valores.every((v) => typeof v.valor === 'number')) { this.erro(simboloAlvo, `Esperado tipo '${valor.tipo}' na atribuição.`); return Promise.resolve(); } } } } } if (valor.imutavel) { this.erro(simboloAlvo, `Constante ${simboloAlvo.lexema} não pode ser modificada.`); return Promise.resolve(); } else { if (this.variaveis[simboloAlvo.lexema]) { this.variaveis[simboloAlvo.lexema].valor = expressao.valor; } } } async visitarDeclaracaoDeExpressao(declaracao) { return await declaracao.expressao.aceitar(this); } visitarDeclaracaoEscolha(declaracao) { var _a, _b; const identificadorOuLiteral = declaracao.identificadorOuLiteral; const valor = (_b = this.variaveis[(_a = identificadorOuLiteral.simbolo) === null || _a === void 0 ? void 0 : _a.lexema]) === null || _b === void 0 ? void 0 : _b.valor; const tipo = typeof valor; for (let caminho of declaracao.caminhos) { for (let condicao of caminho.condicoes) { if (valor instanceof declaracoes_1.Leia && typeof (condicao === null || condicao === void 0 ? void 0 : condicao.valor) !== 'string') { this.erro(condicao, `'caso ${condicao.valor}:' não é do mesmo tipo esperado em 'escolha'`); continue; } if (!(valor instanceof declaracoes_1.Leia) && typeof (condicao === null || condicao === void 0 ? void 0 : condicao.valor) !== tipo) { this.erro(condicao, `'caso ${condicao.valor}:' não é do mesmo tipo esperado em 'escolha'`); } } } 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.variaveis[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 variavelHipotetica = this.variaveis[variavel.simbolo.lexema]; if (!variavelHipotetica) { this.erro(variavel.simbolo, `Variável ${variavel.simbolo.lexema} ainda não foi declarada até este ponto.`); } return Promise.resolve(); } verificarBinario(binario) { this.verificarLadoBinario(binario.direita); this.verificarLadoBinario(binario.esquerda); this.verificarOperadorBinario(binario); return Promise.resolve(); } verificarOperadorBinario(binario) { var _a, _b, _c; const operadoresMatematicos = ['ADICAO', 'SUBTRACAO', 'MULTIPLICACAO', 'DIVISAO', 'MODULO']; if (operadoresMatematicos.includes(binario.operador.tipo)) { if (binario.direita instanceof construtos_1.Variavel && binario.esquerda instanceof construtos_1.Variavel) { const tipoDireita = typeof ((_a = this.variaveis[binario.direita.simbolo.lexema]) === null || _a === void 0 ? void 0 : _a.valor); const tipoEsquerda = typeof ((_b = this.variaveis[binario.esquerda.simbolo.lexema]) === null || _b === void 0 ? void 0 : _b.valor); if (tipoDireita !== tipoEsquerda) { this.erro(binario.operador, `Operação inválida, tipos diferentes.`); } } } if (binario.operador.tipo === 'DIVISAO') { switch (binario.direita.constructor.name) { case 'Variavel': const operadorDireitoLiteral = binario.direita; if (((_c = this.variaveis[operadorDireitoLiteral.simbolo.lexema]) === null || _c === void 0 ? void 0 : _c.valor) === 0) { this.erro(binario.operador, `Divisão por zero.`); } break; } } } verificarLadoBinario(lado) { if (lado instanceof construtos_1.Variavel && !this.variaveis[lado.simbolo.lexema]) { this.erro(lado.simbolo, `Variável ${lado.simbolo.lexema} ainda não foi declarada até este ponto.`); return; } if (lado instanceof construtos_1.Binario) { this.verificarBinario(lado); return; } } verificarLogico(logio) { this.verificarLadoLogico(logio.direita); this.verificarLadoLogico(logio.esquerda); return Promise.resolve(); } verificarChamada(chamada) { let funcaoChamada = chamada.entidadeChamada; if (!this.funcoes[funcaoChamada.simbolo.lexema]) { this.erro(funcaoChamada.simbolo, `Chamada da função '${funcaoChamada.simbolo.lexema}' não existe.`); } return Promise.resolve(); } verificarLadoLogico(lado) { if (lado instanceof construtos_1.Variavel) { let variavel = lado; this.verificarVariavelBinaria(variavel); } } visitarDeclaracaoEscreva(declaracao) { var _a; 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(); } const variaveis = declaracao.argumentos.filter((arg) => arg instanceof construtos_1.Variavel); for (let variavel of variaveis) { if (!this.variaveis[variavel.simbolo.lexema]) { this.erro(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' não existe.`); } if (((_a = this.variaveis[variavel.simbolo.lexema]) === null || _a === void 0 ? void 0 : _a.valor) === undefined) { this.aviso(variavel.simbolo, `Variável '${variavel.simbolo.lexema}' não foi inicializada.`); } } return Promise.resolve(); } visitarDeclaracaoConst(declaracao) { this.verificarTipoAtribuido(declaracao); if (this.variaveis.hasOwnProperty(declaracao.simbolo.lexema)) { this.erro(declaracao.simbolo, 'Declaração de constante já feita.'); } else { this.variaveis[declaracao.simbolo.lexema] = { imutavel: true, tipo: declaracao.tipo, valor: declaracao.inicializador.valor, }; } this.virificarTipoDeclaracaoConst(declaracao); return Promise.resolve(); } virificarTipoDeclaracaoConst(declaracao) { if (declaracao.inicializador instanceof construtos_1.Binario) { // verificar tipos iguais no lado esquerdo e direito const binario = declaracao.inicializador; this.verificarLadoBinario(binario.direita); this.verificarLadoBinario(binario.esquerda); const tipoDireita = typeof binario.direita.valor; const tipoEsquerda = typeof binario.esquerda.valor; if (tipoDireita !== tipoEsquerda) { this.aviso(declaracao.simbolo, 'Declaração de constante com tipos diferentes.'); } } return Promise.resolve(); } visitarDeclaracaoVar(declaracao) { this.verificarTipoAtribuido(declaracao); if (declaracao.inicializador instanceof construtos_1.FuncaoConstruto) { const funcao = declaracao.inicializador; if (funcao.parametros.length >= 255) { this.erro(declaracao.simbolo, 'Função não pode ter mais de 255 parâmetros.'); } } this.variaveis[declaracao.simbolo.lexema] = { imutavel: false, tipo: declaracao.tipo, valor: declaracao.inicializador !== null ? declaracao.inicializador.valor !== undefined ? declaracao.inicializador.valor : declaracao.inicializador : undefined, }; return Promise.resolve(); } visitarExpressaoRetornar(declaracao) { return Promise.resolve(null); } visitarDeclaracaoDefinicaoFuncao(declaracao) { if (declaracao.funcao.tipoRetorno === undefined) { this.erro(declaracao.simbolo, `Declaração de retorno da função é inválido.`); } if (declaracao.funcao.parametros.length >= 255) { this.erro(declaracao.simbolo, 'Função não pode ter mais de 255 parâmetros.'); } let tipoRetornoFuncao = declaracao.funcao.tipoRetorno; if (tipoRetornoFuncao) { let funcaoContemRetorno = declaracao.funcao.corpo.find((c) => c instanceof declaracoes_1.Retorna); if (funcaoContemRetorno) { if (tipoRetornoFuncao === 'vazio') { this.erro(declaracao.simbolo, `A função não pode ter nenhum tipo de retorno.`); } const tipoValor = typeof funcaoContemRetorno.valor.valor; if (!['qualquer'].includes(tipoRetornoFuncao)) { if (tipoValor === 'string') { if (tipoRetornoFuncao != 'texto') { this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`); } } if (tipoValor === 'number') { if (!['inteiro', 'real'].includes(tipoRetornoFuncao)) { this.erro(declaracao.simbolo, `Esperado retorno do tipo '${tipoRetornoFuncao}' dentro da função.`); } } } } else { if (!['vazio', 'qualquer'].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(); } analisar(declaracoes) { // this.pilhaVariaveis = new PilhaVariaveis(); // this.pilhaVariaveis.empilhar() this.variaveis = {}; this.atual = 0; this.diagnosticos = []; while (this.atual < declaracoes.length) { declaracoes[this.atual].aceitar(this); this.atual++; } return { diagnosticos: this.diagnosticos, }; } } exports.AnalisadorSemantico = AnalisadorSemantico; //# sourceMappingURL=analisador-semantico.js.map