@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
987 lines • 57.5 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AvaliadorSintaticoTenda = void 0;
const browser_process_hrtime_1 = __importDefault(require("browser-process-hrtime"));
const construtos_1 = require("../../construtos");
const erro_avaliador_sintatico_1 = require("./../erro-avaliador-sintatico");
const tuplas_1 = require("../../construtos/tuplas");
const declaracoes_1 = require("../../declaracoes");
const avaliador_sintatico_base_1 = require("../avaliador-sintatico-base");
const inferenciador_1 = require("../../inferenciador");
const pilha_escopos_1 = require("./../pilha-escopos");
const informacao_escopo_1 = require("./../informacao-escopo");
const informacao_variavel_ou_constante_1 = require("../../informacao-variavel-ou-constante");
const simbolo_1 = require("../../lexador/simbolo");
const delegua_1 = __importDefault(require("../../tipos-de-dados/delegua"));
const tenda_1 = __importDefault(require("../../tipos-de-simbolos/tenda"));
const primitivas_dicionario_1 = __importDefault(require("../../bibliotecas/primitivas-dicionario"));
const primitivas_numero_1 = __importDefault(require("../../bibliotecas/primitivas-numero"));
const primitivas_texto_1 = __importDefault(require("../../bibliotecas/primitivas-texto"));
const primitivas_vetor_1 = __importDefault(require("../../bibliotecas/primitivas-vetor"));
/**
* Este avaliador sintático emite todos os símbolos de Tenda. No entanto, nem todo
* símbolo emitido aqui pode ser interpretado por este núcleo, já que Tenda tem
* Rust como base, e várias de suas funções requerem interface com um sistema operacional.
* Outros pacotes do ecossistema de Delégua, como `delegua-node` poderão lidar com
* todas as funcionalidades de Tenda.
*/
class AvaliadorSintaticoTenda extends avaliador_sintatico_base_1.AvaliadorSintaticoBase {
constructor(performance = false) {
super();
this.hashArquivo = 0;
this.atual = 0;
this.blocos = 0;
this.erros = [];
this.performance = performance;
this.tiposDefinidosEmCodigo = {};
this.tiposDeFerramentasExternas = {};
this.primitivasConhecidas = {};
for (const [nomePrimitivaDicionario, dadosPrimitiva] of Object.entries(primitivas_dicionario_1.default)) {
this.primitivasConhecidas[nomePrimitivaDicionario] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomePrimitivaDicionario, 'dicionário', true, dadosPrimitiva.argumentos);
}
for (const [nomePrimitivaNumero, dadosPrimitiva] of Object.entries(primitivas_numero_1.default)) {
this.primitivasConhecidas[nomePrimitivaNumero] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomePrimitivaNumero, 'número', true, dadosPrimitiva.argumentos);
}
for (const [nomePrimitivaTexto, dadosPrimitiva] of Object.entries(primitivas_texto_1.default)) {
this.primitivasConhecidas[nomePrimitivaTexto] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomePrimitivaTexto, 'texto', true, dadosPrimitiva.argumentos);
}
for (const [nomePrimitivaVetor, dadosPrimitiva] of Object.entries(primitivas_vetor_1.default)) {
this.primitivasConhecidas[nomePrimitivaVetor] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomePrimitivaVetor, 'vetor', true, dadosPrimitiva.argumentos);
}
// TODO: Por enquanto não há necessidade de validar argumentos aqui, mas isso pode mudar no futuro.
this.primitivasConhecidas['inteiro'] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('inteiro', 'inteiro');
this.primitivasConhecidas['numero'] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('numero', 'número');
this.primitivasConhecidas['número'] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('número', 'número');
this.primitivasConhecidas['texto'] = new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante('texto', 'texto');
this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
}
verificarDefinicaoTipoAtual() {
const tipos = [...Object.values(delegua_1.default)];
if (this.simbolos[this.atual].lexema in this.tiposDefinidosEmCodigo) {
return this.simbolos[this.atual].lexema;
}
const lexemaElementar = this.simbolos[this.atual].lexema.toLowerCase();
const tipoElementarResolvido = tipos.find((tipo) => tipo === lexemaElementar);
if (!tipoElementarResolvido) {
throw this.erro(this.simbolos[this.atual], `Tipo de dados desconhecido: '${this.simbolos[this.atual].lexema}'.`);
}
if (this.verificarTipoProximoSimbolo(tenda_1.default.COLCHETE_ESQUERDO)) {
const tiposVetores = [
'inteiro[]',
'numero[]',
'número[]',
'qualquer[]',
'real[]',
'texto[]',
];
this.avancarEDevolverAnterior();
if (!this.verificarTipoProximoSimbolo(tenda_1.default.COLCHETE_DIREITO)) {
throw this.erro(this.simbolos[this.atual], `Esperado símbolo de fechamento do vetor: ']'. Atual: ${this.simbolos[this.atual].lexema}`);
}
const tipoVetor = tiposVetores.find((tipo) => tipo === `${lexemaElementar}[]`);
this.avancarEDevolverAnterior();
return tipoVetor;
}
return tipoElementarResolvido;
}
obterChaveDicionario() {
switch (this.simbolos[this.atual].tipo) {
case tenda_1.default.NUMERO:
case tenda_1.default.TEXTO:
case tenda_1.default.FALSO:
case tenda_1.default.VERDADEIRO:
return this.primario();
case tenda_1.default.IDENTIFICADOR:
const simboloIdentificador = this.avancarEDevolverAnterior();
let tipoOperando;
if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) {
tipoOperando = simboloIdentificador.lexema;
}
else {
tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema);
}
if (!['numero', 'número', 'texto', 'lógico'].includes(tipoOperando)) {
throw this.erro(simboloIdentificador, `Tipo ${tipoOperando} de identificador ${simboloIdentificador.lexema} não é válido como chave de dicionário.`);
}
return new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando);
case tenda_1.default.COLCHETE_ESQUERDO:
this.avancarEDevolverAnterior();
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.PARENTESE_ESQUERDO)) {
return this.construtoTupla();
}
throw this.erro(this.simbolos[this.atual], `Esperado parêntese esquerdo após colchete esquerdo para definição de chave de dicionário. Atual: ${this.simbolos[this.atual].tipo}.`);
default:
throw this.erro(this.simbolos[this.atual], `Símbolo ${this.simbolos[this.atual].tipo} inesperado ou inválido como chave de dicionário.`);
}
}
construtoDicionario(simboloChaveEsquerda) {
this.avancarEDevolverAnterior();
const chaves = [];
const valores = [];
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.CHAVE_DIREITA)) {
return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), [], []);
}
while (!this.verificarSeSimboloAtualEIgualA(tenda_1.default.CHAVE_DIREITA)) {
const chave = this.obterChaveDicionario();
this.consumir(tenda_1.default.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
const valor = this.atribuir();
chaves.push(chave);
valores.push(valor);
if (this.simbolos[this.atual].tipo !== tenda_1.default.CHAVE_DIREITA) {
this.consumir(tenda_1.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
}
}
return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), chaves, valores);
}
construtoTupla() {
const expressao = this.expressao();
const argumentos = [expressao];
while (this.simbolos[this.atual].tipo === tenda_1.default.VIRGULA) {
this.avancarEDevolverAnterior();
argumentos.push(this.expressao());
}
this.consumir(tenda_1.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
this.consumir(tenda_1.default.COLCHETE_DIREITO, "Esperado ']' após a expressão.");
return new tuplas_1.SeletorTuplas(...argumentos);
}
primario() {
const simboloAtual = this.simbolos[this.atual];
let valores = [];
switch (simboloAtual.tipo) {
case tenda_1.default.CHAVE_ESQUERDA:
return this.construtoDicionario(simboloAtual);
case tenda_1.default.COLCHETE_ESQUERDO:
this.avancarEDevolverAnterior();
valores = [];
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.COLCHETE_DIREITO)) {
return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), [], 0, 'qualquer[]');
}
while (!this.verificarSeSimboloAtualEIgualA(tenda_1.default.COLCHETE_DIREITO)) {
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.PARENTESE_ESQUERDO)) {
return this.construtoTupla();
}
const valor = this.atribuir();
valores.push(valor);
if (this.simbolos[this.atual].tipo !== tenda_1.default.COLCHETE_DIREITO) {
this.consumir(tenda_1.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
}
}
const tipoVetor = (0, inferenciador_1.inferirTipoVariavel)(valores);
return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), valores, valores.length, tipoVetor);
case tenda_1.default.EXPRESSAO_REGULAR:
let valor = '';
let linhaAtual = this.simbolos[this.atual].linha;
let eParExpressaoRegular = this.simbolos.filter((l) => l.linha === linhaAtual && l.tipo === tenda_1.default.EXPRESSAO_REGULAR).length %
2 ===
0;
if (eParExpressaoRegular) {
this.avancarEDevolverAnterior();
while (!this.verificarTipoSimboloAtual(tenda_1.default.EXPRESSAO_REGULAR)) {
valor += this.simbolos[this.atual].lexema || '';
this.avancarEDevolverAnterior();
}
this.avancarEDevolverAnterior();
return new construtos_1.ExpressaoRegular(this.hashArquivo, simboloAtual, valor);
}
case tenda_1.default.FALSO:
this.avancarEDevolverAnterior();
return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), false, 'lógico');
case tenda_1.default.FUNÇÃO:
const simboloFuncao = this.avancarEDevolverAnterior();
// Avançamos o parêntese esquerdo aqui, porque `corpoDaFuncao`
// espera que esse parêntese esquerdo já foi consumido.
this.consumir(tenda_1.default.PARENTESE_ESQUERDO, "Esperado parêntese esquerdo após palavra reservada 'função'.");
const corpoDaFuncao = this.corpoDaFuncao(simboloFuncao.lexema);
this.pilhaEscopos.definirInformacoesVariavel(simboloFuncao.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simboloFuncao.lexema, 'função'));
return corpoDaFuncao;
case tenda_1.default.IDENTIFICADOR:
const simboloIdentificador = this.avancarEDevolverAnterior();
let tipoOperando;
if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) {
tipoOperando = simboloIdentificador.lexema;
}
else {
tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema);
}
// Se o próximo símbolo é um incremento ou um decremento,
// aqui deve retornar um unário correspondente.
// Caso contrário, apenas retornar um construto de variável.
if (this.simbolos[this.atual] &&
[tenda_1.default.INCREMENTAR, tenda_1.default.DECREMENTAR].includes(this.simbolos[this.atual].tipo)) {
const simboloIncrementoDecremento = this.avancarEDevolverAnterior();
return new construtos_1.Unario(this.hashArquivo, simboloIncrementoDecremento, new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando || 'qualquer'), 'DEPOIS');
}
return new construtos_1.Variavel(this.hashArquivo, simboloIdentificador, tipoOperando || 'qualquer');
case tenda_1.default.LEIA:
return this.expressaoLeia();
case tenda_1.default.NADA:
this.avancarEDevolverAnterior();
return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), null, 'nulo');
case tenda_1.default.NUMERO:
case tenda_1.default.NÚMERO:
case tenda_1.default.TEXTO:
const simboloNumeroTexto = this.avancarEDevolverAnterior();
const tipoInferido = (0, inferenciador_1.inferirTipoVariavel)(simboloNumeroTexto.literal);
const tipoDadosElementar = (0, inferenciador_1.tipoInferenciaParaTipoDadosElementar)(tipoInferido);
return new construtos_1.Literal(this.hashArquivo, Number(simboloNumeroTexto.linha), simboloNumeroTexto.literal, tipoDadosElementar);
case tenda_1.default.PARENTESE_ESQUERDO:
this.avancarEDevolverAnterior();
const expressao = this.expressao();
this.consumir(tenda_1.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
return new construtos_1.Agrupamento(this.hashArquivo, Number(simboloAtual.linha), expressao);
case tenda_1.default.SUPER:
const simboloSuper = this.avancarEDevolverAnterior();
if (!this.superclasseAtual) {
throw this.erro(this.simbolos[this.atual], "'Super' usado fora de declaração de classe com herança.");
}
return new construtos_1.Super(this.hashArquivo, simboloSuper, this.superclasseAtual);
case tenda_1.default.VERDADEIRO:
this.avancarEDevolverAnterior();
return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), true, 'lógico');
}
throw this.erro(this.simbolos[this.atual], 'Esperado expressão.');
}
chamar() {
let expressao = this.primario();
while (true) {
let tipoPrimitiva = undefined;
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.PARENTESE_ESQUERDO)) {
expressao = this.finalizarChamada(expressao, tipoPrimitiva);
}
else if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.PONTO)) {
const nome = this.consumir(tenda_1.default.IDENTIFICADOR, "Esperado nome de método ou propriedade após '.'.");
tipoPrimitiva = expressao.tipo;
expressao = new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, expressao, nome);
}
else if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.COLCHETE_ESQUERDO)) {
const indice = this.expressao();
const simboloFechamento = this.consumir(tenda_1.default.COLCHETE_DIREITO, "Esperado ']' após escrita do indice.");
expressao = new construtos_1.AcessoIndiceVariavel(this.hashArquivo, expressao, indice, simboloFechamento);
}
else {
break;
}
}
return expressao;
}
/**
* `AcessoMetodoOuPropriedade` é um construto intermediário em Delégua, e deve ser resolvido como outro
* construto antes de qualquer outra próxima etapa. Algumas validações adicionais também ocorrem aqui.
* @param {AcessoMetodoOuPropriedade} entidadeChamadaResolvida O construto original.
* @returns {Construto} O construto resolvido como um tipo mais específico.
* @see finalizarChamada
*/
resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamadaResolvida) {
const construtoTipado = entidadeChamadaResolvida;
switch (entidadeChamadaResolvida.tipo) {
case delegua_1.default.DICIONARIO:
case delegua_1.default.DICIONÁRIO:
if (!(construtoTipado.simbolo.lexema in primitivas_dicionario_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de dicionário.`);
}
const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno);
case delegua_1.default.INTEIRO:
case delegua_1.default.NUMERO:
case delegua_1.default.NÚMERO:
if (!(construtoTipado.simbolo.lexema in primitivas_numero_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de número.`);
}
const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno);
case delegua_1.default.MODULO:
case delegua_1.default.MÓDULO:
if (construtoTipado.simbolo.lexema in this.tiposDefinidosEmCodigo) {
// Construtor de classe.
return new construtos_1.Variavel(construtoTipado.hashArquivo, construtoTipado.simbolo, construtoTipado.objeto.tipo);
}
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema);
case delegua_1.default.TEXTO:
if (!(construtoTipado.simbolo.lexema in primitivas_texto_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de texto.`);
}
const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno);
case delegua_1.default.VETOR:
case delegua_1.default.VETOR_NUMERO:
case delegua_1.default.VETOR_NÚMERO:
case delegua_1.default.VETOR_TEXTO:
if (!(construtoTipado.simbolo.lexema in primitivas_vetor_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de vetor.`);
}
const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno);
}
return entidadeChamadaResolvida;
}
validarArgumentosEntidadeChamada(argumentosEntidadeChamada, argumentosUtilizados) {
if (argumentosEntidadeChamada.length === 0) {
return [];
}
const possiveisErros = [];
for (const [indice, argumentoEntidadeChamada] of argumentosEntidadeChamada.entries()) {
const argumentoUtilizado = argumentosUtilizados[indice];
if (argumentoUtilizado.tipo === null || argumentoUtilizado.tipo === undefined) {
continue;
}
const argumentoEntidadeChamadaVetor = argumentoEntidadeChamada.tipo.endsWith('[]');
const argumentoUtilizadoVetor = argumentoUtilizado.tipo.endsWith('[]');
if (argumentoEntidadeChamadaVetor !== argumentoUtilizadoVetor) {
possiveisErros.push(`Argumento: ${argumentoEntidadeChamada.nome}. Tipo esperado: ${argumentoEntidadeChamada.tipo}; Tipo utilizado: ${argumentoUtilizado.tipo}`);
continue;
}
const argumentoEntidadeChamadaQualquer = argumentoEntidadeChamada.tipo.startsWith('qualquer');
if (argumentoEntidadeChamadaQualquer) {
continue;
}
if (argumentoUtilizado.tipo !== argumentoEntidadeChamada.tipo) {
possiveisErros.push(`Argumento: ${argumentoEntidadeChamada.nome}. Tipo esperado: ${argumentoEntidadeChamada.tipo}; Tipo utilizado: ${argumentoUtilizado.tipo}`);
}
}
return possiveisErros;
}
/**
* Diversas verificações de resolução de entidade chamada, como resolver chamada da pilha ou usar referência, argumentos, etc.
* @param entidadeChamada O construto da entidade chamada.
* @param argumentos Os argumentos utilizados na chamada.
* @param tipoPrimitiva Se for uma primitiva, o tipo dela. Senão, `undefined`.
* @returns A entidade chamada resolvida, se as validações passarem.
*/
resolverEntidadeChamada(entidadeChamada, argumentos, tipoPrimitiva = undefined) {
if (entidadeChamada.constructor.name === 'Variavel') {
const entidadeChamadaResolvidaVariavel = entidadeChamada;
if (tipoPrimitiva === undefined) {
// Provavelmente uma chamada a alguma função da biblioteca global.
const informacoesPossivelFuncaoBibliotecaGlobal = this.pilhaEscopos.obterBibliotecaGlobal(entidadeChamadaResolvidaVariavel.simbolo.lexema);
if (informacoesPossivelFuncaoBibliotecaGlobal !== undefined) {
const erros = this.validarArgumentosEntidadeChamada(informacoesPossivelFuncaoBibliotecaGlobal.argumentos, argumentos);
if (erros.length > 0) {
throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaResolvidaVariavel.simbolo, `Erros ao resolver argumentos de chamada a ${entidadeChamadaResolvidaVariavel.simbolo.lexema}: \n${erros.reduce((mensagem, erro) => (mensagem += `${erro}\n`), '')}`);
}
return entidadeChamadaResolvidaVariavel;
}
}
if (tipoPrimitiva !== undefined &&
this.primitivasConhecidas.hasOwnProperty(entidadeChamadaResolvidaVariavel.simbolo.lexema)) {
var informacoesPrimitiva = this.primitivasConhecidas[entidadeChamadaResolvidaVariavel.simbolo.lexema];
const erros = this.validarArgumentosEntidadeChamada(informacoesPrimitiva.argumentos, argumentos);
if (erros.length > 0) {
throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaResolvidaVariavel.simbolo, `Erros ao resolver argumentos de chamada a ${entidadeChamadaResolvidaVariavel.simbolo.lexema}: \n${erros.reduce((mensagem, erro) => (mensagem += `${erro}\n`), '')}`);
}
return entidadeChamadaResolvidaVariavel;
}
if (entidadeChamadaResolvidaVariavel.simbolo.lexema in this.tiposDefinidosEmCodigo) {
return entidadeChamadaResolvidaVariavel;
}
const possivelReferencia = this.pilhaEscopos.obterReferenciaFuncao(entidadeChamadaResolvidaVariavel.simbolo.lexema);
if (possivelReferencia !== null) {
return new construtos_1.ReferenciaFuncao(entidadeChamada.hashArquivo, entidadeChamada.linha, entidadeChamadaResolvidaVariavel.simbolo, entidadeChamadaResolvidaVariavel.tipo, possivelReferencia.id);
}
return new construtos_1.ArgumentoReferenciaFuncao(entidadeChamada.hashArquivo, entidadeChamada.linha, entidadeChamadaResolvidaVariavel.simbolo);
}
if (entidadeChamada.constructor.name === 'AcessoMetodoOuPropriedade') {
return this.resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamada);
}
return entidadeChamada;
}
declaracaoDeFuncao(identificador) {
// Se houver chamadas recursivas à função, precisamos definir um tipo
// para ela. Vai ser atualizado após avaliação do corpo da função.
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, 'qualquer'));
const corpoDaFuncao = this.corpoDaFuncao('implícita');
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, corpoDaFuncao.tipo));
const funcaoDeclaracao = new declaracoes_1.FuncaoDeclaracao(identificador, corpoDaFuncao, corpoDaFuncao.tipo);
this.pilhaEscopos.registrarReferenciaFuncao(identificador.lexema, funcaoDeclaracao);
return funcaoDeclaracao;
}
finalizarChamada(entidadeChamada, tipoPrimitiva = undefined) {
const argumentos = [];
if (!this.verificarTipoSimboloAtual(tenda_1.default.PARENTESE_DIREITO)) {
do {
// `apply()` em JavaScript aceita até 255 parâmetros.
if (argumentos.length >= 255) {
throw this.erro(this.simbolos[this.atual], 'Não pode haver mais de 255 argumentos.');
}
argumentos.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.VIRGULA));
}
this.consumir(tenda_1.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos.");
// Toda chamada precisa saber de antemão qual o tipo resolvido.
const entidadeChamadaResolvida = this.resolverEntidadeChamada(entidadeChamada, argumentos, tipoPrimitiva);
// TODO: Criar forma de validar tipos dos argumentos da entidade chamada.
const construtoChamada = new construtos_1.Chamada(this.hashArquivo, entidadeChamadaResolvida, argumentos);
construtoChamada.tipo = 'qualquer';
return construtoChamada;
}
unario() {
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.NÃO, tenda_1.default.SUBTRACAO, tenda_1.default.BIT_NOT, tenda_1.default.INCREMENTAR, tenda_1.default.DECREMENTAR)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.unario();
return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES');
}
return this.chamar();
}
multiplicar() {
let expressao = this.exponenciacao();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.DIVISAO, tenda_1.default.DIVISAO_INTEIRA, tenda_1.default.MODULO, tenda_1.default.MULTIPLICACAO)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.exponenciacao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
/**
* Se símbolo de operação é `+`, `-`, `+=` ou `-=`, monta objeto `Binario` para
* ser avaliado pelo Interpretador.
* @returns Um Construto, normalmente um `Binario`, ou `Unario` se houver alguma operação unária para ser avaliada.
*/
adicaoOuSubtracao() {
let expressao = this.multiplicar();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.SUBTRACAO, tenda_1.default.ADICAO)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.multiplicar();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
bitShift() {
let expressao = this.adicaoOuSubtracao();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.MENOR_MENOR, tenda_1.default.MAIOR_MAIOR)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.adicaoOuSubtracao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
bitE() {
let expressao = this.bitShift();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.BIT_AND)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.bitShift();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
bitOu() {
let expressao = this.bitE();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.BIT_OR, tenda_1.default.BIT_XOR)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.bitE();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
comparar() {
let expressao = this.bitOu();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.MAIOR, tenda_1.default.MAIOR_IGUAL, tenda_1.default.MENOR, tenda_1.default.MENOR_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.bitOu();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
comparacaoIgualdade() {
let expressao = this.comparar();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.NÃO, tenda_1.default.É)) {
const operador = this.simbolos[this.atual - 1];
if (operador.tipo === tenda_1.default.NÃO) {
this.consumir(tenda_1.default.É, `Esperado 'é' após 'não'.`);
operador.tipo = tenda_1.default.DIFERENTE;
operador.lexema = 'não é';
operador.literal = 'não é';
}
const direito = this.comparar();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
em() {
let expressao = this.comparacaoIgualdade();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.TEM)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.comparacaoIgualdade();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
e() {
let expressao = this.em();
while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.E)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.em();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
/**
* Método que resolve atribuições.
* @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`.
*/
atribuir() {
const expressao = this.ou();
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.IGUAL)) {
const igual = this.simbolos[this.atual - 1];
const valor = this.expressao();
switch (expressao.constructor.name) {
case 'Variavel':
return new construtos_1.Atribuir(this.hashArquivo, expressao, valor);
case 'AcessoMetodoOuPropriedade':
const expressaoAcessoMetodoOuPropriedade = expressao;
return new construtos_1.DefinirValor(this.hashArquivo, igual.linha, expressaoAcessoMetodoOuPropriedade.objeto, expressaoAcessoMetodoOuPropriedade.simbolo, valor);
case 'AcessoIndiceVariavel':
const expressaoAcessoIndiceVariavel = expressao;
return new construtos_1.AtribuicaoPorIndice(this.hashArquivo, expressaoAcessoIndiceVariavel.linha, expressaoAcessoIndiceVariavel.entidadeChamada, expressaoAcessoIndiceVariavel.indice, valor);
}
throw this.erro(igual, 'Tarefa de atribuição inválida');
}
return expressao;
}
/**
* Declaração para comando `leia`, para ler dados de entrada do usuário.
* @returns Um objeto da classe `Leia`.
*/
expressaoLeia() {
const simboloLeia = this.avancarEDevolverAnterior();
this.consumir(tenda_1.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos argumentos em instrução `leia`.");
const argumentos = [];
if (this.simbolos[this.atual].tipo !== tenda_1.default.PARENTESE_DIREITO) {
do {
argumentos.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.VIRGULA));
}
this.consumir(tenda_1.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos em instrução `leia`.");
return new construtos_1.Leia(simboloLeia, argumentos);
}
// TODO: Depreciar.
expressao() {
return this.atribuir();
}
blocoEscopo(tipo) {
this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
let declaracoes = [];
while (!this.verificarTipoSimboloAtual(tenda_1.default.FIM) && !this.estaNoFinal()) {
const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else {
declaracoes.push(retornoDeclaracao);
}
}
this.consumir(tenda_1.default.FIM, `Esperado 'fim' para concluir bloco de escopo ${tipo ? 'de ' + tipo : ''}.`);
this.pilhaEscopos.removerUltimo();
return declaracoes;
}
declaracaoComentarioUmaLinha() {
const simboloComentario = this.avancarEDevolverAnterior();
return new declaracoes_1.Comentario(simboloComentario.hashArquivo, simboloComentario.linha, simboloComentario.literal, false);
}
declaracaoContinua() {
if (this.blocos < 1) {
throw this.erro(this.simbolos[this.atual - 1], "'continua' precisa estar em um laço de repetição.");
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(tenda_1.default.PONTO_E_VIRGULA);
return new declaracoes_1.Continua(this.simbolos[this.atual - 1]);
}
declaracaoEnquanto() {
try {
const simboloEnquanto = this.simbolos[this.atual - 1];
this.blocos += 1;
const condicao = this.expressao();
this.consumir(tenda_1.default.FAÇA, "Esperado 'faça' depois da condição.");
const blocoCorpo = this.blocoEscopo('enquanto');
return new declaracoes_1.Enquanto(condicao, new declaracoes_1.Bloco(simboloEnquanto.linha, simboloEnquanto.hashArquivo, blocoCorpo));
}
finally {
this.blocos -= 1;
}
}
declaracaoEscreva() {
const simboloAtual = this.simbolos[this.atual];
this.consumir(tenda_1.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos valores em escreva.");
const argumentos = [];
if (!this.verificarTipoSimboloAtual(tenda_1.default.PARENTESE_DIREITO)) {
do {
argumentos.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.VIRGULA));
}
this.consumir(tenda_1.default.PARENTESE_DIREITO, "Esperado ')' após os valores em escreva.");
// Ponto-e-vírgula é opcional aqui.
// this.verificarSeSimboloAtualEIgualA(tiposDeSimbolos.PONTO_E_VIRGULA);
return new declaracoes_1.Escreva(Number(simboloAtual.linha), simboloAtual.hashArquivo, argumentos);
}
declaracaoExpressao() {
const expressao = this.expressao();
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(tenda_1.default.PONTO_E_VIRGULA);
return new declaracoes_1.Expressao(expressao);
}
declaracaoPara() {
const simboloPara = this.simbolos[this.atual - 1];
this.consumir(tenda_1.default.CADA, `Esperado palavra reservada 'cada' após 'para'.`);
this.blocos += 1;
const nomeVariavelIteracao = this.consumir(tenda_1.default.IDENTIFICADOR, "Esperado identificador de variável de iteração para instrução 'para cada'.");
this.consumir(tenda_1.default.EM, "Esperado palavra reservada 'em' após variável de iteração em instrução 'para cada'.");
// Se for um literal ou identificador numérico, segue um `para`
// tradicional de Delégua, com variável de controle e passo positivo, incrementado em 1.
const literalOuVariavelInicio = this.adicaoOuSubtracao();
this.blocos -= 1;
switch (literalOuVariavelInicio.constructor.name) {
case 'Literal':
return this.declaracaoParaTradicional(simboloPara, nomeVariavelIteracao, literalOuVariavelInicio);
// TODO: Terminar
case 'Variavel':
case 'Vetor':
default:
return this.declaracaoParaCada(simboloPara, nomeVariavelIteracao, literalOuVariavelInicio);
}
}
declaracaoParaCada(simboloParaCada, simboloVariavelIteracao, literalOuVariavelIteravel) {
const tipoVetor = literalOuVariavelIteravel.tipo;
// TODO: Permitir 'qualquer' aqui é bastante frágil. Criar uma forma de validar o tipo
// antes dessa avaliação.
if (!tipoVetor.endsWith('[]') && !['qualquer', 'vetor'].includes(tipoVetor)) {
throw this.erro(simboloParaCada, `Variável ou constante em 'para cada' não é iterável. Tipo resolvido: ${tipoVetor}.`);
}
let tipoVariavelIteracao = 'qualquer';
if (tipoVetor.endsWith('[]')) {
tipoVariavelIteracao = tipoVetor.slice(0, -2);
}
this.pilhaEscopos.definirInformacoesVariavel(simboloVariavelIteracao.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simboloVariavelIteracao.lexema, tipoVariavelIteracao));
this.consumir(tenda_1.default.FAÇA, "Esperado palavra reservada 'faça' após literal ou variável de iteração em declaração 'para cada'.");
const corpo = this.blocoEscopo();
return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloParaCada.linha), new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao), literalOuVariavelIteravel, new declaracoes_1.Bloco(simboloParaCada.hashArquivo, simboloParaCada.linha, corpo));
}
declaracaoParaTradicional(simboloPara, simboloVariavelIteracao, literalOuVariavelInicio) {
this.consumir(tenda_1.default.ATÉ, "Esperado palavra reservada 'até' após literal ou identificador de início de declaração 'para cada'.");
const literalOuVariavelFim = this.adicaoOuSubtracao();
this.consumir(tenda_1.default.FAÇA, "Esperado palavra reservada 'faça' após literal ou variável de passo final em declaração 'para cada'.");
// A variável de iteração precisa ser definida aqui, para que o corpo de `para cada` (um escopo)
// seja capaz de reconhecer a variável e seu tipo.
this.pilhaEscopos.definirInformacoesVariavel(simboloVariavelIteracao.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(simboloVariavelIteracao.lexema, 'inteiro'));
const corpo = this.blocoEscopo();
// `variavelIteracao <= valorFinal`
let operadorCondicao = new simbolo_1.Simbolo(tenda_1.default.MENOR_IGUAL, '<=', null, Number(simboloPara.linha), this.hashArquivo);
let operadorCondicaoIncremento = new simbolo_1.Simbolo(tenda_1.default.MENOR, '<', null, Number(simboloPara.linha), this.hashArquivo);
return new declaracoes_1.Para(this.hashArquivo, Number(simboloPara.linha),
// Inicialização.
new declaracoes_1.Expressao(new construtos_1.Atribuir(this.hashArquivo, new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao, 'inteiro'), literalOuVariavelInicio)),
// Condição.
new construtos_1.Binario(this.hashArquivo, new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao, 'inteiro'), operadorCondicao, literalOuVariavelFim),
// Incremento, feito em construto especial `FimPara`.
new construtos_1.FimPara(this.hashArquivo, Number(simboloPara.linha), new construtos_1.Binario(this.hashArquivo, new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao, 'inteiro'), operadorCondicaoIncremento, literalOuVariavelFim), new declaracoes_1.Expressao(new construtos_1.Atribuir(this.hashArquivo, new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao, 'inteiro'), new construtos_1.Binario(this.hashArquivo, new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao, 'inteiro'), new simbolo_1.Simbolo(tenda_1.default.ADICAO, '+', null, Number(simboloPara.linha), this.hashArquivo), new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), 1))))), new declaracoes_1.Bloco(simboloPara.hashArquivo, simboloPara.linha, corpo));
}
declaracaoRetorna() {
const simboloChave = this.simbolos[this.atual - 1];
let valor = null;
if ([
tenda_1.default.CHAVE_ESQUERDA,
tenda_1.default.COLCHETE_ESQUERDO,
tenda_1.default.FALSO,
tenda_1.default.FUNÇÃO,
tenda_1.default.IDENTIFICADOR,
tenda_1.default.ISTO,
tenda_1.default.NÃO,
tenda_1.default.NUMERO,
tenda_1.default.NADA,
tenda_1.default.PARENTESE_ESQUERDO,
tenda_1.default.SUPER,
tenda_1.default.TEXTO,
tenda_1.default.VERDADEIRO,
].includes(this.simbolos[this.atual].tipo)) {
valor = this.expressao();
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(tenda_1.default.PONTO_E_VIRGULA);
return new declaracoes_1.Retorna(simboloChave, valor);
}
declaracaoSe() {
const condicao = this.expressao();
this.consumir(tenda_1.default.ENTÃO, "Esperado 'então' após a condição.");
const caminhoEntao = this.resolverDeclaracao();
let caminhoSenao = null;
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.SENÃO)) {
caminhoSenao = this.resolverDeclaracao();
}
return new declaracoes_1.Se(condicao, caminhoEntao, [], caminhoSenao);
}
declaracaoSustar() {
if (this.blocos < 1) {
throw this.erro(this.simbolos[this.atual - 1], "'sustar' ou 'pausa' deve estar dentro de um laço de repetição.");
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(tenda_1.default.PONTO_E_VIRGULA);
return new declaracoes_1.Sustar(this.simbolos[this.atual - 1]);
}
/**
* Todas as resoluções triviais da linguagem, ou seja, todas as
* resoluções que podem ocorrer dentro ou fora de um bloco.
* @returns Normalmente uma `Declaracao`, mas há casos em que
* outros objetos podem ser retornados.
* @see resolverDeclaracaoForaDeBloco para as declarações que não podem
* ocorrer em blocos de escopo elementares.
*/
resolverDeclaracao() {
switch (this.simbolos[this.atual].tipo) {
case tenda_1.default.COMENTARIO:
return this.declaracaoComentarioUmaLinha();
case tenda_1.default.CONTINUA:
this.avancarEDevolverAnterior();
return this.declaracaoContinua();
case tenda_1.default.ENQUANTO:
this.avancarEDevolverAnterior();
return this.declaracaoEnquanto();
case tenda_1.default.EXIBA:
this.avancarEDevolverAnterior();
return this.declaracaoEscreva();
case tenda_1.default.FAÇA:
this.avancarEDevolverAnterior();
return this.blocoEscopo();
case tenda_1.default.PARA:
this.avancarEDevolverAnterior();
return this.declaracaoPara();
case tenda_1.default.PAUSA:
case tenda_1.default.SUSTAR:
this.avancarEDevolverAnterior();
return this.declaracaoSustar();
case tenda_1.default.SE:
this.avancarEDevolverAnterior();
return this.declaracaoSe();
case tenda_1.default.RETORNA:
this.avancarEDevolverAnterior();
return this.declaracaoRetorna();
case tenda_1.default.TENTE:
this.avancarEDevolverAnterior();
return this.declaracaoTente();
case tenda_1.default.SEJA:
this.avancarEDevolverAnterior();
return this.declaracaoDeVariaveisOuFuncoes();
}
const simboloAtual = this.simbolos[this.atual];
if (simboloAtual.tipo === tenda_1.default.IDENTIFICADOR) {
// Pela gramática, a seguinte situação não pode ocorrer:
// 1. O símbolo anterior ser um identificador; e
// 2. O símbolo anterior estar na mesma linha do identificador atual.
const simboloAnterior = this.simbolos[this.atual - 1];
if (!!simboloAnterior &&
simboloAnterior.tipo === tenda_1.default.IDENTIFICADOR &&
simboloAnterior.linha === simboloAtual.linha) {
throw this.erro(this.simbolos[this.atual], 'Não é permitido ter dois identificadores seguidos na mesma linha.');
}
}
return this.declaracaoExpressao();
}
logicaComumInferenciaTiposVariaveis(inicializador) {
switch (inicializador.constructor.name) {
case 'AcessoIndiceVariavel':
const entidadeChamadaAcessoIndiceVariavel = inicializador
.entidadeChamada;
return entidadeChamadaAcessoIndiceVariavel.tipo.slice(0, -2);
case 'Chamada':
const entidadeChamadaChamada = inicializador.entidadeChamada;
switch (entidadeChamadaChamada.constructor.name) {
case 'AcessoMetodo':
const entidadeChamadaAcessoMetodo = entidadeChamadaChamada;
return entidadeChamadaAcessoMetodo.tipoRetornoMetodo;
case 'AcessoMetodoOuPropriedade':
// Este caso ocorre quando a variável/constante é do tipo 'qualquer',
// e a chamada normalmente é feita para uma primitiva.
// A inferência, portanto, ocorre pelo uso da primitiva.
const entidadeChamadaAcessoMetodoOuPropriedade = entidadeChamadaChamada;
if (this.primitivasConhecidas.hasOwnProperty(entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema)) {
return this.primitivasConhecidas[entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema].tipo;
}
throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamadaAcessoMetodoOuPropriedade.simbolo, `Primitiva '${entidadeChamadaAcessoMetodoOuPropriedade.simbolo.lexema}' não existe.`);
case 'AcessoPropriedade':
const entidadeChamadaAcessoPropriedade = entidadeChamadaChamada;
return entidadeChamadaAcessoPropriedade.tipoRetornoPropriedade;
case 'ArgumentoReferenciaFuncao':
// TODO: Voltar aqui se necessário.
return 'qualquer';
case 'ReferenciaFuncao':
const entidadeChamadaReferenciaFuncao = entidadeChamadaChamada;
return entidadeChamadaReferenciaFuncao.tipo;
case 'Variavel':
const entidadeChamadaVariavel = entidadeChamadaChamada;
return entidadeChamadaVariavel.tipo;
}
break;
case 'FuncaoConstruto':
const funcaoConstruto = inicializador;
return `função<${funcaoConstruto.tipo}>`;
case 'Leia':
return 'texto';
case 'Dupla':
case 'Trio':
case 'Quarteto':
case 'Quinteto':
case 'Sexteto':
case 'Septeto':
case 'Octeto':
case 'Noneto':
case 'Deceto':
return delegua_1.default.TUPLA;
case 'ImportarBiblioteca':
case 'ModuloDeclaracoes':
return 'módulo';
default:
return inicializador.tipo;
}
}
/**
* Após palavra reservada `seja`, é esperado ou uma variável, ou uma função.
* @returns Um Construto, ou do tipo `Var` para variável, ou do tipo `FuncaoDeclaracao` se for
* declaração de função.
*/
declaracaoDeVariaveisOuFuncoes() {
const identificador = this.consumir(tenda_1.default.IDENTIFICADOR, 'Esperado nome da variável ou função.');
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.PARENTESE_ESQUERDO)) {
return this.declaracaoDeFuncao(identificador);
}
this.consumir(tenda_1.default.IGUAL, "Esperado símbolo de igual após nome de identificador em declaração 'seja'.");
const inicializador = this.expressao();
const tipo = this.logicaComumInferenciaTiposVariaveis(inicializador);
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(identificador.lexema, tipo));
return new declaracoes_1.Var(identificador, inicializador, tipo);
}
logicaComumParametros() {
const parametros = [];
do {
const parametro = {};
parametro.nome = this.consumir(tenda_1.default.IDENTIFICADOR, 'Esperado nome do parâmetro.');
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.IGUAL)) {
const valorPadrao = this.primario();
parametro.valorPadrao = valorPadrao;
}
if (this.verificarSeSimboloAtualEIgualA(tenda_1.default.DOIS_PONTOS)) {
let tipoDadoParametro = this.verificarDefinicaoTipoAtual();
parametro.tipoDado = tipoDadoParametro;
this.avancarEDevolverAnterior();
}
this.pilhaEscopos.definirInformacoesVariavel(parametro.nome.lexema, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(parametro.nome.lexema, parametro.tipoDado || 'qualquer'));
parametros.push(parametro);
if (parametro.abrangencia === 'multiplo')
break;
} while (this.verificarSeSimboloAtualEIgualA(tenda_1.default.VIRGULA));
return parametros;
}
corpoDaFuncao(tipo) {
// O parêntese esquerdo aqui é o símbolo atual.
// Ele já foi lido neste ponto.
const parenteseEsquerdo = this.simbolos[this.atual];
let parametros = [];
if (!this.verificarTipoSimboloAtual(tenda_1.default.PARENTESE_DIREITO)) {
parametros = this.logicaComumParametros();
}
this.consumir(tenda_1.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
switch (tipo) {
case 'função':
this.consumir(tenda_1.default.SUBTRACAO, 'Esperado seta após fechamento de parênteses para declaração de função.');
this.consumir(tenda_1.default.MAIOR, 'Esperado seta após fechamento de parênteses para declaração de função.');
break;
case 'implícita':
this.consumir(tenda_1.default.IGUAL, 'Esperado sinal de igual após fechamento de parênteses para declaração de função.');
break;
}
const corpo = this.resolverDeclaracao();
// Se o corpo for uma `Expressao`, corpo é convertido para `Retorna`.
// Tenda trabalha com retornos implícitos.
let corpoResolvido = [];
if (corpo.constructor.name === 'Expressao') {
const expressaoComoRetorna = new declaracoes_1.Retorna(new simbolo_1.Simbolo(tenda_1.default.RETORNA, 'retorna', 'retorna', parenteseEsquerdo.linha, this.hashArquivo), corpo.expressao);
corpoResolvido.push(expressaoComoRetorna);
}
else {
// TODO: Verificar se `corpo` é sempre um Array aqui.
corpoResolvido = corpo;
}
// TODO: Inferir o tipo de retorno corretamente.
return new construtos_1.FuncaoConstruto(this.hashArquivo, Number(parenteseEsquerdo.linha), parametros, corpoResolvido, 'qualquer');
}
/**
* Até então, Tenda não tem casos de declarações fora de blocos.
* Isso pode mudar futuramente. Portanto, esta seção será mantida.
* @returns Uma `Declaracao` ou várias, dependendo do retorno de `resolverDeclaracao`.
* @see resolverDeclaracao
*/
resolverDeclaracaoForaDeBloco() {
try {
return this.resolverDeclaracao();
}
catch (erro) {
this.sincronizar();
this.erros.push(erro);
return null;
}
}
/**
* Usado quando há erros na avaliação sintática.
* Garante que o código não entre em loop infinito.
* @returns Sempre retorna `void`.
*/
sincronizar() {
this.avancarEDevolverAnterior();
while (!this.estaNoFinal()) {
const tipoSimboloAtual = this.simbolos[this.atual - 1].tipo;
switch (tipoSimboloAtual) {
case tenda_1.default.FUNÇÃO:
case tenda_1.default.VARIAVEL:
case tenda_1.default.PARA:
case tenda_1.default.SE:
case tenda_1.default.ENQUANTO:
case tenda_1.default.EXIBA:
case tenda_1.default.RETORNA:
return;
}
this.avancarEDevolverAnterior();
}
}
/**
* Inicializa o primeiro nível da pilha de escopos, normalmente com ítens da biblioteca global.
*/
inicializarPilhaEscopos() {
this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
// TODO: Escrever algum tipo de validação aqui.
for (const tipos of Object.values(this.tiposDeFerramentasExternas)) {
for (const [nomeTipo, tipo] of Object.entries(tipos)) {
this.pilhaEscopos.definirInformacoesVariavel(nomeTipo, new informacao_variavel_ou_constante_1.InformacaoVariavelOuConstante(nomeTipo, tipo));
}
}
}
analisar(retornoLexador, hashArquivo) {
const inicioAnalise = (0, browser_process_hrtime_1.default)();
this.erros = [];
this.atual = 0;
this.blocos = 0;
this.hashArquivo = hashArquivo || 0;
this.simbolos = (retornoLexador === null || retornoLexador === void 0 ? void 0 : retornoLexador.simbolos) || [];
this.tiposDefinidosEmCodigo = {};
this.inicializarPilhaEscopos();
let declaracoes = [];
while (!this.estaNoFinal()) {
const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else if (retornoDeclaracao !== null) {
declaracoes.push(retornoDeclaracao);
}
}
if (this.performance) {
const deltaAnalise = (0, browser_process_hrtime_1.default)(inicioAnalise);
// eslint-disable-next-line no-undef
console.log(`[Avaliador Sintático Tenda] Tempo para análise: ${deltaAnalise[0] * 1e9 + deltaAnalise[1]}ns`);
}
return {
declaracoes: declaracoes,
erros: this.erros,
};
}
}
exports.AvaliadorSintaticoTenda = AvaliadorSintaticoTenda;
//# sourceMappingURL=avaliador-sintatico-tenda.js.map