@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
514 lines • 24.9 kB
JavaScript
"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}.`);
}
}
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.name) {
case 'Agrupamento':
const valorAgrupamento = valor;
return this.verificarTipoDe(valorAgrupamento.expressao);
case 'Binario':
const valorBinario = valor;
this.verificarTipoDe(valorBinario.direita);
this.verificarTipoDe(valorBinario.esquerda);
break;
case '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();
}
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()) {
// TODO: `argumento` pode ser Literal (tipo já resolvido) ou variável (tipo inferido em outra etapa).
const argumento = argumentos[indice];
if (argumento) {
if (parametro.tipoDado === 'texto' && argumento.tipo !== 'texto') {
this.erro(simboloFuncao, `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(simboloFuncao, `O valor passado para o parâmetro '${parametro.nome.lexema}' (${parametro.tipoDado}) é diferente do esperado pela função (${argumento.tipo}).`);
}
}
}
}
}
visitarChamadaPorArgumentoReferenciaFuncao(argumentoReferenciaFuncao, argumentos) {
const variavelCorrespondente = this.variaveis[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 funcaoChamada = this.variaveis[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) {
switch (expressao.entidadeChamada.constructor.name) {
case 'ArgumentoReferenciaFuncao':
const entidadeChamadaArgumentoReferenciaFuncao = expressao.entidadeChamada;
this.visitarChamadaPorArgumentoReferenciaFuncao(entidadeChamadaArgumentoReferenciaFuncao, expressao.argumentos);
break;
case 'ReferenciaFuncao':
const entidadeChamadaReferenciaFuncao = expressao.entidadeChamada;
this.visitarChamadaPorReferenciaFuncao(entidadeChamadaReferenciaFuncao, expressao.argumentos);
break;
case 'Variavel':
const entidadeChamadaVariavel = expressao.entidadeChamada;
this.visitarChamadaPorVariavel(entidadeChamadaVariavel, expressao.argumentos);
break;
}
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) {
const identificadorOuLiteral = declaracao.identificadorOuLiteral;
const tipo = identificadorOuLiteral.tipo;
for (let caminho of declaracao.caminhos) {
for (let condicao of caminho.condicoes) {
switch (condicao.constructor.name) {
case '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 'Variavel':
const condicaoVariavel = condicao;
this.verificarVariavel(condicaoVariavel);
const variavelHipotetica = this.variaveis[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.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) {
switch (chamada.entidadeChamada.constructor.name) {
case 'Variavel':
let entidadeChamadaVariavel = chamada.entidadeChamada;
if (!this.funcoes[entidadeChamadaVariavel.simbolo.lexema]) {
this.erro(entidadeChamadaVariavel.simbolo, `Chamada da função '${entidadeChamadaVariavel.simbolo.lexema}' não existe.`);
}
break;
}
return Promise.resolve();
}
verificarLadoLogico(lado) {
if (lado instanceof construtos_1.Variavel) {
let variavel = lado;
this.verificarVariavelBinaria(variavel);
}
}
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();
}
const variaveis = declaracao.argumentos.filter((arg) => arg instanceof construtos_1.Variavel);
for (let variavel of variaveis) {
// TODO: Funções também são consideradas "variáveis" até aqui, mas isso deve mudar futuramente.
const possivelVariavel = this.variaveis[variavel.simbolo.lexema];
const possivelFuncao = this.funcoes[variavel.simbolo.lexema];
if (!possivelVariavel && !possivelFuncao) {
this.erro(variavel.simbolo, `Variável ou função '${variavel.simbolo.lexema}' não existe.`);
continue;
}
if (possivelVariavel && possivelVariavel.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) {
switch (declaracao.inicializador.constructor.name) {
case '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.');
}
break;
}
}
const valorInicializador = declaracao.inicializador && declaracao.inicializador.valor
? declaracao.inicializador.valor
: declaracao.inicializador;
this.variaveis[declaracao.simbolo.lexema] = {
imutavel: false,
tipo: declaracao.tipo,
valor: valorInicializador || undefined,
};
return Promise.resolve();
}
visitarExpressaoRetornar(declaracao) {
return Promise.resolve(null);
}
visitarDeclaracaoDefinicaoFuncao(declaracao) {
if (declaracao.funcao.tipo === 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.tipo;
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 (declaracao.funcao.tipoExplicito &&
!['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