@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
906 lines (905 loc) • 2.84 MB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Delegua = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AvaliadorSintaticoBase = void 0;
const construtos_1 = require("../construtos");
const declaracoes_1 = require("../declaracoes");
const erro_avaliador_sintatico_1 = require("./erro-avaliador-sintatico");
const comum_1 = __importDefault(require("../tipos-de-simbolos/comum"));
/**
* O Avaliador Sintático Base é uma tentativa de mapear métodos em comum
* entre todos os outros Avaliadores Sintáticos. Depende de um dicionário
* de tipos de símbolos comuns entre todos os dialetos.
*/
class AvaliadorSintaticoBase {
consumir(tipo, mensagemDeErro) {
if (this.verificarTipoSimboloAtual(tipo))
return this.avancarEDevolverAnterior();
throw this.erro(this.simbolos[this.atual], mensagemDeErro);
}
erro(simbolo, mensagemDeErro) {
const excecao = new erro_avaliador_sintatico_1.ErroAvaliadorSintatico(simbolo, mensagemDeErro);
return excecao;
}
simboloAnterior() {
return this.simbolos[this.atual - 1];
}
verificarTipoSimboloAtual(tipo) {
if (this.estaNoFinal())
return false;
return this.simbolos[this.atual].tipo === tipo;
}
verificarTipoProximoSimbolo(tipo) {
return this.simbolos[this.atual + 1].tipo === tipo;
}
estaNoFinal() {
return this.atual === this.simbolos.length;
}
avancarEDevolverAnterior() {
if (!this.estaNoFinal())
this.atual += 1;
return this.simbolos[this.atual - 1];
}
// TODO: Verificar possibilidade de remoção.
// Regressão de símbolo é uma roubada por N razões.
regredirEDevolverAtual() {
if (this.atual > 0)
this.atual -= 1;
return this.simbolos[this.atual];
}
verificarSeSimboloAtualEIgualA(...argumentos) {
for (let i = 0; i < argumentos.length; i++) {
const tipoAtual = argumentos[i];
if (this.verificarTipoSimboloAtual(tipoAtual)) {
this.avancarEDevolverAnterior();
return true;
}
}
return false;
}
finalizarChamada(entidadeChamada) {
const argumentos = [];
if (!this.verificarTipoSimboloAtual(comum_1.default.PARENTESE_DIREITO)) {
do {
// `apply()` em JavaScript aceita até 255 parâmetros.
if (argumentos.length >= 255) {
throw this.erro(this.simbolos[this.atual], 'Não pode haver mais de 255 argumentos.');
}
argumentos.push(this.expressao());
} while (this.verificarSeSimboloAtualEIgualA(comum_1.default.VIRGULA));
}
this.consumir(comum_1.default.PARENTESE_DIREITO, "Esperado ')' após os argumentos.");
return new construtos_1.Chamada(this.hashArquivo, entidadeChamada, argumentos);
}
unario() {
if (this.verificarSeSimboloAtualEIgualA(comum_1.default.NEGACAO, comum_1.default.SUBTRACAO)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.unario();
return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES');
}
return this.chamar();
}
exponenciacao() {
let expressao = this.unario();
while (this.verificarSeSimboloAtualEIgualA(comum_1.default.EXPONENCIACAO)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.unario();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
multiplicar() {
let expressao = this.exponenciacao();
while (this.verificarSeSimboloAtualEIgualA(comum_1.default.DIVISAO, comum_1.default.DIVISAO_INTEIRA, comum_1.default.MULTIPLICACAO, comum_1.default.MODULO)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.exponenciacao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
adicaoOuSubtracao() {
let expressao = this.multiplicar();
while (this.verificarSeSimboloAtualEIgualA(comum_1.default.SUBTRACAO, comum_1.default.ADICAO)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.multiplicar();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
/**
* Este método é usado por alguns dialetos de Portugol que possuem declarações
* de múltiplas variáveis na mesma linha.
*/
declaracaoDeVariaveis() {
throw new Error('Método não implementado.');
}
comparar() {
let expressao = this.adicaoOuSubtracao();
while (this.verificarSeSimboloAtualEIgualA(comum_1.default.MAIOR, comum_1.default.MAIOR_IGUAL, comum_1.default.MENOR, comum_1.default.MENOR_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.adicaoOuSubtracao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
comparacaoIgualdade() {
let expressao = this.comparar();
while (this.verificarSeSimboloAtualEIgualA(comum_1.default.DIFERENTE, comum_1.default.IGUAL, comum_1.default.IGUAL_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.comparar();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
e() {
let expressao = this.comparacaoIgualdade();
while (this.verificarSeSimboloAtualEIgualA(comum_1.default.E)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.comparacaoIgualdade();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
ou() {
let expressao = this.e();
while (this.verificarSeSimboloAtualEIgualA(comum_1.default.OU)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.e();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
expressao() {
return this.atribuir();
}
funcao(tipo) {
const simboloFuncao = this.avancarEDevolverAnterior();
const nomeFuncao = this.consumir(comum_1.default.IDENTIFICADOR, `Esperado nome ${tipo}.`);
return new declaracoes_1.FuncaoDeclaracao(nomeFuncao, this.corpoDaFuncao(tipo));
}
logicaComumParametros() {
const parametros = [];
do {
if (parametros.length >= 255) {
throw this.erro(this.simbolos[this.atual], 'Função não pode ter mais de 255 parâmetros.');
}
const parametro = {};
if (this.simbolos[this.atual].tipo === comum_1.default.MULTIPLICACAO) {
this.consumir(comum_1.default.MULTIPLICACAO, null);
parametro.abrangencia = 'multiplo';
}
else {
parametro.abrangencia = 'padrao';
}
parametro.nome = this.consumir(comum_1.default.IDENTIFICADOR, 'Esperado nome do parâmetro.');
if (this.verificarSeSimboloAtualEIgualA(comum_1.default.IGUAL)) {
parametro.valorPadrao = this.primario();
}
parametros.push(parametro);
if (parametro.abrangencia === 'multiplo')
break;
} while (this.verificarSeSimboloAtualEIgualA(comum_1.default.VIRGULA));
return parametros;
}
/**
* Os métodos a seguir só devem ser implementados se o dialeto
* em questão realmente possui a funcionalidade, e devem levantar
* erro em caso contrário.
*/
bitShift() {
throw new Error('Método não implementado.');
}
bitE() {
throw new Error('Método não implementado.');
}
bitOu() {
throw new Error('Método não implementado.');
}
declaracaoContinua() {
throw new Error('Método não implementado.');
}
declaracaoDeClasse() {
throw new Error('Método não implementado.');
}
declaracaoDeVariavel() {
throw new Error('Método não implementado.');
}
declaracaoExpressao(simboloAnterior) {
throw new Error('Método não implementado.');
}
declaracaoImportar() {
throw new Error('Método não implementado.');
}
declaracaoRetorna() {
throw new Error('Método não implementado.');
}
declaracaoSustar() {
throw new Error('Método não implementado.');
}
declaracaoTente() {
throw new Error('Método não implementado.');
}
em() {
throw new Error('Método não implementado.');
}
resolverDeclaracao() {
throw new Error('Método não implementado.');
}
}
exports.AvaliadorSintaticoBase = AvaliadorSintaticoBase;
},{"../construtos":44,"../declaracoes":81,"../tipos-de-simbolos/comum":161,"./erro-avaliador-sintatico":7}],2:[function(require,module,exports){
"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 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.
*
* Diferentemente de outros dialetos, em Delégua um construto normalmente retorna um tipo.
* Por isso a separação deste avaliador sintático do avaliador sintático base.
*/
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.tiposDeFerramentasExternas = {};
this.pilhaEscopos = new pilha_escopos_1.PilhaEscopos();
}
verificarDefinicaoTipoAtual() {
const tipos = [...Object.values(delegua_1.default)];
if (this.simbolos[this.atual].lexema in this.tiposDefinidosEmCodigo) {
return this.simbolos[this.atual].lexema;
}
const lexemaElementar = this.simbolos[this.atual].lexema.toLowerCase();
const tipoElementarResolvido = tipos.find((tipo) => tipo === lexemaElementar);
if (!tipoElementarResolvido) {
throw this.erro(this.simbolos[this.atual], `Tipo de dados desconhecido: '${this.simbolos[this.atual].lexema}'.`);
}
if (this.verificarTipoProximoSimbolo(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;
}
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 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 this.construtoTupla();
}
throw this.erro(this.simbolos[this.atual], `Esperado parêntese esquerdo após colchete esquerdo para definição de chave de dicionário. Atual: ${this.simbolos[this.atual].tipo}.`);
default:
throw this.erro(this.simbolos[this.atual], `Símbolo ${this.simbolos[this.atual].tipo} inesperado ou inválido como chave de dicionário.`);
}
}
construtoDicionario(simboloChaveEsquerda) {
this.avancarEDevolverAnterior();
const chaves = [];
const valores = [];
if (this.verificarSeSimboloAtualEIgualA(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 = this.obterChaveDicionario();
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' entre chave e valor.");
const valor = 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);
}
construtoTupla() {
const expressao = this.expressao();
const argumentos = [expressao];
while (this.simbolos[this.atual].tipo === delegua_2.default.VIRGULA) {
this.avancarEDevolverAnterior();
argumentos.push(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);
}
primario() {
const simboloAtual = this.simbolos[this.atual];
let valores = [];
switch (simboloAtual.tipo) {
case delegua_2.default.CHAVE_ESQUERDA:
return 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), [], 0, 'qualquer');
}
while (!this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_DIREITO)) {
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
return this.construtoTupla();
}
const valor = this.atribuir();
valores.push(valor);
if (this.simbolos[this.atual].tipo !== delegua_2.default.COLCHETE_DIREITO) {
this.consumir(delegua_2.default.VIRGULA, 'Esperado vírgula antes da próxima expressão.');
}
}
const tipoVetor = (0, inferenciador_1.inferirTipoVariavel)(valores);
return new construtos_1.Vetor(this.hashArquivo, Number(simboloAtual.linha), valores, valores.length, tipoVetor);
case delegua_2.default.FALSO:
this.avancarEDevolverAnterior();
return new construtos_1.Literal(this.hashArquivo, Number(simboloAtual.linha), false, 'lógico');
case delegua_2.default.FUNCAO:
case delegua_2.default.FUNÇÃO:
const simboloFuncao = this.avancarEDevolverAnterior();
return this.corpoDaFuncao(simboloFuncao.lexema);
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);
}
// 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 this.declaracaoImportar();
case delegua_2.default.ISTO:
this.avancarEDevolverAnterior();
return new construtos_1.Isto(this.hashArquivo, Number(simboloAtual.linha), simboloAtual);
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.TEXTO:
const simboloNumeroTexto = this.avancarEDevolverAnterior();
const tipoInferido = (0, inferenciador_1.inferirTipoVariavel)(simboloNumeroTexto.literal);
const tipoDadosElementar = (0, inferenciador_1.tipoInferenciaParaTipoDadosElementar)(tipoInferido);
return new construtos_1.Literal(this.hashArquivo, Number(simboloNumeroTexto.linha), simboloNumeroTexto.literal, tipoDadosElementar);
case delegua_2.default.PARENTESE_ESQUERDO:
this.avancarEDevolverAnterior();
const expressao = this.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 = this.expressao();
}
if (construto.constructor.name === 'AcessoMetodoOuPropriedade') {
const construtoTipado = construto;
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].tipoRetorno);
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);
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);
}
}
throw this.erro(this.simbolos[this.atual], 'Esperado expressão.');
}
chamar() {
let expressao = this.primario();
while (true) {
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PARENTESE_ESQUERDO)) {
expressao = this.finalizarChamada(expressao);
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO)) {
const nome = this.consumir(delegua_2.default.IDENTIFICADOR, "Esperado nome de método ou propriedade após '.'.");
expressao = new construtos_1.AcessoMetodoOuPropriedade(this.hashArquivo, expressao, nome);
}
else if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.COLCHETE_ESQUERDO)) {
const indice = this.expressao();
const simboloFechamento = this.consumir(delegua_2.default.COLCHETE_DIREITO, "Esperado ']' após escrita do indice.");
expressao = new construtos_1.AcessoIndiceVariavel(this.hashArquivo, expressao, indice, simboloFechamento);
}
else {
break;
}
}
return expressao;
}
/**
* `AcessoMetodoOuPropriedade` é um construto intermediário em Delégua, e deve ser resolvido como outro
* construto antes de qualquer outra próxima etapa. Algumas validações adicionais também ocorrem aqui.
* @param {AcessoMetodoOuPropriedade} entidadeChamadaResolvida O construto original.
* @returns {Construto} O construto resolvido como um tipo mais específico.
* @see finalizarChamada
*/
resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamadaResolvida) {
const construtoTipado = entidadeChamadaResolvida;
switch (entidadeChamadaResolvida.tipo) {
case delegua_1.default.DICIONARIO:
case delegua_1.default.DICIONÁRIO:
if (!(construtoTipado.simbolo.lexema in primitivas_dicionario_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de dicionário.`);
}
const primitivaDicionarioSelecionada = primitivas_dicionario_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaDicionarioSelecionada.tipoRetorno);
case delegua_1.default.INTEIRO:
case delegua_1.default.NUMERO:
case delegua_1.default.NÚMERO:
if (!(construtoTipado.simbolo.lexema in primitivas_numero_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de número.`);
}
const primitivaNumeroSelecionada = primitivas_numero_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaNumeroSelecionada.tipoRetorno);
case delegua_1.default.MODULO:
case delegua_1.default.MÓDULO:
if (construtoTipado.simbolo.lexema in this.tiposDefinidosEmCodigo) {
// Construtor de classe.
return new construtos_1.Variavel(construtoTipado.hashArquivo, construtoTipado.simbolo, construtoTipado.objeto.tipo);
}
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema);
case delegua_1.default.TEXTO:
if (!(construtoTipado.simbolo.lexema in primitivas_texto_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de texto.`);
}
const primitivaTextoSelecionada = primitivas_texto_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaTextoSelecionada.tipoRetorno);
case delegua_1.default.VETOR:
case delegua_1.default.VETOR_NUMERO:
case delegua_1.default.VETOR_NÚMERO:
case delegua_1.default.VETOR_TEXTO:
if (!(construtoTipado.simbolo.lexema in primitivas_vetor_1.default)) {
throw this.erro(construtoTipado.simbolo, `${construtoTipado.simbolo.lexema} não é uma primitiva de vetor.`);
}
const primitivaVetorSelecionada = primitivas_vetor_1.default[construtoTipado.simbolo.lexema];
return new construtos_1.AcessoMetodo(construtoTipado.hashArquivo, construtoTipado.objeto, construtoTipado.simbolo.lexema, primitivaVetorSelecionada.tipoRetorno);
}
return entidadeChamadaResolvida;
}
finalizarChamada(entidadeChamada) {
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(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.
let entidadeChamadaResolvida = entidadeChamada;
if (entidadeChamadaResolvida.constructor.name === 'AcessoMetodoOuPropriedade') {
entidadeChamadaResolvida = this.resolverEntidadeChamadaAcessoMetodoOuPropriedade(entidadeChamadaResolvida);
}
// TODO: Criar forma de validar tipos dos argumentos da entidade chamada.
return new construtos_1.Chamada(this.hashArquivo, entidadeChamadaResolvida, argumentos);
}
unario() {
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.NEGACAO, 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 = this.unario();
return new construtos_1.Unario(this.hashArquivo, operador, direito, 'ANTES');
}
return this.chamar();
}
multiplicar() {
let expressao = 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 = this.exponenciacao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
/**
* Se símbolo de operação é `+`, `-`, `+=` ou `-=`, monta objeto `Binario` para
* ser avaliado pelo Interpretador.
* @returns Um Construto, normalmente um `Binario`, ou `Unario` se houver alguma operação unária para ser avaliada.
*/
adicaoOuSubtracao() {
let expressao = this.multiplicar();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.SUBTRACAO, delegua_2.default.ADICAO, delegua_2.default.MENOS_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.multiplicar();
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 = this.atribuir();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
bitShift() {
let expressao = this.adicaoOuSubtracao();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.MENOR_MENOR, delegua_2.default.MAIOR_MAIOR)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.adicaoOuSubtracao();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
bitE() {
let expressao = this.bitShift();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_AND)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.bitShift();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
bitOu() {
let expressao = this.bitE();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.BIT_OR, delegua_2.default.BIT_XOR)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.bitE();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
comparar() {
let expressao = this.bitOu();
while (this.verificarSeSimboloAtualEIgualA(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 = this.bitOu();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
comparacaoIgualdade() {
let expressao = this.comparar();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.DIFERENTE, delegua_2.default.IGUAL_IGUAL)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.comparar();
expressao = new construtos_1.Binario(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
em() {
let expressao = this.comparacaoIgualdade();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.EM)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.comparacaoIgualdade();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
e() {
let expressao = this.em();
while (this.verificarSeSimboloAtualEIgualA(delegua_2.default.E)) {
const operador = this.simbolos[this.atual - 1];
const direito = this.em();
expressao = new construtos_1.Logico(this.hashArquivo, expressao, operador, direito);
}
return expressao;
}
/**
* Método que resolve atribuições.
* @returns Um construto do tipo `Atribuir`, `Conjunto` ou `AtribuicaoPorIndice`.
*/
atribuir() {
const expressao = this.ou();
if (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)) {
const igual = this.simbolos[this.atual - 1];
const valor = this.expressao();
switch (expressao.constructor.name) {
case 'Variavel':
return new construtos_1.Atribuir(this.hashArquivo, expressao, valor);
case 'AcessoMetodoOuPropriedade':
const expressaoAcessoMetodoOuPropriedade = expressao;
return new construtos_1.DefinirValor(this.hashArquivo, igual.linha, expressaoAcessoMetodoOuPropriedade.objeto, expressaoAcessoMetodoOuPropriedade.simbolo, valor);
/* case 'Super':
const expressaoSuper = expressao as Super;
return new DefinirValor(
this.hashArquivo,
igual.linha,
expressaoSuper.
); */
case 'AcessoIndiceVariavel':
const expressaoAcessoIndiceVariavel = expressao;
return new construtos_1.AtribuicaoPorIndice(this.hashArquivo, expressaoAcessoIndiceVariavel.linha, expressaoAcessoIndiceVariavel.entidadeChamada, expressaoAcessoIndiceVariavel.indice, valor);
}
throw this.erro(igual, 'Tarefa de atribuição inválida');
}
return expressao;
}
expressao() {
if (this.verificarSeSimboloAtualEIgualA(delegua_2.default.LEIA))
return this.declaracaoLeia();
return this.atribuir();
}
blocoEscopo() {
this.pilhaEscopos.empilhar(new informacao_escopo_1.InformacaoEscopo());
let declaracoes = [];
while (!this.verificarTipoSimboloAtual(delegua_2.default.CHAVE_DIREITA) && !this.estaNoFinal()) {
const retornoDeclaracao = this.resolverDeclaracaoForaDeBloco();
if (Array.isArray(retornoDeclaracao)) {
declaracoes = declaracoes.concat(retornoDeclaracao);
}
else {
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 construtos_1.Comentario(simboloComentario.hashArquivo, simboloComentario.linha, conteudos, true);
}
declaracaoComentarioUmaLinha() {
const simboloComentario = this.avancarEDevolverAnterior();
return new construtos_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]);
}
declaracaoEnquanto() {
try {
this.blocos += 1;
const condicao = this.expressao();
const corpo = this.resolverDeclaracao();
return new declaracoes_1.Enquanto(condicao, corpo);
}
finally {
this.blocos -= 1;
}
}
declaracaoEscolha() {
try {
this.blocos += 1;
const condicao = 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 = [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(this.expressao());
this.consumir(delegua_2.default.DOIS_PONTOS, "Esperado ':' após declaração do 'caso'.");
}
let declaracoes = [];
do {
const retornoDeclaracao = 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(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;
}
}
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(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);
}
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 = this.expressao();
// Ponto-e-vírgula é opcional aqui.
this.verificarSeSimboloAtualEIgualA(delegua_2.default.PONTO_E_VIRGULA);
return new declaracoes_1.Expressao(expressao, decoradores);
}
declaracaoFalhar() {
const simboloFalha = this.simbolos[this.atual - 1];
return new declaracoes_1.Falhar(simboloFalha, this.declaracaoExpressao().expressao);
}
declaracaoFazer() {
const simboloFazer = this.simbolos[this.atual - 1];
try {
this.blocos += 1;
const caminhoFazer = this.resolverDeclaracao();
this.consumir(delegua_2.default.ENQUANTO, "Esperado declaração do 'enquanto' após o escopo do 'fazer'.");
const condicaoEnquanto = this.expressao();
return new declaracoes_1.Fazer(simboloFazer.hashArquivo, Number(simboloFazer.linha), caminhoFazer, condicaoEnquanto);
}
finally {
t