@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
451 lines • 21.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}.`);
}
}
// 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