// Generates the text emitters of engine/downlevel.zena (between the GENERATED markers) from the templates below, // and engine/downlevel-text.wat, the text itself as a data segment at TEXT_BASE that scripts/build.sh merges // into build/downlevel.wasm. `@` is the name entry passed to the emitter. (Zena strings would add a // `wasm:js-string` import.) import fs from "node:fs"; import { TEXT_BASE, TEXT_END, dataModule } from "./layout.mjs"; const TEMPLATES = { litVar: "var", litConst: "const", litModExp: "module.exports", litSemi: ";", litRb: "}", litDestr: "var {", litDestrEnd: "}=this;", // Like litDestrEnd, but closes over a dotted namespace chain's own outer segment (its IIFE's own bound // parameter, `o`) instead of `this` — for a merged *intermediate* segment's exports referenced bare // from a deeper, newly-opened segment's body (namespaceStart's ancestor destructure, engine/downlevel.zena). tDestrEndAs: "}=@;", tItem: "@,", tName: "@", tFn: ";(function(@)", // No longer includes its own leading `{`: namespaceStart emits that separately (`litLb`) so an // ancestor destructure (`destructureAs`, engine/downlevel.zena) can be inserted between the `{` that closes // the *previous* segment's signature and this one's own `var @;(function(@)`. litLb: "{", tDotFn: "var @;(function(@)", tCall: ").call(@||(@={}),@);", tCallX: ").call(@=this.@||(this.@={}),@);", tEiNum: ';var @;this.@=@;this[@]="@";', tEiEval: ';var @;this.@=@;typeof @=="string"||(this[@]="@");', tEiStr: ";var @;this.@=@;", tEnNum: ";this[this[@]]=@;", tEnEval: ';typeof this[@]=="string"||(this[this[@]]=@);', tThisIdx: "this[@]", litAuto0: "=0", tAutoI: "=@+1", tAutoN: "=this[@]+1", litAutoVoid: "=void 0", tAssign: "this.@=@;", tLive: 'Object.defineProperty(this,"@",{get(){return @},set(@$){@=@$},enumerable:true,configurable:true});', // Like tLive, but starts non-enumerable and only becomes enumerable the first time it's actually written // (its own setter flips it with a second, get/set-preserving defineProperty call) -- for an ambient namespace // member that might be written only from outside the namespace body, where downlevel mode can't know statically // whether a write is coming (engine/downlevel.zena's ambientVar). `tsc` only ever creates the member once // something actually assigns it; this matches that without needing to know in advance. tAmbientLive: 'Object.defineProperty(this,"@",{get(){return @},set(@$){@=@$;Object.defineProperty(this,"@",{enumerable:true})},enumerable:false,configurable:true});', }; const WORDS = { Infinity: "wInfinity", NaN: "wNaN", eval: "wEval", arguments: "wArguments" }; // All templates live in one text, each ended by `~`, then the WORDS; an emitter is the offset of its template. let text = ""; let fns = ""; for (const [name, t] of Object.entries(TEMPLATES)) { const usesName = t.includes("@"); fns += `function ${name}(${usesName ? "o: i32" : ""}): void {\n tpl(${text.length}, ${usesName ? "o" : "0"});\n}\n\n`; text += t + "~"; } for (const [word, name] of Object.entries(WORDS)) { fns += `function ${name}(a: i32, b: i32): boolean {\n return isWord(a, b, ${text.length}, ${word.length});\n}\n\n`; text += word; } let out = "// GENERATED by scripts/gen/downlevel-text.mjs from its template table. Do not edit by hand.\n"; out += `let TEXT: i32 = ${TEXT_BASE};\n\n`; out += fns; const file = new URL("../../engine/downlevel.zena", import.meta.url); const src = fs.readFileSync(file, "utf8"); const begin = "// BEGIN GENERATED\n"; const end = "// END GENERATED\n"; const a = src.indexOf(begin); const b = src.indexOf(end); if (a < 0 || b < 0) throw new Error("downlevel.zena: GENERATED markers not found"); fs.writeFileSync(file, src.slice(0, a + begin.length) + out + src.slice(b)); const wat = new URL("../../engine/downlevel-text.wat", import.meta.url); fs.writeFileSync(wat, dataModule("downlevel-text.mjs", TEXT_BASE, Buffer.from(text, "latin1"), TEXT_END));