@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
571 lines • 28 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.InterpretadorComDepuracao = void 0;
const lodash_1 = __importDefault(require("lodash"));
const espaco_variaveis_1 = require("../espaco-variaveis");
const quebras_1 = require("../quebras");
const construtos_1 = require("../construtos");
const inferenciador_1 = require("../inferenciador");
const interpretador_1 = require("./interpretador");
/**
* Implementação do Interpretador com suporte a depuração.
* Herda o Interpretador padrão de Delégua e implementa métodos a mais, que são
* usados pelo servidor de depuração.
* Alguns métodos do Interpretador original, como `executarBloco` e `interpretar`,
* são reimplementados aqui.
*
* A separação entre `Interpretador` e `InterpretadorComDepuracao` se faz
* necessária por uma série de motivos.
* O primeiro deles é o desempenho. A depuração torna o desempenho do
* Interpretador com depuração inferior ao Interpretador original pelas
* várias verificações de controle que precisam ser feitas para a
* funcionalidade do suporte a depuração, como verificar pontos de parada,
* estados da pilha de execução e variáveis.
* O segundo deles é manter o Interpretador original tão simples quanto possível.
* Uma implementação mais simples normalmente é mais robusta.
* O terceiro deles é o uso de memória. O Interpretador original não possui
* uma série de variáveis implementadas aqui, o que o torna mais econômico em
* recursos de máquina.
*/
class InterpretadorComDepuracao extends interpretador_1.Interpretador {
constructor(diretorioBase, funcaoDeRetorno, funcaoDeRetornoMesmaLinha) {
super(diretorioBase, false, funcaoDeRetorno, funcaoDeRetornoMesmaLinha);
this.pontosParada = [];
this.pontoDeParadaAtivo = false;
this.avisoPontoParadaAtivado = () => console.log('Aviso: Ponto de parada ativado.');
this.escopoAtual = 0;
this.executandoChamada = false;
this.passos = 0;
}
/**
* Quando um construto ou declaração possui id, significa que o interpretador
* deve resolver a avaliação e guardar seu valor até o final do escopo.
* Isso serve para quando a linguagem está em modo de depuração, e o contexto
* da execução deixa de existir com um ponto de parada, por exemplo.
* @param expressao A expressão a ser avaliada.
* @returns O resultado da avaliação.
*/
async avaliar(expressao) {
if (expressao.hasOwnProperty('id')) {
const escopoAtual = this.pilhaEscoposExecucao.topoDaPilha();
const idChamadaComArgumentos = await this.gerarIdResolucaoChamada(expressao);
if (escopoAtual.ambiente.resolucoesChamadas.hasOwnProperty(idChamadaComArgumentos)) {
return escopoAtual.ambiente.resolucoesChamadas[idChamadaComArgumentos];
}
}
return await expressao.aceitar(this);
}
/**
* Resolve problema de literais que tenham vírgulas, e confundem a resolução de chamadas.
* @param valor O valor do argumento, que pode ser um literal com virgulas.
* @returns Uma string com vírgulas escapadas.
*/
escaparVirgulas(valor) {
return String(valor).replace(/,/i, ',');
}
/**
* Gera um identificador para resolução de chamadas.
* Usado para não chamar funções repetidamente quando usando instruções
* de passo, como "próximo" ou "adentrar-escopo".
* @param expressao A expressão de chamada.
* @returns Uma `Promise` que resolve como `string`.
*/
async gerarIdResolucaoChamada(expressao) {
const argumentosResolvidos = [];
if (expressao.argumentos && expressao.argumentos.length > 0) {
for (let argumento of expressao.argumentos) {
if (argumento instanceof construtos_1.Leia) {
argumentosResolvidos.push(`leia_${argumento.id}`);
}
else {
argumentosResolvidos.push(await this.avaliar(argumento));
}
}
}
return argumentosResolvidos.reduce((acumulador, argumento) => (acumulador += `,${this.escaparVirgulas(argumento.hasOwnProperty('valor') ? argumento.valor : argumento)}`), expressao.id);
}
async visitarExpressaoDeChamada(expressao) {
const _idChamadaComArgumentos = await this.gerarIdResolucaoChamada(expressao);
// Usado na abertura do bloco de escopo da chamada.
this.idChamadaAtual = _idChamadaComArgumentos;
this.executandoChamada = true;
this.proximoEscopo = 'funcao';
const retorno = await super.visitarExpressaoDeChamada(expressao);
this.executandoChamada = false;
const escopoAtual = this.pilhaEscoposExecucao.topoDaPilha();
escopoAtual.ambiente.resolucoesChamadas[_idChamadaComArgumentos] = retorno;
return retorno;
}
async visitarDeclaracaoEnquanto(declaracao) {
const escopoAtual = this.pilhaEscoposExecucao.topoDaPilha();
switch (this.comando) {
case 'proximo':
if (this.eVerdadeiro(await this.avaliar(declaracao.condicao))) {
escopoAtual.emLacoRepeticao = true;
return this.executarBloco(declaracao.corpo.declaracoes);
}
escopoAtual.emLacoRepeticao = false;
return null;
default:
let retornoExecucao;
while (!(retornoExecucao instanceof quebras_1.Quebra) &&
!this.pontoDeParadaAtivo &&
this.eVerdadeiro(await this.avaliar(declaracao.condicao))) {
escopoAtual.emLacoRepeticao = true;
try {
retornoExecucao = await this.executar(declaracao.corpo);
if (retornoExecucao instanceof quebras_1.SustarQuebra) {
return null;
}
if (retornoExecucao instanceof quebras_1.ContinuarQuebra) {
retornoExecucao = null;
}
}
catch (erro) {
return Promise.reject(erro);
}
}
escopoAtual.emLacoRepeticao = false;
return retornoExecucao;
}
}
async avaliarArgumentosEscreva(argumentos) {
let formatoTexto = '';
for (const argumento of argumentos) {
const resultadoAvaliacao = await this.avaliar(argumento);
let valor = (resultadoAvaliacao === null || resultadoAvaliacao === void 0 ? void 0 : resultadoAvaliacao.hasOwnProperty('valor')) ? resultadoAvaliacao.valor : resultadoAvaliacao;
formatoTexto += `${this.paraTexto(valor)} `;
}
return formatoTexto.trimEnd();
}
/**
* Execução de uma escrita na saída configurada, que pode ser `console` (padrão) ou
* alguma função para escrever numa página Web.
* Se ponto de parada foi ativado durante a avaliação de argumentos, não escreve.
* @param declaracao A declaração.
* @returns Sempre nulo, por convenção de visita.
*/
async visitarDeclaracaoEscreva(declaracao) {
try {
const formatoTexto = await this.avaliarArgumentosEscreva(declaracao.argumentos);
if (this.pontoDeParadaAtivo) {
return null;
}
this.funcaoDeRetorno(formatoTexto);
return null;
}
catch (erro) {
this.erros.push({
erroInterno: erro,
linha: declaracao.linha,
hashArquivo: declaracao.hashArquivo,
});
}
}
async visitarDeclaracaoPara(declaracao) {
// Aqui precisamos clonar a declaração porque modificamos
// algumas propriedades que indicam o estado da execução dela.
// Por exemplo, se chamamos uma função que tem dentro dela um bloco Para,
// cada execução do bloco precisa de uma inicialização diferente.
const cloneDeclaracao = lodash_1.default.cloneDeep(declaracao);
const corpoExecucao = cloneDeclaracao.corpo;
const declaracaoInicializador = Array.isArray(cloneDeclaracao.inicializador)
? declaracao.inicializador[0]
: declaracao.inicializador;
if (declaracaoInicializador !== null) {
await this.avaliar(declaracaoInicializador);
// O incremento vai ao final do bloco de escopo.
if (cloneDeclaracao.incrementar !== null) {
corpoExecucao.declaracoes.push(cloneDeclaracao.incrementar);
}
}
cloneDeclaracao.inicializada = true;
const escopoAtual = this.pilhaEscoposExecucao.topoDaPilha();
switch (this.comando) {
case 'proximo':
if (cloneDeclaracao.condicao !== null &&
this.eVerdadeiro(await this.avaliar(cloneDeclaracao.condicao))) {
escopoAtual.emLacoRepeticao = true;
const resultadoBloco = this.executarBloco(corpoExecucao.declaracoes);
return resultadoBloco;
}
escopoAtual.emLacoRepeticao = false;
return null;
default:
let retornoExecucao;
while (!(retornoExecucao instanceof quebras_1.Quebra) && !this.pontoDeParadaAtivo) {
if (cloneDeclaracao.condicao !== null &&
!this.eVerdadeiro(await this.avaliar(cloneDeclaracao.condicao))) {
break;
}
try {
retornoExecucao = await this.executar(corpoExecucao);
if (retornoExecucao instanceof quebras_1.SustarQuebra) {
return null;
}
if (retornoExecucao instanceof quebras_1.ContinuarQuebra) {
retornoExecucao = null;
}
}
catch (erro) {
return Promise.reject(erro);
}
}
// escopoAtual.emLacoRepeticao = false;
return retornoExecucao;
}
}
/**
* Ao executar um retorno, manter o valor retornado no Interpretador para
* uso por linhas que foram executadas com o comando `próximo` do depurador.
* @param declaracao Uma declaracao Retorna
* @returns O resultado da execução da visita.
*/
async visitarExpressaoRetornar(declaracao) {
const retorno = await super.visitarExpressaoRetornar(declaracao);
// O escopo atual é marcado como finalizado, para notificar a
// instrução de que deve ser descartado.
const escopoAtual = this.pilhaEscoposExecucao.topoDaPilha();
escopoAtual.finalizado = true;
// Acha o primeiro escopo de função.
const escopoFuncao = this.pilhaEscoposExecucao.obterEscopoPorTipo('funcao');
if (escopoFuncao === undefined) {
return Promise.reject('retorna() chamado fora de execução de função.');
}
if (escopoFuncao.idChamada !== undefined) {
escopoAtual.ambiente.resolucoesChamadas[escopoFuncao.idChamada] =
retorno && retorno.hasOwnProperty('valor') ? retorno.valor : retorno;
}
return retorno;
}
/**
* Se bloco de execução já foi instanciado antes (por exemplo, quando há um ponto de parada e a
* execução do código é retomada pelo depurador), retoma a execução do bloco do ponto em que havia parado.
* Se bloco de execução ainda não foi instanciado, empilha declarações na pilha de escopos de execução,
* cria um novo ambiente e executa as declarações empilhadas.
* Se depurador comandou uma instrução 'adentrar-escopo', execução do bloco não ocorre, mas
* ponteiros de escopo e execução são atualizados.
* @param declaracoes Um vetor de declaracoes a ser executado.
* @param ambiente O ambiente de execução quando houver, como parâmetros, argumentos, etc.
*/
async executarBloco(declaracoes, ambiente) {
// Se o escopo atual não é o último.
if (this.escopoAtual < this.pilhaEscoposExecucao.elementos() - 1) {
this.escopoAtual++;
const proximoEscopo = this.pilhaEscoposExecucao.naPosicao(this.escopoAtual);
let retornoExecucao;
// Sempre executa a próxima instrução, mesmo que haja ponto de parada.
retornoExecucao = await this.executar(proximoEscopo.declaracoes[proximoEscopo.declaracaoAtual]);
proximoEscopo.declaracaoAtual++;
for (; !(retornoExecucao instanceof quebras_1.Quebra) &&
proximoEscopo.declaracaoAtual < proximoEscopo.declaracoes.length; proximoEscopo.declaracaoAtual++) {
this.pontoDeParadaAtivo = this.verificarPontoParada(proximoEscopo.declaracoes[proximoEscopo.declaracaoAtual]);
if (this.pontoDeParadaAtivo) {
this.avisoPontoParadaAtivado();
break;
}
retornoExecucao = await this.executar(proximoEscopo.declaracoes[proximoEscopo.declaracaoAtual]);
// Um ponto de parada ativo pode ter vindo de um escopo mais interno.
// Por isso verificamos outra parada aqui para evitar que
// `this.declaracaoAtual` seja incrementado.
if (this.pontoDeParadaAtivo) {
this.avisoPontoParadaAtivado();
break;
}
}
this.pilhaEscoposExecucao.removerUltimo();
this.escopoAtual--;
return retornoExecucao;
}
else {
this.abrirNovoBlocoEscopo(declaracoes, ambiente, this.proximoEscopo || 'outro');
const ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
if (this.idChamadaAtual) {
ultimoEscopo.idChamada = this.idChamadaAtual;
this.idChamadaAtual = undefined;
}
this.proximoEscopo = undefined;
if (this.comando !== 'adentrarEscopo') {
return await this.executarUltimoEscopo();
}
}
}
/**
* Para fins de depuração, verifica se há ponto de parada no mesmo pragma da declaração.
* @param declaracao A declaração a ser executada.
* @returns True quando execução deve parar. False caso contrário.
*/
verificarPontoParada(declaracao) {
const buscaPontoParada = this.pontosParada.filter((p) => p.hashArquivo === declaracao.hashArquivo && p.linha === declaracao.linha);
if (buscaPontoParada.length > 0) {
console.log(`Ponto de parada encontrado. Linha: ${declaracao.linha}.`);
return true;
}
return false;
}
/**
* No interpretador com depuração, este método é dividido em dois outros métodos privados:
* - `this.executarUmPassoNoEscopo`, que executa apenas uma instrução e nada mais;
* - `this.executarUltimoEscopoComandoContinuar`, que é a execução trivial de um escopo inteiro,
* ou com todas as instruções, ou até encontrar um ponto de parada.
* @param manterAmbiente Se verdadeiro, junta elementos do último escopo com o escopo
* imediatamente abaixo.
* @param naoVerificarPrimeiraExecucao Booleano que pede ao Interpretador para não
* verificar o ponto de parada na primeira execução.
* Normalmente usado pelo Servidor de Depuração para continuar uma linha.
* @returns O retorno da execução.
*/
async executarUltimoEscopo(manterAmbiente = false, naoVerificarPrimeiraExecucao = false) {
switch (this.comando) {
case 'adentrarEscopo':
case 'proximo':
if (!this.executandoChamada) {
return this.executarUmPassoNoEscopo();
}
else {
return this.executarUltimoEscopoComandoContinuar(manterAmbiente, naoVerificarPrimeiraExecucao);
}
default:
return this.executarUltimoEscopoComandoContinuar(manterAmbiente, naoVerificarPrimeiraExecucao);
}
}
descartarTodosEscoposFinalizados() {
let i = this.pilhaEscoposExecucao.pilha.length - 1;
while (i > 0) {
let ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
if (ultimoEscopo.declaracaoAtual >= ultimoEscopo.declaracoes.length || ultimoEscopo.finalizado) {
this.pilhaEscoposExecucao.removerUltimo();
const escopoAnterior = this.pilhaEscoposExecucao.topoDaPilha();
escopoAnterior.ambiente.resolucoesChamadas = Object.assign(escopoAnterior.ambiente.resolucoesChamadas, ultimoEscopo.ambiente.resolucoesChamadas);
this.escopoAtual--;
}
else {
break;
}
i--;
}
}
descartarEscopoPorRetornoFuncao() {
let ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
while (ultimoEscopo.tipo !== 'funcao') {
this.pilhaEscoposExecucao.removerUltimo();
const escopoAnterior = this.pilhaEscoposExecucao.topoDaPilha();
escopoAnterior.ambiente.resolucoesChamadas = Object.assign(escopoAnterior.ambiente.resolucoesChamadas, ultimoEscopo.ambiente.resolucoesChamadas);
this.escopoAtual--;
ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
}
this.pilhaEscoposExecucao.removerUltimo();
const escopoAnterior = this.pilhaEscoposExecucao.topoDaPilha();
escopoAnterior.ambiente.resolucoesChamadas = Object.assign(escopoAnterior.ambiente.resolucoesChamadas, ultimoEscopo.ambiente.resolucoesChamadas);
this.escopoAtual--;
}
async executarUmPassoNoEscopo() {
const ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
let retornoExecucao;
if (this.passos > 0) {
this.passos--;
retornoExecucao = await this.executar(ultimoEscopo.declaracoes[ultimoEscopo.declaracaoAtual]);
if (!this.pontoDeParadaAtivo && !ultimoEscopo.emLacoRepeticao) {
ultimoEscopo.declaracaoAtual++;
}
if (ultimoEscopo.declaracaoAtual >= ultimoEscopo.declaracoes.length || ultimoEscopo.finalizado) {
if (retornoExecucao instanceof quebras_1.RetornoQuebra) {
this.descartarEscopoPorRetornoFuncao();
}
else {
this.descartarTodosEscoposFinalizados();
}
}
if (this.pilhaEscoposExecucao.elementos() === 1) {
this.finalizacaoDaExecucao();
}
}
return retornoExecucao;
}
/**
* Continua a interpretação parcial do último ponto em que parou.
* Pode ser tanto o começo da execução inteira, ou pós comando do depurador
* quando há um ponto de parada.
* @param manterAmbiente Se verdadeiro, junta elementos do último escopo com o escopo
* imediatamente abaixo.
* @param naoVerificarPrimeiraExecucao Booleano que pede ao Interpretador para não
* verificar o ponto de parada na primeira execução.
* Normalmente usado pelo Servidor de Depuração para continuar uma linha.
* @returns Um objeto de retorno, com erros encontrados se houverem.
*/
async executarUltimoEscopoComandoContinuar(manterAmbiente = false, naoVerificarPrimeiraExecucao = false) {
const ultimoEscopo = this.pilhaEscoposExecucao.topoDaPilha();
let retornoExecucao;
try {
for (; !(retornoExecucao instanceof quebras_1.Quebra) && ultimoEscopo.declaracaoAtual < ultimoEscopo.declaracoes.length; ultimoEscopo.declaracaoAtual++) {
if (naoVerificarPrimeiraExecucao) {
naoVerificarPrimeiraExecucao = false;
}
else {
this.pontoDeParadaAtivo = this.verificarPontoParada(ultimoEscopo.declaracoes[ultimoEscopo.declaracaoAtual]);
if (this.pontoDeParadaAtivo) {
this.avisoPontoParadaAtivado();
break;
}
}
retornoExecucao = await this.executar(ultimoEscopo.declaracoes[ultimoEscopo.declaracaoAtual]);
// Um ponto de parada ativo pode ter vindo de um escopo mais interno.
// Por isso verificamos outra parada aqui para evitar que
// `this.declaracaoAtual` seja incrementado.
if (this.pontoDeParadaAtivo) {
this.avisoPontoParadaAtivado();
break;
}
}
return retornoExecucao;
}
catch (erro) {
this.erros.push(erro);
}
finally {
if (!this.pontoDeParadaAtivo && this.comando !== 'adentrarEscopo') {
this.pilhaEscoposExecucao.removerUltimo();
const escopoAnterior = this.pilhaEscoposExecucao.topoDaPilha();
escopoAnterior.ambiente.resolucoesChamadas = Object.assign(escopoAnterior.ambiente.resolucoesChamadas, ultimoEscopo.ambiente.resolucoesChamadas);
if (manterAmbiente) {
escopoAnterior.ambiente.valores = Object.assign(escopoAnterior.ambiente.valores, ultimoEscopo.ambiente.valores);
}
this.escopoAtual--;
}
if (this.pilhaEscoposExecucao.elementos() === 1) {
this.finalizacaoDaExecucao();
}
}
}
/**
* Continua a interpretação, conforme comando do depurador.
* Quando um ponto de parada é ativado, a pilha de execução do TypeScript é perdida.
* Esse método cria uma nova pilha de execução do lado do JS, começando do último elemento executado do
* primeiro escopo, subindo até o último elemento executado do último escopo.
* Se entre escopos houver ponto de parada ativo, a execução é suspensa até o próximo comando
* do desenvolvedor.
* @see executarUltimoEscopo
*/
async instrucaoContinuarInterpretacao(escopo = 1) {
let retornoExecucao;
if (escopo < this.escopoAtual) {
retornoExecucao = await this.instrucaoContinuarInterpretacao(escopo + 1);
}
if (this.pontoDeParadaAtivo) {
return;
}
await this.executarUltimoEscopoComandoContinuar(false, true);
}
/**
* Empilha um escopo se for possível.
* Se não for, apenas executa a instrução corrente.
*/
async adentrarEscopo() {
throw new Error('Método não implementado.');
}
/**
* Interpreta apenas uma instrução a partir do ponto de parada ativo, conforme comando do depurador.
* Esse método cria uma nova pilha de execução do lado do JS, começando do último elemento executado do
* primeiro escopo, subindo até o último elemento executado do último escopo.
* @param escopo Indica o escopo a ser visitado. Usado para construir uma pilha de chamadas do lado JS.
*/
async instrucaoPasso(escopo = 1) {
this.passos = 1;
const escopoVisitado = this.pilhaEscoposExecucao.naPosicao(escopo);
if (escopo < this.escopoAtual) {
await this.instrucaoPasso(escopo + 1);
}
else {
if (escopoVisitado.declaracaoAtual >= escopoVisitado.declaracoes.length || escopoVisitado.finalizado) {
this.pilhaEscoposExecucao.removerUltimo();
}
if (this.pilhaEscoposExecucao.elementos() === 1) {
return this.finalizacaoDaExecucao();
}
await this.executarUmPassoNoEscopo();
}
}
/**
* Interpreta restante do bloco de execução em que o ponto de parada está, conforme comando do depurador.
* Se houver outros pontos de parada no mesmo escopo à frente da instrução atual, todos são ignorados.
* @param escopo Indica o escopo a ser visitado. Usado para construir uma pilha de chamadas do lado JS.
*/
async instrucaoProximoESair() {
this.executarUltimoEscopoComandoContinuar(false, true);
}
/**
* Prepara a pilha de escopos para uma situação de depuração.
* Não há execução de código neste caso.
* @param declaracoes Um vetor de declarações.
*/
prepararParaDepuracao(declaracoes) {
this.declaracoes = declaracoes;
this.abrirNovoBlocoEscopo(declaracoes);
}
abrirNovoBlocoEscopo(declaracoes, ambiente, tipoEscopo = 'outro') {
const escopoExecucao = {
declaracoes: declaracoes,
declaracaoAtual: 0,
ambiente: ambiente || new espaco_variaveis_1.EspacoVariaveis(),
finalizado: false,
tipo: tipoEscopo,
emLacoRepeticao: false,
};
this.pilhaEscoposExecucao.empilhar(escopoExecucao);
this.escopoAtual++;
}
/**
* Reimplementando este método aqui porque a execução por depuração não requer
* mostrar o resultado em momento algum, ou lidar com o retorno.
* @param declaracao A declaracao a ser executada.
* @param mostrarResultado Sempre falso.
* @returns O resultado da execução.
*/
async executar(declaracao, mostrarResultado = false) {
return await declaracao.aceitar(this);
}
/**
* Interpretação utilizada pelo depurador para avaliar valores de variáveis.
* Diferentemente da interpretação tradicional, não possui indicadores
* de performance porque eles não fazem sentido aqui.
* @param declaracoes Um vetor de declarações.
* @returns Um objeto de retorno, com erros encontrados se houverem.
*/
async interpretar(declaracoes, manterAmbiente = false) {
this.erros = [];
this.declaracoes = declaracoes;
this.abrirNovoBlocoEscopo(declaracoes);
const resultado = await super.executarUltimoEscopo(manterAmbiente);
// Corrigir contador de escopos
this.escopoAtual--;
const retorno = {
erros: this.erros,
// resultado: this.resultadoInterpretador // Removido para simplificar `this.executar()`.
resultado: [resultado],
};
this.resultadoInterpretador = [];
return retorno;
}
/**
* Obtém o valor de uma variável por nome.
* Em versões anteriores, o mecanismo de avaliação fazia toda a avaliação tradicional,
* passando por Lexador, Avaliador Sintático e Interpretador.
* Isso tem sua cota de problemas, sobretudo porque a avaliação insere e descarta escopos,
* entrando em condição de corrida com a interpretação com depuração.
* @param nome O nome da variável.
*/
obterVariavel(nome) {
const valorOuVariavel = this.pilhaEscoposExecucao.obterValorVariavel({ lexema: nome });
return valorOuVariavel.hasOwnProperty('valor')
? valorOuVariavel
: {
valor: valorOuVariavel,
tipo: (0, inferenciador_1.inferirTipoVariavel)(valorOuVariavel),
};
}
}
exports.InterpretadorComDepuracao = InterpretadorComDepuracao;
//# sourceMappingURL=interpretador-com-depuracao.js.map