liquido
Version:
Conjunto de ferramentas para desenvolvimento de aplicações para a internet 100% em português
244 lines • 12.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Resposta = void 0;
const lexador_1 = require("@designliquido/delegua/lexador");
const construtos_1 = require("@designliquido/delegua/construtos");
const declaracoes_1 = require("@designliquido/delegua/declaracoes");
const estruturas_1 = require("@designliquido/delegua/interpretador/estruturas");
const referencia_montao_1 = require("@designliquido/delegua/interpretador/estruturas/referencia-montao");
const gerador_expressoes_1 = require("./utilidades/gerador-expressoes");
/**
* Resolve profundamente um valor do montão durante a execução do interpretador,
* enquanto os escopos ainda estão ativos e as entradas do montão são válidas.
*
* Contexto: vetores e dicionários em Delégua são armazenados no montão como
* `ReferenciaMontao`. Quando o escopo de execução de rota termina, `executarBloco`
* chama `excluirReferencias` e remove essas entradas. Qualquer leitura posterior
* via `resolverValor` falha com "Referência para montão não existe".
*
* Esta função deve ser chamada DURANTE a execução (ex: dentro de `chamar`),
* antes do escopo ser desmontado.
*/
function resolverProfundo(visitante, objeto) {
if (objeto === null || objeto === undefined || typeof objeto !== 'object')
return objeto;
if (objeto instanceof referencia_montao_1.ReferenciaMontao) {
return resolverProfundo(visitante, visitante.resolverValor(objeto));
}
if (Array.isArray(objeto)) {
return objeto.map(el => resolverProfundo(visitante, el));
}
if ('propriedades' in objeto && objeto.propriedades) {
const resultado = {};
for (const [k, v] of Object.entries(objeto.propriedades)) {
resultado[k] = resolverProfundo(visitante, v);
}
return resultado;
}
if ('valor' in objeto && objeto.valor !== undefined) {
return resolverProfundo(visitante, objeto.valor);
}
return objeto;
}
/**
* Implementação nativa do método `json()` da classe `Resposta`.
*
* Não pode ser declarado como método Delégua comum (via `GeradorExpressoes`)
* porque um método declarativo apenas copia a `ReferenciaMontao` do argumento
* para `respostaJson`. Quando o escopo de execução de rota termina, o montão é limpo e
* a referência se torna inválida — Liquido não consegue mais ler o valor.
*
* Ao sobrescrever `chamar`, temos acesso ao `visitante` (interpretador) enquanto
* o escopo ainda está ativo. Chamamos `resolverProfundo` aqui para materializar
* todo o grafo de `ReferenciaMontao` em valores JS simples antes da limpeza.
*/
class MetodoJson extends estruturas_1.DeleguaFuncaoNativa {
constructor() {
super('json', 1, (_instancia, _args) => null);
this.declaracao = new construtos_1.FuncaoConstruto(-1, -1, [
{
abrangencia: 'padrao',
tipoDado: 'dicionário',
nome: new lexador_1.Simbolo('IDENTIFICADOR', 'json', null, -1, -1)
}
], []);
}
async chamar(visitante, argumentos) {
const argBruto = visitante.resolverValor(argumentos[0]);
const valorResolvido = resolverProfundo(visitante, argBruto);
const instancia = this.instancia;
if (instancia) {
instancia.propriedades['respostaJson'] = valorResolvido;
}
return instancia;
}
funcaoPorMetodoDeClasse(instancia) {
const copia = new MetodoJson();
copia.instancia = instancia;
return copia;
}
}
/**
* Implementação nativa do método `lmht()` da classe `Resposta`.
*
* Assim como `json()` (ver `MetodoJson` acima), não pode ser um método declarativo
* gerado por `GeradorExpressoes`: uma atribuição declarativa apenas copia a
* `ReferenciaMontao` do(s) argumento(s) para `isto.valores`. A renderização da visão
* LMHT (`logicaComumRespostaVisaoLmht`) acontece de forma assíncrona, depois que a
* função de rota já retornou e `executarBloco` já chamou `excluirReferencias` —
* nesse ponto a referência do montão já foi limpa, e a leitura tardia falha ou
* devolve um valor inválido (ver `resolverValores` em `FormatadorLmht`).
*
* Sobrescrever `chamar` dá acesso ao `visitante` enquanto o escopo da rota ainda
* está ativo, permitindo materializar `visao`/`valores` com `resolverProfundo`
* antes da limpeza do montão.
*/
class MetodoLmht extends estruturas_1.DeleguaFuncaoNativa {
constructor() {
super('lmht', 0, (_instancia, _args) => null);
this.declaracao = new construtos_1.FuncaoConstruto(-1, -1, [
{
abrangencia: 'multiplo',
tipoDado: 'vetor',
nome: new lexador_1.Simbolo('IDENTIFICADOR', 'visaoEValores', null, -1, -1)
}
], []);
}
async chamar(visitante, argumentos) {
const instancia = this.instancia;
if (instancia) {
instancia.propriedades['lmht'] = true;
if (argumentos.length > 1) {
const visaoBruta = visitante.resolverValor(argumentos[0]);
instancia.propriedades['visao'] = resolverProfundo(visitante, visaoBruta);
const valoresBrutos = visitante.resolverValor(argumentos[1]);
instancia.propriedades['valores'] = resolverProfundo(visitante, valoresBrutos);
}
else if (argumentos.length === 1) {
const valoresBrutos = visitante.resolverValor(argumentos[0]);
instancia.propriedades['valores'] = resolverProfundo(visitante, valoresBrutos);
}
// 0 argumentos: nenhuma atribuição; visão resolvida pela rota em logicaComumRespostaVisaoLmht
}
return instancia;
}
funcaoPorMetodoDeClasse(instancia) {
const copia = new MetodoLmht();
copia.instancia = instancia;
return copia;
}
}
/**
* Classe de resposta exposta às funções de rota em Delégua/Pituguês.
*
* Cada método define propriedades na instância e retorna `isto` para encadeamento.
* O framework lê essas propriedades em `processarPropriedadesResposta` para montar
* a resposta HTTP final.
*
* Funções de rota **não precisam** usar `retorna` explicitamente. Chamar qualquer
* método desta classe como efeito colateral é suficiente — o _framework_ recupera o
* objeto do escopo do interpretador quando o valor de retorno da função é nulo.
*
* Exemplo válido em Delégua:
* ```
* liquido.rotaGet(funcao (requisicao, resposta) {
* resposta.json([{ "id": 1 }])
* })
* ```
*
* Exemplo válido em Pituguês:
* ```
* funcao rotaGet(requisicao, resposta):
* resposta.json([{ "id": 1 }])
*
* liquido.rotaGet(rotaGet)
* ```
*
* Propriedades lidas pelo framework (em ordem de precedência):
* - `destino` → redirecionamento HTTP
* - `lmht` → renderização de visão LMHT
* - `respostaJson` → resposta JSON
* - `mensagem` → resposta em texto puro
* - `statusHttp` → código de status (padrão: 200)
*/
class Resposta extends estruturas_1.DescritorTipoClasse {
constructor() {
const metodos = {};
const propriedades = [
new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'destino', null, -1, -1), 'texto'),
new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'respostaJson', null, -1, -1), 'dicionário'),
new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'mensagem', null, -1, -1), 'texto'),
new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1), 'numero'),
new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'valores', null, -1, -1), 'dicionário'),
new declaracoes_1.PropriedadeClasse(new lexador_1.Simbolo('IDENTIFICADOR', 'visao', null, -1, -1), 'texto')
];
// Há duas formas de construção de métodos apenas usando
// estruturas de alto nível: declarando cada uma manualmente,
// ou usando o gerador de expressões.
// O exemplo abaixo utiliza o gerador de expressões.
// Seria o equivalente à seguinte implementação em Delégua:
// classe Resposta {
// // Outros método e propriedades aqui
// enviar(mensagem: texto) {
// isto.mensagem = mensagem
// retorna isto
// }
// }
const geradorExpressoes = new gerador_expressoes_1.GeradorExpressoes();
metodos['enviar'] = geradorExpressoes.gerarMetodo('enviar', geradorExpressoes.gerarConstrutoFuncao([geradorExpressoes.gerarParametro('mensagem', 'texto')], [
geradorExpressoes.gerarAtribuicaoValorEmPropriedadeClasse('mensagem', geradorExpressoes.gerarReferenciaVariavel('mensagem')),
geradorExpressoes.gerarRetornoDeFuncao('isto')
]));
// O exemplo abaixo gera um método `status` declarando manualmente
// todas as estruturas de alto nível de Delégua.
// Seria o equivalente à seguinte implementação:
// classe Resposta {
// // Outros método e propriedades aqui
// status(statusHttp: numero) {
// isto.statusHttp = statusHttp
// retorna isto
// }
// }
metodos['status'] = new estruturas_1.DeleguaFuncao('status', new construtos_1.FuncaoConstruto(-1, -1, [
{
abrangencia: 'padrao',
tipoDado: 'número',
nome: new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1)
}
], [
new declaracoes_1.Expressao(new construtos_1.DefinirValor(-1, -1, new construtos_1.Isto(-1, -1, new lexador_1.Simbolo('ISTO', 'isto', null, -1, -1)), new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1), new construtos_1.Variavel(-1, new lexador_1.Simbolo('IDENTIFICADOR', 'statusHttp', null, -1, -1)))),
new declaracoes_1.Retorna(new lexador_1.Simbolo('IDENTIFICADOR', 'qualquerCoisa', null, -1, -1), new construtos_1.Variavel(-1, new lexador_1.Simbolo('IDENTIFICADOR', 'isto', null, -1, -1)))
]), null, false);
// Assim como `json`, `lmht` precisa sobrescrever `chamar` para acessar o interpretador
// e materializar os valores antes da limpeza do montão.
// Ler motivos mais detalhados na documentação de `MetodoLmht` deste fonte.
// Seria o equivalente à seguinte implementação declarativa (que tem o bug descrito):
// classe Resposta {
// visao: texto
// valores: dicionário
//
// lmht(...visaoEValores) {
// se visaoEValores.tamanho() > 1 {
// isto.visao = visaoEValores[0]
// isto.valores = visaoEValores[1]
// } senão se visaoEValores.tamanho() == 1 {
// isto.valores = visaoEValores[0]
// }
// // 0 argumentos: visao resolvida automaticamente pela rota
//
// retorna isto
// }
// }
metodos['lmht'] = new MetodoLmht();
metodos['redirecionar'] = geradorExpressoes.gerarMetodo('redirecionar', geradorExpressoes.gerarConstrutoFuncao([geradorExpressoes.gerarParametro('destino', 'texto')], [
geradorExpressoes.gerarAtribuicaoValorEmPropriedadeClasse('destino', geradorExpressoes.gerarReferenciaVariavel('destino')),
geradorExpressoes.gerarRetornoDeFuncao('isto')
]));
// Há também esta forma de declaração, necessária quando o método precisa sobrescrever `chamar` para acessar o interpretador.
// Ler motivos mais detalhados na documentação de `MetodoJson` deste fonte.
metodos['json'] = new MetodoJson();
super(new lexador_1.Simbolo('IDENTIFICADOR', 'Resposta', null, -1, -1), undefined, metodos, propriedades);
}
}
exports.Resposta = Resposta;
//# sourceMappingURL=resposta.js.map