@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
901 lines (896 loc) • 41.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TradutorWebAssembly = void 0;
const construtos_1 = require("../construtos");
const declaracoes_1 = require("../declaracoes");
class TradutorWebAssembly {
constructor() {
// ── Seções do módulo WAT ───────────────────────────────────────────────
/** Segmentos de dados estáticos para strings literais */
this.segmentosTexto = [];
/** Próximo byte livre na memória linear para strings */
this.deslocamentoTexto = 0;
/** Texto WAT completo de cada função declarada pelo usuário */
this.funcoesCompletas = [];
/** Declarações WAT de variáveis globais `(global ...)` */
this.declaracoesGlobais = [];
// ── Contexto da função sendo traduzida no momento ──────────────────────
/** Instruções WAT emitidas no corpo da função corrente */
this.corpoDaFuncaoAtual = '';
/** Declarações `(param ...)` e `(local ...)` da função corrente */
this.declaracoesLocaisAtual = '';
/** Nomes de locais/params já declarados (evita duplicatas) */
this.locaisDeclaradosAtual = new Set();
/** Estamos dentro do corpo de uma função nomeada? */
this.dentroFuncao = false;
/** A função corrente possui ao menos um `retorna` com valor? */
this.funcaoTemRetorno = false;
// ── Mapa de variáveis ─────────────────────────────────────────────────
/** Nome Delégua → metadados WAT */
this.variaveis = new Map();
// ── Controle de fluxo (break/continue) ───────────────────────────────
/** Pilha de rótulos de laço para `sustar`/`continua` */
this.pilhaDeControle = [];
// ── Geração de rótulos ────────────────────────────────────────────────
this.contadorRotulos = 0;
// =========================================================================
// Dicionário de construtos (expressões)
// =========================================================================
this.dicionarioConstrutos = {
AcessoIndiceVariavel: this.traduzirAcessoIndiceVariavel.bind(this),
AcessoMetodoOuPropriedade: this.traduzirAcessoMetodo.bind(this),
Agrupamento: this.traduzirAgrupamento.bind(this),
AtribuicaoPorIndice: this.traduzirAtribuicaoPorIndice.bind(this),
Atribuir: this.traduzirAtribuir.bind(this),
Binario: this.traduzirBinario.bind(this),
Chamada: this.traduzirChamada.bind(this),
DefinirValor: this.traduzirDefinirValor.bind(this),
FuncaoConstruto: this.traduzirFuncaoConstruto.bind(this),
Literal: this.traduzirLiteral.bind(this),
Logico: this.traduzirLogico.bind(this),
TipoDe: this.traduzirTipoDe.bind(this),
Unario: this.traduzirUnario.bind(this),
Variavel: this.traduzirVariavel.bind(this),
Vetor: this.traduzirVetor.bind(this),
};
// =========================================================================
// Dicionário de declarações (instruções)
// =========================================================================
this.dicionarioDeclaracoes = {
Bloco: this.traduzirBloco.bind(this),
Classe: this.traduzirClasse.bind(this),
Const: this.traduzirConst.bind(this),
Continua: this.traduzirContinua.bind(this),
Enquanto: this.traduzirEnquanto.bind(this),
Escolha: this.traduzirEscolha.bind(this),
Escreva: this.traduzirEscreva.bind(this),
EscrevaMesmaLinha: this.traduzirEscreva.bind(this),
Expressao: this.traduzirExpressao.bind(this),
Falhar: this.traduzirFalhar.bind(this),
Fazer: this.traduzirFazer.bind(this),
FuncaoDeclaracao: this.traduzirFuncaoDeclaracao.bind(this),
Importar: this.traduzirImportar.bind(this),
Para: this.traduzirPara.bind(this),
ParaCada: this.traduzirParaCada.bind(this),
Retorna: this.traduzirRetorna.bind(this),
Se: this.traduzirSe.bind(this),
Sustar: this.traduzirSustar.bind(this),
Tente: this.traduzirTente.bind(this),
Var: this.traduzirVar.bind(this),
};
}
// =========================================================================
// Helpers internos
// =========================================================================
gerarRotulo() {
return `$L${this.contadorRotulos++}`;
}
/** Emite uma instrução indentada no corpo da função corrente. */
emitir(instrucao) {
this.corpoDaFuncaoAtual += ` ${instrucao}\n`;
}
/**
* Converte uma string JavaScript para bytes UTF-8.
* Usa `encodeURIComponent` para extrair os bytes — funciona em qualquer ambiente JS
* sem depender de APIs específicas de Node.js (Buffer) ou browser (TextEncoder).
*/
stringParaBytesUtf8(valor) {
const encoded = encodeURIComponent(valor);
const bytes = [];
for (let i = 0; i < encoded.length;) {
if (encoded[i] === '%') {
bytes.push(parseInt(encoded.slice(i + 1, i + 3), 16));
i += 3;
}
else {
bytes.push(encoded.charCodeAt(i));
i++;
}
}
return bytes;
}
/**
* Converte uma string JavaScript para o formato de literal de string WAT.
* Bytes não-ASCII e caracteres especiais são hex-escapados como `\xx`.
* Retorna o literal pronto para uso em `(data ...)` e o comprimento em bytes UTF-8.
*/
escaparStringWat(valor) {
const bytes = this.stringParaBytesUtf8(valor);
let watLiteral = '';
for (const byte of bytes) {
if (byte === 0x22) {
watLiteral += '\\"'; // aspas duplas
}
else if (byte === 0x5c) {
watLiteral += '\\\\'; // contrabarra
}
else if (byte >= 0x20 && byte <= 0x7e) {
watLiteral += String.fromCharCode(byte); // ASCII imprimível
}
else {
watLiteral += `\\${byte.toString(16).padStart(2, '0')}`; // \xx
}
}
return { watLiteral, byteLen: bytes.length };
}
/**
* Internaliza uma string literal na memória linear estática.
* Strings idênticas são deduplicadas.
* O comprimento armazenado é o comprimento em bytes UTF-8, não em caracteres.
*/
internalizarTexto(valor) {
const existente = this.segmentosTexto.find((s) => s.conteudo === valor);
if (existente) {
return { offset: existente.deslocamento, len: existente.tamanho };
}
const { byteLen } = this.escaparStringWat(valor);
const offset = this.deslocamentoTexto;
this.segmentosTexto.push({ deslocamento: offset, conteudo: valor, tamanho: byteLen });
this.deslocamentoTexto += byteLen;
return { offset, len: byteLen };
}
/**
* Infere o tipo WAT de um construto.
* Strings → i32 (ponteiro). Tudo mais → i64 (padrão, inclusive booleanos).
*/
inferirTipo(construto) {
if (construto instanceof construtos_1.Literal && typeof construto.valor === 'string')
return 'i32';
return 'i64';
}
/**
* Declara um parâmetro ou variável local para a função corrente.
* `ehParam = true` emite `(param ...)`, senão `(local ...)`.
*/
declararParamLocal(nome, tipo, ehParam) {
if (this.locaisDeclaradosAtual.has(nome))
return;
this.locaisDeclaradosAtual.add(nome);
const watNome = `$${nome}`;
const diretiva = ehParam ? 'param' : 'local';
this.declaracoesLocaisAtual += ` (${diretiva} ${watNome} ${tipo})`;
this.variaveis.set(nome, { watNome, tipo, escopo: 'local' });
}
/** Salva o contexto da função corrente e inicializa um novo. */
salvarEIniciarContextoFuncao() {
const snapshot = {
corpoDaFuncaoAtual: this.corpoDaFuncaoAtual,
declaracoesLocaisAtual: this.declaracoesLocaisAtual,
locaisDeclaradosAtual: this.locaisDeclaradosAtual,
dentroFuncao: this.dentroFuncao,
funcaoTemRetorno: this.funcaoTemRetorno,
variaveis: new Map(this.variaveis),
};
this.corpoDaFuncaoAtual = '';
this.declaracoesLocaisAtual = '';
this.locaisDeclaradosAtual = new Set();
this.dentroFuncao = true;
this.funcaoTemRetorno = false;
// Remove locais do mapa — globais permanecem
for (const [nome, meta] of this.variaveis) {
if (meta.escopo === 'local')
this.variaveis.delete(nome);
}
return snapshot;
}
/** Restaura o contexto de função a partir de um snapshot. */
restaurarContextoFuncao(snapshot) {
this.corpoDaFuncaoAtual = snapshot.corpoDaFuncaoAtual;
this.declaracoesLocaisAtual = snapshot.declaracoesLocaisAtual;
this.locaisDeclaradosAtual = snapshot.locaisDeclaradosAtual;
this.dentroFuncao = snapshot.dentroFuncao;
this.funcaoTemRetorno = snapshot.funcaoTemRetorno;
this.variaveis = snapshot.variaveis;
}
/**
* Garante que uma variável existe no escopo correto.
* Retorna o nome WAT da variável (e.g. `$x`).
*/
garantirVariavel(nome, tipo = 'i64') {
if (this.variaveis.has(nome)) {
return this.variaveis.get(nome).watNome;
}
if (this.dentroFuncao) {
this.declararParamLocal(nome, tipo, false);
return `$${nome}`;
}
// Global
const watNome = `$${nome}`;
this.declaracoesGlobais.push(` (global ${watNome} (mut ${tipo}) (${tipo}.const 0))`);
this.variaveis.set(nome, { watNome, tipo, escopo: 'global' });
return watNome;
}
// =========================================================================
// Dispatch helpers
// =========================================================================
traduzirConstruto(construto) {
const handler = this.dicionarioConstrutos[construto.constructor.name];
if (handler)
return handler(construto);
return `(i64.const 0) ;; construto não suportado: ${construto.constructor.name}`;
}
traduzirDeclaracaoInterna(declaracao) {
const handler = this.dicionarioDeclaracoes[declaracao.constructor.name];
if (handler)
handler(declaracao);
else
this.emitir(`;; declaração não suportada: ${declaracao.constructor.name}`);
}
// =========================================================================
// Construtos
// =========================================================================
traduzirLiteral(construto) {
if (typeof construto.valor === 'boolean') {
return `(i64.const ${construto.valor ? 1 : 0})`;
}
if (typeof construto.valor === 'string') {
// Retorna apenas o ponteiro; `escreva` usa internalizarTexto diretamente
// para obter o comprimento também.
const { offset } = this.internalizarTexto(construto.valor);
return `(i32.const ${offset})`;
}
if (typeof construto.valor === 'bigint') {
return `(i64.const ${construto.valor})`;
}
// Número: trunca para i64
return `(i64.const ${Math.trunc(Number(construto.valor))})`;
}
traduzirVariavel(construto) {
var _a;
const nome = (_a = construto.simbolo) === null || _a === void 0 ? void 0 : _a.lexema;
if (!nome)
return '(i64.const 0)';
const meta = this.variaveis.get(nome);
if (!meta)
return `(i64.const 0) ;; variável desconhecida: ${nome}`;
const instrucao = meta.escopo === 'local' ? 'local.get' : 'global.get';
return `(${instrucao} ${meta.watNome})`;
}
traduzirAtribuir(construto) {
var _a;
let nome;
if (construto.alvo instanceof construtos_1.Variavel) {
nome = (_a = construto.alvo.simbolo) === null || _a === void 0 ? void 0 : _a.lexema;
}
if (!nome)
return '(i64.const 0)';
const tipoInferido = this.inferirTipo(construto.valor);
const watNome = this.garantirVariavel(nome, tipoInferido);
const valor = this.traduzirConstruto(construto.valor);
const meta = this.variaveis.get(nome);
const instrucao = meta.escopo === 'local' ? 'local.set' : 'global.set';
return `(${instrucao} ${watNome} ${valor})`;
}
traduzirBinario(construto) {
const esq = this.traduzirConstruto(construto.esquerda);
const dir = this.traduzirConstruto(construto.direita);
const op = construto.operador.lexema;
switch (op) {
case '+':
return `(i64.add ${esq} ${dir})`;
case '-':
return `(i64.sub ${esq} ${dir})`;
case '*':
return `(i64.mul ${esq} ${dir})`;
case '/':
return `(i64.div_s ${esq} ${dir})`;
case '%':
return `(i64.rem_s ${esq} ${dir})`;
case '<':
return `(i64.extend_i32_s (i64.lt_s ${esq} ${dir}))`;
case '>':
return `(i64.extend_i32_s (i64.gt_s ${esq} ${dir}))`;
case '<=':
return `(i64.extend_i32_s (i64.le_s ${esq} ${dir}))`;
case '>=':
return `(i64.extend_i32_s (i64.ge_s ${esq} ${dir}))`;
case '==':
case '===':
return `(i64.extend_i32_s (i64.eq ${esq} ${dir}))`;
case '!=':
case '!==':
return `(i64.extend_i32_s (i64.ne ${esq} ${dir}))`;
default:
return `(i64.const 0) ;; operador não suportado: ${op}`;
}
}
traduzirLogico(construto) {
const esq = this.traduzirConstruto(construto.esquerda);
const dir = this.traduzirConstruto(construto.direita);
const op = construto.operador.lexema;
// Ambos os lados avaliados (sem curto-circuito na v1).
// Converte i64 → i32 (ne 0), aplica i32.and / i32.or, estende de volta.
const esqBool = `(i32.ne (i32.const 0) (i32.wrap_i64 ${esq}))`;
const dirBool = `(i32.ne (i32.const 0) (i32.wrap_i64 ${dir}))`;
if (op === 'e' || op === '&&') {
return `(i64.extend_i32_s (i32.and ${esqBool} ${dirBool}))`;
}
if (op === 'ou' || op === '||') {
return `(i64.extend_i32_s (i32.or ${esqBool} ${dirBool}))`;
}
return `(i64.const 0) ;; operador lógico não suportado: ${op}`;
}
traduzirUnario(construto) {
var _a;
const op = construto.operador.lexema;
const operando = this.traduzirConstruto(construto.operando);
if (op === '-') {
return `(i64.sub (i64.const 0) ${operando})`;
}
if (op === '!' || op === 'nao') {
return `(i64.extend_i32_s (i64.eqz ${operando}))`;
}
if (op === '++' || op === '--') {
// Apenas pré-incremento/decremento de variável simples na v1
let nome;
if (construto.operando instanceof construtos_1.Variavel) {
nome = (_a = construto.operando.simbolo) === null || _a === void 0 ? void 0 : _a.lexema;
}
if (!nome)
return operando;
const meta = this.variaveis.get(nome);
if (!meta)
return operando;
const instrSet = meta.escopo === 'local' ? 'local.set' : 'global.set';
const instrGet = meta.escopo === 'local' ? 'local.get' : 'global.get';
const delta = op === '++' ? 1 : -1;
const novoValor = `(i64.add ${operando} (i64.const ${delta}))`;
// Retorna uma sequência: set + get (usando local.tee para locais, ou set;get para globais)
if (meta.escopo === 'local') {
return `(local.tee ${meta.watNome} ${novoValor})`;
}
// Para globais não existe tee; emitimos o set e retornamos o get
this.emitir(`(${instrSet} ${meta.watNome} ${novoValor})`);
return `(${instrGet} ${meta.watNome})`;
}
return operando;
}
traduzirAgrupamento(construto) {
return this.traduzirConstruto(construto.expressao);
}
traduzirChamada(construto) {
var _a;
let nomeFuncao = 'desconhecida';
if (construto.entidadeChamada instanceof construtos_1.Variavel) {
nomeFuncao = ((_a = construto.entidadeChamada.simbolo) === null || _a === void 0 ? void 0 : _a.lexema) || 'desconhecida';
}
const args = construto.argumentos
.map((arg) => this.traduzirConstruto(arg))
.join(' ');
return `(call $${nomeFuncao}${args ? ' ' + args : ''})`;
}
traduzirAcessoIndiceVariavel(construto) {
var _a;
let baseExpr = '(i32.const 0)';
if (construto.entidadeChamada instanceof construtos_1.Variavel) {
const nome = (_a = construto.entidadeChamada.simbolo) === null || _a === void 0 ? void 0 : _a.lexema;
const meta = nome ? this.variaveis.get(nome) : undefined;
if (meta) {
const instrGet = meta.escopo === 'local' ? 'local.get' : 'global.get';
baseExpr = `(${instrGet} ${meta.watNome})`;
}
}
const indice = this.traduzirConstruto(construto.indice);
// base + indice * 8 (cada elemento i64 ocupa 8 bytes)
const offset = `(i32.add ${baseExpr} (i32.wrap_i64 (i64.mul ${indice} (i64.const 8))))`;
return `(i64.load ${offset})`;
}
traduzirAtribuicaoPorIndice(construto) {
var _a;
let baseExpr = '(i32.const 0)';
if (construto.objeto instanceof construtos_1.Variavel) {
const nome = (_a = construto.objeto.simbolo) === null || _a === void 0 ? void 0 : _a.lexema;
const meta = nome ? this.variaveis.get(nome) : undefined;
if (meta) {
const instrGet = meta.escopo === 'local' ? 'local.get' : 'global.get';
baseExpr = `(${instrGet} ${meta.watNome})`;
}
}
const indice = this.traduzirConstruto(construto.indice);
const valor = this.traduzirConstruto(construto.valor);
const offset = `(i32.add ${baseExpr} (i32.wrap_i64 (i64.mul ${indice} (i64.const 8))))`;
return `(i64.store ${offset} ${valor})`;
}
traduzirVetor(construto) {
var _a, _b;
// Aloca espaço na memória linear para N × 8 bytes.
// Os valores são emitidos como instruções de store na função corrente.
const n = (_b = (_a = construto.valores) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
const offset = this.deslocamentoTexto;
// Reserva n * 8 bytes (sem conteúdo estático; preenchido em tempo de execução)
this.deslocamentoTexto += n * 8;
if (construto.valores) {
for (let i = 0; i < n; i++) {
const valor = this.traduzirConstruto(construto.valores[i]);
const enderecoElem = `(i32.const ${offset + i * 8})`;
this.emitir(`(i64.store ${enderecoElem} ${valor})`);
}
}
return `(i32.const ${offset})`;
}
traduzirDefinirValor(construto) {
// Acesso a propriedade de objeto — simplificado na v1
const valor = this.traduzirConstruto(construto.valor);
return `${valor} ;; definir valor não totalmente suportado na v1`;
}
traduzirFuncaoConstruto(construto) {
var _a;
// Função anônima: emite como função nomeada com rótulo gerado
const rotulo = `__anonima_${this.contadorRotulos++}`;
const snapshot = this.salvarEIniciarContextoFuncao();
for (const decl of (_a = construto.corpo) !== null && _a !== void 0 ? _a : []) {
this.traduzirDeclaracaoInterna(decl);
}
this.emitir('(i64.const 0)');
const funcWat = ` (func $${rotulo}${this.declaracoesLocaisAtual} (result i64)\n` +
this.corpoDaFuncaoAtual +
` )`;
this.funcoesCompletas.push(funcWat);
this.restaurarContextoFuncao(snapshot);
return `(i32.const 0) ;; referência à função anônima $${rotulo}`;
}
traduzirAcessoMetodo(_construto) {
return '(i64.const 0) ;; acesso a método não suportado na v1';
}
traduzirTipoDe(construto) {
return this.traduzirConstruto(construto.valor);
}
// =========================================================================
// Declarações
// =========================================================================
traduzirVar(declaracao) {
var _a;
const nome = (_a = declaracao.simbolo) === null || _a === void 0 ? void 0 : _a.lexema;
if (!nome)
return;
if (declaracao.inicializador instanceof construtos_1.Vetor) {
// Vetor: aloca em memória e guarda o ponteiro i32 na variável
if (this.dentroFuncao) {
this.declararParamLocal(nome, 'i32', false);
}
else {
this.declaracoesGlobais.push(` (global $${nome} (mut i32) (i32.const 0))`);
this.variaveis.set(nome, { watNome: `$${nome}`, tipo: 'i32', escopo: 'global' });
}
const ponteiro = this.traduzirVetor(declaracao.inicializador);
const meta = this.variaveis.get(nome);
const instrSet = meta.escopo === 'local' ? 'local.set' : 'global.set';
this.emitir(`(${instrSet} $${nome} ${ponteiro})`);
return;
}
const tipo = declaracao.inicializador ? this.inferirTipo(declaracao.inicializador) : 'i64';
if (this.dentroFuncao) {
this.declararParamLocal(nome, tipo, false);
if (declaracao.inicializador) {
const valor = this.traduzirConstruto(declaracao.inicializador);
this.emitir(`(local.set $${nome} ${valor})`);
}
}
else {
// Globais WAT só aceitam const-exprs como inicializadores.
// Literais → inicializador imediato; expressões complexas → init 0 + global.set em $principal.
if (declaracao.inicializador instanceof construtos_1.Literal) {
const init = this.traduzirGlobalInit(declaracao.inicializador, tipo);
this.declaracoesGlobais.push(` (global $${nome} (mut ${tipo}) ${init})`);
this.variaveis.set(nome, { watNome: `$${nome}`, tipo, escopo: 'global' });
}
else {
this.declaracoesGlobais.push(` (global $${nome} (mut ${tipo}) (${tipo}.const 0))`);
this.variaveis.set(nome, { watNome: `$${nome}`, tipo, escopo: 'global' });
if (declaracao.inicializador) {
const valor = this.traduzirConstruto(declaracao.inicializador);
this.emitir(`(global.set $${nome} ${valor})`);
}
}
}
}
traduzirConst(declaracao) {
var _a;
const nome = (_a = declaracao.simbolo) === null || _a === void 0 ? void 0 : _a.lexema;
if (!nome)
return;
const tipo = this.inferirTipo(declaracao.inicializador);
if (this.dentroFuncao) {
// Dentro de função: trata como local (sem imutabilidade real em WAT)
this.declararParamLocal(nome, tipo, false);
const valor = this.traduzirConstruto(declaracao.inicializador);
this.emitir(`(local.set $${nome} ${valor})`);
}
else {
// Constante global: sem `mut`; apenas literais são válidos como init
if (declaracao.inicializador instanceof construtos_1.Literal) {
const init = this.traduzirGlobalInit(declaracao.inicializador, tipo);
this.declaracoesGlobais.push(` (global $${nome} ${tipo} ${init})`);
}
else {
// Expressão complexa: global imutável não pode ser sobrescrita em WAT.
// Tratamos como mutável para permitir inicialização tardia.
this.declaracoesGlobais.push(` (global $${nome} (mut ${tipo}) (${tipo}.const 0))`);
const valor = this.traduzirConstruto(declaracao.inicializador);
this.emitir(`(global.set $${nome} ${valor})`);
}
this.variaveis.set(nome, { watNome: `$${nome}`, tipo, escopo: 'global' });
}
}
/**
* Traduz um literal para um WAT const-expr válido como inicializador de global.
* Apenas literais são aceitos como inicializadores de globais em WAT.
*/
traduzirGlobalInit(construto, tipo) {
if (typeof construto.valor === 'boolean')
return `(i64.const ${construto.valor ? 1 : 0})`;
if (typeof construto.valor === 'string') {
const { offset } = this.internalizarTexto(construto.valor);
return `(i32.const ${offset})`;
}
if (typeof construto.valor === 'bigint')
return `(i64.const ${construto.valor})`;
return `(${tipo}.const ${Math.trunc(Number(construto.valor))})`;
}
traduzirSe(declaracao) {
const condicao = this.traduzirConstruto(declaracao.condicao);
this.emitir(`(if`);
this.emitir(` (i32.wrap_i64 ${condicao})`);
this.emitir(` (then`);
if (this.dicionarioDeclaracoes[declaracao.caminhoEntao.constructor.name]) {
const corpoPrevio = this.corpoDaFuncaoAtual;
this.corpoDaFuncaoAtual = '';
this.traduzirDeclaracaoInterna(declaracao.caminhoEntao);
const corpoEntao = this.corpoDaFuncaoAtual
.split('\n')
.map((l) => ` ${l}`)
.join('\n');
this.corpoDaFuncaoAtual = corpoPrevio + corpoEntao;
}
this.emitir(` )`);
if (declaracao.caminhoSenao) {
this.emitir(` (else`);
const corpoPrevio = this.corpoDaFuncaoAtual;
this.corpoDaFuncaoAtual = '';
this.traduzirDeclaracaoInterna(declaracao.caminhoSenao);
const corpoSenao = this.corpoDaFuncaoAtual
.split('\n')
.map((l) => ` ${l}`)
.join('\n');
this.corpoDaFuncaoAtual = corpoPrevio + corpoSenao;
this.emitir(` )`);
}
this.emitir(`)`);
}
traduzirEnquanto(declaracao) {
const labelSaida = this.gerarRotulo();
const labelLaco = this.gerarRotulo();
this.pilhaDeControle.push({ labelSaida, labelLaco });
const condicao = this.traduzirConstruto(declaracao.condicao);
this.emitir(`(block ${labelSaida}`);
this.emitir(` (loop ${labelLaco}`);
this.emitir(` (br_if ${labelSaida}`);
this.emitir(` (i32.eqz (i32.wrap_i64 ${condicao}))`);
this.emitir(` )`);
this.traduzirDeclaracaoInterna(declaracao.corpo);
this.emitir(` (br ${labelLaco})`);
this.emitir(` )`);
this.emitir(`)`);
this.pilhaDeControle.pop();
}
traduzirPara(declaracao) {
// Inicializador
if (declaracao.inicializador) {
if (Array.isArray(declaracao.inicializador)) {
for (const decl of declaracao.inicializador) {
this.traduzirDeclaracaoInterna(decl);
}
}
else {
this.traduzirDeclaracaoInterna(declaracao.inicializador);
}
}
const labelSaida = this.gerarRotulo();
const labelLaco = this.gerarRotulo();
this.pilhaDeControle.push({ labelSaida, labelLaco });
const condicao = declaracao.condicao
? this.traduzirConstruto(declaracao.condicao)
: '(i64.const 1)';
this.emitir(`(block ${labelSaida}`);
this.emitir(` (loop ${labelLaco}`);
this.emitir(` (br_if ${labelSaida}`);
this.emitir(` (i32.eqz (i32.wrap_i64 ${condicao}))`);
this.emitir(` )`);
if (declaracao.corpo) {
this.traduzirDeclaracaoInterna(declaracao.corpo);
}
if (declaracao.incrementar) {
const inc = this.traduzirConstruto(declaracao.incrementar);
this.emitir(`(drop ${inc})`);
}
this.emitir(` (br ${labelLaco})`);
this.emitir(` )`);
this.emitir(`)`);
this.pilhaDeControle.pop();
}
traduzirFazer(declaracao) {
var _a;
const labelSaida = this.gerarRotulo();
const labelLaco = this.gerarRotulo();
this.pilhaDeControle.push({ labelSaida, labelLaco });
this.emitir(`(block ${labelSaida}`);
this.emitir(` (loop ${labelLaco}`);
if ((_a = declaracao.caminhoFazer) === null || _a === void 0 ? void 0 : _a.declaracoes) {
for (const decl of declaracao.caminhoFazer.declaracoes) {
this.traduzirDeclaracaoInterna(decl);
}
}
if (declaracao.condicaoEnquanto) {
const condicao = this.traduzirConstruto(declaracao.condicaoEnquanto);
// Repete se a condição for verdadeira
this.emitir(` (br_if ${labelLaco}`);
this.emitir(` (i32.wrap_i64 ${condicao})`);
this.emitir(` )`);
}
this.emitir(` )`);
this.emitir(`)`);
this.pilhaDeControle.pop();
}
traduzirEscolha(declaracao) {
const rotuloEscolha = `__escolha_${this.contadorRotulos++}`;
const tipo = 'i64';
// Declara local temporário para o valor do escolha
this.declararParamLocal(rotuloEscolha, tipo, false);
const sujeito = this.traduzirConstruto(declaracao.identificadorOuLiteral);
this.emitir(`(local.set $${rotuloEscolha} ${sujeito})`);
// Gera cadeia de if/else aninhados
this.emitirCadeiaEscolha(rotuloEscolha, declaracao.caminhos, declaracao.caminhoPadrao);
}
emitirCadeiaEscolha(rotuloEscolha, caminhos, caminhoPadrao, indice = 0) {
var _a, _b;
if (indice >= caminhos.length) {
// Emite o caminho padrão (senao) se existir
if (caminhoPadrao) {
for (const decl of (_a = caminhoPadrao.declaracoes) !== null && _a !== void 0 ? _a : []) {
this.traduzirDeclaracaoInterna(decl);
}
}
return;
}
const caminho = caminhos[indice];
if (!caminho.condicoes || caminho.condicoes.length === 0) {
this.emitirCadeiaEscolha(rotuloEscolha, caminhos, caminhoPadrao, indice + 1);
return;
}
// Condição: qualquer das condicoes casa com o valor do escolha
const primeiraCond = `(i64.eq (local.get $${rotuloEscolha}) ${this.traduzirConstruto(caminho.condicoes[0])})`;
let condicaoFinal = primeiraCond;
for (let i = 1; i < caminho.condicoes.length; i++) {
const outraCond = `(i64.eq (local.get $${rotuloEscolha}) ${this.traduzirConstruto(caminho.condicoes[i])})`;
condicaoFinal = `(i32.or ${condicaoFinal} ${outraCond})`;
}
this.emitir(`(if`);
this.emitir(` ${condicaoFinal}`);
this.emitir(` (then`);
for (const decl of (_b = caminho.declaracoes) !== null && _b !== void 0 ? _b : []) {
this.traduzirDeclaracaoInterna(decl);
}
this.emitir(` )`);
const temProximo = indice + 1 < caminhos.length || caminhoPadrao != null;
if (temProximo) {
this.emitir(` (else`);
this.emitirCadeiaEscolha(rotuloEscolha, caminhos, caminhoPadrao, indice + 1);
this.emitir(` )`);
}
this.emitir(`)`);
}
traduzirBloco(declaracao) {
var _a;
for (const decl of (_a = declaracao.declaracoes) !== null && _a !== void 0 ? _a : []) {
this.traduzirDeclaracaoInterna(decl);
}
}
traduzirExpressao(declaracao) {
if (!declaracao.expressao)
return;
const expr = this.traduzirConstruto(declaracao.expressao);
// Atribuições e stores não deixam valor na pilha; outros sim → drop
const deixaValor = !(declaracao.expressao instanceof construtos_1.Atribuir) &&
!(declaracao.expressao instanceof construtos_1.AtribuicaoPorIndice);
if (deixaValor) {
this.emitir(`(drop ${expr})`);
}
else {
this.emitir(expr);
}
}
traduzirEscreva(declaracao) {
for (const arg of declaracao.argumentos) {
if (arg instanceof construtos_1.Literal && typeof arg.valor === 'string') {
const { offset, len } = this.internalizarTexto(arg.valor);
this.emitir(`(call $__escreva_texto (i32.const ${offset}) (i32.const ${len}))`);
}
else {
const expr = this.traduzirConstruto(arg);
this.emitir(`(call $__escreva_inteiro ${expr})`);
}
}
}
traduzirRetorna(declaracao) {
this.funcaoTemRetorno = true;
if (declaracao.valor) {
const valor = this.traduzirConstruto(declaracao.valor);
this.emitir(`(return ${valor})`);
}
else {
this.emitir(`(return (i64.const 0))`);
}
}
traduzirSustar(_declaracao) {
const topo = this.pilhaDeControle[this.pilhaDeControle.length - 1];
if (topo) {
this.emitir(`(br ${topo.labelSaida})`);
}
}
traduzirContinua(_declaracao) {
const topo = this.pilhaDeControle[this.pilhaDeControle.length - 1];
if (topo) {
this.emitir(`(br ${topo.labelLaco})`);
}
}
traduzirFuncaoDeclaracao(declaracao) {
var _a, _b, _c, _d, _e, _f;
const nome = (_b = (_a = declaracao.simbolo) === null || _a === void 0 ? void 0 : _a.lexema) !== null && _b !== void 0 ? _b : `__func_${this.contadorRotulos++}`;
const snapshot = this.salvarEIniciarContextoFuncao();
// Declara parâmetros
for (const param of (_d = (_c = declaracao.funcao) === null || _c === void 0 ? void 0 : _c.parametros) !== null && _d !== void 0 ? _d : []) {
this.declararParamLocal(param.nome.lexema, 'i64', true);
}
// Traduz corpo
for (const decl of (_f = (_e = declaracao.funcao) === null || _e === void 0 ? void 0 : _e.corpo) !== null && _f !== void 0 ? _f : []) {
this.traduzirDeclaracaoInterna(decl);
}
// Retorno implícito 0 (garante stack completo)
this.emitir('(i64.const 0)');
const ehPrincipal = nome === 'principal';
const exportar = ehPrincipal ? ' (export "principal")' : '';
const funcWat = ` (func $${nome}${exportar}${this.declaracoesLocaisAtual} (result i64)\n` +
this.corpoDaFuncaoAtual +
` )`;
this.funcoesCompletas.push(funcWat);
this.restaurarContextoFuncao(snapshot);
}
traduzirFalhar(declaracao) {
// `unreachable` = trap incondicional em WebAssembly
let msg = '';
if (declaracao.explicacao) {
try {
msg = ` ;; ${this.traduzirConstruto(declaracao.explicacao)}`;
}
catch (_a) {
// ignore
}
}
this.emitir(`(unreachable)${msg}`);
}
traduzirImportar(_declaracao) {
this.emitir(`;; importar não suportado em WebAssembly nativo`);
}
traduzirTente(declaracao) {
var _a;
this.emitir(`;; tente/pegue: tratamento de exceções é v2`);
for (const decl of (_a = declaracao.caminhoTente) !== null && _a !== void 0 ? _a : []) {
this.traduzirDeclaracaoInterna(decl);
}
}
traduzirClasse(_declaracao) {
this.emitir(`;; classe: não suportada na v1`);
}
traduzirParaCada(declaracao) {
this.emitir(`;; para cada: requer suporte a iteráveis (v2)`);
if (declaracao.corpo) {
this.traduzirDeclaracaoInterna(declaracao.corpo);
}
}
// =========================================================================
// Método principal
// =========================================================================
/**
* Traduz uma lista de declarações Delégua para um módulo WAT.
* Retorna a string completa do módulo.
*/
traduzir(declaracoes) {
// Verifica se o usuário declarou explicitamente `funcao principal()`
const principalDeclarado = declaracoes.some((d) => { var _a; return d instanceof declaracoes_1.FuncaoDeclaracao && ((_a = d.simbolo) === null || _a === void 0 ? void 0 : _a.lexema) === 'principal'; });
// Processa todas as declarações de nível superior
for (const declaracao of declaracoes) {
this.traduzirDeclaracaoInterna(declaracao);
}
// Se o usuário não declarou principal(), envolve os stmts de topo em $principal
if (!principalDeclarado) {
const corpoTopo = this.corpoDaFuncaoAtual;
const locaisDeclarados = this.declaracoesLocaisAtual;
const funcPrincipal = ` (func $principal (export "principal")${locaisDeclarados} (result i64)\n` +
corpoTopo +
` (i64.const 0)\n` +
` )`;
this.funcoesCompletas.push(funcPrincipal);
}
// ── Monta as seções do módulo ────────────────────────────────────────
const linhasImports = [
` (import "delegua" "escreva_texto" (func $__escreva_texto (param i32 i32)))`,
` (import "delegua" "escreva_inteiro" (func $__escreva_inteiro (param i64)))`,
];
const linhaMemoria = ` (memory (export "memory") 1)`;
const linhasDados = this.segmentosTexto.map((s) => {
const { watLiteral } = this.escaparStringWat(s.conteudo);
return ` (data (i32.const ${s.deslocamento}) "${watLiteral}")`;
});
const partes = ['(module', ...linhasImports, '', linhaMemoria];
if (linhasDados.length > 0) {
partes.push('');
partes.push(...linhasDados);
}
if (this.declaracoesGlobais.length > 0) {
partes.push('');
partes.push(...this.declaracoesGlobais);
}
if (this.funcoesCompletas.length > 0) {
partes.push('');
partes.push(...this.funcoesCompletas);
}
partes.push(')');
return partes.join('\n');
}
// =========================================================================
// Arquivo host JavaScript
// =========================================================================
/**
* Gera um arquivo `.mjs` host para executar o módulo WASM no Node.js.
* Fornece os imports necessários (`delegua.escreva_texto`, `delegua.escreva_inteiro`).
*/
gerarArquivoHost() {
return `// Gerado por Delégua -> WebAssembly
// Uso: node delegua-host.mjs <arquivo.wasm>
import { readFileSync } from 'fs';
const args = process.argv.slice(2);
const wasmPath = args[0] ?? 'saida.wasm';
let memoryExport;
const importObject = {
delegua: {
/** Imprime texto a partir de um ponteiro e comprimento na memória linear. */
escreva_texto(ptr, len) {
const bytes = new Uint8Array(memoryExport.buffer, ptr, len);
process.stdout.write(new TextDecoder('utf-8').decode(bytes));
},
/** Imprime um inteiro de 64 bits (recebido como BigInt no JS). */
escreva_inteiro(valor) {
process.stdout.write(String(valor));
},
}
};
const wasmBuffer = readFileSync(wasmPath);
const { instance } = await WebAssembly.instantiate(wasmBuffer, importObject);
memoryExport = instance.exports.memory;
const codigoSaida = Number(instance.exports.principal());
process.exit(codigoSaida);
`;
}
}
exports.TradutorWebAssembly = TradutorWebAssembly;
//# sourceMappingURL=tradutor-webassembly.js.map