diff --git a/README.md b/README.md index 4d3a09a..f1ceb58 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Raku, D, Nim, Groovy, Swift, Haskell, and ALGOL 60: | 19 | Infix function calls — `$a max $b` | Haskell/Kotlin | [19-infix](features/19-infix/) | | 20 | `lazy` locals (`lazy val`) — computed on first read, memoised | Scala | [20-lazy-val](features/20-lazy-val/) | | 21 | `apply` builder blocks — lambda-with-receiver | Kotlin | [21-apply](features/21-apply/) | +| 22 | Native markup expressions — JSX-style `
Welcome to PHP, where markup is a first-class expression.
+ >; + } +} + +echoWelcome to PHP, where markup is a first-class expression.
+ >; + } +} + +echoBody content.
+...
*/]), // body slot +// ) + +// Dynamic tags: the value classifies at runtime (see Tags) +<$tag class="box">{$content}$tag> +// → \Html\render_dynamic($tag, ['class' => 'box'], [$content]) +``` + +Two properties fall directly out of this lowering. Markup is **zero-cost sugar** - writing the objects by hand produces the same AST and opcodes. And markup obeys **ordinary expression semantics** - attributes and children are argument expressions, evaluated eagerly, scoped and typed like any other PHP code. + +Every lowering in this document can be reproduced on the reference implementation with no extra tooling: `php -d zend.assertions=1 -r 'assert(false &&Body content flows to the slot.
+fish & chips
` needs no escaping. The decoded value is an ordinary string escaped at render time, so `&` round-trips and `<script>` can never smuggle real markup structure. Text decodes *after* whitespace normalization; comment contents and interpolated PHP strings are never decoded, and `token_get_all()` still sees the raw source. +* **Literal braces**: `{` begins interpolation, so a literal `{` in text is written as in HTML or JSX - a character reference (`{` / `{`) or an interpolated string (`{'{'}`). A lone `}` is already literal. No additional escape syntax (eg. Blade-style `@{{`) is introduced. A block of literal JSON/JS inside `; + +echo ; + +echoBody content flows to the slot.
++fish & chips
` needs no escaping. The decoded value is an ordinary string escaped at render time, so `&` round-trips and `<script>` can never smuggle real markup structure. Text decodes *after* whitespace normalization; comment contents and interpolated PHP strings are never decoded, and `token_get_all()` still sees the raw source. ++* **Literal braces**: `{` begins interpolation, so a literal `{` in text is written as in HTML or JSX - a character reference (`{` / `{`) or an interpolated string (`{'{'}`). A lone `}` is already literal. No additional escape syntax (eg. Blade-style `@{{`) is introduced. A block of literal JSON/JS inside `; ++ ++echo ; ++ ++echoTom & Jerry <3 "quotes" 'apostrophe'
++ ++<script>alert(1)</script> ++ ++ ++TypeError: Attribute "class" value must be of type string|int|float|bool|null|Stringable, array given ++x ++x ++x ++x ++Body
++
++x
++<b>x</b>
++<b>x</b>
++bool(true) ++ValueError: Invalid attribute name "x" onmouseover="alert(1)" ++ValueError: Invalid tag name "a>" ++ValueError: Invalid tag name "" ++ValueError: Invalid attribute name "" ++Body {$name}
>; ++var_dump($frag instanceof Html\Fragment); ++echo $frag->__toString(), "\n"; ++ ++// Interpolation can hold any expression; arrays flatten, null renders "". ++$items = ['x', 'y']; ++echo ({true ? 'yes' : null}
)->__toString(), "\n"; ++ ++// --- JSX-style whitespace normalization --- ++// Newlines + indentation between block elements are dropped. ++echo (Hello {$who}, welcome back
)->__toString(), "\n"; ++ ++// Leading/trailing blank lines and indentation around text collapse away. ++echo (++ Just some text. ++
)->__toString(), "\n"; ++ ++// Words on separate lines join with a single space. ++echo (++ one ++ two ++ three ++
)->__toString(), "\n"; ++ ++// Inline trailing space with no newline is kept (so adjacent elements don't fuse). ++echo (a b c
)->__toString(), "\n"; ++ ++// --- childless elements: empty or self-closed are equivalent --- ++// and are equivalent - both allowed, so real HTML can be ++// pasted into markup unchanged. ++var_dump((string) === (string) ); ++echo , "\n"; ++echo , "\n"; ++ ++// A whitespace-only multi-line body normalizes away to the same empty element. ++echo
)->__toString(), "\n";
++// ...non-void self-closed source expands to open+close output.
++echo ()->__toString(), "\n";
++echo ()->__toString(), "\n";
++
++// Empty components and fragments are allowed (only HTML elements must self-close).
++class C implements Html\Htmlable {
++ public function toHtml(): Html\Htmlable { return Html\raw('C'); }
++}
++echo ({x}
)->__toString(), "\n"; ++echo ({x}
)->__toString(), "\n"; ++ ++// The JSX form: an interpolated string literal. ++echo ({'{'}x{'}'}
)->__toString(), "\n"; ++ ++// A lone } in text is already literal. ++echo (a } b
)->__toString(), "\n"; ++ ++// Literal JSON in a )->__toString(), "\n"; ++ ++// --- comments: is literal output; {/* */} renders nothing --- ++$x = 'ignored'; ++ ++// is emitted verbatim; its content is literal (no interpolation). ++echo (body
a{/* dropped */}b
)->__toString(), "\n"; ++ ++// an empty interpolation renders nothing too ++echo (x{}y
)->__toString(), "\n"; ++ ++// comment between block elements survives (it is real output, not whitespace) ++echo (cast
, "\n"; // after a cast ++echoBody Ada & <friends>
++yes
++Hello Ada, welcome back
++Just some text.
++one two three
++a b c
++bool(true) ++ ++ ++ ++
++
++
++C
++string(0) ""
++{x}
++{x}
++{x}
++a } b
++ ++body
ab
++xy
++cast
++{$this->slot}
; ++ } ++ } ++ class Card implements \Html\Htmlable { ++ public function __construct(public string $title) {} ++ public function toHtml(): \Html\Htmlable { ++ return new \Html\Element('b', [], [$this->title]); ++ } ++ } ++ class Author { ++ public static function byline(string $name): \Html\Htmlable { ++ return new \Html\Element('i', [], ['By ', $name]); ++ } ++ } ++} ++ ++// --- use-alias resolution from another namespace --- ++namespace App\Views { ++ use App\Components\Card; ++ //hi
++G ++fq
++C ++By Ada ++By Grace ++local ++"Wrong\Card" is not a component: no such class implementing Html\Htmlable +diff --git a/ext/html/tests/syntax_doctype.phpt b/ext/html/tests/syntax_doctype.phpt +new file mode 100644 +index 00000000..3de7054b +--- /dev/null ++++ b/ext/html/tests/syntax_doctype.phpt +@@ -0,0 +1,50 @@ ++--TEST-- ++Markup syntax: emission rules and non-doctype ++ ++hi
++>)->__toString(), "\n"; ++ ++// Case-insensitive, and content is literal (no interpolation). ++echo (<>>)->__toString(), "\n"; ++ ++// Legacy doctypes with public/system identifiers pass through too. ++echo (<>x
>)->__toString(), "\n"; ++ ++// Position is not validated: emitted where written, like a comment. ++echo (hi
++ ++x
++once
++int(1) ++direct
++Á vs á
, "\n"; ++ ++// Numeric references: decimal, hex (either x/X case), multi-codepoint named. ++echoABC 😀 ≫⃒
, "\n"; ++ ++// is a real U+00A0, not the entity text (htmlspecialchars leaves it alone). ++var_dump((string) === "\u{00A0}"); ++ ++// --- lenient handling of invalid references --- ++// Lenient like HTML/JSX: unknown names, bare "&", missing semicolons, ++// and invalid numerics (zero, surrogate, out of range, empty) stay literal ++// and are escaped on output. ++echoa & b &nosuchentity; & c
, "\n"; ++echoG;
, "\n"; ++ ++// --- attribute values --- ++// Attribute values decode too; the "&" re-escapes when serialized. ++echo x, "\n"; ++ ++// --- entities cannot smuggle markup structure --- ++// lt/gt decode to real angle brackets in the value, escaped on output -- ++// so entities cannot smuggle markup structure. ++echo<script>alert(1)</script>
, "\n"; ++ ++// --- interaction with whitespace normalization, comments, interpolation --- ++// Entities decode after whitespace normalization, so survives where a ++// literal trailing space would be trimmed (the entity spelling of JSX {' '}). ++echoa ++
, "\n"; ++ ++// Entities inside stay verbatim (comments are raw output). ++echo , "\n"; ++ ++// Interpolated strings are never entity-decoded; they are plain PHP values. ++echo{"&"}
, "\n"; ++ ++// --- tokenizer round-trip --- ++// Entity decoding happens only for the compiler; the tokenizer must see the ++// raw source text so token streams concatenate back to the original file. ++$code = 'Fish & chips ❤; ?>'; ++ ++$joined = ''; ++foreach (token_get_all($code) as $token) { ++ $joined .= is_array($token) ? $token[1] : $token; ++} ++var_dump($joined === $code); ++?> ++--EXPECT-- ++Fish & chips — £5
++Á vs á
++ABC 😀 ≫⃒
++bool(true) ++a & b &nosuchentity; & c
++� � � &#; &#x; &#xG;
++x ++<script>alert(1)</script>
++a
++ ++&
++bool(true) +diff --git a/ext/html/tests/syntax_lineno.phpt b/ext/html/tests/syntax_lineno.phpt +new file mode 100644 +index 00000000..8f6da867 +--- /dev/null ++++ b/ext/html/tests/syntax_lineno.phpt +@@ -0,0 +1,37 @@ ++--TEST-- ++Markup syntax: multi-line elements report errors on the opening-tag line, like any multi-line call ++--EXTENSIONS-- ++html ++--FILE-- ++{$this->title}Fish & chips — £5
'), "\n"; ++?> ++--EXPECT-- ++new \Html\Element('div', ['class' => 'box', 'id' => $id], ['Hello ', $name]) ++new \Html\Fragment([\Html\render_component(Card::class, ['title' => 'Hi', ...$attrs], new \Html\Fragment([new \Html\Element('b', [], ['f']), 'body']))]) ++\Html\render_component(Author::class . '::byline', ['name' => 'Ada'], null) ++\Html\render_component(Auth::class, [], new \Html\LazyFragment(fn() => new \Html\Fragment(['Hello ', $name]))) ++\Html\render_dynamic($tag, ['class' => 'x'], [$content]) ++new \Html\Element('p', [], ['Fish & chips — £5']) +diff --git a/ext/html/tests/syntax_mismatch.phpt b/ext/html/tests/syntax_mismatch.phpt +new file mode 100644 +index 00000000..a11b3e3b +--- /dev/null ++++ b/ext/html/tests/syntax_mismatch.phpt +@@ -0,0 +1,10 @@ ++--TEST-- ++Markup syntax: a mismatched closing tag is a compile error ++--EXTENSIONS-- ++html ++--FILE-- ++oops; ++?> ++--EXPECTF-- ++Fatal error: Mismatched markup closing tag: expectedbold
' \ + "$("$PHP" -r 'echo{Html\raw("bold")}
;')" + +check "component tag dispatches to render_component" 'hi' \ + "$("$PHP" -r 'class Badge implements Html\Htmlable { public function __construct(public string $label){} + public function toHtml(): Html\Htmlable { return new Html\Element("span",["class"=>"badge"],[$this->label]); } } + echo1 2.5 1
+x & yz
+b
[/Note] +[Author::byline]c[/Author::byline] +<<[Note]d
[/Note]>> +Component decorator forloose body
(c) 2026Hi {$who}
Hi Ada & co
By Ada & co
+New & +By Ada
+Error: Component "Author::broken" did not return an Html\Htmlable +Error: Component class "NoSuchClass" not found +FROM CLASS +Tom & Jerry <3 "quotes" 'apostrophe'
+ +<script>alert(1)</script> + + +TypeError: Attribute "class" value must be of type string|int|float|bool|null|Stringable, array given +x +x +x +x +Body
+
+x
+<b>x</b>
+<b>x</b>
+bool(true) +ValueError: Invalid attribute name "x" onmouseover="alert(1)" +ValueError: Invalid tag name "a>" +ValueError: Invalid tag name "" +ValueError: Invalid attribute name "" +hi
,Body {$name}
>; +var_dump($frag instanceof Html\Fragment); +echo $frag->__toString(), "\n"; + +// Interpolation can hold any expression; arrays flatten, null renders "". +$items = ['x', 'y']; +echo ({true ? 'yes' : null}
)->__toString(), "\n"; + +// --- JSX-style whitespace normalization --- +// Newlines + indentation between block elements are dropped. +echo (Hello {$who}, welcome back
)->__toString(), "\n"; + +// Leading/trailing blank lines and indentation around text collapse away. +echo (+ Just some text. +
)->__toString(), "\n"; + +// Words on separate lines join with a single space. +echo (+ one + two + three +
)->__toString(), "\n"; + +// Inline trailing space with no newline is kept (so adjacent elements don't fuse). +echo (a b c
)->__toString(), "\n"; + +// --- childless elements: empty or self-closed are equivalent --- +// and are equivalent - both allowed, so real HTML can be +// pasted into markup unchanged. +var_dump((string) === (string) ); +echo , "\n"; +echo , "\n"; + +// A whitespace-only multi-line body normalizes away to the same empty element. +echo
)->__toString(), "\n";
+// ...non-void self-closed source expands to open+close output.
+echo ()->__toString(), "\n";
+echo ()->__toString(), "\n";
+
+// Empty components and fragments are allowed (only HTML elements must self-close).
+class C implements Html\Htmlable {
+ public function toHtml(): Html\Htmlable { return Html\raw('C'); }
+}
+echo ({x}
)->__toString(), "\n"; +echo ({x}
)->__toString(), "\n"; + +// The JSX form: an interpolated string literal. +echo ({'{'}x{'}'}
)->__toString(), "\n"; + +// A lone } in text is already literal. +echo (a } b
)->__toString(), "\n"; + +// Literal JSON in a )->__toString(), "\n"; + +// --- comments: is literal output; {/* */} renders nothing --- +$x = 'ignored'; + +// is emitted verbatim; its content is literal (no interpolation). +echo (body
a{/* dropped */}b
)->__toString(), "\n"; + +// an empty interpolation renders nothing too +echo (x{}y
)->__toString(), "\n"; + +// comment between block elements survives (it is real output, not whitespace) +echo (cast
, "\n"; // after a cast +echoBody Ada & <friends>
+yes
+Hello Ada, welcome back
+Just some text.
+one two three
+a b c
+bool(true) + + + +
+
+
+C
+string(0) ""
+{x}
+{x}
+{x}
+a } b
+ +body
ab
+xy
+cast
+{$this->slot}
; + } + } + class Card implements \Html\Htmlable { + public function __construct(public string $title) {} + public function toHtml(): \Html\Htmlable { + return new \Html\Element('b', [], [$this->title]); + } + } + class Author { + public static function byline(string $name): \Html\Htmlable { + return new \Html\Element('i', [], ['By ', $name]); + } + } +} + +// --- use-alias resolution from another namespace --- +namespace App\Views { + use App\Components\Card; + //hi
+G +fq
+C +By Ada +By Grace +local +"Wrong\Card" is not a component: no such class implementing Html\Htmlable diff --git a/features/22-native-markup/tests/syntax_doctype.phpt b/features/22-native-markup/tests/syntax_doctype.phpt new file mode 100644 index 0000000..3de7054 --- /dev/null +++ b/features/22-native-markup/tests/syntax_doctype.phpt @@ -0,0 +1,50 @@ +--TEST-- +Markup syntax: emission rules and non-doctype + +hi
+>)->__toString(), "\n"; + +// Case-insensitive, and content is literal (no interpolation). +echo (<>>)->__toString(), "\n"; + +// Legacy doctypes with public/system identifiers pass through too. +echo (<>x
>)->__toString(), "\n"; + +// Position is not validated: emitted where written, like a comment. +echo (hi
+ +x
+once
+int(1) +direct
+Á vs á
, "\n"; + +// Numeric references: decimal, hex (either x/X case), multi-codepoint named. +echoABC 😀 ≫⃒
, "\n"; + +// is a real U+00A0, not the entity text (htmlspecialchars leaves it alone). +var_dump((string) === "\u{00A0}"); + +// --- lenient handling of invalid references --- +// Lenient like HTML/JSX: unknown names, bare "&", missing semicolons, +// and invalid numerics (zero, surrogate, out of range, empty) stay literal +// and are escaped on output. +echoa & b &nosuchentity; & c
, "\n"; +echoG;
, "\n"; + +// --- attribute values --- +// Attribute values decode too; the "&" re-escapes when serialized. +echo x, "\n"; + +// --- entities cannot smuggle markup structure --- +// lt/gt decode to real angle brackets in the value, escaped on output -- +// so entities cannot smuggle markup structure. +echo<script>alert(1)</script>
, "\n"; + +// --- interaction with whitespace normalization, comments, interpolation --- +// Entities decode after whitespace normalization, so survives where a +// literal trailing space would be trimmed (the entity spelling of JSX {' '}). +echoa +
, "\n"; + +// Entities inside stay verbatim (comments are raw output). +echo , "\n"; + +// Interpolated strings are never entity-decoded; they are plain PHP values. +echo{"&"}
, "\n"; + +// --- tokenizer round-trip --- +// Entity decoding happens only for the compiler; the tokenizer must see the +// raw source text so token streams concatenate back to the original file. +$code = 'Fish & chips ❤; ?>'; + +$joined = ''; +foreach (token_get_all($code) as $token) { + $joined .= is_array($token) ? $token[1] : $token; +} +var_dump($joined === $code); +?> +--EXPECT-- +Fish & chips — £5
+Á vs á
+ABC 😀 ≫⃒
+bool(true) +a & b &nosuchentity; & c
+� � � &#; &#x; &#xG;
+x +<script>alert(1)</script>
+a
+ +&
+bool(true) diff --git a/features/22-native-markup/tests/syntax_lineno.phpt b/features/22-native-markup/tests/syntax_lineno.phpt new file mode 100644 index 0000000..8f6da86 --- /dev/null +++ b/features/22-native-markup/tests/syntax_lineno.phpt @@ -0,0 +1,37 @@ +--TEST-- +Markup syntax: multi-line elements report errors on the opening-tag line, like any multi-line call +--EXTENSIONS-- +html +--FILE-- +{$this->title}Fish & chips — £5
'), "\n"; +?> +--EXPECT-- +new \Html\Element('div', ['class' => 'box', 'id' => $id], ['Hello ', $name]) +new \Html\Fragment([\Html\render_component(Card::class, ['title' => 'Hi', ...$attrs], new \Html\Fragment([new \Html\Element('b', [], ['f']), 'body']))]) +\Html\render_component(Author::class . '::byline', ['name' => 'Ada'], null) +\Html\render_component(Auth::class, [], new \Html\LazyFragment(fn() => new \Html\Fragment(['Hello ', $name]))) +\Html\render_dynamic($tag, ['class' => 'x'], [$content]) +new \Html\Element('p', [], ['Fish & chips — £5']) diff --git a/features/22-native-markup/tests/syntax_mismatch.phpt b/features/22-native-markup/tests/syntax_mismatch.phpt new file mode 100644 index 0000000..a11b3e3 --- /dev/null +++ b/features/22-native-markup/tests/syntax_mismatch.phpt @@ -0,0 +1,10 @@ +--TEST-- +Markup syntax: a mismatched closing tag is a compile error +--EXTENSIONS-- +html +--FILE-- +oops; +?> +--EXPECTF-- +Fatal error: Mismatched markup closing tag: expectedMarkup is a first-class value: return it, compose it, pass it around.
+{Html\\raw("already safe")}
, "\\n"; `, }; diff --git a/wasm/dist/22-native-markup/php.mjs b/wasm/dist/22-native-markup/php.mjs new file mode 100644 index 0000000..211e70b --- /dev/null +++ b/wasm/dist/22-native-markup/php.mjs @@ -0,0 +1,2 @@ +async function createPHP(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var programArgs=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}programArgs=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var runtimeInitialized=false;var runtimeExited=false;function getMemoryBuffer(){try{var b=wasmMemory.toResizableBuffer();return b}catch{}return wasmMemory.buffer}function updateMemoryViews(){if(HEAP8?.buffer?.resizable)return;var b=getMemoryBuffer();HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){var preRun=Module["preRun"];if(preRun){if(typeof preRun=="function")preRun=[preRun];onPreRuns.push(...preRun)}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;SOCKFS.root=FS.mount(SOCKFS,{},null);if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();PIPEFS.root=FS.mount(PIPEFS,{},null);wasmExports["Na"]();FS.ignorePermissions=false}function exitRuntime(){___funcs_on_exit();FS.quit();TTY.shutdown();clearTimers();runtimeExited=true}function postRun(){var postRun=Module["postRun"];if(postRun){if(typeof postRun=="function")postRun=[postRun];onPostRuns.push(...postRun)}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("php.wasm")}return new URL("php.wasm",import.meta.url).href}function getBinarySync(file){if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();var instantiateWasm=Module["instantiateWasm"];if(instantiateWasm){return new Promise(resolve=>{instantiateWasm(info,inst=>resolve(receiveInstance(inst)))})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var onPreRuns=[];var noExitRuntime=false;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var getWasmTableEntry=funcPtr=>wasmTable.get(funcPtr);var ___call_sighandler=(fp,sig)=>getWasmTableEntry(fp)(sig);var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>(nodeCrypto.randomFillSync(view),0)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start