@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
2,835 lines • 173 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.AvaliadorSintatico = 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_elemento_sintatico_1 = require("../informacao-elemento-sintatico");
const comum_1 = require("./comum");
const montao_tipos_1 = require("./montao-tipos");
const elemento_montao_tipos_1 = require("./elemento-montao-tipos");
const delegua_1 = __importDefault(require("../tipos-de-dados/delegua"));
const delegua_2 = __importDefault(require("../tipos-de-simbolos/delegua"));
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"));
/**
* O avaliador sintático (_Parser_) é responsável por transformar os símbolos do Lexador em estruturas de alto nível.
* Essas estruturas de alto nível são as partes que executam lógica de programação de fato.
* Há dois grupos de estruturas de alto nível: Construtos e Declarações.
*
* Construtos não existem por si só: cada construto precisa estar dentro de uma declaração para ser
* aceito pela próxima etapa, como tradução, interpretação, análise semântica, etc.
*
* Este é o avaliador sintático de Delégua que, assim como todos os demais dialetos baseados
* neste núcleo, são uma derivação do avaliador sintático base. Aqui estão implementadas várias mecânicas
* a mais relacionadas a tipagem e registros de bibliotecas externas:
*
* - `tiposDeFerramentasExternas` é utilizada em [Liquido](https://github.com/DesignLiquido/liquido)
* para registro de tipos exclusivos de Liquido, como classes de requisição e resposta;
* - `primitivasConhecidas` é utilizada aqui para registro de métodos relacionados a tipos, e também
* para as bibliotecas externas de Delégua registrarem suas respectivas resoluções de tipos;
* - `montaoTipos` é uma implementação de montão muito semelhante ao montão do interpretador, para
* tipos complexos com N níveis de profundidade, como dicionários e objetos.
*/
class AvaliadorSintatico 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.interfacesDeclaradas = {};
this.tiposDeFerramentasExternas = {};
this.primitivasConhecidas = {};
this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
this.montaoTipos = new montao_tipos_1.MontaoTipos();
this.intuirTipoQualquerParaIdentificadores = false;
this.emAjuda = false;
(0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'dicionário', primitivas_dicionario_1.default);
(0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'número', primitivas_numero_1.default);
(0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'texto', primitivas_texto_1.default);
(0, comum_1.registrarPrimitiva)(this.primitivasConhecidas, 'vetor', primitivas_vetor_1.default);
}
verificarDefinicaoTipoAtual() {
const tipos = [...Object.values(delegua_1.default)];
if (this.simbolos[this.atual].lexema in this.tiposDefinidosEmCodigo) {
return this.simbolos[this.atual].lexema;
}
if (this.simbolos[this.atual].lexema in this.interfacesDeclaradas) {
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(delegua_2.default.COLCHETE_ESQUERDO)) {
const tiposVetores = [
'inteiro[]',
'numero[]',
'número[]',
'qualquer[]',
'real[]',
'texto[]',
];
this.avancarEDevolverAnterior();
if (!this.verificarTipoProximoSimbolo(delegua_2.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;
}
async construtoAjuda() {
const simboloAjuda = this.avancarEDevolverAnterior();
if (this.estaNoFinal() ||
this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_ESQUERDO) {
return new construtos_1.AjudaComoConstruto(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, false);
}
this.avancarEDevolverAnterior(); // parêntese esquerdo
if (this.simbolos[this.atual].tipo === delegua_2.default.PARENTESE_DIREITO) {
this.avancarEDevolverAnterior();
return new construtos_1.AjudaComoConstruto(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, true);
}
this.emAjuda = true;
const expressaoAjuda = await this.expressao();
this.emAjuda = false;
this.consumir(delegua_2.default.PARENTESE_DIREITO, `Esperado parêntese direito após expressão usada como argumento em ajuda(). Atual: ${this.simbolos[this.atual].lexema}.`);
return new construtos_1.AjudaComoConstruto(simboloAjuda.hashArquivo, simboloAjuda.linha, expressaoAjuda);
}
async obterChaveDicionario() {
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.NUMERO:
case delegua_2.default.TEXTO:
case delegua_2.default.FALSO:
case delegua_2.default.VERDADEIRO:
return await this.primario();
case delegua_2.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 delegua_2.default.COLCHETE_ESQUERDO:
this.avancarEDevolverAnterior();
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
return await 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.`);
}
}
async construtoDicionario(simboloChaveEsquerda) {
this.avancarEDevolverAnterior();
const chaves = [];
const valores = [];
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA)) {
return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), [], []);
}
while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA)) {
const chave = await this.obterChaveDicionario();
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
const valor = await this.atribuir();
chaves.push(chave);
valores.push(valor);
if (this.simbolos[this.atual].tipo !== delegua_2.default.CHAVE_DIREITA) {
this.consumir(delegua_2.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
}
}
return new construtos_1.Dicionario(this.hashArquivo, Number(simboloChaveEsquerda.linha), chaves, valores);
}
/**
* `delegua-node` e a extensão para VSCode precisam que este método seja assíncrono.
* @returns
*/
async construtoImportar() {
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após declaração.");
const caminho = await this.expressao();
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após declaração.");
return Promise.resolve(new construtos_1.ImportarComoConstruto(caminho));
}
async construtoTupla() {
const expressao = await this.expressao();
const argumentos = [expressao];
while (this.simbolos[this.atual].tipo === delegua_2.default.VIRGULA) {
this.avancarEDevolverAnterior();
argumentos.push(await this.expressao());
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após a expressão.");
return new tuplas_1.SeletorTuplas(...argumentos);
}
async enquantoComoConstruto() {
const { condicao, corpo } = await this.logicaComumEnquanto();
return new construtos_1.EnquantoComoConstruto(condicao, corpo);
}
async fazerComoConstruto(simboloFazer) {
try {
this.blocos += 1;
const { caminhoFazer, condicaoEnquanto } = await this.logicaComumFazer();
return new construtos_1.FazerComoConstruto(simboloFazer.hashArquivo, Number(simboloFazer.linha), caminhoFazer, condicaoEnquanto);
}
finally {
this.blocos -= 1;
}
}
async paraCadaComoConstrutoVetor(simboloPara) {
const { variavelIteracao, vetor, corpo } = await this.logicaComumParaCadaVetor(simboloPara);
return new construtos_1.ParaCadaComoConstruto(this.hashArquivo, Number(simboloPara.linha), variavelIteracao, vetor, corpo);
}
async paraCadaComoConstrutoDicionario(simboloPara) {
const { nomeVariavelChave, nomeVariavelValor, dicionario, corpo } = await this.logicaParaCadaDicionario(simboloPara);
return new construtos_1.ParaCadaComoConstruto(this.hashArquivo, Number(simboloPara.linha), new tuplas_1.Dupla(new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelChave.lexema), new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelValor.lexema)), dicionario, corpo);
}
async paraCadaComoConstruto(simboloPara) {
if (this.verificarTipoSimboloAtual(delegua_2.default.IDENTIFICADOR)) {
return await this.paraCadaComoConstrutoVetor(simboloPara);
}
if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
return await this.paraCadaComoConstrutoDicionario(simboloPara);
}
throw this.erro(simboloPara, 'Identificador de iteração deve ser ou um par chave-valor, ou um nome de variável.');
}
async paraTradicionalComoConstruto(simboloPara) {
const { inicializador, condicao, incrementar, corpo } = await this.logicaComumPara();
return new construtos_1.ParaComoConstruto(simboloPara.hashArquivo, simboloPara.linha, inicializador, condicao, incrementar, corpo);
}
/**
* Método que resolve `para` ou `para cada` como construto.
*/
async paraComoConstruto(simboloPara) {
try {
this.blocos += 1;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CADA)) {
return await this.paraCadaComoConstruto(simboloPara);
}
return await this.paraTradicionalComoConstruto(simboloPara);
}
finally {
this.blocos -= 1;
}
}
/**
* Resolve uma lista de compreensão.
* @returns {ListaCompreensao} A lista de compreensão resolvida.
*/
async resolverCompreensaoDeLista(retornoExpressao) {
this.consumir(delegua_2.default.PARA, "Esperado instrução 'para' após identificador.");
this.consumir(delegua_2.default.CADA, "Esperado instrução 'cada' após 'para'.");
const simboloVariavelIteracao = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável após 'para cada'.");
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.DE, delegua_2.default.EM)) {
throw this.erro(this.simbolos[this.atual], "Esperado palavras reservadas 'em' ou 'de' após variável de iteração em instrução em lista de compreensão.");
}
const localizacaoVetor = this.simboloAnterior();
const vetor = await this.ou();
let condicao = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SE)) {
condicao = await this.expressao();
}
else {
condicao = new construtos_1.Literal(this.hashArquivo, Number(localizacaoVetor.linha), true);
}
this.consumir(delegua_2.default.COLCHETE_DIREITO, 'Espero fechamento de colchetes após condição.');
const tipoVetor = vetor.tipo;
if (!tipoVetor.endsWith('[]') && !['qualquer', 'vetor'].includes(tipoVetor)) {
throw this.erro(localizacaoVetor, `Variável ou constante em 'para cada' não é iterável. Tipo resolvido: ${tipoVetor}.`);
}
const variavelIteracao = new construtos_1.Variavel(this.hashArquivo, simboloVariavelIteracao);
return new construtos_1.ListaCompreensao(Number(this.simbolos[this.atual]), this.hashArquivo, retornoExpressao, vetor, new construtos_1.ParaCadaComoConstruto(retornoExpressao.hashArquivo, retornoExpressao.linha, variavelIteracao, vetor, new declaracoes_1.Bloco(retornoExpressao.hashArquivo, retornoExpressao.linha, [
new declaracoes_1.Se(condicao, new declaracoes_1.Bloco(retornoExpressao.hashArquivo, retornoExpressao.linha, [
new declaracoes_1.Retorna(simboloVariavelIteracao, retornoExpressao),
]), [], null),
])), retornoExpressao.tipo ? `${retornoExpressao.tipo}[]` : 'qualquer[]');
}
async primario() {
const simboloAtual = this.simbolos[this.atual];
let valores = [];
switch (simboloAtual.tipo) {
case delegua_2.default.AJUDA:
return await this.construtoAjuda();
case delegua_2.default.CHAVE_ESQUERDA:
return await this.construtoDicionario(simboloAtual);
case delegua_2.default.COLCHETE_ESQUERDO:
this.avancarEDevolverAnterior();
valores = [];
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) {
return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), [], 'qualquer[]');
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
return this.construtoTupla();
}
// Ao resolver a expressão aqui, identificadores dentro da expressão de compreensão
// de lista serão tratados como 'qualquer', para evitar erros de tipo.
this.intuirTipoQualquerParaIdentificadores = true;
const retornoExpressaoOuPrimeiroValor = await this.seTernario();
this.intuirTipoQualquerParaIdentificadores = false;
if (this.simbolos[this.atual].tipo === delegua_2.default.PARA) {
return await this.resolverCompreensaoDeLista(retornoExpressaoOuPrimeiroValor);
}
// Aqui já sabemos que não é uma compreensão de lista.
valores.push(retornoExpressaoOuPrimeiroValor);
while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) {
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.VIRGULA:
const simboloVirgula = this.avancarEDevolverAnterior();
valores.push(new construtos_1.Separador(simboloVirgula));
break;
case delegua_2.default.COMENTARIO:
const simboloComentario = this.avancarEDevolverAnterior();
valores.push(new construtos_1.ComentarioComoConstruto(simboloComentario));
break;
default:
const valor = await this.seTernario();
valores.push(valor);
break;
}
}
// Remover comentários, verificar se vírgulas fazem sentido.
const valoresSemComentarios = valores.filter((v) => v.constructor !== construtos_1.ComentarioComoConstruto);
let elementoSeparador = false; // O primeiro elemento não pode ser separador.
for (const elemento of valoresSemComentarios) {
if (elementoSeparador) {
if (elemento.constructor !== construtos_1.Separador) {
throw this.erro(elemento.simbolo, 'Não podem haver duas vírgulas seguidas em uma definição de vetor, ou definição de vetor começando em vírgula.');
}
elementoSeparador = false;
}
else {
if (elemento.constructor === construtos_1.Separador) {
throw this.erro(elemento.simbolo, 'Não podem haver duas vírgulas seguidas em uma definição de vetor, ou definição de vetor começando em vírgula.');
}
elementoSeparador = true;
}
}
const valoresSemSeparadores = valoresSemComentarios.filter((v) => v.constructor !== construtos_1.Separador);
const tipoVetor = (0, inferenciador_1.inferirTipoVariavel)(valoresSemSeparadores);
return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), valores, tipoVetor);
case delegua_2.default.ENQUANTO:
this.avancarEDevolverAnterior();
return await this.enquantoComoConstruto();
case delegua_2.default.EXPRESSAO_REGULAR:
let valor = '';
let linhaAtual = this.simbolos[this.atual].linha;
let eParExpressaoRegular = this.simbolos.filter((l) => l.linha === linhaAtual && l.tipo === delegua_2.default.EXPRESSAO_REGULAR).length %
2 ===
0;
if (eParExpressaoRegular) {
this.avancarEDevolverAnterior();
while (!this.verificarTipoSimboloAtual(delegua_2.default.EXPRESSAO_REGULAR)) {
valor += this.simbolos[this.atual].lexema || '';
this.avancarEDevolverAnterior();
}
this.avancarEDevolverAnterior();
return new construtos_1.ExpressaoRegular(this.hashArquivo, simboloAtual, valor);
}
case delegua_2.default.FALSO:
this.avancarEDevolverAnterior();
return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), false, 'lógico');
case delegua_2.default.FAZER:
const simboloFazer = this.avancarEDevolverAnterior();
return await this.fazerComoConstruto(simboloFazer);
case delegua_2.default.FUNCAO:
case delegua_2.default.FUNÇÃO:
const simboloFuncao = this.avancarEDevolverAnterior();
const corpoDaFuncao = await this.corpoDaFuncao(simboloFuncao.lexema);
this.pilhaEscopos.definirInformacoesVariavel(simboloFuncao.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simboloFuncao.lexema, 'função'));
return corpoDaFuncao;
case delegua_2.default.IDENTIFICADOR:
const simboloIdentificador = this.avancarEDevolverAnterior();
let tipoOperando;
if (this.intuirTipoQualquerParaIdentificadores) {
// Esta indicação é utilizada para compreensões de lista, onde o
// tipo do identificador de iteração é 'qualquer' por definição.
tipoOperando = 'qualquer';
this.pilhaEscopos.definirInformacoesVariavel(simboloIdentificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simboloIdentificador.lexema, 'qualquer') // TODO: Talvez um dia inferir o tipo aqui.
);
}
else if (simboloIdentificador.lexema in this.tiposDefinidosEmCodigo) {
tipoOperando = simboloIdentificador.lexema;
}
else {
try {
tipoOperando = this.pilhaEscopos.obterTipoVariavelPorNome(simboloIdentificador.lexema);
}
catch (erro) {
throw this.erro(simboloIdentificador, erro.message);
}
}
// 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] &&
[delegua_2.default.INCREMENTAR, delegua_2.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 delegua_2.default.IMPORTAR:
this.avancarEDevolverAnterior();
return await this.construtoImportar();
case delegua_2.default.ISTO:
this.avancarEDevolverAnterior();
return new construtos_1.Isto(this.hashArquivo, Number(simboloAtual.linha), simboloAtual);
case delegua_2.default.LEIA:
return await this.expressaoLeia();
case delegua_2.default.NULO:
this.avancarEDevolverAnterior();
return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), null, 'nulo');
case delegua_2.default.NUMERO:
case delegua_2.default.NÚMERO:
case delegua_2.default.TEXTO:
const simboloNumeroTexto = this.avancarEDevolverAnterior();
const tipoInferido = (0, inferenciador_1.inferirTipoVariavel)(simboloNumeroTexto.literal);
return new construtos_1.Literal(this.hashArquivo, Number(simboloNumeroTexto.linha), simboloNumeroTexto.literal, tipoInferido);
case delegua_2.default.PARA:
const simboloPara = this.avancarEDevolverAnterior();
return await this.paraComoConstruto(simboloPara);
case delegua_2.default.PARENTESE_ESQUERDO:
this.avancarEDevolverAnterior();
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_DIREITO)) {
return new construtos_1.TuplaN(this.hashArquivo, Number(simboloAtual.linha), []);
}
const expressao = await this.tupla();
if (expressao instanceof construtos_1.TuplaN) {
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após itens da tupla.");
return expressao;
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após a expressão.");
return new construtos_1.Agrupamento(this.hashArquivo, Number(simboloAtual.linha), expressao);
case delegua_2.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 delegua_2.default.VERDADEIRO:
this.avancarEDevolverAnterior();
return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), true, 'lógico');
case delegua_2.default.TIPO:
this.avancarEDevolverAnterior();
this.consumir(delegua_2.default.DE, "Esperado 'de' após 'tipo'.");
let construto;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ESCREVA, delegua_2.default.LEIA, delegua_2.default.FUNCAO, delegua_2.default.FUNÇÃO, delegua_2.default.SE, delegua_2.default.ENQUANTO, delegua_2.default.PARA, delegua_2.default.RETORNA, delegua_1.default.INTEIRO, delegua_1.default.TEXTO, delegua_1.default.VETOR, delegua_1.default.LOGICO, delegua_1.default.LÓGICO, delegua_1.default.VAZIO)) {
construto = new construtos_1.ComponenteLinguagem(this.hashArquivo, this.simboloAnterior());
}
else {
construto = await this.expressao();
}
if (construto.constructor === construtos_1.AcessoMetodoOuPropriedade) {
const construtoTipado = construto;
const acessoMetodoConhecido = this.resolverAcessoMetodoConhecido(construtoTipado);
if (acessoMetodoConhecido) {
construto = acessoMetodoConhecido;
}
else {
switch (construtoTipado.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];
construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno);
break;
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];
construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno);
break;
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];
construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno);
break;
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];
construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno);
break;
default:
if (construtoTipado.tipo in this.tiposDefinidosEmCodigo) {
const tipoCorrespondente = this.tiposDefinidosEmCodigo[construtoTipado.tipo];
const possivelMetodo = tipoCorrespondente.metodos.filter((m) => m.simbolo.lexema === construtoTipado.simbolo.lexema);
if (possivelMetodo.length > 0) {
construto = new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, possivelMetodo[0].tipo);
break;
}
const possivelPropriedade = tipoCorrespondente.propriedades.filter((p) => p.nome.lexema === construtoTipado.simbolo.lexema);
if (possivelPropriedade.length > 0) {
construto = new construtos_1.AcessoPropriedade(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, possivelPropriedade[0].tipo);
break;
}
}
}
}
}
return new construtos_1.TipoDe(this.hashArquivo, simboloAtual, construto);
}
// TODO: O correto seria emitir algum aviso aqui que este avaliador sintático não consegue
// lidar com tópicos de ajuda neste ponto.
if (this.emAjuda) {
console.log(this.simbolos[this.atual]);
}
throw this.erro(this.simbolos[this.atual], 'Esperado expressão.');
}
resolverTipoAcessoIndiceVariavel(expressaoAnterior) {
let identificadorAcessado = '';
switch (expressaoAnterior.constructor) {
case construtos_1.Variavel:
identificadorAcessado = expressaoAnterior.simbolo.lexema;
break;
default:
return 'qualquer';
}
// Primeiro verificar se é acesso a índice de vetor.
let tipoIdentificadorCorrespondente;
try {
tipoIdentificadorCorrespondente = this.pilhaEscopos.obterTipoVariavelPorNome(expressaoAnterior.simbolo.lexema);
}
catch (erro) {
// Referência a identificador ainda não declarado; assumimos 'qualquer' e deixamos
// o analisador semântico emitir o diagnóstico quando apropriado.
tipoIdentificadorCorrespondente = 'qualquer';
}
if (!tipoIdentificadorCorrespondente.endsWith('[]') &&
!['dicionário', 'qualquer', 'texto', 'tupla', 'vetor'].includes(tipoIdentificadorCorrespondente)) {
throw this.erro(this.simbolos[this.atual], `Tipo ${tipoIdentificadorCorrespondente} não suporta acesso por índice.`);
}
let tipoAcesso = 'qualquer';
if (tipoIdentificadorCorrespondente.endsWith('[]')) {
tipoAcesso = tipoIdentificadorCorrespondente.replace('[]', '');
}
else {
tipoAcesso = 'qualquer';
}
return tipoAcesso;
}
/**
* Resolve um construto do tipo `Variavel`, `AcessoMetodoOuPropriedade` ou `AcessoIndiceVariavel`
* para um `ElementoMontaoTipos` correspondente, caso exista.
* @param construto O construto.
* @returns O `ElementoMontaoTipos` correspondente ou `null` se não existir.
*/
resolverElementoMontao(construto) {
switch (construto.constructor) {
case construtos_1.Variavel:
try {
const nomeVariavel = construto.simbolo.lexema;
const elementoMontao = this.pilhaEscopos.obterElementoMontaoTipos(nomeVariavel);
if (elementoMontao.endereco) {
const referenciaMontaoTipos = this.montaoTipos.obterReferencia(construto.hashArquivo, construto.linha, elementoMontao.endereco);
if (referenciaMontaoTipos instanceof elemento_montao_tipos_1.ElementoMontaoTipos) {
return referenciaMontaoTipos;
}
return null;
}
return elementoMontao;
}
catch (_a) {
return null;
}
case construtos_1.AcessoMetodoOuPropriedade: {
const acesso = construto;
const elementoObjeto = this.resolverElementoMontao(acesso.objeto);
if (!elementoObjeto) {
return null;
}
return elementoObjeto.subElementos[acesso.simbolo.lexema] || null;
}
case construtos_1.AcessoIndiceVariavel: {
const acessoIndice = construto;
const elementoObjeto = this.resolverElementoMontao(acessoIndice.entidadeChamada);
if (!elementoObjeto || !(acessoIndice.indice instanceof construtos_1.Literal)) {
return null;
}
return elementoObjeto.subElementos[String(acessoIndice.indice.valor)] || null;
}
default:
return null;
}
}
async resolverCadeiaChamadas(expressaoAnterior, tipoAnterior = 'qualquer') {
if (!this.simbolos[this.atual]) {
return expressaoAnterior;
}
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.PARENTESE_ESQUERDO:
this.avancarEDevolverAnterior();
const chamada = await this.finalizarChamada(expressaoAnterior, tipoAnterior);
return await this.resolverCadeiaChamadas(chamada);
case delegua_2.default.PONTO:
this.avancarEDevolverAnterior();
this.verificarSeSimboloAtualEIgualA();
const nome = this.avancarEDevolverAnterior();
let tipoInferido = expressaoAnterior.tipo;
// Se não for um dicionário anônimo (ou seja, ser variável ou constante com nome)
if (expressaoAnterior.tipo === 'dicionário' &&
expressaoAnterior.constructor !== construtos_1.Dicionario) {
const elementoMontaoTipos = this.resolverElementoMontao(expressaoAnterior);
if (elementoMontaoTipos && nome.lexema in elementoMontaoTipos.subElementos) {
tipoInferido = elementoMontaoTipos.subElementos[nome.lexema].tipo;
}
}
const acesso = new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, expressaoAnterior, nome, tipoInferido);
return await this.resolverCadeiaChamadas(acesso, tipoInferido);
case delegua_2.default.COLCHETE_ESQUERDO:
const tipoAcesso = this.resolverTipoAcessoIndiceVariavel(expressaoAnterior);
this.avancarEDevolverAnterior();
const indice = await this.expressao();
const simboloFechamento = this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após escrita do indice.");
const acessoVariavel = new construtos_1.AcessoIndiceVariavel(this.hashArquivo, expressaoAnterior, indice, simboloFechamento, tipoAcesso);
return await this.resolverCadeiaChamadas(acessoVariavel);
default:
return expressaoAnterior;
}
}
async chamar() {
let expressao = await this.primario();
return await this.resolverCadeiaChamadas(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;
const acessoMetodoConhecido = this.resolverAcessoMetodoConhecido(construtoTipado);
if (acessoMetodoConhecido) {
return acessoMetodoConhecido;
}
switch (entidadeChamadaResolvida.tipo) {
case delegua_1.default.DICIONARIO:
case delegua_1.default.DICIONÁRIO:
if (construtoTipado.simbolo.lexema in primitivas_dicionario_1.default) {
const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno);
}
break;
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) {
const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno);
}
break;
case delegua_1.default.MODULO:
case delegua_1.default.MÓDULO:
// Há dois casos para resolução de módulo:
// Um quando o módulo é definido no próprio código (por exemplo, em um outro arquivo `.delegua`).
// Outro quando é importado de uma biblioteca externa.
// Este é o caso quando o módulo vem de outro arquivo `.delegua`.
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) {
const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno);
}
break;
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) {
const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno);
}
break;
}
return entidadeChamadaResolvida;
}
obterTiposParaConsultaPrimitiva(tipo) {
switch (tipo) {
case delegua_1.default.DICIONARIO:
case delegua_1.default.DICIONÁRIO:
return ['dicionário'];
case delegua_1.default.INTEIRO:
case delegua_1.default.NUMERO:
case delegua_1.default.NÚMERO:
case delegua_1.default.REAL:
return ['número'];
case delegua_1.default.TEXTO:
return ['texto'];
case delegua_1.default.VETOR:
case delegua_1.default.VETOR_INTEIRO:
case delegua_1.default.VETOR_LOGICO:
case delegua_1.default.VETOR_LÓGICO:
case delegua_1.default.VETOR_NUMERO:
case delegua_1.default.VETOR_NÚMERO:
case delegua_1.default.VETOR_QUALQUER:
case delegua_1.default.VETOR_TEXTO:
return ['vetor'];
default:
return [tipo];
}
}
resolverAcessoMetodoConhecido(construtoTipado) {
const tiposParaConsulta = this.obterTiposParaConsultaPrimitiva(construtoTipado.tipo);
for (const tipoConsulta of tiposParaConsulta) {
const primitivasPorTipo = this.primitivasConhecidas[tipoConsulta];
if (!primitivasPorTipo) {
continue;
}
const informacoesPrimitiva = primitivasPorTipo[construtoTipado.simbolo.lexema];
if (informacoesPrimitiva) {
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, informacoesPrimitiva.tipo);
}
}
return null;
}
validarArgumentosEntidadeChamada(argumentosEntidadeChamada, argumentosUtilizados) {
if (argumentosEntidadeChamada.length === 0) {
return [];
}
const possiveisErros = [];
for (const [indice, argumentoEntidadeChamada] of argumentosEntidadeChamada.entries()) {
const argumentoUtilizado = argumentosUtilizados[indice];
if (!argumentoUtilizado) {
if (argumentoEntidadeChamada.obrigatorio) {
possiveisErros.push(`Argumento ${argumentoEntidadeChamada.nome} é obrigatório, mas não foi fornecido.`);
}
continue;
}
if (argumentoUtilizado.tipo === null || argumentoUtilizado.tipo === undefined) {
continue;
}
const argumentoEntidadeChamadaQualquer = argumentoEntidadeChamada.tipo.startsWith('qualquer');
const argumentoUtilizadoQualquer = argumentoUtilizado.tipo.startsWith('qualquer');
// Este caso é tarefa do analisador semântico apontar.
if (argumentoEntidadeChamadaQualquer || argumentoUtilizadoQualquer) {
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 tipoArgumentoUtilizado = argumentoUtilizado.tipo.startsWith('funcao') ||
argumentoUtilizado.tipo.startsWith('função') ||
argumentoUtilizado instanceof construtos_1.FuncaoConstruto
? 'função'
: argumentoUtilizado.tipo;
const tipoArgumentoEntidadeChamada = argumentoEntidadeChamada.tipo.startsWith('funcao') ||
argumentoEntidadeChamada.tipo.startsWith('função')
? 'função'
: argumentoEntidadeChamada.tipo;
if (tipoArgumentoUtilizado !== tipoArgumentoEntidadeChamada) {
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 === construtos_1.AcessoMetodoOuPropriedade) {
return this.resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamada);
}
if (entidadeChamada.constructor === construtos_1.Variavel) {
const entidadeChamadaResolvidaVariavel = entidadeChamada;
const informacoesFuncaoBibliotecaGlobal = this.pilhaEscopos.obterBibliotecaGlobal(entidadeChamadaResolvidaVariavel.simbolo.lexema);
if (informacoesFuncaoBibliotecaGlobal &&
Array.isArray(informacoesFuncaoBibliotecaGlobal.subElementos) &&
informacoesFuncaoBibliotecaGlobal.subElementos.length > 0) {
const erros = this.validarArgumentosEntidadeChamada(informacoesFuncaoBibliotecaGlobal.subElementos, 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 === 'qualquer') {
// Provavelmente uma chamada a alguma função da biblioteca global.
// Até então, é sempre do tipo `InformacaoElementoSintatico`.
const informacoesPossivelFuncaoBibliotecaGlobal = this.pilhaEscopos.obterBibliotecaGlobal(entidadeChamadaResolvidaVariavel.simbolo.lexema);
if (informacoesPossivelFuncaoBibliotecaGlobal) {
const erros = this.validarArgumentosEntidadeChamada(informacoesPossivelFuncaoBibliotecaGlobal.subElementos, 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 !== 'qualquer' &&
this.primitivasConhecidas[tipoPrimitiva].hasOwnProperty(entidadeChamadaResolvidaVariavel.simbolo.lexema)) {
var informacoesPrimitiva = this.primitivasConhecidas[tipoPrimitiva][entidadeChamadaResolvidaVariavel.simbolo.lexema];
const erros = this.validarArgumentosEntidadeChamada(informacoesPrimitiva.subElementos, 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(entidadeChamadaResolvidaVariavel.hashArquivo, entidadeChamadaResolvidaVariavel.linha, entidadeChamadaResolvidaVariavel.simbolo, entidadeChamadaResolvidaVariavel.tipo, possivelReferencia.id);
}
return new construtos_1.ArgumentoReferenciaFuncao(entidadeChamadaResolvidaVariavel.hashArquivo, entidadeChamadaResolvidaVariavel.linha, entidadeChamadaResolvidaVariavel.simbolo);
}
return entidadeChamada;
}
async finalizarChamada(entidadeChamada, tipoPrimitiva = undefined) {
const argumentos = [];
if (!this.verificarTipoSimboloAtual(delegua_2.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(await this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
this.consumir(delegua_2.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);
const construtoChamada = new construtos_1.Chamada(this.hashArquivo, entidadeChamadaResolvida, argumentos);
// A validação de tipos dos argumentos da entidade chamada existe em
// avaliadores sintáticos derivados deste, como em `delegua-node`.
// Pode ser que esta lógica seja trazida para cá no futuro.
construtoChamada.tipo = 'qualquer';
return construtoChamada;
}
async unario() {
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.NAO, delegua_2.default.NEGACAO, delegua_2.default.ADICAO, delegua_2.default.SUBTRACAO, delegua_2.default.BIT_NOT, delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.unario();
return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES');
}
return await this.chamar();
}
/**
* A exponenciacão é uma exceção na ordem de avaliação (resolve primeiro à direita).
* Por isso `direito` chama `exponenciacao()`, e não `unario()`.
* @returns {Binario} A expressão binária na forma do construto `Binario`.
*/
async exponenciacao() {
let expressao = await this.unario();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EXPONENCIACAO)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.exponenciacao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
/**
* Verifica recursivamente se um construto é ou contém uma operação unária em um vetor.
* Isso bloqueia padrões de ofuscação como !![] usado em operações aritméticas.
*/
verificarOperacaoUnariaEmVetor(construto) {
if (construto instanceof construtos_1.Unario) {
const operando = construto.operando;
// Verifica se o operando é um vetor
if (operando instanceof construtos_1.Vetor ||
operando.tipo === 'vetor' ||
operando.tipo.endsWith('[]')) {
return true;
}
// Verifica recursivamente para casos como !![]
if (operando instanceof construtos_1.Unario) {
return this.verificarOperacaoUnariaEmVetor(operando);
}
}
return false;
}
verificacaoOperacoesBinariasIlegais(esquerdo, direito, operador) {
// Bloquear operações aritméticas com operações unárias em vetores (padrão de ofuscação tipo !![] * 1)
if (this.verificarOperacaoUnariaEmVetor(esquerdo)) {
throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} com expressão unária aplicada a vetor.`);
}
if (this.verificarOperacaoUnariaEmVetor(direito)) {
throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} com expressão unária aplicada a vetor.`);
}
if (esquerdo.tipo === 'vetor' || esquerdo.tipo.endsWith('[]')) {
if (['dicionario', 'dicionário', 'nulo'].includes(direito.tipo)) {
throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre vetor e ${direito.tipo}.`);
}
}
if (direito.tipo === 'vetor' || direito.tipo.endsWith('[]')) {
if (['dicionario', 'dicionário', 'nulo'].includes(esquerdo.tipo)) {
throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre vetor e ${esquerdo.tipo}.`);
}
}
if (esquerdo.tipo === 'dicionario' || esquerdo.tipo === 'dicionário') {
if (['vetor', 'nulo'].includes(direito.tipo)) {
throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre dicionário e ${direito.tipo}.`);
}
}
if (direito.tipo === 'dicionario' || direito.tipo === 'dicionário') {
if (['vetor', 'nulo'].includes(esquerdo.tipo)) {
throw this.erro(operador, `Operação inválida: não é possível realizar operação ${operador.lexema} entre dicionário e ${esquerdo.tipo}.`);
}
}
}
async multiplicar() {
let expressao = await this.exponenciacao();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIVISAO, delegua_2.default.DIVISAO_IGUAL, delegua_2.default.DIVISAO_INTEIRA, delegua_2.default.DIVISAO_INTEIRA_IGUAL, delegua_2.default.MODULO, delegua_2.default.MODULO_IGUAL, delegua_2.default.MULTIPLICACAO, delegua_2.default.MULTIPLICACAO_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.exponenciacao();
this.verificacaoOperacoesBinariasIlegais(expressao, direito, operador);
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.
*/
async adicaoOuSubtracao() {
let expressao = await this.multiplicar();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SUBTRACAO, delegua_2.default.ADICAO, delegua_2.default.MENOS_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.multiplicar();
this.verificacaoOperacoesBinariasIlegais(expressao, direito, operador);
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MAIS_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.ou();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
async bitShift() {
let expressao = await this.adicaoOuSubtracao();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MENOR_MENOR, delegua_2.default.MAIOR_MAIOR)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.adicaoOuSubtracao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
async bitE() {
let expressao = await this.bitShift();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_AND)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.bitShift();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
async bitOu() {
let expressao = await this.bitE();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_OR, delegua_2.default.CIRCUMFLEXO)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.bitE();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
async comparar() {
let expressao = await this.bitOu();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MAIOR, delegua_2.default.MAIOR_IGUAL, delegua_2.default.MENOR, delegua_2.default.MENOR_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.bitOu();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
async comparacaoIgualdade() {
let expressao = await this.comparar();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIFERENTE, delegua_2.default.IGUAL_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.comparar();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
async em() {
let expressao = await this.comparacaoIgualdade();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EM, delegua_2.default.CONTEM, delegua_2.default.NAO)) {
let operador = this.simbolos[this.atual - 1];
let negado = false;
if (operador.tipo === delegua_2.default.NAO) {
operador = this.consumir(delegua_2.default.CONTEM, `Esperado palavra reservada 'contém' ou 'contem' após palavra reservada ${operador.lexema}.`);
negado = true;
}
const direito = await this.comparacaoIgualdade();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
expressao.negado = negado;
}
return expressao;
}
async e() {
let expressao = await this.em();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.E)) {
const operador = this.simbolos[this.atual - 1];
const direito = await this.em();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
async elvis() {
let expressao = await this.ou();
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ELVIS)) {
const direito = await this.ou();
return new construtos_1.Elvis(this.hashArquivo, expressao, direito);
}
return expressao;
}
async seTernario() {
let expressaoOuCondicao = await this.elvis();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.INTERROGACAO)) {
const operador = this.simbolos[this.atual - 1];
const expressaoEntao = await this.seTernario();
this.consumir(delegua_2.default.DOIS_PONTOS, `Esperado dois-pontos após caminho positivo em se ternário. Atual: ${this.simbolos[this.atual].lexema}.`);
const expressaoSenao = await this.seTernario();
expressaoOuCondicao = new construtos_1.SeTernario(this.hashArquivo, expressaoOuCondicao, expressaoEntao, operador, expressaoSenao);
}
return expressaoOuCondicao;
}
/**
* Método que resolve atribuições.
* @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`.
*/
/**
* Processa tuplas, que são expressões separadas por vírgula entre parênteses.
* Se não houver vírgula, retorna apenas a expressão simples.
* Sobrescreve o método da base para usar seTernario() em vez de ou().
*/
async tupla() {
let expressao = await this.seTernario();
// Se não há vírgula, retorna a expressão simples
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA)) {
return expressao;
}
// Se há vírgula, então é uma tupla
const elementos = [expressao];
do {
if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
break;
}
elementos.push(await this.seTernario());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
return new construtos_1.TuplaN(this.hashArquivo, expressao.linha, elementos);
}
/**
* Método que resolve atribuições.
* @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`.
*/
async atribuir() {
const expressao = await this.seTernario();
if (expressao instanceof construtos_1.Binario &&
[
delegua_2.default.MAIS_IGUAL,
delegua_2.default.MENOS_IGUAL,
delegua_2.default.MULTIPLICACAO_IGUAL,
delegua_2.default.DIVISAO_IGUAL,
delegua_2.default.DIVISAO_INTEIRA_IGUAL,
delegua_2.default.MODULO_IGUAL,
].includes(expressao.operador.tipo)) {
if (expressao.esquerda instanceof construtos_1.AcessoIndiceVariavel) {
const entidade = expressao.esquerda;
return new construtos_1.Atribuir(this.hashArquivo, entidade.entidadeChamada, expressao, entidade.indice, expressao.operador);
}
return new construtos_1.Atribuir(this.hashArquivo, expressao.esquerda, expressao, undefined, expressao.operador);
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
const igual = this.simbolos[this.atual - 1];
const valor = await this.seTernario();
switch (expressao.constructor) {
case construtos_1.Variavel:
return new construtos_1.Atribuir(this.hashArquivo, expressao, valor);
case construtos_1.AcessoMetodoOuPropriedade:
const expressaoAcessoMetodoOuPropriedade = expressao;
return new construtos_1.DefinirValor(this.hashArquivo, igual.linha, expressaoAcessoMetodoOuPropriedade.objeto, expressaoAcessoMetodoOuPropriedade.simbolo, valor);
case construtos_1.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`.
*/
async expressaoLeia() {
const simboloLeia = this.avancarEDevolverAnterior();
if (this.emAjuda && this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_ESQUERDO) {
return new construtos_1.Leia(simboloLeia, []);
}
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos argumentos em instrução `leia`.");
const argumentos = [];
if (this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_DIREITO) {
do {
argumentos.push(await this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos em instrução `leia`.");
return new construtos_1.Leia(simboloLeia, argumentos);
}
// TODO: Depreciar.
async expressao() {
return await this.atribuir();
}
async blocoEscopo() {
this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
let declaracoes = [];
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
const retornoDeclaracao = await this.resolverDeclaracaoForaDeBloco();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else if (retornoDeclaracao !== null) {
declaracoes.push(retornoDeclaracao);
}
}
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o bloco.");
this.pilhaEscopos.removerUltimo();
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return declaracoes;
}
declaracaoComentarioMultilinha() {
let simboloComentario;
const conteudos = [];
do {
simboloComentario = this.avancarEDevolverAnterior();
conteudos.push(simboloComentario.literal);
} while (this.verificarTipoSimboloAtual(delegua_2.default.LINHA_COMENTARIO));
return new declaracoes_1.Comentario(simboloComentario.hashArquivo, simboloComentario.linha, conteudos, true);
}
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(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Continua(this.simbolos[this.atual - 1]);
}
async logicaComumEnquanto() {
const condicao = await this.expressao();
const corpo = await this.declaracaoBloco();
return {
condicao,
corpo,
};
}
async declaracaoEnquanto() {
try {
this.blocos += 1;
const { condicao, corpo } = await this.logicaComumEnquanto();
return new declaracoes_1.Enquanto(condicao, corpo);
}
finally {
this.blocos -= 1;
}
}
async declaracaoEscolha() {
try {
this.blocos += 1;
const condicao = await this.expressao();
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo do 'escolha'.");
const caminhos = [];
let caminhoPadrao = null;
while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CASO)) {
const caminhoCondicoes = [await this.expressao()];
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após o 'caso'.");
while (this.verificarTipoSimboloAtual(delegua_2.default.CASO)) {
this.consumir(delegua_2.default.CASO, null);
caminhoCondicoes.push(await this.expressao());
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'caso'.");
}
let declaracoes = [];
do {
const retornoDeclaracao = await this.resolverDeclaracao();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else {
declaracoes.push(retornoDeclaracao);
}
} while (!this.verificarTipoSimboloAtual(delegua_2.default.CASO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.PADRAO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA));
caminhos.push({
condicoes: caminhoCondicoes,
declaracoes,
});
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PADRAO)) {
if (caminhoPadrao !== null) {
const excecao = new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(this.simbolos[this.atual], "Você só pode ter um 'padrao' em cada declaração de 'escolha'.");
this.erros.push(excecao);
throw excecao;
}
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'padrao'.");
const declaracoes = [];
do {
declaracoes.push(await this.resolverDeclaracao());
} while (!this.verificarTipoSimboloAtual(delegua_2.default.CASO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.PADRAO) &&
!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA));
caminhoPadrao = {
declaracoes,
};
}
}
return new declaracoes_1.Escolha(condicao, caminhos, caminhoPadrao);
}
finally {
this.blocos -= 1;
}
}
async declaracaoEscreva() {
const simboloAtual = this.simbolos[this.atual];
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' antes dos valores em escreva.");
const argumentos = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
do {
argumentos.push(await this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após os valores em escreva.");
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Escreva(Number(simboloAtual.linha), simboloAtual.hashArquivo, argumentos);
}
async declaracaoExpressao() {
// Se há decoradores a serem adicionados aqui, obtemo-los agora,
// para evitar que outros passos recursivos peguem-los antes.
const decoradores = Array.from(this.pilhaDecoradores);
this.pilhaDecoradores = [];
const expressao = await this.expressao();
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Expressao(expressao, decoradores);
}
async declaracaoFalhar() {
const simboloFalha = this.simbolos[this.atual - 1];
const expressaoFalha = await this.expressao();
return new declaracoes_1.Falhar(simboloFalha, expressaoFalha);
}
async logicaComumFazer() {
const caminhoFazer = await this.declaracaoBloco();
this.consumir(delegua_2.default.ENQUANTO, "Esperado declaração do 'enquanto' após o escopo do 'fazer'.");
const condicaoEnquanto = await this.expressao();
return {
caminhoFazer,
condicaoEnquanto,
};
}
async declaracaoFazer(simboloFazer) {
try {
this.blocos += 1;
const { caminhoFazer, condicaoEnquanto } = await this.logicaComumFazer();
return new declaracoes_1.Fazer(simboloFazer.hashArquivo, Number(simboloFazer.linha), caminhoFazer, condicaoEnquanto);
}
finally {
this.blocos -= 1;
}
}
/**
* O símbolo é emitido aqui para fins de formatação, mas este método é
* sobrescrito em `delegua-node`.
* @returns {Importar} Uma declaração `Importar`.
*/
declaracaoImportar() {
let identificadorDeTudo = null;
const elementosImportacao = [];
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.TUDO:
this.avancarEDevolverAnterior();
this.consumir(delegua_2.default.COMO, "Esperado 'como' após 'tudo' em declaração de importação.");
identificadorDeTudo = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador após 'como' em declaração de importação de 'tudo'.");
break;
case delegua_2.default.CHAVE_ESQUERDA:
this.avancarEDevolverAnterior();
do {
const identificadorImportacao = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado identificador de elemento a ser importado.');
elementosImportacao.push(identificadorImportacao);
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após lista de elementos a serem importados.");
break;
default:
throw this.erro(this.simbolos[this.atual], "Esperado ou palavra reservada 'tudo' ou abertura de chaves após palavra reservada 'importar'.");
}
this.consumir(delegua_2.default.DE, "Esperado 'de' após identificador em declaração de importação de 'tudo'.");
let construtoCaminhoModulo;
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.TEXTO:
const simboloCaminhoModulo = this.avancarEDevolverAnterior();
construtoCaminhoModulo = new construtos_1.Literal(simboloCaminhoModulo.hashArquivo, Number(simboloCaminhoModulo.linha), simboloCaminhoModulo.literal);
break;
case delegua_2.default.IDENTIFICADOR:
const identificadorModulo = this.avancarEDevolverAnterior();
construtoCaminhoModulo = new construtos_1.Literal(identificadorModulo.hashArquivo, Number(identificadorModulo.linha), identificadorModulo.lexema);
break;
}
const importar = new declaracoes_1.Importar(construtoCaminhoModulo);
if (identificadorDeTudo !== null) {
importar.simboloTudo = identificadorDeTudo;
}
else {
importar.elementosImportacao = elementosImportacao;
}
return Promise.resolve(importar);
}
async declaracaoPara() {
try {
const simboloPara = this.simbolos[this.atual - 1];
this.blocos += 1;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CADA)) {
return await this.declaracaoParaCada(simboloPara);
}
return await this.declaracaoParaTradicional(simboloPara);
}
finally {
this.blocos -= 1;
}
}
async logicaParaCadaDicionario(simboloPara) {
this.avancarEDevolverAnterior(); // chave esquerda
const nomeVariavelChave = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável para chave de iteração, em instrução 'para cada'.");
this.consumir(delegua_2.default.VIRGULA, "Esperado vírgula após nome de variável para chave de iteração, em instrução 'para cada'.");
const nomeVariavelValor = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado identificador de variável para valor de iteração, em instrução 'para cada'.");
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado chave direita após nome de variável para valor de iteração, em instrução 'para cada'.");
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.DE, delegua_2.default.EM)) {
throw this.erro(this.simbolos[this.atual], "Esperado palavras reservadas 'em' ou 'de' após variável de iteração em instrução 'para cada'.");
}
const dicionario = await this.expressao();
if (!dicionario.hasOwnProperty('tipo')) {
throw this.erro(simboloPara, `Variável ou constante em 'para cada' não parece ser um dicionário.`);
}
const tipoDicionario = dicionario.tipo;
if (tipoDicionario !== 'dicionário') {
throw this.erro(simboloPara, `Variável ou constante em 'para cada' não é um dicionário. Tipo resolvido: ${tipoDicionario}.`);
}
this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelChave.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeVariavelChave.lexema, 'qualquer'));
this.pilhaEscopos.definirInformacoesVariavel(nomeVariavelValor.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeVariavelValor.lexema, 'qualquer'));
const corpo = await this.declaracaoBloco();
return {
nomeVariavelChave,
nomeVariavelValor,
dicionario,
corpo,
};
}
async declaracaoParaCadaDicionario(simboloPara) {
const { nomeVariavelChave, nomeVariavelValor, dicionario, corpo } = await this.logicaParaCadaDicionario(simboloPara);
return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloPara.linha), new tuplas_1.Dupla(new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelChave.lexema), new construtos_1.Literal(this.hashArquivo, Number(simboloPara.linha), nomeVariavelValor.lexema)), dicionario, corpo);
}
async logicaComumParaCadaVetor(simboloPara) {
const nomeVariavelIteracao = this.avancarEDevolverAnterior();
const variavelIteracao = new construtos_1.Variavel(this.hashArquivo, nomeVariavelIteracao);
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.DE, delegua_2.default.EM)) {
throw this.erro(this.simbolos[this.atual], "Esperado palavras reservadas 'em' ou 'de' após variável de iteração em instrução 'para cada'.");
}
let vetorOuDicionario = await this.expressao();
if (vetorOuDicionario.constructor === construtos_1.AcessoIndiceVariavel) {
const construtoAcessoIndiceVariavel = vetorOuDicionario;
if (construtoAcessoIndiceVariavel.entidadeChamada.tipo === 'dicionário') {
// A avaliação sintática não deve verificar valores de dicionários.
// Aqui se supõe que o programador sabe o que está fazendo.
// TODO: Talvez pensar numa forma melhor de fazer isso.
vetorOuDicionario.tipo = 'vetor';
}
}
const tipoVetor = vetorOuDicionario.tipo;
if (!tipoVetor.endsWith('[]') &&
!['dicionário', 'qualquer', 'texto', 'vetor'].includes(tipoVetor)) {
throw this.erro(simboloPara, `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(nomeVariavelIteracao.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeVariavelIteracao.lexema, tipoVariavelIteracao));
const corpo = await this.declaracaoBloco();
return {
variavelIteracao,
vetor: vetorOuDicionario,
corpo,
};
}
async declaracaoParaCadaVetor(simboloPara) {
const { variavelIteracao, vetor, corpo } = await this.logicaComumParaCadaVetor(simboloPara);
return new declaracoes_1.ParaCada(this.hashArquivo, Number(simboloPara.linha), variavelIteracao, vetor, corpo);
}
async declaracaoParaCada(simboloPara) {
if (this.verificarTipoSimboloAtual(delegua_2.default.IDENTIFICADOR)) {
return await this.declaracaoParaCadaVetor(simboloPara);
}
if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
return await this.declaracaoParaCadaDicionario(simboloPara);
}
throw this.erro(simboloPara, 'Identificador de iteração deve ser ou um par chave-valor, ou um nome de variável.');
}
async logicaComumPara() {
const comParenteses = this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO);
let inicializador;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA)) {
inicializador = null;
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VARIAVEL)) {
inicializador = await this.declaracaoDeVariaveis();
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CONSTANTE)) {
inicializador = await this.declaracaoDeConstantes();
}
else {
inicializador = await this.declaracaoExpressao();
}
let condicao = null;
if (!this.verificarTipoSimboloAtual(delegua_2.default.PONTO_E_VIRGULA)) {
condicao = await this.expressao();
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
let incrementar = null;
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
incrementar = await this.expressao();
this.verificarSeSimboloAtualEIgualA(delegua_2.default.INCREMENTAR, delegua_2.default.DECREMENTAR);
}
if (comParenteses) {
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após cláusulas de inicialização, condição e incremento.");
}
const corpo = await this.declaracaoBloco();
return {
inicializador,
condicao,
incrementar,
corpo,
};
}
async declaracaoParaTradicional(simboloPara) {
const { inicializador, condicao, incrementar, corpo } = await this.logicaComumPara();
return new declaracoes_1.Para(this.hashArquivo, Number(simboloPara.linha), inicializador, condicao, incrementar, corpo);
}
async declaracaoRetorna() {
const simboloChave = this.simbolos[this.atual - 1];
let valor = null;
if ([
delegua_2.default.CHAVE_ESQUERDA,
delegua_2.default.COLCHETE_ESQUERDO,
delegua_2.default.FALSO,
delegua_2.default.FUNCAO,
delegua_2.default.FUNÇÃO,
delegua_2.default.IDENTIFICADOR,
delegua_2.default.ISTO,
delegua_2.default.NAO,
delegua_2.default.NEGACAO,
delegua_2.default.NUMERO,
delegua_2.default.NULO,
delegua_2.default.PARENTESE_ESQUERDO,
delegua_2.default.SUPER,
delegua_2.default.TEXTO,
delegua_2.default.VERDADEIRO,
].includes(this.simbolos[this.atual].tipo)) {
valor = await this.expressao();
}
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Retorna(simboloChave, valor);
}
async declaracaoSe() {
const condicao = await this.expressao();
const caminhoEntao = (await this.resolverDeclaracao());
let caminhoSenao = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SENAO, delegua_2.default.SENÃO)) {
caminhoSenao = await 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(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Sustar(this.simbolos[this.atual - 1]);
}
async declaracaoTente() {
const simboloTente = this.simbolos[this.atual - 1];
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'tente'.");
const blocoTente = await this.blocoEscopo();
let blocoPegue = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PEGUE)) {
if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_ESQUERDO)) {
// Caso 1: com parâmetro de erro.
// `pegue` recebe um `FuncaoConstruto`.
blocoPegue = await this.corpoDaFuncao('bloco `pegue`');
}
else {
// Caso 2: sem parâmetro de erro.
// `pegue` recebe um bloco.
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'pegue'.");
blocoPegue = await this.blocoEscopo();
}
}
let blocoSenao = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SENAO, delegua_2.default.SENÃO)) {
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'senão'.");
blocoSenao = await this.blocoEscopo();
}
let blocoFinalmente = null;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.FINALMENTE)) {
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após a declaração 'finalmente'.");
blocoFinalmente = await this.blocoEscopo();
}
return new declaracoes_1.Tente(simboloTente.hashArquivo, Number(simboloTente.linha), blocoTente, blocoPegue, blocoSenao, blocoFinalmente);
}
async resolverDecoradores() {
while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
this.avancarEDevolverAnterior(); // Arroba
let nomeDecorador = '@';
let linha;
let parametros = [];
const atributos = {};
const primeiraParteNomeDecorador = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome de decorador após '@'.");
linha = Number(primeiraParteNomeDecorador.linha);
nomeDecorador += primeiraParteNomeDecorador.lexema;
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO)) {
const parteNomeDecorador = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome de decorador após '.'.");
nomeDecorador += '.' + parteNomeDecorador.lexema;
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
parametros = await this.logicaComumParametros();
}
for (const parametro of parametros) {
if (parametro.nome.lexema in atributos) {
throw this.erro(parametro.nome, `Atributo de decorador declarado duas ou mais vezes: ${parametro.nome.lexema}`);
}
atributos[parametro.nome.lexema] = parametro.valorPadrao;
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após nome de decorador.");
}
this.pilhaDecoradores.push(new construtos_1.Decorador(this.hashArquivo, linha, nomeDecorador, atributos));
}
}
async declaracaoTendoComo() {
const simboloTendo = this.simbolos[this.atual - 1];
const expressaoInicializacao = await this.expressao();
this.consumir(delegua_2.default.COMO, "Esperado palavra reservada 'como' após expressão de inicialização de variável, em declaração 'tendo'.");
const simboloNomeVariavel = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome do identificador em declaração 'tendo'.");
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado chave esquerda para abertura de bloco em declaração 'tendo'.");
let tipoInicializacao = 'qualquer';
switch (expressaoInicializacao.constructor) {
case construtos_1.Chamada:
const construtoChamada = expressaoInicializacao;
switch (construtoChamada.entidadeChamada.constructor) {
case construtos_1.AcessoMetodo:
const entidadeChamadaAcessoMetodo = construtoChamada.entidadeChamada;
tipoInicializacao = entidadeChamadaAcessoMetodo.tipoRetornoMetodo.replace('<T>', entidadeChamadaAcessoMetodo.objeto.tipo);
break;
case construtos_1.Variavel:
const entidadeChamadaVariavel = construtoChamada.entidadeChamada;
tipoInicializacao = entidadeChamadaVariavel.tipo;
break;
// TODO: Demais casos
default:
break;
}
break;
// TODO: Demais casos
default:
break;
}
this.pilhaEscopos.definirInformacoesVariavel(simboloNomeVariavel.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simboloNomeVariavel.lexema, tipoInicializacao));
const blocoCorpo = await this.blocoEscopo();
return new declaracoes_1.TendoComo(simboloTendo.linha, simboloTendo.hashArquivo, simboloNomeVariavel, expressaoInicializacao, new declaracoes_1.Bloco(simboloTendo.linha, simboloTendo.hashArquivo, blocoCorpo));
}
async declaracaoDesestruturacaoVariavel() {
const identificadores = [];
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
this.consumir(delegua_2.default.CHAVE_DIREITA, 'Esperado chave direita para concluir relação de variáveis a serem desestruturadas.');
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
throw this.erro(this.simbolos[this.atual], 'Esperado igual ou seta esquerda após relação de propriedades da desestruturação.');
}
const inicializador = await this.expressao();
const retornos = [];
for (let identificador of identificadores) {
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, this.logicaComumInferenciaTiposVariaveisEConstantes(inicializador, 'qualquer')));
const declaracaoVar = new declaracoes_1.Var(identificador, new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, inicializador, identificador));
declaracaoVar.decoradores = Array.from(this.pilhaDecoradores);
retornos.push(declaracaoVar);
}
this.pilhaDecoradores = [];
return retornos;
}
logicaComumInferenciaTiposAcessoMetodoOuPropriedade(entidadeChamada) {
// Algumas coisas podem acontecer aqui.
// Uma delas é a variável/constante ser uma classe padrão.
// Isso ocorre quando a importação é feita de uma biblioteca Node.js.
// Nesse caso, o tipo de `entidadeChamada.objeto` começa com uma letra maiúscula.
if (entidadeChamada.objeto.tipo && entidadeChamada.objeto.tipo.match(/^[A-Z]/)) {
const tipoCorrespondente = this.tiposDefinidosPorBibliotecas[entidadeChamada.objeto.tipo];
if (!tipoCorrespondente) {
throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamada.simbolo, `Tipo '${entidadeChamada.objeto.tipo}' não foi encontrado entre os tipos definidos por bibliotecas.`);
}
if (!(entidadeChamada.simbolo.lexema in tipoCorrespondente.metodos) &&
!(entidadeChamada.simbolo.lexema in tipoCorrespondente.propriedades)) {
throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamada.simbolo, `Membro '${entidadeChamada.simbolo.lexema}' não existe no tipo '${entidadeChamada.objeto.tipo}'.`);
}
if (entidadeChamada.simbolo.lexema in tipoCorrespondente.metodos) {
const metodoCorrespondente = tipoCorrespondente.metodos[entidadeChamada.simbolo.lexema];
return metodoCorrespondente.tipoRetorno || 'qualquer';
}
const propriedadeCorrespondente = tipoCorrespondente.propriedades[entidadeChamada.simbolo.lexema];
return propriedadeCorrespondente.tipo;
}
// 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.
for (const primitiva in this.primitivasConhecidas) {
if (this.primitivasConhecidas[primitiva].hasOwnProperty(entidadeChamada.simbolo.lexema)) {
return this.primitivasConhecidas[primitiva][entidadeChamada.simbolo.lexema].tipo;
}
}
throw new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(entidadeChamada.simbolo, `Primitiva '${entidadeChamada.simbolo.lexema}' não existe.`);
}
logicaComumInferenciaTiposVariaveisEConstantes(inicializador, tipo) {
if (tipo !== 'qualquer') {
return tipo;
}
switch (inicializador.constructor) {
case construtos_1.AcessoIndiceVariavel:
const entidadeChamadaAcessoIndiceVariavel = inicializador
.entidadeChamada;
// Este condicional ocorre com chamadas aninhadas. Por exemplo, `vetor[1][2]`.
if (entidadeChamadaAcessoIndiceVariavel.constructor === construtos_1.AcessoIndiceVariavel) {
return this.logicaComumInferenciaTiposVariaveisEConstantes(entidadeChamadaAcessoIndiceVariavel, tipo);
}
if (entidadeChamadaAcessoIndiceVariavel.tipo.endsWith('[]')) {
return entidadeChamadaAcessoIndiceVariavel.tipo.slice(0, -2);
}
// Normalmente, `entidadeChamadaAcessoIndiceVariavel.tipo` aqui será 'vetor'.
return 'qualquer';
case construtos_1.Chamada:
const entidadeChamadaChamada = inicializador.entidadeChamada;
switch (entidadeChamadaChamada.constructor) {
case construtos_1.AcessoMetodo:
const entidadeChamadaAcessoMetodo = entidadeChamadaChamada;
const tipoRetornoAcessoMetodoResolvido = entidadeChamadaAcessoMetodo.tipoRetornoMetodo.replace('<T>', entidadeChamadaAcessoMetodo.objeto.tipo);
return tipoRetornoAcessoMetodoResolvido;
case construtos_1.AcessoMetodoOuPropriedade:
return this.logicaComumInferenciaTiposAcessoMetodoOuPropriedade(entidadeChamadaChamada);
case construtos_1.AcessoPropriedade:
const entidadeChamadaAcessoPropriedade = entidadeChamadaChamada;
return entidadeChamadaAcessoPropriedade.tipoRetornoPropriedade;
case construtos_1.ArgumentoReferenciaFuncao:
// TODO: Voltar aqui se necessário.
return 'qualquer';
case construtos_1.ReferenciaFuncao:
const entidadeChamadaReferenciaFuncao = entidadeChamadaChamada;
return entidadeChamadaReferenciaFuncao.tipo;
case construtos_1.Variavel:
const entidadeChamadaVariavel = entidadeChamadaChamada;
return entidadeChamadaVariavel.tipo;
}
break;
case construtos_1.FuncaoConstruto:
const funcaoConstruto = inicializador;
return `função<${funcaoConstruto.tipo}>`;
case construtos_1.Leia:
return 'texto';
case tuplas_1.Dupla:
case tuplas_1.Trio:
case tuplas_1.Quarteto:
case tuplas_1.Quinteto:
case tuplas_1.Sexteto:
case tuplas_1.Septeto:
case tuplas_1.Octeto:
case tuplas_1.Noneto:
case tuplas_1.Deceto:
return delegua_1.default.TUPLA;
default:
// Construtos mapeados em `delegua-node`.
switch (inicializador.constructor.name) {
case 'ImportarBiblioteca':
case 'ModuloDeclaracoes':
return 'módulo';
}
return inicializador.tipo;
}
}
resolverValorConstruto(construto) {
if (construto instanceof construtos_1.Literal) {
return String(construto.valor);
}
throw this.erro({ hashArquivo: construto.hashArquivo, linha: construto.linha }, `Construto do tipo ${construto.constructor} não possui um mapeamento de valor.`);
}
resolverInformacaoElementoSintaticoDeDicionario(construto) {
let retorno;
if (construto instanceof construtos_1.Dicionario) {
retorno = new elemento_montao_tipos_1.ElementoMontaoTipos('dicionário');
const subElementos = {};
for (var i = 0; i < construto.valores.length; i++) {
const chaveCorrespondente = this.resolverValorConstruto(construto.chaves[i]);
const valorCorrespondente = construto.valores[i];
subElementos[chaveCorrespondente] =
this.resolverInformacaoElementoSintaticoDeDicionario(valorCorrespondente);
}
retorno.subElementos = subElementos;
}
else {
retorno = new elemento_montao_tipos_1.ElementoMontaoTipos(construto.tipo);
}
const endereco = this.montaoTipos.adicionarReferencia(retorno);
retorno.endereco = endereco;
return retorno;
}
/**
* Caso símbolo atual seja `var`, devolve uma declaração de variável.
* @returns Um Construto do tipo Var.
*/
async declaracaoDeVariaveis() {
const simboloVariavel = this.simboloAnterior();
const identificadores = [];
const retorno = [];
let tipo = 'qualquer';
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
return await this.declaracaoDesestruturacaoVariavel();
}
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
let tipoExplicito = false;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipo = this.verificarDefinicaoTipoAtual();
tipoExplicito = true;
this.avancarEDevolverAnterior();
}
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
// Inicialização de variáveis sem valor.
for (let identificador of identificadores.values()) {
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, tipo));
retorno.push(new declaracoes_1.Var(identificador, null, tipo, tipoExplicito, Array.from(this.pilhaDecoradores)));
}
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
this.pilhaDecoradores = [];
return retorno;
}
const inicializadores = [];
do {
inicializadores.push(await this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
if (identificadores.length !== inicializadores.length) {
throw this.erro(simboloVariavel, 'Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores à direita.');
}
for (let [indice, identificador] of identificadores.entries()) {
const tipoOriginal = tipo; // Preserva o tipo antes da inferência
tipo = this.logicaComumInferenciaTiposVariaveisEConstantes(inicializadores[indice], tipo);
if (tipo !== 'dicionário') {
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, tipo));
}
else {
const inicializadorDicionario = inicializadores[indice];
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, this.resolverInformacaoElementoSintaticoDeDicionario(inicializadorDicionario));
}
retorno.push(new declaracoes_1.Var(identificador, inicializadores[indice], tipo, tipoExplicito, Array.from(this.pilhaDecoradores), tipoOriginal // Passa o tipo original para o construtor
));
}
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
this.pilhaDecoradores = [];
return retorno;
}
async declaracaoDesestruturacaoConstante() {
const identificadores = [];
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da variável.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
this.consumir(delegua_2.default.CHAVE_DIREITA, 'Esperado chave direita para concluir relação de variáveis a serem desestruturadas.');
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
throw this.erro(this.simbolos[this.atual], 'Esperado igual ou seta esquerda após relação de propriedades da desestruturação.');
}
const inicializador = await this.expressao();
const retornos = [];
for (let identificador of identificadores) {
// TODO: Melhorar dicionário para intuir o tipo de cada propriedade.
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, 'qualquer'));
const declaracaoConst = new declaracoes_1.Const(identificador, new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, inicializador, identificador));
declaracaoConst.decoradores = Array.from(this.pilhaDecoradores);
retornos.push(declaracaoConst);
}
return retornos;
}
/**
* Caso símbolo atual seja `const, constante ou fixo`, devolve uma declaração de const.
* @returns Um Construto do tipo Const.
*/
async declaracaoDeConstantes() {
const simboloConstante = this.simboloAnterior();
const identificadores = [];
let tipo = 'qualquer';
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CHAVE_ESQUERDA)) {
return await this.declaracaoDesestruturacaoConstante();
}
do {
identificadores.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da constante.'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
let tipoExplicito = false;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipo = this.verificarDefinicaoTipoAtual();
tipoExplicito = true;
this.avancarEDevolverAnterior();
}
if (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL, delegua_2.default.SETA_ESQUERDA)) {
throw this.erro(this.simbolos[this.atual], "Esperado '=' ou '<-' após identificador em instrução 'constante'.");
}
const inicializadores = [];
do {
inicializadores.push(await this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
if (identificadores.length !== inicializadores.length) {
throw this.erro(simboloConstante, 'Quantidade de identificadores à esquerda do igual é diferente da quantidade de valores à direita.');
}
let retorno = [];
for (let [indice, identificador] of identificadores.entries()) {
// Se tipo ainda não foi definido, infere.
tipo = this.logicaComumInferenciaTiposVariaveisEConstantes(inicializadores[indice], tipo);
if (tipo !== 'dicionário') {
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(identificador.lexema, tipo));
}
else {
const inicializadorDicionario = inicializadores[indice];
this.pilhaEscopos.definirInformacoesVariavel(identificador.lexema, this.resolverInformacaoElementoSintaticoDeDicionario(inicializadorDicionario));
}
retorno.push(new declaracoes_1.Const(identificador, inicializadores[indice], tipo, tipoExplicito, Array.from(this.pilhaDecoradores)));
}
this.pilhaDecoradores = [];
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return retorno;
}
async funcao(tipo) {
let simbolo;
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.CONSTRUTOR:
simbolo = this.avancarEDevolverAnterior();
break;
default:
simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, `Esperado nome de ${tipo}.`);
break;
}
const decoradores = Array.from(this.pilhaDecoradores);
this.pilhaDecoradores = [];
// 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(simbolo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simbolo.lexema, 'qualquer'));
const corpoDaFuncao = await this.corpoDaFuncao(tipo);
const tipoDaFuncao = `função<${corpoDaFuncao.tipo}>`;
this.pilhaEscopos.definirInformacoesVariavel(simbolo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(simbolo.lexema, tipoDaFuncao));
const funcaoDeclaracao = new declaracoes_1.FuncaoDeclaracao(simbolo, corpoDaFuncao, tipoDaFuncao, decoradores);
this.pilhaEscopos.registrarReferenciaFuncao(simbolo.lexema, funcaoDeclaracao);
return funcaoDeclaracao;
}
async logicaComumParametros() {
const parametros = [];
do {
const parametro = {};
if (this.simbolos[this.atual].tipo === delegua_2.default.MULTIPLICACAO) {
this.consumir(delegua_2.default.MULTIPLICACAO, null);
parametro.abrangencia = 'multiplo';
}
else {
parametro.abrangencia = 'padrao';
}
parametro.nome = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome do parâmetro.');
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
try {
const valorPadrao = await this.primario();
parametro.valorPadrao = valorPadrao;
}
catch (erro) {
throw this.erro(this.simbolos[this.atual], `Valor padrão do parâmetro '${parametro.nome.lexema}' é inválido.`);
}
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
let tipoDadoParametro = this.verificarDefinicaoTipoAtual();
parametro.tipoDado = tipoDadoParametro;
this.avancarEDevolverAnterior();
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IGUAL)) {
const valorPadrao = await this.primario();
parametro.valorPadrao = valorPadrao;
}
this.pilhaEscopos.definirInformacoesVariavel(parametro.nome.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(parametro.nome.lexema, parametro.tipoDado || 'qualquer'));
parametros.push(parametro);
if (parametro.abrangencia === 'multiplo')
break;
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
return parametros;
}
async corpoDaFuncao(tipo) {
// O parêntese esquerdo é considerado o símbolo inicial para
// fins de localização.
const parenteseEsquerdo = this.consumir(delegua_2.default.PARENTESE_ESQUERDO, `Esperado '(' após o nome ${tipo}.`);
let parametros = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
parametros = await this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
let tipoRetorno = 'qualquer';
let definicaoExplicitaDeTipo = false;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipoRetorno = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
definicaoExplicitaDeTipo = true;
}
this.consumir(delegua_2.default.CHAVE_ESQUERDA, `Esperado '{' antes do escopo do ${tipo}.`);
const corpo = await this.blocoEscopo();
let expressoesRetorna = [];
for (const declaracao of corpo) {
expressoesRetorna = expressoesRetorna.concat((0, comum_1.buscarRetornos)(declaracao));
}
if (tipoRetorno === 'vazio' && expressoesRetorna.length > 0) {
// Filtra retornos que têm tipo conhecido e diferente de 'vazio'.
// 'qualquer' é excluído pois o tipo não pode ser determinado em tempo de análise sintática.
const retornosNaoVazios = expressoesRetorna.filter((e) => e.tipo !== 'vazio' && e.tipo !== 'qualquer');
if (retornosNaoVazios.length > 0) {
throw this.erro(retornosNaoVazios[0].simboloChave, `Função declara explicitamente 'vazio', mas usa expressão 'retorna' com tipo de retorno diferente de vazio.`);
}
}
const tiposRetornos = new Set(expressoesRetorna.filter((e) => e.tipo !== 'qualquer').map((e) => e.tipo));
let retornaChamadoExplicitamente = tiposRetornos.size > 0;
if (tiposRetornos.size > 1 && tipoRetorno !== 'qualquer') {
let tiposEncontrados = Array.from(tiposRetornos).reduce((acumulador, valor) => (acumulador += valor + ', '), '');
tiposEncontrados = tiposEncontrados.slice(0, -2);
throw this.erro(parenteseEsquerdo, `Função retorna valores com mais de um tipo. Tipo esperado: ${tipoRetorno}. Tipos encontrados: ${tiposEncontrados}.`);
}
tiposRetornos.delete('qualquer');
if (tipoRetorno === 'qualquer') {
if (tiposRetornos.size > 0) {
// Se o tipo de retorno é 'qualquer', seja implícito ou explícito,
// este avaliador sintático pode restringir o tipo baseado nos construtos
// de retornos encontrados nos blocos internos da função.
const tipoRetornoDeduzido = tiposRetornos.values().next().value;
tipoRetorno = tipoRetornoDeduzido;
}
else if (!retornaChamadoExplicitamente && !definicaoExplicitaDeTipo) {
// Ou, se esses retornos sequer existem, e o tipo explícito não é
// 'qualquer', o tipo inferido é 'vazio'.
tipoRetorno = 'vazio';
}
}
return new construtos_1.FuncaoConstruto(this.hashArquivo, Number(parenteseEsquerdo.linha), parametros, corpo, tipoRetorno, definicaoExplicitaDeTipo);
}
/**
* Analisa uma declaração de interface.
* Interfaces definem contratos de métodos e propriedades que as classes devem implementar.
* A verificação é feita em tempo de análise (parse-time).
*/
async declaracaoDeInterface() {
const simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da interface.');
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo da interface.");
const metodos = [];
const propriedades = [];
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA))
break;
const nomeMembro = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome de método ou propriedade na interface.');
if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_ESQUERDO)) {
// Assinatura de método: nome(params): tipoRetorno
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após nome do método.");
let params = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
params = await this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros.");
let tipoRetorno = 'qualquer';
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipoRetorno = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
}
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
metodos.push(new declaracoes_1.AssinaturaMetodo(nomeMembro, params, tipoRetorno));
}
else {
// Assinatura de propriedade: nome: tipo
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após nome de propriedade na interface.");
const tipo = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
propriedades.push(new declaracoes_1.PropriedadeClasse(nomeMembro, tipo));
}
}
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o corpo da interface.");
const declaracao = new declaracoes_1.InterfaceDeclaracao(simbolo, metodos, propriedades);
this.interfacesDeclaradas[simbolo.lexema] = declaracao;
return declaracao;
}
async declaracaoDeClasse() {
var _a;
// Modificadores opcionais no nível da classe: `abstrata` e/ou `estática`.
// Sintaxe: `classe abstrata NomeDaClasse` ou `classe estática NomeDaClasse`.
let ehAbstrata = false;
let ehEstatica = false;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ABSTRATO))
ehAbstrata = true;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.ESTATICO))
ehEstatica = true;
// Também permite a ordem invertida: `classe estática abstrata`
if (!ehAbstrata && this.verificarSeSimboloAtualEIgualA(delegua_2.default.ABSTRATO))
ehAbstrata = true;
const simbolo = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da classe.');
// Pré-registro para permitir auto-referência no corpo da própria classe,
// como em `MinhaClasse.propriedadeEstatica` dentro de métodos.
this.tiposDefinidosEmCodigo[simbolo.lexema] =
(_a = this.tiposDefinidosEmCodigo[simbolo.lexema]) !== null && _a !== void 0 ? _a : {};
const pilhaDecoradoresClasse = Array.from(this.pilhaDecoradores);
// Verificar `herda SuperclasseA, SuperclasseB`
const superClasses = [];
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.HERDA)) {
do {
const simboloSuperclasse = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome da Superclasse.');
if (superClasses.length === 0) {
this.superclasseAtual = simboloSuperclasse.lexema;
}
superClasses.push(new construtos_1.Variavel(this.hashArquivo, this.simbolos[this.atual - 1], simboloSuperclasse.lexema));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
// Verificar `implementa InterfaceA, InterfaceB`
const implementaInterfaces = [];
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.IMPLEMENTA)) {
do {
implementaInterfaces.push(this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome de interface após "implementa".'));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
// Verificar `mescla MisturávelA, MisturávelB`
const mesclas = [];
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MESCLA)) {
do {
const simboloMisturavel = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado nome de classe após "mescla".');
mesclas.push(new construtos_1.Variavel(this.hashArquivo, this.simbolos[this.atual - 1], simboloMisturavel.lexema));
} while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.VIRGULA));
}
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do escopo da classe.");
this.pilhaDecoradores = [];
const metodos = [];
const propriedades = [];
/**
* Analisa membros do corpo da classe com um contexto de acesso e estático padrão.
* Suporta blocos de contexto aninhados: `estático { }`, `abstrato { }`, `privado { }`, `protegido { }`, `publico { }`.
*/
const compreenderMembros = async (acessoPadrao, ehEstaticoPadrao, ehAbstratoPadrao = false) => {
var _a;
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
// Pular comentários normais dentro do corpo da classe.
if (this.simbolos[this.atual].tipo === delegua_2.default.COMENTARIO ||
this.simbolos[this.atual].tipo === delegua_2.default.LINHA_COMENTARIO) {
this.avancarEDevolverAnterior();
continue;
}
// Documentário (/** ... */)
let docAtual = null;
if (this.simbolos[this.atual].tipo === delegua_2.default.DOCUMENTARIO) {
const simboloDoc = this.avancarEDevolverAnterior();
docAtual = new construtos_1.ComentarioComoConstruto(simboloDoc);
}
// Decorador
if (this.simbolos[this.atual].tipo === delegua_2.default.ARROBA) {
await this.resolverDecoradores();
continue;
}
// Detecção de bloco de contexto: modificador seguido de '{'
const tipoAtual = this.simbolos[this.atual].tipo;
const tipoProximo = (_a = this.simbolos[this.atual + 1]) === null || _a === void 0 ? void 0 : _a.tipo;
const ehBlocoAcesso = [delegua_2.default.PRIVADO, delegua_2.default.PROTEGIDO].includes(tipoAtual) &&
tipoProximo === delegua_2.default.CHAVE_ESQUERDA;
const ehBlocoEstatico = tipoAtual === delegua_2.default.ESTATICO &&
tipoProximo === delegua_2.default.CHAVE_ESQUERDA;
const ehBlocoAbstrato = tipoAtual === delegua_2.default.ABSTRATO &&
tipoProximo === delegua_2.default.CHAVE_ESQUERDA;
if (ehBlocoAcesso) {
const novoAcesso = tipoAtual === delegua_2.default.PRIVADO ? 'privado' : 'protegido';
this.avancarEDevolverAnterior(); // consume modificador de acesso
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após modificador de acesso de bloco.");
await compreenderMembros(novoAcesso, ehEstaticoPadrao, ehAbstratoPadrao);
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' para fechar bloco de modificador de acesso.");
continue;
}
if (ehBlocoEstatico) {
this.avancarEDevolverAnterior(); // consume 'estático'
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após 'estático'.");
await compreenderMembros(acessoPadrao, true, ehAbstratoPadrao);
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' para fechar bloco estático.");
continue;
}
if (ehBlocoAbstrato) {
this.avancarEDevolverAnterior(); // consume 'abstrato'
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após 'abstrato'.");
await compreenderMembros(acessoPadrao, ehEstaticoPadrao, true);
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' para fechar bloco abstrato.");
continue;
}
const modificadorAcesso = acessoPadrao;
const ehEstatico = ehEstaticoPadrao;
// Método operador sobrecarregado: `operador+ (outro) { ... }`
if (this.simbolos[this.atual].tipo === delegua_2.default.OPERADOR) {
const simboloOperadorKeyword = this.avancarEDevolverAnterior();
const simboloDoOperador = this.avancarEDevolverAnterior();
const nomeMetodoOp = 'operador' + simboloDoOperador.lexema;
const simboloNomeMetodo = {
tipo: delegua_2.default.IDENTIFICADOR,
lexema: nomeMetodoOp,
literal: null,
linha: simboloOperadorKeyword.linha,
hashArquivo: this.hashArquivo,
};
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após operador sobrecarregado.");
let paramsOp = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
paramsOp = await this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do operador.");
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo do operador.");
const corpoOp = await this.blocoEscopo();
const corpoFuncaoOp = new construtos_1.FuncaoConstruto(this.hashArquivo, simboloNomeMetodo.linha, paramsOp, corpoOp);
const metodoOp = new declaracoes_1.FuncaoDeclaracao(simboloNomeMetodo, corpoFuncaoOp);
metodoOp.estatico = ehEstatico;
metodoOp.acesso = modificadorAcesso;
metodoOp.documentacao = docAtual;
metodos.push(metodoOp);
this.pilhaDecoradores = [];
continue;
}
// Método ou propriedade, determinado pelo token seguinte ao nome
const proximoSimbolo = this.simbolos[this.atual + 1];
switch (proximoSimbolo === null || proximoSimbolo === void 0 ? void 0 : proximoSimbolo.tipo) {
case delegua_2.default.PARENTESE_ESQUERDO: {
// Analisa: nome ( params ) [abstrato] [: tipoRetorno] { corpo }
const nomeMetodo = this.avancarEDevolverAnterior();
// Pré-registrar para suportar chamadas recursivas (igual a funcao()).
this.pilhaEscopos.definirInformacoesVariavel(nomeMetodo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeMetodo.lexema, 'qualquer'));
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após nome do método.");
let params = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
params = await this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do método.");
const ehAbstrato = ehAbstratoPadrao;
// Tipo de retorno opcional (igual a corpoDaFuncao())
let tipoRetorno = 'qualquer';
let definicaoExplicitaDeTipo = false;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipoRetorno = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
definicaoExplicitaDeTipo = true;
}
if (ehAbstrato) {
// Método abstrato: sem corpo
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
const corpoVazio = new construtos_1.FuncaoConstruto(this.hashArquivo, nomeMetodo.linha, params, [], tipoRetorno);
const metodoAbstrato = new declaracoes_1.FuncaoDeclaracao(nomeMetodo, corpoVazio, tipoRetorno);
metodoAbstrato.estatico = ehEstatico;
metodoAbstrato.abstrato = true;
metodoAbstrato.acesso = modificadorAcesso;
metodoAbstrato.documentacao = docAtual;
metodos.push(metodoAbstrato);
}
else {
// Método concreto: com corpo.
// Inferência de tipo de retorno igual a corpoDaFuncao().
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo do método.");
const corpo = await this.blocoEscopo();
let expressoesRetorna = [];
for (const declaracao of corpo) {
expressoesRetorna = expressoesRetorna.concat((0, comum_1.buscarRetornos)(declaracao));
}
const tiposRetornos = new Set(expressoesRetorna
.filter((e) => e.tipo !== 'qualquer')
.map((e) => e.tipo));
const retornaChamadoExplicitamente = tiposRetornos.size > 0;
tiposRetornos.delete('qualquer');
if (tipoRetorno === 'qualquer') {
if (tiposRetornos.size > 0) {
tipoRetorno = tiposRetornos.values().next().value;
}
else if (!retornaChamadoExplicitamente &&
!definicaoExplicitaDeTipo) {
tipoRetorno = 'vazio';
}
}
const corpoFuncao = new construtos_1.FuncaoConstruto(this.hashArquivo, nomeMetodo.linha, params, corpo, tipoRetorno);
const tipoDaFuncao = `função<${tipoRetorno}>`;
const metodo = new declaracoes_1.FuncaoDeclaracao(nomeMetodo, corpoFuncao, tipoDaFuncao);
metodo.estatico = ehEstatico;
metodo.acesso = modificadorAcesso;
metodo.decoradores = Array.from(this.pilhaDecoradores);
metodo.documentacao = docAtual;
metodos.push(metodo);
this.pilhaEscopos.definirInformacoesVariavel(nomeMetodo.lexema, new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeMetodo.lexema, tipoDaFuncao));
this.pilhaEscopos.registrarReferenciaFuncao(nomeMetodo.lexema, metodo);
}
this.pilhaDecoradores = [];
break;
}
case delegua_2.default.DOIS_PONTOS: {
const nomePropriedade = this.consumir(delegua_2.default.IDENTIFICADOR, 'Esperado identificador para nome de propriedade.');
this.consumir(delegua_2.default.DOIS_PONTOS, 'Esperado dois-pontos após nome de propriedade.');
const tipoPropriedade = this.avancarEDevolverAnterior();
let nomeTipoPropriedade = tipoPropriedade.lexema;
if (this.verificarTipoSimboloAtual(delegua_2.default.COLCHETE_ESQUERDO)) {
this.avancarEDevolverAnterior(); // consume '['
this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após '[' na definição do tipo de propriedade.");
nomeTipoPropriedade = `${nomeTipoPropriedade}[]`;
}
const prop = new declaracoes_1.PropriedadeClasse(nomePropriedade, nomeTipoPropriedade, Array.from(this.pilhaDecoradores), modificadorAcesso, ehEstatico);
prop.documentacao = docAtual;
// Auto-propriedade: `nome: tipo { obter; definir; }`
// Ou corpo personalizado: `nome: tipo { obter() { ... } definir(valor) { ... } }`
if (this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_ESQUERDA)) {
this.avancarEDevolverAnterior(); // consume '{'
let temCorpoPersonalizado = false;
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
const lexema = String(this.simbolos[this.atual].lexema || '').toLowerCase();
if (lexema === 'obter' || lexema === 'definir') {
const ehObter = lexema === 'obter';
this.avancarEDevolverAnterior(); // consume 'obter' / 'definir'
if (this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_ESQUERDO)) {
// Corpo personalizado: obter() { ... } / definir(valor) { ... }
temCorpoPersonalizado = true;
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após acessor.");
let paramsAcessor = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
paramsAcessor = await this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do acessor.");
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo do acessor.");
const corpoAcessor = await this.blocoEscopo();
// Inferência de tipo de retorno
let tipoAcessor = 'qualquer';
let expressoesRetornaAcessor = [];
for (const declaracao of corpoAcessor) {
expressoesRetornaAcessor =
expressoesRetornaAcessor.concat((0, comum_1.buscarRetornos)(declaracao));
}
const tiposRetornosAcessor = new Set(expressoesRetornaAcessor
.filter((e) => e.tipo !== 'qualquer')
.map((e) => e.tipo));
const retornaExplicitamenteAcessor = tiposRetornosAcessor.size > 0;
tiposRetornosAcessor.delete('qualquer');
if (tiposRetornosAcessor.size > 0) {
tipoAcessor = tiposRetornosAcessor
.values()
.next().value;
}
else if (!retornaExplicitamenteAcessor) {
tipoAcessor = 'vazio';
}
const corpoFuncaoAcessor = new construtos_1.FuncaoConstruto(this.hashArquivo, nomePropriedade.linha, paramsAcessor, corpoAcessor, tipoAcessor);
const tipoDaFuncaoAcessor = `função<${tipoAcessor}>`;
const metodoAcessor = new declaracoes_1.FuncaoDeclaracao(nomePropriedade, corpoFuncaoAcessor, tipoDaFuncaoAcessor);
metodoAcessor.estatico = ehEstatico;
metodoAcessor.acesso = modificadorAcesso;
metodoAcessor.eObtenedor = ehObter;
metodoAcessor.eDefinidor = !ehObter;
metodos.push(metodoAcessor);
}
else {
// Forma trivial: obter; / definir;
if (ehObter) {
prop.autoObter = true;
}
else {
prop.autoDefinir = true;
}
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
}
}
else {
break;
}
}
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após acessores da propriedade.");
// Corpo personalizado: obtenedor/definidor são métodos — não há campos base a declarar.
// Para auto-propriedades, o campo base iniciado por '_' é criado em tempo de execução com base
// nos indicadores autoObter/autoDefinir. O avaliador sintático usa o nome original ('nome').
if (!temCorpoPersonalizado) {
propriedades.push(prop);
}
}
else {
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
propriedades.push(prop);
}
this.pilhaDecoradores = [];
break;
}
default:
throw this.erro(this.simbolos[this.atual], 'Esperado definição de método ou propriedade.');
}
}
};
await compreenderMembros('publico', false);
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' após o escopo da classe.");
// Verificação em tempo de análise: classe deve implementar todos os contratos das interfaces.
const linhaFinalClasse = this.simbolos[this.atual - 1].linha;
for (const nomeInterface of implementaInterfaces) {
const interfaceDecl = this.interfacesDeclaradas[nomeInterface.lexema];
if (!interfaceDecl) {
this.erros.push(this.erro(nomeInterface, `Interface '${nomeInterface.lexema}' não foi declarada antes da classe '${simbolo.lexema}'.`));
continue;
}
const membrosFaltando = [];
for (const assinatura of interfaceDecl.metodos) {
const nomeMetodo = assinatura.nome.lexema;
const implementado = metodos.some((m) => m.simbolo.lexema === nomeMetodo);
if (!implementado) {
membrosFaltando.push({
tipo: 'metodo',
nome: nomeMetodo,
parametros: assinatura.parametros.map((p) => ({
nome: p.nome.lexema,
tipoDado: p.tipoDado,
})),
tipoRetorno: assinatura.tipoRetorno,
});
}
}
for (const prop of interfaceDecl.propriedades) {
const nomeProp = prop.nome.lexema;
const implementada = propriedades.some((p) => p.nome.lexema === nomeProp);
if (!implementada) {
membrosFaltando.push({
tipo: 'propriedade',
nome: nomeProp,
tipoPropriedade: prop.tipo,
});
}
}
if (membrosFaltando.length > 0) {
const correcaoSugerida = {
tipo: 'implementar-interface',
nomeInterface: nomeInterface.lexema,
nomeClasse: simbolo.lexema,
membrosFaltando,
linhaFinalClasse,
};
for (const membro of membrosFaltando) {
const mensagem = membro.tipo === 'metodo'
? `Classe '${simbolo.lexema}' não implementa o método '${membro.nome}' exigido pela interface '${nomeInterface.lexema}'.`
: `Classe '${simbolo.lexema}' não declara a propriedade '${membro.nome}' exigida pela interface '${nomeInterface.lexema}'.`;
const erro = this.erro(simbolo, mensagem);
erro.correcaoSugerida = correcaoSugerida;
this.erros.push(erro);
}
}
}
const definicaoClasse = new declaracoes_1.Classe(simbolo, superClasses, metodos, propriedades, pilhaDecoradoresClasse, ehAbstrata, ehEstatica, implementaInterfaces, mesclas);
this.tiposDefinidosEmCodigo[definicaoClasse.simbolo.lexema] = definicaoClasse;
this.superclasseAtual = undefined;
return definicaoClasse;
}
/**
* Analisa uma declaração de extensão:
* `extensão [global] de <tipo> { <métodos> }`
*/
async declaracaoDeExtensao() {
let ehGlobal = false;
if (this.simbolos[this.atual].tipo === delegua_2.default.IDENTIFICADOR &&
this.simbolos[this.atual].lexema === 'global') {
this.avancarEDevolverAnterior();
ehGlobal = true;
}
this.consumir(delegua_2.default.DE, "Esperado 'de' após 'extensão'.");
const simboloTipo = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome do tipo após 'de'.");
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' após o nome do tipo.");
const metodos = [];
const nomeTipo = simboloTipo.lexema;
if (!this.primitivasConhecidas[nomeTipo]) {
this.primitivasConhecidas[nomeTipo] = {};
}
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) &&
!this.estaNoFinal()) {
// Pular comentários dentro do corpo da extensão.
if (this.simbolos[this.atual].tipo === delegua_2.default.COMENTARIO ||
this.simbolos[this.atual].tipo === delegua_2.default.LINHA_COMENTARIO) {
this.avancarEDevolverAnterior();
continue;
}
// Apenas métodos são suportados em extensões.
const proximoSimbolo = this.simbolos[this.atual + 1];
if ((proximoSimbolo === null || proximoSimbolo === void 0 ? void 0 : proximoSimbolo.tipo) !== delegua_2.default.PARENTESE_ESQUERDO) {
throw this.erro(this.simbolos[this.atual], 'Esperado declaração de método na extensão.');
}
const nomeMetodo = this.avancarEDevolverAnterior();
this.consumir(delegua_2.default.PARENTESE_ESQUERDO, "Esperado '(' após nome do método.");
let params = [];
if (!this.verificarTipoSimboloAtual(delegua_2.default.PARENTESE_DIREITO)) {
params = await this.logicaComumParametros();
}
this.consumir(delegua_2.default.PARENTESE_DIREITO, "Esperado ')' após parâmetros do método.");
let tipoRetorno = 'qualquer';
let definicaoExplicitaDeTipo = false;
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DOIS_PONTOS)) {
tipoRetorno = this.verificarDefinicaoTipoAtual();
this.avancarEDevolverAnterior();
definicaoExplicitaDeTipo = true;
}
this.consumir(delegua_2.default.CHAVE_ESQUERDA, "Esperado '{' antes do corpo do método.");
const corpo = await this.blocoEscopo();
let expressoesRetorna = [];
for (const declaracao of corpo) {
expressoesRetorna = expressoesRetorna.concat((0, comum_1.buscarRetornos)(declaracao));
}
const tiposRetornos = new Set(expressoesRetorna.filter((e) => e.tipo !== 'qualquer').map((e) => e.tipo));
const retornaChamadoExplicitamente = tiposRetornos.size > 0;
tiposRetornos.delete('qualquer');
if (tipoRetorno === 'qualquer') {
if (tiposRetornos.size > 0) {
tipoRetorno = tiposRetornos.values().next().value;
}
else if (!retornaChamadoExplicitamente && !definicaoExplicitaDeTipo) {
tipoRetorno = 'vazio';
}
}
const corpoFuncao = new construtos_1.FuncaoConstruto(this.hashArquivo, nomeMetodo.linha, params, corpo, tipoRetorno);
const tipoDaFuncao = `função<${tipoRetorno}>`;
const metodo = new declaracoes_1.FuncaoDeclaracao(nomeMetodo, corpoFuncao, tipoDaFuncao);
metodos.push(metodo);
const argumentos = params.map((parametro) => new informacao_elemento_sintatico_1.InformacaoElementoSintatico(parametro.nome.lexema, parametro.tipoDado || 'qualquer', parametro.valorPadrao === undefined));
this.primitivasConhecidas[nomeTipo][nomeMetodo.lexema] =
new informacao_elemento_sintatico_1.InformacaoElementoSintatico(nomeMetodo.lexema, tipoRetorno, true, argumentos);
}
this.consumir(delegua_2.default.CHAVE_DIREITA, "Esperado '}' ao final da extensão.");
return new declaracoes_1.Extensao(simboloTipo, metodos, ehGlobal, this.hashArquivo);
}
/**
* Declarações fora de bloco precisam ser verificadas primeiro por
* uma série de motivos, como, por exemplo:
*
* - Não é possível declarar uma classe/função dentro de um bloco `enquanto`,
* `fazer ... enquanto`, `para`, `escolha`, etc;
* - Qualquer declaração pode ter um decorador.
* @returns Uma função ou classe se o símbolo atual resolver aqui.
* O retorno de `resolverDeclaracao()` em caso contrário.
* @see resolverDeclaracao
* @see resolverDecoradores
*/
async resolverDeclaracaoForaDeBloco() {
try {
while (this.verificarTipoSimboloAtual(delegua_2.default.ARROBA)) {
await this.resolverDecoradores();
}
// Documentário (/** ... */) antes de uma declaração de função.
let docTopLevel = null;
if (this.verificarTipoSimboloAtual(delegua_2.default.DOCUMENTARIO)) {
const simboloDoc = this.avancarEDevolverAnterior();
docTopLevel = new construtos_1.ComentarioComoConstruto(simboloDoc);
}
if (this.verificarTipoSimboloAtual(delegua_2.default.FUNCAO) ||
this.verificarTipoSimboloAtual(delegua_2.default.FUNÇÃO)) {
if (this.verificarTipoProximoSimbolo(delegua_2.default.DE)) {
this.avancarEDevolverAnterior(); // `função`
this.avancarEDevolverAnterior(); // `de`
const simboloDecorador = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado 'decorador' após 'função de'.");
if (simboloDecorador.lexema !== 'decorador') {
throw this.erro(simboloDecorador, "Esperado 'decorador' após 'função de'.");
}
const declaracaoFuncao = (await this.funcao('funcao'));
declaracaoFuncao.eFuncaoDeDecorador = true;
declaracaoFuncao.documentacao = docTopLevel;
return declaracaoFuncao;
}
if (this.verificarTipoProximoSimbolo(delegua_2.default.IDENTIFICADOR)) {
this.avancarEDevolverAnterior();
const declaracaoFuncao = (await this.funcao('funcao'));
declaracaoFuncao.documentacao = docTopLevel;
return declaracaoFuncao;
}
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.CLASSE)) {
return await this.declaracaoDeClasse();
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.INTERFACE)) {
return await this.declaracaoDeInterface();
}
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EXTENSAO)) {
return await this.declaracaoDeExtensao();
}
return await 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(); // avança além do token com erro
while (!this.estaNoFinal()) {
// Um ponto-e-vírgula já consumido indica fronteira limpa entre declarações.
if (this.simbolos[this.atual - 1].tipo === delegua_2.default.PONTO_E_VIRGULA)
return;
// Uma palavra-chave de início de declaração ou fecha-chave à frente:
// retorna SEM consumir o token, para que o chamador o analise normalmente.
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.CHAVE_DIREITA:
case delegua_2.default.CLASSE:
case delegua_2.default.FUNCAO:
case delegua_2.default.FUNÇÃO:
case delegua_2.default.VARIAVEL:
case delegua_2.default.PARA:
case delegua_2.default.SE:
case delegua_2.default.ENQUANTO:
case delegua_2.default.ESCREVA:
case delegua_2.default.RETORNA:
return;
}
this.avancarEDevolverAnterior();
}
}
/**
* 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.
*/
async resolverDeclaracao() {
switch (this.simbolos[this.atual].tipo) {
case delegua_2.default.AJUDA:
return await this.declaracaoAjuda();
case delegua_2.default.CHAVE_ESQUERDA:
return await this.declaracaoBloco();
case delegua_2.default.COMENTARIO:
return this.declaracaoComentarioUmaLinha();
case delegua_2.default.DOCUMENTARIO: {
const simboloDocumentario = this.avancarEDevolverAnterior();
// Se o próximo token for uma declaração de função com identificador,
// anexa o documentário como documentação da função.
if ((this.verificarTipoSimboloAtual(delegua_2.default.FUNCAO) ||
this.verificarTipoSimboloAtual(delegua_2.default.FUNÇÃO)) &&
this.verificarTipoProximoSimbolo(delegua_2.default.IDENTIFICADOR)) {
this.avancarEDevolverAnterior();
const declaracaoFuncao = (await this.funcao('funcao'));
declaracaoFuncao.documentacao = new construtos_1.ComentarioComoConstruto(simboloDocumentario);
return declaracaoFuncao;
}
return new declaracoes_1.Comentario(simboloDocumentario.hashArquivo, simboloDocumentario.linha, simboloDocumentario.literal, false);
}
case delegua_2.default.CONSTANTE:
this.avancarEDevolverAnterior();
return await this.declaracaoDeConstantes();
case delegua_2.default.CONTINUA:
this.avancarEDevolverAnterior();
return this.declaracaoContinua();
case delegua_2.default.ENQUANTO:
this.avancarEDevolverAnterior();
return await this.declaracaoEnquanto();
case delegua_2.default.ESCOLHA:
this.avancarEDevolverAnterior();
return await this.declaracaoEscolha();
case delegua_2.default.ESCREVA:
this.avancarEDevolverAnterior();
return await this.declaracaoEscreva();
case delegua_2.default.FALHAR:
this.avancarEDevolverAnterior();
return await this.declaracaoFalhar();
case delegua_2.default.FAZER:
const simboloFazer = this.avancarEDevolverAnterior();
return await this.declaracaoFazer(simboloFazer);
case delegua_2.default.IMPORTAR:
this.avancarEDevolverAnterior();
return await this.declaracaoImportar();
case delegua_2.default.LINHA_COMENTARIO:
return this.declaracaoComentarioMultilinha();
case delegua_2.default.PARA:
this.avancarEDevolverAnterior();
return await this.declaracaoPara();
case delegua_2.default.SUSTAR:
this.avancarEDevolverAnterior();
return this.declaracaoSustar();
case delegua_2.default.SE:
this.avancarEDevolverAnterior();
return await this.declaracaoSe();
case delegua_2.default.RETORNA:
this.avancarEDevolverAnterior();
return await this.declaracaoRetorna();
case delegua_2.default.TENDO:
this.avancarEDevolverAnterior();
return await this.declaracaoTendoComo();
case delegua_2.default.TENTE:
this.avancarEDevolverAnterior();
return await this.declaracaoTente();
case delegua_2.default.VARIAVEL:
this.avancarEDevolverAnterior();
return await this.declaracaoDeVariaveis();
}
const simboloAtual = this.simbolos[this.atual];
if (simboloAtual.tipo === delegua_2.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 === delegua_2.default.IDENTIFICADOR &&
simboloAnterior.linha === simboloAtual.linha) {
throw this.erro(this.simbolos[this.atual], 'Não é permitido ter dois identificadores seguidos na mesma linha.');
}
}
return await this.declaracaoExpressao();
}
async declaracaoAjuda() {
const simboloAjuda = this.avancarEDevolverAnterior();
if (this.estaNoFinal() ||
this.simbolos[this.atual].tipo !== delegua_2.default.PARENTESE_ESQUERDO) {
return new declaracoes_1.Ajuda(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, false);
}
this.avancarEDevolverAnterior(); // parêntese esquerdo
if (this.simbolos[this.atual].tipo === delegua_2.default.PARENTESE_DIREITO) {
this.avancarEDevolverAnterior();
return new declaracoes_1.Ajuda(simboloAjuda.hashArquivo, simboloAjuda.linha, undefined, true);
}
const expressaoAjuda = await this.expressao();
this.consumir(delegua_2.default.PARENTESE_DIREITO, `Esperado parêntese direito após expressão usada como argumento em ajuda(). Atual: ${this.simbolos[this.atual].lexema}.`);
return new declaracoes_1.Ajuda(simboloAjuda.hashArquivo, simboloAjuda.linha, expressaoAjuda);
}
/**
* Inicializa o primeiro nível da pilha de escopos, normalmente com ítens da biblioteca global.
* É separada da inicialização do avaliador sintático, pois é necessário manipular essa
* inicialização de outra forma em `delegua-node`.
*/
inicializarPilhaEscopos() {
this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
// Funções nativas de Delégua
this.pilhaEscopos.definirInformacoesVariavel('aleatorio', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('aleatorio', 'número'));
this.pilhaEscopos.definirInformacoesVariavel('aleatório', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('aleatório', 'número'));
this.pilhaEscopos.definirInformacoesVariavel('aleatorioEntre', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('aleatorioEntre', 'número', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('minimo', 'número'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('maximo', 'número'),
]));
this.pilhaEscopos.definirInformacoesVariavel('aleatórioEntre', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('aleatórioEntre', 'número', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('minimo', 'número'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('maximo', 'número'),
]));
this.pilhaEscopos.definirInformacoesVariavel('algum', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('algum', 'lógico', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('clonar', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('clonar', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrar', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrar', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarIndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarIndice', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarÍndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarÍndice', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarUltimo', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarÚltimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarÚltimo', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarUltimoIndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarUltimoIndice', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('encontrarÚltimoÍndice', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('encontrarÚltimoÍndice', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoPesquisa', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('filtrarPor', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('filtrarPor', 'qualquer[]', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('incluido', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('incluido', 'lógico', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('incluído', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('incluído', 'lógico', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('inteiro', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('inteiro', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('longo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('longo', 'longo', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valor', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('intervalo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('intervalo', 'inteiro[]', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorInicial', 'qualquer'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorFinal', 'qualquer', false),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorPasso', 'qualquer', false),
]));
this.pilhaEscopos.definirInformacoesVariavel('mapear', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('mapear', 'qualquer[]', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoMapeamento', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('maximo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('maximo', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
]));
this.pilhaEscopos.definirInformacoesVariavel('máximo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('máximo', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
]));
this.pilhaEscopos.definirInformacoesVariavel('minimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('minimo', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
]));
this.pilhaEscopos.definirInformacoesVariavel('mínimo', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('mínimo', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
]));
this.pilhaEscopos.definirInformacoesVariavel('numero', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('número', 'número', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('número', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('número', 'número', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('ordenar', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('ordenar', 'qualquer[]', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoOrdenacao', 'função', false),
]));
this.pilhaEscopos.definirInformacoesVariavel('paraCada', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('paraCada', 'qualquer[]', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('primeiroEmCondicao', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('primeiroEmCondicao', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('primeiroEmCondição', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('primeiroEmCondição', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoFiltragem', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('real', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('real', 'número', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('reduzir', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('reduzir', 'qualquer', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoReducao', 'função'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorInicial', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('tamanho', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('tamanho', 'inteiro', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('objeto', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('texto', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('texto', 'texto', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('valorParaConverter', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('todos', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('todos', 'lógico', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('iteravel', 'qualquer'),
]));
this.pilhaEscopos.definirInformacoesVariavel('todosEmCondicao', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('todosEmCondicao', 'lógico', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('iteravel', 'qualquer'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoCondicional', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('todosEmCondição', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('todosEmCondição', 'lógico', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('iteravel', 'qualquer'),
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('funcaoCondicional', 'função'),
]));
this.pilhaEscopos.definirInformacoesVariavel('tupla', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('tupla', 'tupla', true, [
new informacao_elemento_sintatico_1.InformacaoElementoSintatico('vetor', 'qualquer[]'),
]));
// Classe base global `Objeto`, registrada pelo interpretador em tempo de execução.
this.pilhaEscopos.definirInformacoesVariavel('Objeto', new informacao_elemento_sintatico_1.InformacaoElementoSintatico('Objeto', 'qualquer'));
// 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_elemento_sintatico_1.InformacaoElementoSintatico(nomeTipo, tipo));
}
}
}
async 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.pilhaDecoradores = [];
this.tiposDefinidosEmCodigo = {};
this.interfacesDeclaradas = {};
this.montaoTipos = new montao_tipos_1.MontaoTipos();
this.inicializarPilhaEscopos();
let declaracoes = [];
while (!this.estaNoFinal()) {
await this.resolverDecoradores();
const retornoDeclaracao = await 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] Tempo para análise: ${deltaAnalise[0] * 1e9 + deltaAnalise[1]}ns`);
}
return {
declaracoes: declaracoes,
erros: this.erros,
};
}
}
exports.AvaliadorSintatico = AvaliadorSintatico;
//# sourceMappingURL=avaliador-sintatico.js.map