From 3bc55ce4f2599fb6072c7903cb50676416d9c99c Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 24 Oct 2022 11:33:35 -0700 Subject: [PATCH 01/18] migrate to Wasm components --- .gitignore | 10 +- chompfile.toml | 204 ++++------- lib/lexer.asm.js | 223 ------------ lib/lexer.emcc.asm.js | 9 - lib/lexer.wasm | Bin 8078 -> 0 bytes package.json | 2 +- src/lexer-world.wit | 175 +++++++++ src/lexer.c | 802 ++++++++++++++++++++++++++---------------- src/lexer.h | 264 +++----------- src/lexer.js | 85 +++++ test/_unit.cjs | 1 + 11 files changed, 869 insertions(+), 906 deletions(-) delete mode 100644 lib/lexer.asm.js delete mode 100644 lib/lexer.emcc.asm.js delete mode 100755 lib/lexer.wasm create mode 100644 src/lexer-world.wit mode change 100755 => 100644 src/lexer.c mode change 100755 => 100644 src/lexer.h create mode 100644 src/lexer.js diff --git a/.gitignore b/.gitignore index 005b27b..117bbf8 100755 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,4 @@ -node_modules -lib/lexer.wat -dist +/node_modules +/obj +/dist package-lock.json -yarn.lock -lib/lexer.emcc.js -src/lexer.js -types/lexer.d.ts diff --git a/chompfile.toml b/chompfile.toml index a9f546d..5c1193e 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -1,42 +1,17 @@ version = 0.1 -default-task = 'build' -extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint', 'chomp@0.1:terser'] +extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] -WASI_PATH = '../wasi-sdk-12.0' -EMSDK_PATH = '../emsdk' -WABT_PATH = '../wabt' +WASI_PATH = '/opt/wasi-sdk' +WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' +WITPACK = '../witpack/src/witpack.js' +WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' +WIT_COMPONENT = '../wit-bindgen/target/debug/wit-component' [[task]] name = 'build' -deps = ['dist/lexer.js', 'dist/lexer.cjs', 'dist/lexer.asm.js', 'types/lexer.d.ts'] - -[[task]] -name = 'bench' -serial = true -deps = ['bench:js', 'bench:wasm'] - -[[task]] -name = 'bench:js' -deps = ['dist/lexer.asm.js'] -env = { BENCH = 'js' } -run = 'node --expose-gc bench/index.js' - -[[task]] -name = 'bench:wasm' -deps = ['dist/lexer.js'] -env = { BENCH = 'wasm' } -run = 'node --expose-gc bench/index.js' - -[[task]] -target = 'dist/lexer.asm.js' -dep = 'lib/lexer.asm.js' -template = 'terser' -[task.template-options] -module = true -compress = { ecma = 6, unsafe = true } -output = { preamble = '/* es-module-lexer #PJSON_VERSION */' } +deps = ['dist/lexer.js', 'dist/lexer.cjs', 'dist/lexer.asm.js', 'dist/lexer.d.ts'] [[task]] target = 'dist/lexer.cjs' @@ -44,135 +19,75 @@ deps = ['dist/lexer.js'] run = 'babel dist/lexer.js | terser -c -m -o dist/lexer.cjs' [[task]] -name = 'build:swc' -target = 'src/lexer.js' -dep = 'src/lexer.ts' -# Note we should use the chomp swc template, but -# https://github.com/swc-project/cli/issues/113 means we always get a sourcemap -# even when we set "source-maps = false", so for now we have ejected the -# template to its raw "run" command, and added an "rm" step. +targets = ['dist/lexer.js', 'dist/lexer.d.ts'] +deps = ['obj/lexer-component.wasm', 'obj/preamble'] run = ''' -node ./node_modules/@swc/cli/bin/swc.js $DEP -o $TARGET --no-swcrc -C jsc.parser.syntax=typescript -C jsc.parser.importAssertions=true -C jsc.parser.topLevelAwait=true -C jsc.parser.importMeta=true -C jsc.parser.privateMethod=true -C jsc.parser.dynamicImport=true -C jsc.target=es2016 -C jsc.experimental.keepImportAssertions=true -rm src/lexer.js.map + ${{ WITPACK }} host obj/lexer-component.wasm -o dist --name=lexer --valid-lifting-optimization --optimize --minify -b9000 -q + cat obj/preamble > dist/tmp ; cat dist/lexer.js >> dist/tmp ; mv dist/tmp dist/lexer.js ''' [[task]] -# Note swc does not support emitting typings -# (https://github.com/swc-project/swc/issues/657), so while swc is used to -# generate the .js file, tsc is still needed to generate the d.ts file. -name = 'build:types' -target = 'types/lexer.d.ts' -dep = 'src/lexer.ts' +target = 'dist/lexer.asm.js' +dep = 'obj/lexer-component.wasm' run = ''' - tsc --strict --declaration --emitDeclarationOnly --outdir types src/lexer.ts + ${{ WITPACK }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q + cat obj/preamble.asm > dist/tmp ; cat dist/lexer.asm.js >> dist/tmp ; mv dist/tmp dist/lexer.asm.js ''' [[task]] -target = 'dist/lexer.js' -deps = ['src/lexer.js', 'lib/lexer.wasm', 'package.json'] +targets = ['obj/preamble', 'obj/preamble.asm'] +dep = 'package.json' engine = 'node' run = ''' - import { readFileSync, writeFileSync } from 'fs'; - import { minify } from 'terser'; - - const wasmBuffer = readFileSync('lib/lexer.wasm'); - const jsSource = readFileSync('src/lexer.js', 'utf8'); - const pjson = JSON.parse(readFileSync('package.json', 'utf8')); - - const jsSourceProcessed = jsSource.replace('WASM_BINARY', wasmBuffer.toString('base64')); - - const { code: minified } = await minify(jsSourceProcessed, { - module: true, - output: { - preamble: `/* es-module-lexer ${pjson.version} */` - } - }); +import { readFile, writeFile } from 'fs/promises'; +const { version } = JSON.parse(await readFile('package.json')); +await writeFile('obj/preamble', `/* es-module-lexer ${version} */\n`); +await writeFile('obj/preamble.asm', `/* es-module-lexer asm.js ${version} */\n`); +''' - writeFileSync('dist/lexer.js', minified ? minified : jsSourceProcessed); +[[task]] +target = 'obj/lexer-component.wasm' +stdio = 'stderr-only' +deps = ['obj/lexer.wasm', 'src/lexer-world.wit'] +run = ''' + ${{ WIT_COMPONENT }} $DEP --encoding utf16 --interface src/lexer-world.wit -o $TARGET ''' [[task]] -target = 'lib/lexer.wasm' -deps = ['src/lexer.h', 'src/lexer.c'] +target = 'obj/lexer.wasm' +deps = ['obj/lexer_world.c', 'obj/lexer_world.h', 'src/lexer.c', 'src/lexer.h'] +stdio = 'stderr-only' run = """ - ${{ WASI_PATH }}/bin/clang src/lexer.c --sysroot=${{ WASI_PATH }}/share/wasi-sysroot -o lib/lexer.wasm -nostartfiles \ - "-Wl,-z,stack-size=13312,--no-entry,--compress-relocations,--strip-all,\ - --export=parse,--export=sa,--export=e,--export=ri,--export=re,--export=is,--export=ie,--export=ss,--export=ip,--export=se,--export=ai,--export=id,--export=es,--export=ee,--export=els,--export=ele,--export=f,--export=__heap_base" \ - -Wno-logical-op-parentheses -Wno-parentheses \ - -Oz + ${{ WASI_PATH }}/bin/clang src/lexer.c obj/lexer_world.c --sysroot=${{ WASI_PATH }}/share/wasi-sysroot -o obj/lexer.wasm -nostartfiles \ + "-Wl,-z,stack-size=1331200,--no-entry,--compress-relocations,--strip-all" \ + -Wno-logical-op-parentheses -Wno-parentheses \ + -Oz """ [[task]] -target = 'lib/lexer.emcc.asm.js' -deps = ['src/lexer.h', 'src/lexer.c'] -env = { PYTHONHOME = '' } -run = """ - ${{ EMSDK_PATH }}/emsdk install 1.40.1-fastcomp - ${{ EMSDK_PATH }}/emsdk activate 1.40.1-fastcomp +targets = ['obj/lexer_world.c', 'obj/lexer_world.h'] +dep = 'src/lexer-world.wit' +stdio = 'stderr-only' +run = ''' + ${{ WIT_BINDGEN }} guest c --name lexer_world --string-encoding utf16 --default src/lexer-world.wit --no-helpers --out-dir obj +''' - ${{ EMSDK_PATH }}/fastcomp/emscripten/emcc ./src/lexer.c -o lib/lexer.emcc.js -s WASM=0 -Oz --closure 1 \ - -s EXPORTED_FUNCTIONS="['_parse','_sa','_e','_ri','_re','_is','_ie','_ss','_ip','_se','_ai','_id','_es','_ee','_els','_ele','_f','_setSource']" \ - -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s SINGLE_FILE=1 -s TOTAL_STACK=4997968 -s --separate-asm -Wno-logical-op-parentheses -Wno-parentheses +[[task]] +name = 'bench' +serial = true +deps = ['bench:js', 'bench:wasm'] - # rm lib/lexer.emcc.js -""" +[[task]] +name = 'bench:js' +deps = ['dist/lexer.asm.js'] +env = { BENCH = 'js' } +run = 'node --expose-gc bench/index.js' [[task]] -target = 'lib/lexer.asm.js' -deps = ['lib/lexer.emcc.asm.js', 'src/lexer.asm.js'] -engine = 'node' -run = ''' - import { readFileSync, writeFileSync } from 'fs'; - - const wrapper_start = readFileSync('src/lexer.asm.js', 'utf8'); - let source = readFileSync('lib/lexer.emcc.asm.js', 'utf8').trim(); - - const endFuncs = 'EMSCRIPTEN_END_FUNCS'; - const removeFunc = name => [new RegExp(`function ${name}\\([^]+?}\\s*(function|return\\s?{[^{}]+};?\\s*}\\s*$)`), '$1']; - - const replacements = [ - [/Module\["asm"\]=\s?\(\/\*\* @suppress {uselessCode} \*\/ function\(/, 'function asmInit('], - [/\)$/, ''], - [/,\s?_(\w+):/g, ',$1:', null, endFuncs], - ['setSource:', 'ses:', null, endFuncs], - ['parse:', 'p:', null, endFuncs], - [/___errno_location:\s?(\w+),/, '', removeFunc, endFuncs], - [/_apply_relocations:\s?(\w+),/, '', removeFunc, endFuncs], - [/,\s?free:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s?malloc:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s?memcpy:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s?memset:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s?stackAlloc:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s?emscripten_get_sbrk_ptr:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s?stackRestore:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s?stackSave:\s?(\w+)/, '', removeFunc, endFuncs], - [/,\s*\w+\s?=\s?env\.\w+\|0,\s*\w+\s?=\s?env\.\w+\|0,\s*\w+\s?=\s?0,\s*\w+\s?=\s?0,\s*\w+\s?=\s?0,\s*\w+\s?=\s?0,\s*\w+\s?=\s?0,\s*\w+\s?=\s?0,\s*\w+\s?=\s?0,\s*\w+\s?=\s?0\.0,\s*\w+\s?=\s?env\.\w+,\s*\w+\s?=\s?env\.\w+,\s*\w+\s?=\s?env\.\w+,\s*\w+\s?=\s?env\.\w+,\s*\w+\s?=\s?env\.\w+,\s*\w+\s?=\s?env\.\w+/, ''], - [/,\s*\w+\s?=\s?\d+,\s*\w+\s?=\s?0.0;/, ';'], - [/function \w+\(\w+\)\s?{[^{}]+{[^{}s]+s\(\)[^{}]+}[^{}]+}/, ''], - [/\s*\/\/ EMSCRIPTEN_END_FUNCS\s*return\{/, ` function su(a) { - a = a | 0; - v = a + 992 + 15 & -16; - return 992; - } - return { - su,`], - [/\s*\/\/ EMSCRIPTEN_START_FUNCS\s*/, ''], - ]; - - for (const [from, to, add, after] of replacements) { - const [matched, match] = source.match(from) || []; - if (!matched) { - console.log(source.slice(0, 1000)); - throw new Error(`Match not found for ${from} -> ${to}${after ? `, after ${after}` : ''}`); - } - const afterIndex = after ? source.indexOf(after) : 0; - const replaced = source.slice(0, afterIndex) + source.slice(afterIndex).replace(from, to); - if (add) replacements.push(add(match)); - source = replaced; - } - - writeFileSync(process.env.TARGET, wrapper_start + source); -''' +name = 'bench:wasm' +deps = ['dist/lexer.js'] +env = { BENCH = 'wasm' } +run = 'node --expose-gc bench/index.js' [[task]] name = 'test' @@ -195,9 +110,14 @@ env = { WASM = '1' } run = 'mocha -b -u tdd test/*.cjs' [[task]] -target = 'lib/lexer.wat' -dep = 'lib/lexer.wasm' -run = '${{ WABT_PATH }}/bin/wasm2wat lib/lexer.wasm -o lib/lexer.wat' +target = 'obj/lexer.wat' +dep = 'obj/lexer.wasm' +run = '${{ WASM_TOOLS }} parse -t $DEP -o $TARGET' + +[[task]] +target = 'obj/lexer-component.core.wat' +dep = 'obj/lexer-component.core.wasm' +run = '${{ WASM_TOOLS }} parse -t $DEP -o $TARGET' [[task]] name = 'footprint' diff --git a/lib/lexer.asm.js b/lib/lexer.asm.js deleted file mode 100644 index 54f261e..0000000 --- a/lib/lexer.asm.js +++ /dev/null @@ -1,223 +0,0 @@ -let asm, asmBuffer, allocSize = 2<<19, addr; - -const copy = new Uint8Array(new Uint16Array([1]).buffer)[0] === 1 ? function (src, outBuf16) { - const len = src.length; - let i = 0; - while (i < len) - outBuf16[i] = src.charCodeAt(i++); -} : function (src, outBuf16) { - const len = src.length; - let i = 0; - while (i < len) { - const ch = src.charCodeAt(i); - outBuf16[i++] = (ch & 0xff) << 8 | ch >>> 8; - } -}; -const words = 'xportmportlassetafromsyncunctionssertvoyiedelecontininstantybreareturdebuggeawaithrwhileforifcatcfinallels'; - -let source, name; -export function parse (_source, _name = '@') { - source = _source; - name = _name; - // 2 bytes per string code point - // + analysis space (2^17) - // remaining space is EMCC stack space (2^17) - const memBound = source.length * 2 + (2 << 18); - if (memBound > allocSize || !asm) { - while (memBound > allocSize) allocSize *= 2; - asmBuffer = new ArrayBuffer(allocSize); - copy(words, new Uint16Array(asmBuffer, 16, words.length)); - asm = asmInit(typeof self !== 'undefined' ? self : global, {}, asmBuffer); - // lexer.c bulk allocates string space + analysis space - addr = asm.su(allocSize - (2<<17)); - } - const len = source.length + 1; - asm.ses(addr); - asm.sa(len - 1); - - copy(source, new Uint16Array(asmBuffer, addr, len)); - - if (!asm.p()) { - acornPos = asm.e(); - syntaxError(); - } - - const imports = [], exports = []; - while (asm.ri()) { - const s = asm.is(), e = asm.ie(), a = asm.ai(), d = asm.id(), ss = asm.ss(), se = asm.se(); - let n; - if (asm.ip()) - n = readString(d === -1 ? s : s + 1, source.charCodeAt(d === -1 ? s - 1 : s)); - imports.push({ n, s, e, ss, se, d, a }); - } - while (asm.re()) { - const s = asm.es(), e = asm.ee(), ls = asm.els(), le = asm.ele(); - const ch = source.charCodeAt(s); - const lch = ls >= 0 ? source.charCodeAt(ls) : -1; - exports.push({ - s, e, ls, le, - n: (ch === 34 || ch === 39) ? readString(s + 1, ch) : source.slice(s, e), - ln: ls < 0 ? undefined : (lch === 34 || lch === 39) ? readString(ls + 1, lch) : source.slice(ls, le), - }); - } - - return [imports, exports, !!asm.f()]; -} - -/* - * Ported from Acorn - * - * MIT License - - * Copyright (C) 2012-2020 by various contributors (see AUTHORS) - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -let acornPos; -function readString (start, quote) { - acornPos = start; - let out = '', chunkStart = acornPos; - for (;;) { - if (acornPos >= source.length) syntaxError(); - const ch = source.charCodeAt(acornPos); - if (ch === quote) break; - if (ch === 92) { // '\' - out += source.slice(chunkStart, acornPos); - out += readEscapedChar(); - chunkStart = acornPos; - } - else if (ch === 0x2028 || ch === 0x2029) { - ++acornPos; - } - else { - if (isBr(ch)) syntaxError(); - ++acornPos; - } - } - out += source.slice(chunkStart, acornPos++); - return out; -} - -// Used to read escaped characters - -function readEscapedChar () { - let ch = source.charCodeAt(++acornPos); - ++acornPos; - switch (ch) { - case 110: return '\n'; // 'n' -> '\n' - case 114: return '\r'; // 'r' -> '\r' - case 120: return String.fromCharCode(readHexChar(2)); // 'x' - case 117: return readCodePointToString(); // 'u' - case 116: return '\t'; // 't' -> '\t' - case 98: return '\b'; // 'b' -> '\b' - case 118: return '\u000b'; // 'v' -> '\u000b' - case 102: return '\f'; // 'f' -> '\f' - case 13: if (source.charCodeAt(acornPos) === 10) ++acornPos; // '\r\n' - case 10: // ' \n' - return ''; - case 56: - case 57: - syntaxError(); - default: - if (ch >= 48 && ch <= 55) { - let octalStr = source.substr(acornPos - 1, 3).match(/^[0-7]+/)[0]; - let octal = parseInt(octalStr, 8); - if (octal > 255) { - octalStr = octalStr.slice(0, -1); - octal = parseInt(octalStr, 8); - } - acornPos += octalStr.length - 1; - ch = source.charCodeAt(acornPos); - if (octalStr !== '0' || ch === 56 || ch === 57) - syntaxError(); - return String.fromCharCode(octal); - } - if (isBr(ch)) { - // Unicode new line characters after \ get removed from output in both - // template literals and strings - return ''; - } - return String.fromCharCode(ch); - } -} - -// Used to read character escape sequences ('\x', '\u', '\U'). - -function readHexChar (len) { - const start = acornPos; - let total = 0, lastCode = 0; - for (let i = 0; i < len; ++i, ++acornPos) { - let code = source.charCodeAt(acornPos), val; - - if (code === 95) { - if (lastCode === 95 || i === 0) syntaxError(); - lastCode = code; - continue; - } - - if (code >= 97) val = code - 97 + 10; // a - else if (code >= 65) val = code - 65 + 10; // A - else if (code >= 48 && code <= 57) val = code - 48; // 0-9 - else break; - if (val >= 16) break; - lastCode = code; - total = total * 16 + val; - } - - if (lastCode === 95 || acornPos - start !== len) syntaxError(); - - return total; -} - -// Read a string value, interpreting backslash-escapes. - -function readCodePointToString () { - const ch = source.charCodeAt(acornPos); - let code; - if (ch === 123) { // '{' - ++acornPos; - code = readHexChar(source.indexOf('}', acornPos) - acornPos); - ++acornPos; - if (code > 0x10FFFF) syntaxError(); - } else { - code = readHexChar(4); - } - // UTF-16 Decoding - if (code <= 0xFFFF) return String.fromCharCode(code); - code -= 0x10000; - return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00); -} - -function isBr (c) { - return c === 13/*\r*/ || c === 10/*\n*/; -} - -function syntaxError () { - throw Object.assign(new Error(`Parse error ${name}:${source.slice(0, acornPos).split('\n').length}:${acornPos - source.lastIndexOf('\n', acornPos - 1)}`), { idx: acornPos }); -} - -// function asmInit () { ... } from lib/lexer.asm.js is concatenated at the end here -function asmInit(global,env,buffer) { -"use asm";var a=new global.Int8Array(buffer),b=new global.Int16Array(buffer),c=new global.Int32Array(buffer),d=new global.Uint8Array(buffer),e=new global.Uint16Array(buffer),v=1024;function z(){var d=0,f=0,g=0,h=0,i=0,j=0,k=0;k=v;v=v+10240|0;a[795]=1;b[395]=0;b[396]=0;c[67]=c[2];a[796]=0;c[66]=0;a[794]=0;c[68]=k+2048;c[69]=k;a[797]=0;d=(c[3]|0)+-2|0;c[70]=d;f=d+(c[64]<<1)|0;c[71]=f;a:while(1){g=d+2|0;c[70]=g;if(d>>>0>=f>>>0){i=18;break}b:do switch(b[g>>1]|0){case 9:case 10:case 11:case 12:case 13:case 32:break;case 101:{if((((b[396]|0)==0?da(g)|0:0)?(Q(d+4|0,16,10)|0)==0:0)?(B(),(a[795]|0)==0):0){i=9;break a}else i=17;break}case 105:{if(da(g)|0?(Q(d+4|0,26,10)|0)==0:0){C();i=17}else i=17;break}case 59:{i=17;break}case 47:switch(b[d+4>>1]|0){case 47:{_();break b}case 42:{P(1);break b}default:{i=16;break a}}default:{i=16;break a}}while(0);if((i|0)==17){i=0;c[67]=c[70]}d=c[70]|0;f=c[71]|0}if((i|0)==9){d=c[70]|0;c[67]=d;i=19}else if((i|0)==16){a[795]=0;c[70]=d;i=19}else if((i|0)==18)if(!(a[794]|0)){d=g;i=19}else d=0;do if((i|0)==19){c:while(1){f=d+2|0;c[70]=f;h=f;if(d>>>0>=(c[71]|0)>>>0){i=82;break}d:do switch(b[f>>1]|0){case 9:case 10:case 11:case 12:case 13:case 32:break;case 101:{if(((b[396]|0)==0?da(f)|0:0)?(Q(d+4|0,16,10)|0)==0:0){B();i=81}else i=81;break}case 105:{if(da(f)|0?(Q(d+4|0,26,10)|0)==0:0){C();i=81}else i=81;break}case 99:{if((da(f)|0?(Q(d+4|0,36,8)|0)==0:0)?na(b[d+12>>1]|0)|0:0){a[797]=1;i=81}else i=81;break}case 40:{h=c[68]|0;f=b[396]|0;i=f&65535;c[h+(i<<3)>>2]=1;g=c[67]|0;b[396]=f+1<<16>>16;c[h+(i<<3)+4>>2]=g;i=81;break}case 41:{f=b[396]|0;if(!(f<<16>>16)){i=36;break c}f=f+-1<<16>>16;b[396]=f;g=b[395]|0;if(g<<16>>16!=0?(j=c[(c[69]|0)+((g&65535)+-1<<2)>>2]|0,(c[j+20>>2]|0)==(c[(c[68]|0)+((f&65535)<<3)+4>>2]|0)):0){f=j+4|0;if(!(c[f>>2]|0))c[f>>2]=h;c[j+12>>2]=d+4;b[395]=g+-1<<16>>16;i=81}else i=81;break}case 123:{i=c[67]|0;h=c[61]|0;d=i;do if((b[i>>1]|0)==41&(h|0)!=0?(c[h+4>>2]|0)==(i|0):0){f=c[62]|0;c[61]=f;if(!f){c[57]=0;break}else{c[f+28>>2]=0;break}}while(0);h=c[68]|0;g=b[396]|0;i=g&65535;c[h+(i<<3)>>2]=(a[797]|0)==0?2:6;b[396]=g+1<<16>>16;c[h+(i<<3)+4>>2]=d;a[797]=0;i=81;break}case 125:{d=b[396]|0;if(!(d<<16>>16)){i=49;break c}h=c[68]|0;i=d+-1<<16>>16;b[396]=i;if((c[h+((i&65535)<<3)>>2]|0)==4){H();i=81}else i=81;break}case 39:{J(39);i=81;break}case 34:{J(34);i=81;break}case 47:switch(b[d+4>>1]|0){case 47:{_();break d}case 42:{P(1);break d}default:{d=c[67]|0;h=b[d>>1]|0;e:do if(!(T(h)|0)){switch(h<<16>>16){case 41:if(ba(c[(c[68]|0)+(e[396]<<3)+4>>2]|0)|0){i=69;break e}else{i=66;break e}case 125:break;default:{i=66;break e}}f=c[68]|0;g=e[396]|0;if(!(O(c[f+(g<<3)+4>>2]|0)|0)?(c[f+(g<<3)>>2]|0)!=6:0)i=66;else i=69}else switch(h<<16>>16){case 46:if(((b[d+-2>>1]|0)+-48&65535)<10){i=66;break e}else{i=69;break e}case 43:if((b[d+-2>>1]|0)==43){i=66;break e}else{i=69;break e}case 45:if((b[d+-2>>1]|0)==45){i=66;break e}else{i=69;break e}default:{i=69;break e}}while(0);f:do if((i|0)==66){i=0;if(!(D(d)|0)){switch(h<<16>>16){case 0:{i=69;break f}case 47:{if(a[796]|0){i=69;break f}break}default:{}}g=c[3]|0;f=h;do{if(d>>>0<=g>>>0)break;d=d+-2|0;c[67]=d;f=b[d>>1]|0}while(!(Z(f)|0));if(ca(f)|0){do{if(d>>>0<=g>>>0)break;d=d+-2|0;c[67]=d}while(ca(b[d>>1]|0)|0);if(X(d)|0){N();a[796]=0;i=81;break d}else d=1}else d=1}else i=69}while(0);if((i|0)==69){N();d=0}a[796]=d;i=81;break d}}case 96:{h=c[68]|0;g=b[396]|0;i=g&65535;c[h+(i<<3)+4>>2]=c[67];b[396]=g+1<<16>>16;c[h+(i<<3)>>2]=3;H();i=81;break}default:i=81}while(0);if((i|0)==81){i=0;c[67]=c[70]}d=c[70]|0}if((i|0)==36){ma();d=0;break}else if((i|0)==49){ma();d=0;break}else if((i|0)==82){d=(a[794]|0)==0?(b[395]|b[396])<<16>>16==0:0;break}}while(0);v=k;return d|0}function B(){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;h=c[70]|0;i=c[63]|0;l=h+12|0;c[70]=l;f=I(1)|0;d=c[70]|0;if(!((d|0)==(l|0)?!(S(f)|0):0))k=3;a:do if((k|0)==3){b:do switch(f<<16>>16){case 123:{c[70]=d+2;d=I(1)|0;f=c[70]|0;while(1){if(oa(d)|0){J(d);d=(c[70]|0)+2|0;c[70]=d}else{$(d)|0;d=c[70]|0}I(1)|0;d=K(f,d)|0;if(d<<16>>16==44){c[70]=(c[70]|0)+2;d=I(1)|0}e=f;f=c[70]|0;if(d<<16>>16==125){k=15;break}if((f|0)==(e|0)){k=12;break}if(f>>>0>(c[71]|0)>>>0){k=14;break}}if((k|0)==12){ma();break a}else if((k|0)==14){ma();break a}else if((k|0)==15){c[70]=f+2;break b}break}case 42:{c[70]=d+2;I(1)|0;l=c[70]|0;K(l,l)|0;break}default:{a[795]=0;switch(f<<16>>16){case 100:{h=d+14|0;c[70]=h;e=I(1)|0;if(e<<16>>16==97){e=c[70]|0;if((da(e)|0?(Q(e+2|0,58,8)|0)==0:0)?(g=e+10|0,ca(b[g>>1]|0)|0):0){c[70]=g;e=I(0)|0;k=23}else{e=97;k=32}}else k=23;c:do if((k|0)==23){if(e<<16>>16==102){e=c[70]|0;if(!(da(e)|0)){e=102;k=32;break}if(Q(e+2|0,66,14)|0){e=102;k=32;break}f=e+16|0;e=b[f>>1]|0;if(!(na(e)|0))switch(e<<16>>16){case 40:case 42:break;default:{e=102;k=32;break c}}c[70]=f;e=I(1)|0;if(e<<16>>16==42){c[70]=(c[70]|0)+2;e=I(1)|0}if(e<<16>>16==40){W(d,h,0,0);c[70]=d+12;break a}else g=1}else g=0;f=c[70]|0;do if(e<<16>>16==99)if((da(f)|0?(Q(f+2|0,36,8)|0)==0:0)?(j=f+10|0,l=b[j>>1]|0,na(l)|0|l<<16>>16==123):0){c[70]=j;e=I(1)|0;if(e<<16>>16==123){W(d,h,0,0);c[70]=d+12;break a}else{f=c[70]|0;$(e)|0;break}}else{e=99;k=40}else k=40;while(0);if((k|0)==40){$(e)|0;if(!g){k=43;break}}e=c[70]|0;if(e>>>0>f>>>0){W(d,h,f,e);d=(c[70]|0)+-2|0}else k=43}while(0);if((k|0)==32){$(e)|0;k=43}if((k|0)==43){W(d,h,0,0);d=d+12|0}c[70]=d;break a}case 97:{c[70]=d+10;I(1)|0;d=c[70]|0;k=46;break}case 102:{k=46;break}case 99:{if((Q(d+2|0,36,8)|0)==0?(e=d+10|0,Z(b[e>>1]|0)|0):0){c[70]=e;l=I(1)|0;k=c[70]|0;$(l)|0;l=c[70]|0;W(k,l,k,l);c[70]=(c[70]|0)+-2;break a}d=d+4|0;c[70]=d;break}case 108:case 118:break;default:break a}if((k|0)==46){c[70]=d+16;d=I(1)|0;if(d<<16>>16==42){c[70]=(c[70]|0)+2;d=I(1)|0}k=c[70]|0;$(d)|0;l=c[70]|0;W(k,l,k,l);c[70]=(c[70]|0)+-2;break a}d=d+4|0;c[70]=d;a[795]=0;d:while(1){c[70]=d+2;l=I(1)|0;d=c[70]|0;switch(($(l)|0)<<16>>16){case 91:case 123:break d;default:{}}e=c[70]|0;if((e|0)==(d|0))break a;W(d,e,d,e);if((I(1)|0)<<16>>16!=44)break;d=c[70]|0}c[70]=(c[70]|0)+-2;break a}}while(0);l=(I(1)|0)<<16>>16==102;d=c[70]|0;if(l?(Q(d+2|0,52,6)|0)==0:0){c[70]=d+8;E(h,I(1)|0);d=(i|0)==0?232:i+16|0;while(1){d=c[d>>2]|0;if(!d)break a;c[d+12>>2]=0;c[d+8>>2]=0;d=d+16|0}}c[70]=d+-2}while(0);return}function C(){var d=0,e=0,f=0,g=0,h=0,i=0;h=c[70]|0;e=h+12|0;c[70]=e;a:do switch((I(1)|0)<<16>>16){case 40:{e=c[68]|0;i=b[396]|0;f=i&65535;c[e+(f<<3)>>2]=5;d=c[70]|0;b[396]=i+1<<16>>16;c[e+(f<<3)+4>>2]=d;if((b[c[67]>>1]|0)!=46){c[70]=d+2;i=I(1)|0;L(h,c[70]|0,0,d);e=c[61]|0;f=c[69]|0;h=b[395]|0;b[395]=h+1<<16>>16;c[f+((h&65535)<<2)>>2]=e;switch(i<<16>>16){case 39:{J(39);break}case 34:{J(34);break}default:{c[70]=(c[70]|0)+-2;break a}}d=(c[70]|0)+2|0;c[70]=d;switch((I(1)|0)<<16>>16){case 44:{c[70]=(c[70]|0)+2;I(1)|0;h=c[61]|0;c[h+4>>2]=d;i=c[70]|0;c[h+16>>2]=i;a[h+24>>0]=1;c[70]=i+-2;break a}case 41:{b[396]=(b[396]|0)+-1<<16>>16;i=c[61]|0;c[i+4>>2]=d;c[i+12>>2]=(c[70]|0)+2;a[i+24>>0]=1;b[395]=(b[395]|0)+-1<<16>>16;break a}default:{c[70]=(c[70]|0)+-2;break a}}}break}case 46:{c[70]=(c[70]|0)+2;if(((I(1)|0)<<16>>16==109?(d=c[70]|0,(Q(d+2|0,44,6)|0)==0):0)?(b[c[67]>>1]|0)!=46:0)L(h,h,d+8|0,2);break}case 42:case 39:case 34:{g=17;break}case 123:{d=c[70]|0;if(b[396]|0){c[70]=d+-2;break a}while(1){if(d>>>0>=(c[71]|0)>>>0)break;d=I(1)|0;if(!(oa(d)|0)){if(d<<16>>16==125){g=32;break}}else J(d);d=(c[70]|0)+2|0;c[70]=d}if((g|0)==32)c[70]=(c[70]|0)+2;I(1)|0;d=c[70]|0;if(Q(d,50,8)|0){ma();break a}c[70]=d+8;d=I(1)|0;if(oa(d)|0){E(h,d);break a}else{ma();break a}}default:if((c[70]|0)==(e|0))c[70]=h+10;else g=17}while(0);do if((g|0)==17){if(b[396]|0){c[70]=(c[70]|0)+-2;break}d=c[71]|0;e=c[70]|0;while(1){if(e>>>0>=d>>>0){g=24;break}f=b[e>>1]|0;if(oa(f)|0){g=22;break}i=e+2|0;c[70]=i;e=i}if((g|0)==22){E(h,f);break}else if((g|0)==24){ma();break}}while(0);return}function D(a){a=a|0;a:do switch(b[a>>1]|0){case 100:switch(b[a+-2>>1]|0){case 105:{a=V(a+-4|0,90,2)|0;break a}case 108:{a=V(a+-4|0,94,3)|0;break a}default:{a=0;break a}}case 101:switch(b[a+-2>>1]|0){case 115:switch(b[a+-4>>1]|0){case 108:{a=Y(a+-6|0,101)|0;break a}case 97:{a=Y(a+-6|0,99)|0;break a}default:{a=0;break a}}case 116:{a=V(a+-4|0,100,4)|0;break a}case 117:{a=V(a+-4|0,108,6)|0;break a}default:{a=0;break a}}case 102:{if((b[a+-2>>1]|0)==111?(b[a+-4>>1]|0)==101:0)switch(b[a+-6>>1]|0){case 99:{a=V(a+-8|0,120,6)|0;break a}case 112:{a=V(a+-8|0,132,2)|0;break a}default:{a=0;break a}}else a=0;break}case 107:{a=V(a+-2|0,136,4)|0;break}case 110:{a=a+-2|0;if(Y(a,105)|0)a=1;else a=V(a,144,5)|0;break}case 111:{a=Y(a+-2|0,100)|0;break}case 114:{a=V(a+-2|0,154,7)|0;break}case 116:{a=V(a+-2|0,168,4)|0;break}case 119:switch(b[a+-2>>1]|0){case 101:{a=Y(a+-4|0,110)|0;break a}case 111:{a=V(a+-4|0,176,3)|0;break a}default:{a=0;break a}}default:a=0}while(0);return a|0}function E(a,b){a=a|0;b=b|0;var d=0,e=0;d=(c[70]|0)+2|0;switch(b<<16>>16){case 39:{J(39);e=5;break}case 34:{J(34);e=5;break}default:ma()}do if((e|0)==5){L(a,d,c[70]|0,1);c[70]=(c[70]|0)+2;e=(I(0)|0)<<16>>16==97;b=c[70]|0;if(e?(Q(b+2|0,80,10)|0)==0:0){c[70]=b+12;if((I(1)|0)<<16>>16!=123){c[70]=b;break}a=c[70]|0;d=a;a:while(1){c[70]=d+2;d=I(1)|0;switch(d<<16>>16){case 39:{J(39);c[70]=(c[70]|0)+2;d=I(1)|0;break}case 34:{J(34);c[70]=(c[70]|0)+2;d=I(1)|0;break}default:d=$(d)|0}if(d<<16>>16!=58){e=16;break}c[70]=(c[70]|0)+2;switch((I(1)|0)<<16>>16){case 39:{J(39);break}case 34:{J(34);break}default:{e=20;break a}}c[70]=(c[70]|0)+2;switch((I(1)|0)<<16>>16){case 125:{e=25;break a}case 44:break;default:{e=24;break a}}c[70]=(c[70]|0)+2;if((I(1)|0)<<16>>16==125){e=25;break}d=c[70]|0}if((e|0)==16){c[70]=b;break}else if((e|0)==20){c[70]=b;break}else if((e|0)==24){c[70]=b;break}else if((e|0)==25){e=c[61]|0;c[e+16>>2]=a;c[e+12>>2]=(c[70]|0)+2;break}}c[70]=b+-2}while(0);return}function H(){var a=0,d=0,e=0,f=0;d=c[71]|0;e=c[70]|0;a:while(1){a=e+2|0;if(e>>>0>=d>>>0){d=10;break}switch(b[a>>1]|0){case 96:{d=7;break a}case 36:{if((b[e+4>>1]|0)==123){d=6;break a}break}case 92:{a=e+4|0;break}default:{}}e=a}if((d|0)==6){a=e+4|0;c[70]=a;d=c[68]|0;f=b[396]|0;e=f&65535;c[d+(e<<3)>>2]=4;b[396]=f+1<<16>>16;c[d+(e<<3)+4>>2]=a}else if((d|0)==7){c[70]=a;e=c[68]|0;f=(b[396]|0)+-1<<16>>16;b[396]=f;if((c[e+((f&65535)<<3)>>2]|0)!=3)ma()}else if((d|0)==10){c[70]=a;ma()}return}function I(a){a=a|0;var d=0,e=0,f=0;e=c[70]|0;a:do{d=b[e>>1]|0;b:do if(d<<16>>16!=47)if(a)if(na(d)|0)break;else break a;else if(ca(d)|0)break;else break a;else switch(b[e+2>>1]|0){case 47:{_();break b}case 42:{P(a);break b}default:{d=47;break a}}while(0);f=c[70]|0;e=f+2|0;c[70]=e}while(f>>>0<(c[71]|0)>>>0);return d|0}function J(a){a=a|0;var d=0,e=0,f=0,g=0;g=c[71]|0;d=c[70]|0;while(1){f=d+2|0;if(d>>>0>=g>>>0){d=9;break}e=b[f>>1]|0;if(e<<16>>16==a<<16>>16){d=10;break}if(e<<16>>16==92){e=d+4|0;if((b[e>>1]|0)==13){d=d+6|0;d=(b[d>>1]|0)==10?d:e}else d=e}else if(ra(e)|0){d=9;break}else d=f}if((d|0)==9){c[70]=f;ma()}else if((d|0)==10)c[70]=f;return}function K(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0;e=c[70]|0;f=b[e>>1]|0;h=(a|0)==(d|0);g=h?0:a;h=h?0:d;if(f<<16>>16==97){c[70]=e+4;e=I(1)|0;a=c[70]|0;if(oa(e)|0){J(e);d=(c[70]|0)+2|0;c[70]=d}else{$(e)|0;d=c[70]|0}f=I(1)|0;e=c[70]|0}if((e|0)!=(a|0))W(a,d,g,h);return f|0}function L(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0;g=c[65]|0;c[65]=g+32;h=c[61]|0;c[((h|0)==0?228:h+28|0)>>2]=g;c[62]=h;c[61]=g;c[g+8>>2]=b;if(2==(f|0))b=e;else b=1==(f|0)?e+2|0:0;c[g+12>>2]=b;c[g>>2]=d;c[g+4>>2]=e;c[g+16>>2]=0;c[g+20>>2]=f;a[g+24>>0]=1==(f|0)&1;c[g+28>>2]=0;return}function M(){var a=0,d=0,e=0;e=c[71]|0;d=c[70]|0;a:while(1){a=d+2|0;if(d>>>0>=e>>>0){d=6;break}switch(b[a>>1]|0){case 13:case 10:{d=6;break a}case 93:{d=7;break a}case 92:{a=d+4|0;break}default:{}}d=a}if((d|0)==6){c[70]=a;ma();a=0}else if((d|0)==7){c[70]=a;a=93}return a|0}function N(){var a=0,d=0,e=0;a:while(1){a=c[70]|0;d=a+2|0;c[70]=d;if(a>>>0>=(c[71]|0)>>>0){e=7;break}switch(b[d>>1]|0){case 13:case 10:{e=7;break a}case 47:break a;case 91:{M()|0;break}case 92:{c[70]=a+4;break}default:{}}}if((e|0)==7)ma();return}function O(a){a=a|0;switch(b[a>>1]|0){case 62:{a=(b[a+-2>>1]|0)==61;break}case 41:case 59:{a=1;break}case 104:{a=V(a+-2|0,202,4)|0;break}case 121:{a=V(a+-2|0,210,6)|0;break}case 101:{a=V(a+-2|0,222,3)|0;break}default:a=0}return a|0}function P(a){a=a|0;var d=0,e=0,f=0,g=0,h=0;g=(c[70]|0)+2|0;c[70]=g;e=c[71]|0;while(1){d=g+2|0;if(g>>>0>=e>>>0)break;f=b[d>>1]|0;if(!a?ra(f)|0:0)break;if(f<<16>>16==42?(b[g+4>>1]|0)==47:0){h=8;break}g=d}if((h|0)==8){c[70]=d;d=g+4|0}c[70]=d;return}function Q(b,c,d){b=b|0;c=c|0;d=d|0;var e=0,f=0;a:do if(!d)b=0;else{while(1){e=a[b>>0]|0;f=a[c>>0]|0;if(e<<24>>24!=f<<24>>24)break;d=d+-1|0;if(!d){b=0;break a}else{b=b+1|0;c=c+1|0}}b=(e&255)-(f&255)|0}while(0);return b|0}function S(a){a=a|0;a:do switch(a<<16>>16){case 38:case 37:case 33:{a=1;break}default:if((a&-8)<<16>>16==40|(a+-58&65535)<6)a=1;else{switch(a<<16>>16){case 91:case 93:case 94:{a=1;break a}default:{}}a=(a+-123&65535)<4}}while(0);return a|0}function T(a){a=a|0;a:do switch(a<<16>>16){case 38:case 37:case 33:break;default:if(!((a+-58&65535)<6|(a+-40&65535)<7&a<<16>>16!=41)){switch(a<<16>>16){case 91:case 94:break a;default:{}}return a<<16>>16!=125&(a+-123&65535)<4|0}}while(0);return 1}function U(a){a=a|0;var d=0,e=0,f=0,g=0;e=v;v=v+16|0;f=e;c[f>>2]=0;c[64]=a;d=c[3]|0;g=d+(a<<1)|0;a=g+2|0;b[g>>1]=0;c[f>>2]=a;c[65]=a;c[57]=0;c[61]=0;c[59]=0;c[58]=0;c[63]=0;c[60]=0;v=e;return d|0}function V(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0;f=a+(0-e<<1)|0;g=f+2|0;a=c[3]|0;if(g>>>0>=a>>>0?(Q(g,d,e<<1)|0)==0:0)if((g|0)==(a|0))a=1;else a=Z(b[f>>1]|0)|0;else a=0;return a|0}function W(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;f=c[65]|0;c[65]=f+20;g=c[63]|0;c[((g|0)==0?232:g+16|0)>>2]=f;c[63]=f;c[f>>2]=a;c[f+4>>2]=b;c[f+8>>2]=d;c[f+12>>2]=e;c[f+16>>2]=0;return}function X(a){a=a|0;switch(b[a>>1]|0){case 107:{a=V(a+-2|0,136,4)|0;break}case 101:{if((b[a+-2>>1]|0)==117)a=V(a+-4|0,108,6)|0;else a=0;break}default:a=0}return a|0}function Y(a,d){a=a|0;d=d|0;var e=0;e=c[3]|0;if(e>>>0<=a>>>0?(b[a>>1]|0)==d<<16>>16:0)if((e|0)==(a|0))e=1;else e=Z(b[a+-2>>1]|0)|0;else e=0;return e|0}function Z(a){a=a|0;a:do if((a+-9&65535)<5)a=1;else{switch(a<<16>>16){case 32:case 160:{a=1;break a}default:{}}a=a<<16>>16!=46&(S(a)|0)}while(0);return a|0}function _(){var a=0,d=0,e=0;a=c[71]|0;e=c[70]|0;a:while(1){d=e+2|0;if(e>>>0>=a>>>0)break;switch(b[d>>1]|0){case 13:case 10:break a;default:e=d}}c[70]=d;return}function $(a){a=a|0;while(1){if(na(a)|0)break;if(S(a)|0)break;a=(c[70]|0)+2|0;c[70]=a;a=b[a>>1]|0;if(!(a<<16>>16)){a=0;break}}return a|0}function aa(){var a=0;a=c[(c[59]|0)+20>>2]|0;switch(a|0){case 1:{a=-1;break}case 2:{a=-2;break}default:a=a-(c[3]|0)>>1}return a|0}function ba(a){a=a|0;if(!(V(a,182,5)|0)?!(V(a,192,3)|0):0)a=V(a,198,2)|0;else a=1;return a|0}function ca(a){a=a|0;switch(a<<16>>16){case 160:case 32:case 12:case 11:case 9:{a=1;break}default:a=0}return a|0}function da(a){a=a|0;if((c[3]|0)==(a|0))a=1;else a=Z(b[a+-2>>1]|0)|0;return a|0}function ea(){var a=0;a=c[(c[60]|0)+12>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function fa(){var a=0;a=c[(c[59]|0)+12>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ga(){var a=0;a=c[(c[60]|0)+8>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ha(){var a=0;a=c[(c[59]|0)+16>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ia(){var a=0;a=c[(c[59]|0)+4>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ja(){var a=0;a=c[59]|0;a=c[((a|0)==0?228:a+28|0)>>2]|0;c[59]=a;return (a|0)!=0|0}function ka(){var a=0;a=c[60]|0;a=c[((a|0)==0?232:a+16|0)>>2]|0;c[60]=a;return (a|0)!=0|0}function ma(){a[794]=1;c[66]=(c[70]|0)-(c[3]|0)>>1;c[70]=(c[71]|0)+2;return}function na(a){a=a|0;return (a|128)<<16>>16==160|(a+-9&65535)<5|0}function oa(a){a=a|0;return a<<16>>16==39|a<<16>>16==34|0}function pa(){return (c[(c[59]|0)+8>>2]|0)-(c[3]|0)>>1|0}function qa(){return (c[(c[60]|0)+4>>2]|0)-(c[3]|0)>>1|0}function ra(a){a=a|0;return a<<16>>16==13|a<<16>>16==10|0}function sa(){return (c[c[59]>>2]|0)-(c[3]|0)>>1|0}function ta(){return (c[c[60]>>2]|0)-(c[3]|0)>>1|0}function ua(){return d[(c[59]|0)+24>>0]|0|0}function va(a){a=a|0;c[3]=a;return}function ya(){return (a[795]|0)!=0|0}function Ca(){return c[66]|0} function su(a) { - a = a | 0; - v = a + 992 + 15 & -16; - return 992; - } - return { - su,ai:ha,e:Ca,ee:qa,ele:ea,els:ga,es:ta,f:ya,id:aa,ie:ia,ip:ua,is:sa,p:z,re:ka,ri:ja,sa:U,se:fa,ses:va,ss:pa}} \ No newline at end of file diff --git a/lib/lexer.emcc.asm.js b/lib/lexer.emcc.asm.js deleted file mode 100644 index b738ebc..0000000 --- a/lib/lexer.emcc.asm.js +++ /dev/null @@ -1,9 +0,0 @@ -Module["asm"]=(/** @suppress {uselessCode} */ function(global,env,buffer) { -"use asm";var a=new global.Int8Array(buffer),b=new global.Int16Array(buffer),c=new global.Int32Array(buffer),d=new global.Uint8Array(buffer),e=new global.Uint16Array(buffer),f=env.g|0,g=env.h|0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0.0,p=env.a,q=env.b,r=env.c,s=env.d,t=env.e,u=env.f,v=1024,w=4998992,x=0.0; -// EMSCRIPTEN_START_FUNCS -function y(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,w=0,x=0;x=v;v=v+16|0;n=x;do if(a>>>0<245){k=a>>>0<11?16:a+11&-8;a=k>>>3;m=c[73]|0;d=m>>>a;if(d&3|0){e=(d&1^1)+a|0;f=332+(e<<1<<2)|0;b=f+8|0;a=c[b>>2]|0;g=a+8|0;d=c[g>>2]|0;if((d|0)==(f|0))c[73]=m&~(1<>2]=f;c[b>>2]=d}w=e<<3;c[a+4>>2]=w|3;w=a+w+4|0;c[w>>2]=c[w>>2]|1;w=g;v=x;return w|0}l=c[75]|0;if(k>>>0>l>>>0){if(d|0){i=2<>>12&16;d=d>>>i;a=d>>>5&8;d=d>>>a;g=d>>>2&4;d=d>>>g;b=d>>>1&2;d=d>>>b;e=d>>>1&1;e=(a|i|g|b|e)+(d>>>e)|0;d=332+(e<<1<<2)|0;b=d+8|0;g=c[b>>2]|0;i=g+8|0;a=c[i>>2]|0;if((a|0)==(d|0)){a=m&~(1<>2]=d;c[b>>2]=a;a=m}w=e<<3;h=w-k|0;c[g+4>>2]=k|3;f=g+k|0;c[f+4>>2]=h|1;c[g+w>>2]=h;if(l|0){e=c[78]|0;b=l>>>3;d=332+(b<<1<<2)|0;b=1<>2]|0}c[b>>2]=e;c[a+12>>2]=e;c[e+8>>2]=a;c[e+12>>2]=d}c[75]=h;c[78]=f;w=i;v=x;return w|0}g=c[74]|0;if(g){i=(g&0-g)+-1|0;f=i>>>12&16;i=i>>>f;e=i>>>5&8;i=i>>>e;h=i>>>2&4;i=i>>>h;d=i>>>1&2;i=i>>>d;j=i>>>1&1;j=c[596+((e|f|h|d|j)+(i>>>j)<<2)>>2]|0;i=(c[j+4>>2]&-8)-k|0;d=j;while(1){a=c[d+16>>2]|0;if(!a){a=c[d+20>>2]|0;if(!a)break}d=(c[a+4>>2]&-8)-k|0;h=d>>>0>>0;i=h?d:i;d=a;j=h?a:j}h=j+k|0;if(h>>>0>j>>>0){f=c[j+24>>2]|0;b=c[j+12>>2]|0;do if((b|0)==(j|0)){a=j+20|0;b=c[a>>2]|0;if(!b){a=j+16|0;b=c[a>>2]|0;if(!b){d=0;break}}while(1){e=b+20|0;d=c[e>>2]|0;if(!d){e=b+16|0;d=c[e>>2]|0;if(!d)break;else{b=d;a=e}}else{b=d;a=e}}c[a>>2]=0;d=b}else{d=c[j+8>>2]|0;c[d+12>>2]=b;c[b+8>>2]=d;d=b}while(0);do if(f|0){b=c[j+28>>2]|0;a=596+(b<<2)|0;if((j|0)==(c[a>>2]|0)){c[a>>2]=d;if(!d){c[74]=g&~(1<>2]|0)==(j|0)?w:f+20|0)>>2]=d;if(!d)break}c[d+24>>2]=f;b=c[j+16>>2]|0;if(b|0){c[d+16>>2]=b;c[b+24>>2]=d}b=c[j+20>>2]|0;if(b|0){c[d+20>>2]=b;c[b+24>>2]=d}}while(0);if(i>>>0<16){w=i+k|0;c[j+4>>2]=w|3;w=j+w+4|0;c[w>>2]=c[w>>2]|1}else{c[j+4>>2]=k|3;c[h+4>>2]=i|1;c[h+i>>2]=i;if(l|0){e=c[78]|0;b=l>>>3;d=332+(b<<1<<2)|0;b=1<>2]|0}c[b>>2]=e;c[a+12>>2]=e;c[e+8>>2]=a;c[e+12>>2]=d}c[75]=i;c[78]=h}w=j+8|0;v=x;return w|0}else m=k}else m=k}else m=k}else if(a>>>0<=4294967231){a=a+11|0;k=a&-8;e=c[74]|0;if(e){d=0-k|0;a=a>>>8;if(a)if(k>>>0>16777215)j=31;else{m=(a+1048320|0)>>>16&8;q=a<>>16&4;q=q<>>16&2;j=14-(i|m|j)+(q<>>15)|0;j=k>>>(j+7|0)&1|j<<1}else j=0;a=c[596+(j<<2)>>2]|0;a:do if(!a){f=0;a=0;q=61}else{f=0;h=k<<((j|0)==31?0:25-(j>>>1)|0);i=a;a=0;while(1){g=(c[i+4>>2]&-8)-k|0;if(g>>>0>>0)if(!g){d=0;f=i;a=i;q=65;break a}else{d=g;a=i}q=c[i+20>>2]|0;i=c[i+16+(h>>>31<<2)>>2]|0;f=(q|0)==0|(q|0)==(i|0)?f:q;if(!i){q=61;break}else h=h<<1}}while(0);if((q|0)==61){if((f|0)==0&(a|0)==0){a=2<>>12&16;a=a>>>i;h=a>>>5&8;a=a>>>h;j=a>>>2&4;a=a>>>j;m=a>>>1&2;a=a>>>m;f=a>>>1&1;f=c[596+((h|i|j|m|f)+(a>>>f)<<2)>>2]|0;a=0}if(!f){i=d;g=a}else q=65}if((q|0)==65)while(1){m=(c[f+4>>2]&-8)-k|0;g=m>>>0>>0;d=g?m:d;g=g?f:a;a=c[f+16>>2]|0;if(!a)a=c[f+20>>2]|0;if(!a){i=d;break}else{f=a;a=g}}if(((g|0)!=0?i>>>0<((c[75]|0)-k|0)>>>0:0)?(l=g+k|0,l>>>0>g>>>0):0){h=c[g+24>>2]|0;b=c[g+12>>2]|0;do if((b|0)==(g|0)){a=g+20|0;b=c[a>>2]|0;if(!b){a=g+16|0;b=c[a>>2]|0;if(!b){b=0;break}}while(1){f=b+20|0;d=c[f>>2]|0;if(!d){f=b+16|0;d=c[f>>2]|0;if(!d)break;else{b=d;a=f}}else{b=d;a=f}}c[a>>2]=0}else{w=c[g+8>>2]|0;c[w+12>>2]=b;c[b+8>>2]=w}while(0);do if(h){a=c[g+28>>2]|0;d=596+(a<<2)|0;if((g|0)==(c[d>>2]|0)){c[d>>2]=b;if(!b){e=e&~(1<>2]|0)==(g|0)?w:h+20|0)>>2]=b;if(!b)break}c[b+24>>2]=h;a=c[g+16>>2]|0;if(a|0){c[b+16>>2]=a;c[a+24>>2]=b}a=c[g+20>>2]|0;if(a){c[b+20>>2]=a;c[a+24>>2]=b}}while(0);b:do if(i>>>0<16){w=i+k|0;c[g+4>>2]=w|3;w=g+w+4|0;c[w>>2]=c[w>>2]|1}else{c[g+4>>2]=k|3;c[l+4>>2]=i|1;c[l+i>>2]=i;b=i>>>3;if(i>>>0<256){d=332+(b<<1<<2)|0;a=c[73]|0;b=1<>2]|0}c[b>>2]=l;c[a+12>>2]=l;c[l+8>>2]=a;c[l+12>>2]=d;break}b=i>>>8;if(b)if(i>>>0>16777215)d=31;else{u=(b+1048320|0)>>>16&8;w=b<>>16&4;w=w<>>16&2;d=14-(t|u|d)+(w<>>15)|0;d=i>>>(d+7|0)&1|d<<1}else d=0;b=596+(d<<2)|0;c[l+28>>2]=d;a=l+16|0;c[a+4>>2]=0;c[a>>2]=0;a=1<>2]=l;c[l+24>>2]=b;c[l+12>>2]=l;c[l+8>>2]=l;break}b=c[b>>2]|0;c:do if((c[b+4>>2]&-8|0)!=(i|0)){e=i<<((d|0)==31?0:25-(d>>>1)|0);while(1){d=b+16+(e>>>31<<2)|0;a=c[d>>2]|0;if(!a)break;if((c[a+4>>2]&-8|0)==(i|0)){b=a;break c}else{e=e<<1;b=a}}c[d>>2]=l;c[l+24>>2]=b;c[l+12>>2]=l;c[l+8>>2]=l;break b}while(0);u=b+8|0;w=c[u>>2]|0;c[w+12>>2]=l;c[u>>2]=l;c[l+8>>2]=w;c[l+12>>2]=b;c[l+24>>2]=0}while(0);w=g+8|0;v=x;return w|0}else m=k}else m=k}else m=-1;while(0);d=c[75]|0;if(d>>>0>=m>>>0){a=d-m|0;b=c[78]|0;if(a>>>0>15){w=b+m|0;c[78]=w;c[75]=a;c[w+4>>2]=a|1;c[b+d>>2]=a;c[b+4>>2]=m|3}else{c[75]=0;c[78]=0;c[b+4>>2]=d|3;w=b+d+4|0;c[w>>2]=c[w>>2]|1}w=b+8|0;v=x;return w|0}h=c[76]|0;if(h>>>0>m>>>0){t=h-m|0;c[76]=t;w=c[79]|0;u=w+m|0;c[79]=u;c[u+4>>2]=t|1;c[w+4>>2]=m|3;w=w+8|0;v=x;return w|0}if(!(c[191]|0)){c[193]=4096;c[192]=4096;c[194]=-1;c[195]=-1;c[196]=0;c[184]=0;c[191]=n&-16^1431655768;a=4096}else a=c[193]|0;i=m+48|0;j=m+47|0;g=a+j|0;e=0-a|0;k=g&e;if(k>>>0<=m>>>0){w=0;v=x;return w|0}a=c[183]|0;if(a|0?(l=c[181]|0,n=l+k|0,n>>>0<=l>>>0|n>>>0>a>>>0):0){w=0;v=x;return w|0}d:do if(!(c[184]&4)){d=c[79]|0;e:do if(d){f=740;while(1){n=c[f>>2]|0;if(n>>>0<=d>>>0?(n+(c[f+4>>2]|0)|0)>>>0>d>>>0:0)break;a=c[f+8>>2]|0;if(!a){q=128;break e}else f=a}b=g-h&e;if(b>>>0<2147483647){a=R(b)|0;if((a|0)==((c[f>>2]|0)+(c[f+4>>2]|0)|0)){if((a|0)!=(-1|0)){h=a;g=b;q=145;break d}}else{e=a;q=136}}else b=0}else q=128;while(0);do if((q|0)==128){d=R(0)|0;if((d|0)!=(-1|0)?(b=d,o=c[192]|0,p=o+-1|0,b=((p&b|0)==0?0:(p+b&0-o)-b|0)+k|0,o=c[181]|0,p=b+o|0,b>>>0>m>>>0&b>>>0<2147483647):0){n=c[183]|0;if(n|0?p>>>0<=o>>>0|p>>>0>n>>>0:0){b=0;break}a=R(b)|0;if((a|0)==(d|0)){h=d;g=b;q=145;break d}else{e=a;q=136}}else b=0}while(0);do if((q|0)==136){d=0-b|0;if(!(i>>>0>b>>>0&(b>>>0<2147483647&(e|0)!=(-1|0))))if((e|0)==(-1|0)){b=0;break}else{h=e;g=b;q=145;break d}a=c[193]|0;a=j-b+a&0-a;if(a>>>0>=2147483647){h=e;g=b;q=145;break d}if((R(a)|0)==(-1|0)){R(d)|0;b=0;break}else{h=e;g=a+b|0;q=145;break d}}while(0);c[184]=c[184]|4;q=143}else{b=0;q=143}while(0);if(((q|0)==143?k>>>0<2147483647:0)?(r=R(k)|0,p=R(0)|0,t=p-r|0,s=t>>>0>(m+40|0)>>>0,!((r|0)==(-1|0)|s^1|r>>>0

>>0&((r|0)!=(-1|0)&(p|0)!=(-1|0))^1)):0){h=r;g=s?t:b;q=145}if((q|0)==145){b=(c[181]|0)+g|0;c[181]=b;if(b>>>0>(c[182]|0)>>>0)c[182]=b;j=c[79]|0;f:do if(j){e=740;while(1){b=c[e>>2]|0;a=c[e+4>>2]|0;if((h|0)==(b+a|0)){q=154;break}d=c[e+8>>2]|0;if(!d)break;else e=d}if(((q|0)==154?(u=e+4|0,(c[e+12>>2]&8|0)==0):0)?h>>>0>j>>>0&b>>>0<=j>>>0:0){c[u>>2]=a+g;w=(c[76]|0)+g|0;t=j+8|0;t=(t&7|0)==0?0:0-t&7;u=j+t|0;t=w-t|0;c[79]=u;c[76]=t;c[u+4>>2]=t|1;c[j+w+4>>2]=40;c[80]=c[195];break}if(h>>>0<(c[77]|0)>>>0)c[77]=h;d=h+g|0;a=740;while(1){if((c[a>>2]|0)==(d|0)){q=162;break}b=c[a+8>>2]|0;if(!b)break;else a=b}if((q|0)==162?(c[a+12>>2]&8|0)==0:0){c[a>>2]=h;l=a+4|0;c[l>>2]=(c[l>>2]|0)+g;l=h+8|0;l=h+((l&7|0)==0?0:0-l&7)|0;b=d+8|0;b=d+((b&7|0)==0?0:0-b&7)|0;k=l+m|0;i=b-l-m|0;c[l+4>>2]=m|3;g:do if((j|0)==(b|0)){w=(c[76]|0)+i|0;c[76]=w;c[79]=k;c[k+4>>2]=w|1}else{if((c[78]|0)==(b|0)){w=(c[75]|0)+i|0;c[75]=w;c[78]=k;c[k+4>>2]=w|1;c[k+w>>2]=w;break}a=c[b+4>>2]|0;if((a&3|0)==1){h=a&-8;e=a>>>3;h:do if(a>>>0<256){a=c[b+8>>2]|0;d=c[b+12>>2]|0;if((d|0)==(a|0)){c[73]=c[73]&~(1<>2]=d;c[d+8>>2]=a;break}}else{g=c[b+24>>2]|0;a=c[b+12>>2]|0;do if((a|0)==(b|0)){e=b+16|0;d=e+4|0;a=c[d>>2]|0;if(!a){a=c[e>>2]|0;if(!a){a=0;break}else d=e}while(1){f=a+20|0;e=c[f>>2]|0;if(!e){f=a+16|0;e=c[f>>2]|0;if(!e)break;else{a=e;d=f}}else{a=e;d=f}}c[d>>2]=0}else{w=c[b+8>>2]|0;c[w+12>>2]=a;c[a+8>>2]=w}while(0);if(!g)break;d=c[b+28>>2]|0;e=596+(d<<2)|0;do if((c[e>>2]|0)!=(b|0)){w=g+16|0;c[((c[w>>2]|0)==(b|0)?w:g+20|0)>>2]=a;if(!a)break h}else{c[e>>2]=a;if(a|0)break;c[74]=c[74]&~(1<>2]=g;e=b+16|0;d=c[e>>2]|0;if(d|0){c[a+16>>2]=d;c[d+24>>2]=a}d=c[e+4>>2]|0;if(!d)break;c[a+20>>2]=d;c[d+24>>2]=a}while(0);b=b+h|0;f=h+i|0}else f=i;b=b+4|0;c[b>>2]=c[b>>2]&-2;c[k+4>>2]=f|1;c[k+f>>2]=f;b=f>>>3;if(f>>>0<256){d=332+(b<<1<<2)|0;a=c[73]|0;b=1<>2]|0}c[b>>2]=k;c[a+12>>2]=k;c[k+8>>2]=a;c[k+12>>2]=d;break}b=f>>>8;do if(!b)e=0;else{if(f>>>0>16777215){e=31;break}u=(b+1048320|0)>>>16&8;w=b<>>16&4;w=w<>>16&2;e=14-(t|u|e)+(w<>>15)|0;e=f>>>(e+7|0)&1|e<<1}while(0);a=596+(e<<2)|0;c[k+28>>2]=e;b=k+16|0;c[b+4>>2]=0;c[b>>2]=0;b=c[74]|0;d=1<>2]=k;c[k+24>>2]=a;c[k+12>>2]=k;c[k+8>>2]=k;break}b=c[a>>2]|0;i:do if((c[b+4>>2]&-8|0)!=(f|0)){e=f<<((e|0)==31?0:25-(e>>>1)|0);while(1){d=b+16+(e>>>31<<2)|0;a=c[d>>2]|0;if(!a)break;if((c[a+4>>2]&-8|0)==(f|0)){b=a;break i}else{e=e<<1;b=a}}c[d>>2]=k;c[k+24>>2]=b;c[k+12>>2]=k;c[k+8>>2]=k;break g}while(0);u=b+8|0;w=c[u>>2]|0;c[w+12>>2]=k;c[u>>2]=k;c[k+8>>2]=w;c[k+12>>2]=b;c[k+24>>2]=0}while(0);w=l+8|0;v=x;return w|0}a=740;while(1){b=c[a>>2]|0;if(b>>>0<=j>>>0?(w=b+(c[a+4>>2]|0)|0,w>>>0>j>>>0):0)break;a=c[a+8>>2]|0}f=w+-47|0;a=f+8|0;a=f+((a&7|0)==0?0:0-a&7)|0;f=j+16|0;a=a>>>0>>0?j:a;b=a+8|0;d=g+-40|0;t=h+8|0;t=(t&7|0)==0?0:0-t&7;u=h+t|0;t=d-t|0;c[79]=u;c[76]=t;c[u+4>>2]=t|1;c[h+d+4>>2]=40;c[80]=c[195];d=a+4|0;c[d>>2]=27;c[b>>2]=c[185];c[b+4>>2]=c[186];c[b+8>>2]=c[187];c[b+12>>2]=c[188];c[185]=h;c[186]=g;c[188]=0;c[187]=b;b=a+24|0;do{u=b;b=b+4|0;c[b>>2]=7}while((u+8|0)>>>0>>0);if((a|0)!=(j|0)){g=a-j|0;c[d>>2]=c[d>>2]&-2;c[j+4>>2]=g|1;c[a>>2]=g;b=g>>>3;if(g>>>0<256){d=332+(b<<1<<2)|0;a=c[73]|0;b=1<>2]|0}c[b>>2]=j;c[a+12>>2]=j;c[j+8>>2]=a;c[j+12>>2]=d;break}b=g>>>8;if(b)if(g>>>0>16777215)e=31;else{u=(b+1048320|0)>>>16&8;w=b<>>16&4;w=w<>>16&2;e=14-(t|u|e)+(w<>>15)|0;e=g>>>(e+7|0)&1|e<<1}else e=0;d=596+(e<<2)|0;c[j+28>>2]=e;c[j+20>>2]=0;c[f>>2]=0;b=c[74]|0;a=1<>2]=j;c[j+24>>2]=d;c[j+12>>2]=j;c[j+8>>2]=j;break}b=c[d>>2]|0;j:do if((c[b+4>>2]&-8|0)!=(g|0)){e=g<<((e|0)==31?0:25-(e>>>1)|0);while(1){d=b+16+(e>>>31<<2)|0;a=c[d>>2]|0;if(!a)break;if((c[a+4>>2]&-8|0)==(g|0)){b=a;break j}else{e=e<<1;b=a}}c[d>>2]=j;c[j+24>>2]=b;c[j+12>>2]=j;c[j+8>>2]=j;break f}while(0);u=b+8|0;w=c[u>>2]|0;c[w+12>>2]=j;c[u>>2]=j;c[j+8>>2]=w;c[j+12>>2]=b;c[j+24>>2]=0}}else{w=c[77]|0;if((w|0)==0|h>>>0>>0)c[77]=h;c[185]=h;c[186]=g;c[188]=0;c[82]=c[191];c[81]=-1;c[86]=332;c[85]=332;c[88]=340;c[87]=340;c[90]=348;c[89]=348;c[92]=356;c[91]=356;c[94]=364;c[93]=364;c[96]=372;c[95]=372;c[98]=380;c[97]=380;c[100]=388;c[99]=388;c[102]=396;c[101]=396;c[104]=404;c[103]=404;c[106]=412;c[105]=412;c[108]=420;c[107]=420;c[110]=428;c[109]=428;c[112]=436;c[111]=436;c[114]=444;c[113]=444;c[116]=452;c[115]=452;c[118]=460;c[117]=460;c[120]=468;c[119]=468;c[122]=476;c[121]=476;c[124]=484;c[123]=484;c[126]=492;c[125]=492;c[128]=500;c[127]=500;c[130]=508;c[129]=508;c[132]=516;c[131]=516;c[134]=524;c[133]=524;c[136]=532;c[135]=532;c[138]=540;c[137]=540;c[140]=548;c[139]=548;c[142]=556;c[141]=556;c[144]=564;c[143]=564;c[146]=572;c[145]=572;c[148]=580;c[147]=580;w=g+-40|0;t=h+8|0;t=(t&7|0)==0?0:0-t&7;u=h+t|0;t=w-t|0;c[79]=u;c[76]=t;c[u+4>>2]=t|1;c[h+w+4>>2]=40;c[80]=c[195]}while(0);b=c[76]|0;if(b>>>0>m>>>0){t=b-m|0;c[76]=t;w=c[79]|0;u=w+m|0;c[79]=u;c[u+4>>2]=t|1;c[w+4>>2]=m|3;w=w+8|0;v=x;return w|0}}c[(Ba()|0)>>2]=48;w=0;v=x;return w|0}function z(){var d=0,f=0,g=0,h=0,i=0,j=0,k=0;k=v;v=v+10240|0;a[795]=1;b[395]=0;b[396]=0;c[67]=c[2];a[796]=0;c[66]=0;a[794]=0;c[68]=k+2048;c[69]=k;a[797]=0;d=(c[3]|0)+-2|0;c[70]=d;f=d+(c[64]<<1)|0;c[71]=f;a:while(1){g=d+2|0;c[70]=g;if(d>>>0>=f>>>0){i=18;break}b:do switch(b[g>>1]|0){case 9:case 10:case 11:case 12:case 13:case 32:break;case 101:{if((((b[396]|0)==0?da(g)|0:0)?(Q(d+4|0,16,10)|0)==0:0)?(B(),(a[795]|0)==0):0){i=9;break a}else i=17;break}case 105:{if(da(g)|0?(Q(d+4|0,26,10)|0)==0:0){C();i=17}else i=17;break}case 59:{i=17;break}case 47:switch(b[d+4>>1]|0){case 47:{_();break b}case 42:{P(1);break b}default:{i=16;break a}}default:{i=16;break a}}while(0);if((i|0)==17){i=0;c[67]=c[70]}d=c[70]|0;f=c[71]|0}if((i|0)==9){d=c[70]|0;c[67]=d;i=19}else if((i|0)==16){a[795]=0;c[70]=d;i=19}else if((i|0)==18)if(!(a[794]|0)){d=g;i=19}else d=0;do if((i|0)==19){c:while(1){f=d+2|0;c[70]=f;h=f;if(d>>>0>=(c[71]|0)>>>0){i=82;break}d:do switch(b[f>>1]|0){case 9:case 10:case 11:case 12:case 13:case 32:break;case 101:{if(((b[396]|0)==0?da(f)|0:0)?(Q(d+4|0,16,10)|0)==0:0){B();i=81}else i=81;break}case 105:{if(da(f)|0?(Q(d+4|0,26,10)|0)==0:0){C();i=81}else i=81;break}case 99:{if((da(f)|0?(Q(d+4|0,36,8)|0)==0:0)?na(b[d+12>>1]|0)|0:0){a[797]=1;i=81}else i=81;break}case 40:{h=c[68]|0;f=b[396]|0;i=f&65535;c[h+(i<<3)>>2]=1;g=c[67]|0;b[396]=f+1<<16>>16;c[h+(i<<3)+4>>2]=g;i=81;break}case 41:{f=b[396]|0;if(!(f<<16>>16)){i=36;break c}f=f+-1<<16>>16;b[396]=f;g=b[395]|0;if(g<<16>>16!=0?(j=c[(c[69]|0)+((g&65535)+-1<<2)>>2]|0,(c[j+20>>2]|0)==(c[(c[68]|0)+((f&65535)<<3)+4>>2]|0)):0){f=j+4|0;if(!(c[f>>2]|0))c[f>>2]=h;c[j+12>>2]=d+4;b[395]=g+-1<<16>>16;i=81}else i=81;break}case 123:{i=c[67]|0;h=c[61]|0;d=i;do if((b[i>>1]|0)==41&(h|0)!=0?(c[h+4>>2]|0)==(i|0):0){f=c[62]|0;c[61]=f;if(!f){c[57]=0;break}else{c[f+28>>2]=0;break}}while(0);h=c[68]|0;g=b[396]|0;i=g&65535;c[h+(i<<3)>>2]=(a[797]|0)==0?2:6;b[396]=g+1<<16>>16;c[h+(i<<3)+4>>2]=d;a[797]=0;i=81;break}case 125:{d=b[396]|0;if(!(d<<16>>16)){i=49;break c}h=c[68]|0;i=d+-1<<16>>16;b[396]=i;if((c[h+((i&65535)<<3)>>2]|0)==4){H();i=81}else i=81;break}case 39:{J(39);i=81;break}case 34:{J(34);i=81;break}case 47:switch(b[d+4>>1]|0){case 47:{_();break d}case 42:{P(1);break d}default:{d=c[67]|0;h=b[d>>1]|0;e:do if(!(T(h)|0)){switch(h<<16>>16){case 41:if(ba(c[(c[68]|0)+(e[396]<<3)+4>>2]|0)|0){i=69;break e}else{i=66;break e}case 125:break;default:{i=66;break e}}f=c[68]|0;g=e[396]|0;if(!(O(c[f+(g<<3)+4>>2]|0)|0)?(c[f+(g<<3)>>2]|0)!=6:0)i=66;else i=69}else switch(h<<16>>16){case 46:if(((b[d+-2>>1]|0)+-48&65535)<10){i=66;break e}else{i=69;break e}case 43:if((b[d+-2>>1]|0)==43){i=66;break e}else{i=69;break e}case 45:if((b[d+-2>>1]|0)==45){i=66;break e}else{i=69;break e}default:{i=69;break e}}while(0);f:do if((i|0)==66){i=0;if(!(D(d)|0)){switch(h<<16>>16){case 0:{i=69;break f}case 47:{if(a[796]|0){i=69;break f}break}default:{}}g=c[3]|0;f=h;do{if(d>>>0<=g>>>0)break;d=d+-2|0;c[67]=d;f=b[d>>1]|0}while(!(Z(f)|0));if(ca(f)|0){do{if(d>>>0<=g>>>0)break;d=d+-2|0;c[67]=d}while(ca(b[d>>1]|0)|0);if(X(d)|0){N();a[796]=0;i=81;break d}else d=1}else d=1}else i=69}while(0);if((i|0)==69){N();d=0}a[796]=d;i=81;break d}}case 96:{h=c[68]|0;g=b[396]|0;i=g&65535;c[h+(i<<3)+4>>2]=c[67];b[396]=g+1<<16>>16;c[h+(i<<3)>>2]=3;H();i=81;break}default:i=81}while(0);if((i|0)==81){i=0;c[67]=c[70]}d=c[70]|0}if((i|0)==36){ma();d=0;break}else if((i|0)==49){ma();d=0;break}else if((i|0)==82){d=(a[794]|0)==0?(b[395]|b[396])<<16>>16==0:0;break}}while(0);v=k;return d|0}function A(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0;if(!a)return;d=a+-8|0;e=c[77]|0;a=c[a+-4>>2]|0;b=a&-8;k=d+b|0;do if(!(a&1)){f=c[d>>2]|0;if(!(a&3))return;g=d+(0-f)|0;h=f+b|0;if(g>>>0>>0)return;if((c[78]|0)==(g|0)){b=k+4|0;a=c[b>>2]|0;if((a&3|0)!=3){i=g;j=g;b=h;break}c[75]=h;c[b>>2]=a&-2;c[g+4>>2]=h|1;c[g+h>>2]=h;return}d=f>>>3;if(f>>>0<256){a=c[g+8>>2]|0;b=c[g+12>>2]|0;if((b|0)==(a|0)){c[73]=c[73]&~(1<>2]=b;c[b+8>>2]=a;i=g;j=g;b=h;break}}f=c[g+24>>2]|0;a=c[g+12>>2]|0;do if((a|0)==(g|0)){d=g+16|0;b=d+4|0;a=c[b>>2]|0;if(!a){a=c[d>>2]|0;if(!a){d=0;break}else b=d}while(1){e=a+20|0;d=c[e>>2]|0;if(!d){e=a+16|0;d=c[e>>2]|0;if(!d)break;else{a=d;b=e}}else{a=d;b=e}}c[b>>2]=0;d=a}else{d=c[g+8>>2]|0;c[d+12>>2]=a;c[a+8>>2]=d;d=a}while(0);if(f){a=c[g+28>>2]|0;b=596+(a<<2)|0;if((c[b>>2]|0)==(g|0)){c[b>>2]=d;if(!d){c[74]=c[74]&~(1<>2]|0)==(g|0)?j:f+20|0)>>2]=d;if(!d){i=g;j=g;b=h;break}}c[d+24>>2]=f;b=g+16|0;a=c[b>>2]|0;if(a|0){c[d+16>>2]=a;c[a+24>>2]=d}a=c[b+4>>2]|0;if(a){c[d+20>>2]=a;c[a+24>>2]=d;i=g;j=g;b=h}else{i=g;j=g;b=h}}else{i=g;j=g;b=h}}else{i=d;j=d}while(0);if(i>>>0>=k>>>0)return;a=k+4|0;d=c[a>>2]|0;if(!(d&1))return;if(!(d&2)){if((c[79]|0)==(k|0)){k=(c[76]|0)+b|0;c[76]=k;c[79]=j;c[j+4>>2]=k|1;if((j|0)!=(c[78]|0))return;c[78]=0;c[75]=0;return}if((c[78]|0)==(k|0)){k=(c[75]|0)+b|0;c[75]=k;c[78]=i;c[j+4>>2]=k|1;c[i+k>>2]=k;return}f=(d&-8)+b|0;e=d>>>3;do if(d>>>0<256){b=c[k+8>>2]|0;a=c[k+12>>2]|0;if((a|0)==(b|0)){c[73]=c[73]&~(1<>2]=a;c[a+8>>2]=b;break}}else{g=c[k+24>>2]|0;a=c[k+12>>2]|0;do if((a|0)==(k|0)){d=k+16|0;b=d+4|0;a=c[b>>2]|0;if(!a){a=c[d>>2]|0;if(!a){d=0;break}else b=d}while(1){e=a+20|0;d=c[e>>2]|0;if(!d){e=a+16|0;d=c[e>>2]|0;if(!d)break;else{a=d;b=e}}else{a=d;b=e}}c[b>>2]=0;d=a}else{d=c[k+8>>2]|0;c[d+12>>2]=a;c[a+8>>2]=d;d=a}while(0);if(g|0){a=c[k+28>>2]|0;b=596+(a<<2)|0;if((c[b>>2]|0)==(k|0)){c[b>>2]=d;if(!d){c[74]=c[74]&~(1<>2]|0)==(k|0)?h:g+20|0)>>2]=d;if(!d)break}c[d+24>>2]=g;b=k+16|0;a=c[b>>2]|0;if(a|0){c[d+16>>2]=a;c[a+24>>2]=d}a=c[b+4>>2]|0;if(a|0){c[d+20>>2]=a;c[a+24>>2]=d}}}while(0);c[j+4>>2]=f|1;c[i+f>>2]=f;if((j|0)==(c[78]|0)){c[75]=f;return}}else{c[a>>2]=d&-2;c[j+4>>2]=b|1;c[i+b>>2]=b;f=b}a=f>>>3;if(f>>>0<256){d=332+(a<<1<<2)|0;b=c[73]|0;a=1<>2]|0}c[a>>2]=j;c[b+12>>2]=j;c[j+8>>2]=b;c[j+12>>2]=d;return}a=f>>>8;if(a)if(f>>>0>16777215)e=31;else{i=(a+1048320|0)>>>16&8;k=a<>>16&4;k=k<>>16&2;e=14-(h|i|e)+(k<>>15)|0;e=f>>>(e+7|0)&1|e<<1}else e=0;b=596+(e<<2)|0;c[j+28>>2]=e;c[j+20>>2]=0;c[j+16>>2]=0;a=c[74]|0;d=1<>2]=j;c[j+24>>2]=b;c[j+12>>2]=j;c[j+8>>2]=j}else{a=c[b>>2]|0;b:do if((c[a+4>>2]&-8|0)!=(f|0)){e=f<<((e|0)==31?0:25-(e>>>1)|0);while(1){d=a+16+(e>>>31<<2)|0;b=c[d>>2]|0;if(!b)break;if((c[b+4>>2]&-8|0)==(f|0)){a=b;break b}else{e=e<<1;a=b}}c[d>>2]=j;c[j+24>>2]=a;c[j+12>>2]=j;c[j+8>>2]=j;break a}while(0);i=a+8|0;k=c[i>>2]|0;c[k+12>>2]=j;c[i>>2]=j;c[j+8>>2]=k;c[j+12>>2]=a;c[j+24>>2]=0}while(0);k=(c[81]|0)+-1|0;c[81]=k;if(k|0)return;a=748;while(1){a=c[a>>2]|0;if(!a)break;else a=a+8|0}c[81]=-1;return}function B(){var d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0;h=c[70]|0;i=c[63]|0;l=h+12|0;c[70]=l;f=I(1)|0;d=c[70]|0;if(!((d|0)==(l|0)?!(S(f)|0):0))k=3;a:do if((k|0)==3){b:do switch(f<<16>>16){case 123:{c[70]=d+2;d=I(1)|0;f=c[70]|0;while(1){if(oa(d)|0){J(d);d=(c[70]|0)+2|0;c[70]=d}else{$(d)|0;d=c[70]|0}I(1)|0;d=K(f,d)|0;if(d<<16>>16==44){c[70]=(c[70]|0)+2;d=I(1)|0}e=f;f=c[70]|0;if(d<<16>>16==125){k=15;break}if((f|0)==(e|0)){k=12;break}if(f>>>0>(c[71]|0)>>>0){k=14;break}}if((k|0)==12){ma();break a}else if((k|0)==14){ma();break a}else if((k|0)==15){c[70]=f+2;break b}break}case 42:{c[70]=d+2;I(1)|0;l=c[70]|0;K(l,l)|0;break}default:{a[795]=0;switch(f<<16>>16){case 100:{h=d+14|0;c[70]=h;e=I(1)|0;if(e<<16>>16==97){e=c[70]|0;if((da(e)|0?(Q(e+2|0,58,8)|0)==0:0)?(g=e+10|0,ca(b[g>>1]|0)|0):0){c[70]=g;e=I(0)|0;k=23}else{e=97;k=32}}else k=23;c:do if((k|0)==23){if(e<<16>>16==102){e=c[70]|0;if(!(da(e)|0)){e=102;k=32;break}if(Q(e+2|0,66,14)|0){e=102;k=32;break}f=e+16|0;e=b[f>>1]|0;if(!(na(e)|0))switch(e<<16>>16){case 40:case 42:break;default:{e=102;k=32;break c}}c[70]=f;e=I(1)|0;if(e<<16>>16==42){c[70]=(c[70]|0)+2;e=I(1)|0}if(e<<16>>16==40){W(d,h,0,0);c[70]=d+12;break a}else g=1}else g=0;f=c[70]|0;do if(e<<16>>16==99)if((da(f)|0?(Q(f+2|0,36,8)|0)==0:0)?(j=f+10|0,l=b[j>>1]|0,na(l)|0|l<<16>>16==123):0){c[70]=j;e=I(1)|0;if(e<<16>>16==123){W(d,h,0,0);c[70]=d+12;break a}else{f=c[70]|0;$(e)|0;break}}else{e=99;k=40}else k=40;while(0);if((k|0)==40){$(e)|0;if(!g){k=43;break}}e=c[70]|0;if(e>>>0>f>>>0){W(d,h,f,e);d=(c[70]|0)+-2|0}else k=43}while(0);if((k|0)==32){$(e)|0;k=43}if((k|0)==43){W(d,h,0,0);d=d+12|0}c[70]=d;break a}case 97:{c[70]=d+10;I(1)|0;d=c[70]|0;k=46;break}case 102:{k=46;break}case 99:{if((Q(d+2|0,36,8)|0)==0?(e=d+10|0,Z(b[e>>1]|0)|0):0){c[70]=e;l=I(1)|0;k=c[70]|0;$(l)|0;l=c[70]|0;W(k,l,k,l);c[70]=(c[70]|0)+-2;break a}d=d+4|0;c[70]=d;break}case 108:case 118:break;default:break a}if((k|0)==46){c[70]=d+16;d=I(1)|0;if(d<<16>>16==42){c[70]=(c[70]|0)+2;d=I(1)|0}k=c[70]|0;$(d)|0;l=c[70]|0;W(k,l,k,l);c[70]=(c[70]|0)+-2;break a}d=d+4|0;c[70]=d;a[795]=0;d:while(1){c[70]=d+2;l=I(1)|0;d=c[70]|0;switch(($(l)|0)<<16>>16){case 91:case 123:break d;default:{}}e=c[70]|0;if((e|0)==(d|0))break a;W(d,e,d,e);if((I(1)|0)<<16>>16!=44)break;d=c[70]|0}c[70]=(c[70]|0)+-2;break a}}while(0);l=(I(1)|0)<<16>>16==102;d=c[70]|0;if(l?(Q(d+2|0,52,6)|0)==0:0){c[70]=d+8;E(h,I(1)|0);d=(i|0)==0?232:i+16|0;while(1){d=c[d>>2]|0;if(!d)break a;c[d+12>>2]=0;c[d+8>>2]=0;d=d+16|0}}c[70]=d+-2}while(0);return}function C(){var d=0,e=0,f=0,g=0,h=0,i=0;h=c[70]|0;e=h+12|0;c[70]=e;a:do switch((I(1)|0)<<16>>16){case 40:{e=c[68]|0;i=b[396]|0;f=i&65535;c[e+(f<<3)>>2]=5;d=c[70]|0;b[396]=i+1<<16>>16;c[e+(f<<3)+4>>2]=d;if((b[c[67]>>1]|0)!=46){c[70]=d+2;i=I(1)|0;L(h,c[70]|0,0,d);e=c[61]|0;f=c[69]|0;h=b[395]|0;b[395]=h+1<<16>>16;c[f+((h&65535)<<2)>>2]=e;switch(i<<16>>16){case 39:{J(39);break}case 34:{J(34);break}default:{c[70]=(c[70]|0)+-2;break a}}d=(c[70]|0)+2|0;c[70]=d;switch((I(1)|0)<<16>>16){case 44:{c[70]=(c[70]|0)+2;I(1)|0;h=c[61]|0;c[h+4>>2]=d;i=c[70]|0;c[h+16>>2]=i;a[h+24>>0]=1;c[70]=i+-2;break a}case 41:{b[396]=(b[396]|0)+-1<<16>>16;i=c[61]|0;c[i+4>>2]=d;c[i+12>>2]=(c[70]|0)+2;a[i+24>>0]=1;b[395]=(b[395]|0)+-1<<16>>16;break a}default:{c[70]=(c[70]|0)+-2;break a}}}break}case 46:{c[70]=(c[70]|0)+2;if(((I(1)|0)<<16>>16==109?(d=c[70]|0,(Q(d+2|0,44,6)|0)==0):0)?(b[c[67]>>1]|0)!=46:0)L(h,h,d+8|0,2);break}case 42:case 39:case 34:{g=17;break}case 123:{d=c[70]|0;if(b[396]|0){c[70]=d+-2;break a}while(1){if(d>>>0>=(c[71]|0)>>>0)break;d=I(1)|0;if(!(oa(d)|0)){if(d<<16>>16==125){g=32;break}}else J(d);d=(c[70]|0)+2|0;c[70]=d}if((g|0)==32)c[70]=(c[70]|0)+2;I(1)|0;d=c[70]|0;if(Q(d,50,8)|0){ma();break a}c[70]=d+8;d=I(1)|0;if(oa(d)|0){E(h,d);break a}else{ma();break a}}default:if((c[70]|0)==(e|0))c[70]=h+10;else g=17}while(0);do if((g|0)==17){if(b[396]|0){c[70]=(c[70]|0)+-2;break}d=c[71]|0;e=c[70]|0;while(1){if(e>>>0>=d>>>0){g=24;break}f=b[e>>1]|0;if(oa(f)|0){g=22;break}i=e+2|0;c[70]=i;e=i}if((g|0)==22){E(h,f);break}else if((g|0)==24){ma();break}}while(0);return}function D(a){a=a|0;a:do switch(b[a>>1]|0){case 100:switch(b[a+-2>>1]|0){case 105:{a=V(a+-4|0,90,2)|0;break a}case 108:{a=V(a+-4|0,94,3)|0;break a}default:{a=0;break a}}case 101:switch(b[a+-2>>1]|0){case 115:switch(b[a+-4>>1]|0){case 108:{a=Y(a+-6|0,101)|0;break a}case 97:{a=Y(a+-6|0,99)|0;break a}default:{a=0;break a}}case 116:{a=V(a+-4|0,100,4)|0;break a}case 117:{a=V(a+-4|0,108,6)|0;break a}default:{a=0;break a}}case 102:{if((b[a+-2>>1]|0)==111?(b[a+-4>>1]|0)==101:0)switch(b[a+-6>>1]|0){case 99:{a=V(a+-8|0,120,6)|0;break a}case 112:{a=V(a+-8|0,132,2)|0;break a}default:{a=0;break a}}else a=0;break}case 107:{a=V(a+-2|0,136,4)|0;break}case 110:{a=a+-2|0;if(Y(a,105)|0)a=1;else a=V(a,144,5)|0;break}case 111:{a=Y(a+-2|0,100)|0;break}case 114:{a=V(a+-2|0,154,7)|0;break}case 116:{a=V(a+-2|0,168,4)|0;break}case 119:switch(b[a+-2>>1]|0){case 101:{a=Y(a+-4|0,110)|0;break a}case 111:{a=V(a+-4|0,176,3)|0;break a}default:{a=0;break a}}default:a=0}while(0);return a|0}function E(a,b){a=a|0;b=b|0;var d=0,e=0;d=(c[70]|0)+2|0;switch(b<<16>>16){case 39:{J(39);e=5;break}case 34:{J(34);e=5;break}default:ma()}do if((e|0)==5){L(a,d,c[70]|0,1);c[70]=(c[70]|0)+2;e=(I(0)|0)<<16>>16==97;b=c[70]|0;if(e?(Q(b+2|0,80,10)|0)==0:0){c[70]=b+12;if((I(1)|0)<<16>>16!=123){c[70]=b;break}a=c[70]|0;d=a;a:while(1){c[70]=d+2;d=I(1)|0;switch(d<<16>>16){case 39:{J(39);c[70]=(c[70]|0)+2;d=I(1)|0;break}case 34:{J(34);c[70]=(c[70]|0)+2;d=I(1)|0;break}default:d=$(d)|0}if(d<<16>>16!=58){e=16;break}c[70]=(c[70]|0)+2;switch((I(1)|0)<<16>>16){case 39:{J(39);break}case 34:{J(34);break}default:{e=20;break a}}c[70]=(c[70]|0)+2;switch((I(1)|0)<<16>>16){case 125:{e=25;break a}case 44:break;default:{e=24;break a}}c[70]=(c[70]|0)+2;if((I(1)|0)<<16>>16==125){e=25;break}d=c[70]|0}if((e|0)==16){c[70]=b;break}else if((e|0)==20){c[70]=b;break}else if((e|0)==24){c[70]=b;break}else if((e|0)==25){e=c[61]|0;c[e+16>>2]=a;c[e+12>>2]=(c[70]|0)+2;break}}c[70]=b+-2}while(0);return}function F(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0;if((e|0)>=512){t(b|0,d|0,e|0)|0;return b|0}h=b|0;g=b+e|0;if((b&3)==(d&3)){while(b&3){if(!e)return h|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}e=g&-4|0;f=e-64|0;while((b|0)<=(f|0)){c[b>>2]=c[d>>2];c[b+4>>2]=c[d+4>>2];c[b+8>>2]=c[d+8>>2];c[b+12>>2]=c[d+12>>2];c[b+16>>2]=c[d+16>>2];c[b+20>>2]=c[d+20>>2];c[b+24>>2]=c[d+24>>2];c[b+28>>2]=c[d+28>>2];c[b+32>>2]=c[d+32>>2];c[b+36>>2]=c[d+36>>2];c[b+40>>2]=c[d+40>>2];c[b+44>>2]=c[d+44>>2];c[b+48>>2]=c[d+48>>2];c[b+52>>2]=c[d+52>>2];c[b+56>>2]=c[d+56>>2];c[b+60>>2]=c[d+60>>2];b=b+64|0;d=d+64|0}while((b|0)<(e|0)){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0}}else{e=g-4|0;while((b|0)<(e|0)){a[b>>0]=a[d>>0]|0;a[b+1>>0]=a[d+1>>0]|0;a[b+2>>0]=a[d+2>>0]|0;a[b+3>>0]=a[d+3>>0]|0;b=b+4|0;d=d+4|0}}while((b|0)<(g|0)){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0}return h|0}function G(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;h=b+e|0;d=d&255;if((e|0)>=67){while(b&3){a[b>>0]=d;b=b+1|0}f=h&-4|0;i=d|d<<8|d<<16|d<<24;g=f-64|0;while((b|0)<=(g|0)){c[b>>2]=i;c[b+4>>2]=i;c[b+8>>2]=i;c[b+12>>2]=i;c[b+16>>2]=i;c[b+20>>2]=i;c[b+24>>2]=i;c[b+28>>2]=i;c[b+32>>2]=i;c[b+36>>2]=i;c[b+40>>2]=i;c[b+44>>2]=i;c[b+48>>2]=i;c[b+52>>2]=i;c[b+56>>2]=i;c[b+60>>2]=i;b=b+64|0}while((b|0)<(f|0)){c[b>>2]=i;b=b+4|0}}while((b|0)<(h|0)){a[b>>0]=d;b=b+1|0}return h-e|0}function H(){var a=0,d=0,e=0,f=0;d=c[71]|0;e=c[70]|0;a:while(1){a=e+2|0;if(e>>>0>=d>>>0){d=10;break}switch(b[a>>1]|0){case 96:{d=7;break a}case 36:{if((b[e+4>>1]|0)==123){d=6;break a}break}case 92:{a=e+4|0;break}default:{}}e=a}if((d|0)==6){a=e+4|0;c[70]=a;d=c[68]|0;f=b[396]|0;e=f&65535;c[d+(e<<3)>>2]=4;b[396]=f+1<<16>>16;c[d+(e<<3)+4>>2]=a}else if((d|0)==7){c[70]=a;e=c[68]|0;f=(b[396]|0)+-1<<16>>16;b[396]=f;if((c[e+((f&65535)<<3)>>2]|0)!=3)ma()}else if((d|0)==10){c[70]=a;ma()}return}function I(a){a=a|0;var d=0,e=0,f=0;e=c[70]|0;a:do{d=b[e>>1]|0;b:do if(d<<16>>16!=47)if(a)if(na(d)|0)break;else break a;else if(ca(d)|0)break;else break a;else switch(b[e+2>>1]|0){case 47:{_();break b}case 42:{P(a);break b}default:{d=47;break a}}while(0);f=c[70]|0;e=f+2|0;c[70]=e}while(f>>>0<(c[71]|0)>>>0);return d|0}function J(a){a=a|0;var d=0,e=0,f=0,g=0;g=c[71]|0;d=c[70]|0;while(1){f=d+2|0;if(d>>>0>=g>>>0){d=9;break}e=b[f>>1]|0;if(e<<16>>16==a<<16>>16){d=10;break}if(e<<16>>16==92){e=d+4|0;if((b[e>>1]|0)==13){d=d+6|0;d=(b[d>>1]|0)==10?d:e}else d=e}else if(ra(e)|0){d=9;break}else d=f}if((d|0)==9){c[70]=f;ma()}else if((d|0)==10)c[70]=f;return}function K(a,d){a=a|0;d=d|0;var e=0,f=0,g=0,h=0;e=c[70]|0;f=b[e>>1]|0;h=(a|0)==(d|0);g=h?0:a;h=h?0:d;if(f<<16>>16==97){c[70]=e+4;e=I(1)|0;a=c[70]|0;if(oa(e)|0){J(e);d=(c[70]|0)+2|0;c[70]=d}else{$(e)|0;d=c[70]|0}f=I(1)|0;e=c[70]|0}if((e|0)!=(a|0))W(a,d,g,h);return f|0}function L(b,d,e,f){b=b|0;d=d|0;e=e|0;f=f|0;var g=0,h=0;g=c[65]|0;c[65]=g+32;h=c[61]|0;c[((h|0)==0?228:h+28|0)>>2]=g;c[62]=h;c[61]=g;c[g+8>>2]=b;if(2==(f|0))b=e;else b=1==(f|0)?e+2|0:0;c[g+12>>2]=b;c[g>>2]=d;c[g+4>>2]=e;c[g+16>>2]=0;c[g+20>>2]=f;a[g+24>>0]=1==(f|0)&1;c[g+28>>2]=0;return}function M(){var a=0,d=0,e=0;e=c[71]|0;d=c[70]|0;a:while(1){a=d+2|0;if(d>>>0>=e>>>0){d=6;break}switch(b[a>>1]|0){case 13:case 10:{d=6;break a}case 93:{d=7;break a}case 92:{a=d+4|0;break}default:{}}d=a}if((d|0)==6){c[70]=a;ma();a=0}else if((d|0)==7){c[70]=a;a=93}return a|0}function N(){var a=0,d=0,e=0;a:while(1){a=c[70]|0;d=a+2|0;c[70]=d;if(a>>>0>=(c[71]|0)>>>0){e=7;break}switch(b[d>>1]|0){case 13:case 10:{e=7;break a}case 47:break a;case 91:{M()|0;break}case 92:{c[70]=a+4;break}default:{}}}if((e|0)==7)ma();return}function O(a){a=a|0;switch(b[a>>1]|0){case 62:{a=(b[a+-2>>1]|0)==61;break}case 41:case 59:{a=1;break}case 104:{a=V(a+-2|0,202,4)|0;break}case 121:{a=V(a+-2|0,210,6)|0;break}case 101:{a=V(a+-2|0,222,3)|0;break}default:a=0}return a|0}function P(a){a=a|0;var d=0,e=0,f=0,g=0,h=0;g=(c[70]|0)+2|0;c[70]=g;e=c[71]|0;while(1){d=g+2|0;if(g>>>0>=e>>>0)break;f=b[d>>1]|0;if(!a?ra(f)|0:0)break;if(f<<16>>16==42?(b[g+4>>1]|0)==47:0){h=8;break}g=d}if((h|0)==8){c[70]=d;d=g+4|0}c[70]=d;return}function Q(b,c,d){b=b|0;c=c|0;d=d|0;var e=0,f=0;a:do if(!d)b=0;else{while(1){e=a[b>>0]|0;f=a[c>>0]|0;if(e<<24>>24!=f<<24>>24)break;d=d+-1|0;if(!d){b=0;break a}else{b=b+1|0;c=c+1|0}}b=(e&255)-(f&255)|0}while(0);return b|0}function R(a){a=a|0;var b=0,d=0,e=0;e=a+3&-4;d=za()|0;a=c[d>>2]|0;b=a+e|0;do if((e|0)<1|b>>>0>a>>>0){if(b>>>0>(s()|0)>>>0?(u(b|0)|0)==0:0)break;c[d>>2]=b;e=a;return e|0}while(0);c[(Ba()|0)>>2]=48;e=-1;return e|0}function S(a){a=a|0;a:do switch(a<<16>>16){case 38:case 37:case 33:{a=1;break}default:if((a&-8)<<16>>16==40|(a+-58&65535)<6)a=1;else{switch(a<<16>>16){case 91:case 93:case 94:{a=1;break a}default:{}}a=(a+-123&65535)<4}}while(0);return a|0}function T(a){a=a|0;a:do switch(a<<16>>16){case 38:case 37:case 33:break;default:if(!((a+-58&65535)<6|(a+-40&65535)<7&a<<16>>16!=41)){switch(a<<16>>16){case 91:case 94:break a;default:{}}return a<<16>>16!=125&(a+-123&65535)<4|0}}while(0);return 1}function U(a){a=a|0;var d=0,e=0,f=0,g=0;e=v;v=v+16|0;f=e;c[f>>2]=0;c[64]=a;d=c[3]|0;g=d+(a<<1)|0;a=g+2|0;b[g>>1]=0;c[f>>2]=a;c[65]=a;c[57]=0;c[61]=0;c[59]=0;c[58]=0;c[63]=0;c[60]=0;v=e;return d|0}function V(a,d,e){a=a|0;d=d|0;e=e|0;var f=0,g=0;f=a+(0-e<<1)|0;g=f+2|0;a=c[3]|0;if(g>>>0>=a>>>0?(Q(g,d,e<<1)|0)==0:0)if((g|0)==(a|0))a=1;else a=Z(b[f>>1]|0)|0;else a=0;return a|0}function W(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;f=c[65]|0;c[65]=f+20;g=c[63]|0;c[((g|0)==0?232:g+16|0)>>2]=f;c[63]=f;c[f>>2]=a;c[f+4>>2]=b;c[f+8>>2]=d;c[f+12>>2]=e;c[f+16>>2]=0;return}function X(a){a=a|0;switch(b[a>>1]|0){case 107:{a=V(a+-2|0,136,4)|0;break}case 101:{if((b[a+-2>>1]|0)==117)a=V(a+-4|0,108,6)|0;else a=0;break}default:a=0}return a|0}function Y(a,d){a=a|0;d=d|0;var e=0;e=c[3]|0;if(e>>>0<=a>>>0?(b[a>>1]|0)==d<<16>>16:0)if((e|0)==(a|0))e=1;else e=Z(b[a+-2>>1]|0)|0;else e=0;return e|0}function Z(a){a=a|0;a:do if((a+-9&65535)<5)a=1;else{switch(a<<16>>16){case 32:case 160:{a=1;break a}default:{}}a=a<<16>>16!=46&(S(a)|0)}while(0);return a|0}function _(){var a=0,d=0,e=0;a=c[71]|0;e=c[70]|0;a:while(1){d=e+2|0;if(e>>>0>=a>>>0)break;switch(b[d>>1]|0){case 13:case 10:break a;default:e=d}}c[70]=d;return}function $(a){a=a|0;while(1){if(na(a)|0)break;if(S(a)|0)break;a=(c[70]|0)+2|0;c[70]=a;a=b[a>>1]|0;if(!(a<<16>>16)){a=0;break}}return a|0}function aa(){var a=0;a=c[(c[59]|0)+20>>2]|0;switch(a|0){case 1:{a=-1;break}case 2:{a=-2;break}default:a=a-(c[3]|0)>>1}return a|0}function ba(a){a=a|0;if(!(V(a,182,5)|0)?!(V(a,192,3)|0):0)a=V(a,198,2)|0;else a=1;return a|0}function ca(a){a=a|0;switch(a<<16>>16){case 160:case 32:case 12:case 11:case 9:{a=1;break}default:a=0}return a|0}function da(a){a=a|0;if((c[3]|0)==(a|0))a=1;else a=Z(b[a+-2>>1]|0)|0;return a|0}function ea(){var a=0;a=c[(c[60]|0)+12>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function fa(){var a=0;a=c[(c[59]|0)+12>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ga(){var a=0;a=c[(c[60]|0)+8>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ha(){var a=0;a=c[(c[59]|0)+16>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ia(){var a=0;a=c[(c[59]|0)+4>>2]|0;if(!a)a=-1;else a=a-(c[3]|0)>>1;return a|0}function ja(){var a=0;a=c[59]|0;a=c[((a|0)==0?228:a+28|0)>>2]|0;c[59]=a;return (a|0)!=0|0}function ka(){var a=0;a=c[60]|0;a=c[((a|0)==0?232:a+16|0)>>2]|0;c[60]=a;return (a|0)!=0|0}function la(a){a=a|0;var b=0;b=v;v=v+a|0;v=v+15&-16;return b|0}function ma(){a[794]=1;c[66]=(c[70]|0)-(c[3]|0)>>1;c[70]=(c[71]|0)+2;return}function na(a){a=a|0;return (a|128)<<16>>16==160|(a+-9&65535)<5|0}function oa(a){a=a|0;return a<<16>>16==39|a<<16>>16==34|0}function pa(){return (c[(c[59]|0)+8>>2]|0)-(c[3]|0)>>1|0}function qa(){return (c[(c[60]|0)+4>>2]|0)-(c[3]|0)>>1|0}function ra(a){a=a|0;return a<<16>>16==13|a<<16>>16==10|0}function sa(){return (c[c[59]>>2]|0)-(c[3]|0)>>1|0}function ta(){return (c[c[60]>>2]|0)-(c[3]|0)>>1|0}function ua(){return d[(c[59]|0)+24>>0]|0|0}function va(a){a=a|0;c[3]=a;return}function wa(){c[3]=g}function xa(a){a=a|0;v=a}function ya(){return (a[795]|0)!=0|0}function za(){return 816}function Aa(){return v|0}function Ba(){return 288}function Ca(){return c[66]|0} - -// EMSCRIPTEN_END_FUNCS -return{___errno_location:Ba,__apply_relocations:wa,_ai:ha,_e:Ca,_ee:qa,_ele:ea,_els:ga,_emscripten_get_sbrk_ptr:za,_es:ta,_f:ya,_free:A,_id:aa,_ie:ia,_ip:ua,_is:sa,_malloc:y,_memcpy:F,_memset:G,_parse:z,_re:ka,_ri:ja,_sa:U,_se:fa,_setSource:va,_ss:pa,stackAlloc:la,stackRestore:xa,stackSave:Aa}}) - - diff --git a/lib/lexer.wasm b/lib/lexer.wasm deleted file mode 100755 index f2a7ac800b3f4c451254115df73c385f019293d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8078 zcma)BTZmoN8D5vY&+N-Nlbtx$G?T`?PZBeUiKEa?F+rFWm>Ggti_q3SC8M4+b8<16 z8I6i$hQ@-3DWOOkXsDs25NIDnd@LmtDtVCNgAY<{i6jQwrZrewEGqW<{Q~MOYDup4n&td>?rutNN$9AQ)S@BO< zYl8r!EPnA4Hk2At4HE{TKB~12tya3R$}DO3()%i1Qt}u&dQ;56v!gtWI9uwf%tBOHSo#!h9GzHD=z3+d5ZHFLv9? zsa+Z$cr|#NFdt(DS1uLTF2G0;BnCn=bhc54uK_0mEUH4|32ctRG$nw`@A4@lsTsa=lJEoGiXxy5ulaNfKy>5NEsMvl>6U2@rB03O z=aZJsZFbWX9@+(6_ru5~+3hWzjd+u^pc<7spHSJ;3HJH*R2u7__v=-5GwAt+vP=^+ zdRzx>>$Xj|RBqk)Oe@H~($uNb*)3Q-20InT?t$mxE(dt>NY>aP@pKue-b8L0a$(VBsv#he75*0p#ff zIY)L-&bFCq(VWIDb=!g8I1Y$%d>Er8JJ>=_4V{w1(kY z%my&9q|%@wOT=S{z(DS64#SkBa2nWwBivS|6TH70x9t?Y0~)!OK@MNXFjGIM`)TIq z^@0IkS!&hopz2msx31v;ryc5Cf;12El5=UUofv9Td9j0@YBBCK?Vtw?SZk?VfRJf6 zQ@15s+XOP(cq9xZpcoQDMgh0>;MN@005>)O+}OY+eY&@8_Eo4X8?F0JcQ{aywowWx zPIPIT+oxL%##Z&dW4aYK8w?`rK2bh^U_U8OOcm6mbV#;tO4gEH|5V$+zS(FC1rGjn z4N?qh4&Uy2uO@q17UTW9fg-!uwq36PKqTfGKpQ6EER|;KC6h2-DHJmvMVp_WDois5 zx9n^5w747jhwfn(oH>S3>dIheMJSA!v@j~$3Z$z0n0M&E(*QQ?Q^1)_xIw{mwE^T( zR9w?7Ah<4oLeV9=m3b@>vNbuRy#G(`)*dy>xXeK$D?DyqNSa~Fmm(y{L+0?NO|v#r z6{f)W91zSr#Y%B5;TI@$9-()dFZ}A08x#HOs=Emhn0^gN@OtNp_}5ZW1qs=P^bd(1 zLR;iFqiQvQQ=bFbi4+D9yREQQn)1(bv5zMxu^CF#nXH$CKLR^imbMd;Bqc?eVp@O} zi1Xo&B-Hk}+aix_Y+b32MNERl+Db(FOiB~Mf7?1xpp&+uW!l~gZj{M>j+JFBm=WHV z(K_8qa!Uu3&xPc^mO{v3<$$!M5N+(pwytX$F*qwjzdb~$dVmAtIw9aF zpmB*er-C^l(lrnSLNq3>fgD0(kX*ui+jZw7H;iJ7w{e}dLni$Oll~BrexsN4hnkT$ zLdkcFjk|1l=(;f@KZ>@=h0{!P#xL-uB0e4Y#>mu2nBX+afvMunZ?=8(}VeKSK zn_`heJF{DT69)i``APu1c;>0tICKx9qtI3Zp`oM`f?6p05G`~MoL}Tx!_qWHNpZxW z0qIUayjdxEMIeqvSEQI2$ik#0b`yky;R;_fpakW8$b`B*WFoz(!c2K0^VKOj5V(#9_`-Ck`{eOAhMkDw-BneCfTKwbS1{d|1 zs&nh34B7QFNEj*~T$v zSEPGT&&^=c#!-c7gFiHY`yTod+*~N1RE2#lljIzpN}zUaVjPrownY*LIr6}FKJlrACL<-b zKrQKLNQ7iOb(~2`qybnk4e9dniPvI4Jzq@yP#dwSc3)e*bn?cjixUr^KzMB`yg>C5 zsUT?CN5a$4ut%^K64IM&3o=h{wl*&$sa_C2Gv(!slz_(=2YRM4dH32duAvX&- zkhi!FrG&?Ap^r+5M1U2@H*ZNE4ZJ8?8@5C^_izJFy{_e}MgunDK*J@|qtzkTOOmw( zLLyaqNbX;#rrXOyTzD^$!js=!#{_b2x@^}QIO=27)){;}SdI>O1{l&oQvn=^eZ}(exx{&%`dwHVVyO#?F>sFT~Dd8w=VmJqhi~^|X!u$$@c# zU<&-g04#Y_@%Ih^E;{{Dc`M7YasLInEm)oCx>sV9(2aGs>+YE@U)*nE7xbpwMUjOT z@NcM|p-Fes~G`N3dfwgWEFhZ~fQkD#WVc<7j8Ug4aEoVeC zc);{Y9qV|mL^45M7v~LZ;&~FcqrC&U>N_#zsRgmK3$Ly0tB&^zG7))C|eo0 z|0%|HXQ9W4(k8|~8^=Ky$BlR>j-yx_eK9a&@Mpw;N)nCpszNlm-$rz%*copZBYK&g z2Y-6IPGOe?^@NV(Ht*lJeY@bU!-omRpX$1IRJKWo;ro^9I|#oT^&E(|KQa&Zd-^?4 zXjGIz1aHFWsgl!;lG7E;3}M|9UH7M&2HvjKY5+-pE4dNKSdFuxa({(1K%8>#M+9|+ ztgDrf*!FSchU%H;O3c-E0Fd4*1)+!}4Q2jWQNZ_~2r@k?zuq57xQCMq1YX8r+p?ZV zGsor#g&uL3Z9e8V-C4(ab<0!6`YGP>_;8n$w{J zt+r zz>SEm+!nZi7keuu168DEm;<+cpFhX$FB>bQ+9|hlg#e}A6=P}jYNi8=L1rb3J~_90 zAn|4d=BqUvU?4`2yFot;(KXwyy6?tqa^H?-dbk()fRZmy011#{h!g})4n)gUGcbX@ zOhP(WIF0CRGdrsSzn-F`3`Q=a$h6U&!EP{)#Ej&@^w%ueqLjd)Ev>Z0{R)-S1@d$e z`h>xnC|Q6G01E+B*?;#RXn@?_B~*Z|c<(o2b(ugawxnWM&P*PXXNa~9z`&25ZX5g^ zFNeaT8C13DeBB9(hQ8DF%>|Ie16i-NkX9F=!o~uJMIICjUI|E7sx@0drodj6mo}JJ zhJs>wbAML-2@Y5A%JbTx6uL~!2XYxg7mY{(AMnB8VkRkVjlWqQ^a(D#ip1=eqx4m+ z)D!BcdQ{D;MRmCMt%J8&wI8F8V)QX}L_Lg=IlLWF3;5~C`p5D9Bt|>x5&Ry)@58c! zE04(Ug3vjF&nMMG*wX=3elCLMyzF`i-+q7}&K$?@9Oe#S?l?Z@gyw$GoX7Y5pgxP) ghw+W{XE95jhait~m*O;oGRojr;qT^m-mTRC08;n;t^fc4 diff --git a/package.json b/package.json index 7de2cbb..c52697c 100755 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@types/node": "^18.7.1", "kleur": "^2.0.2", "mocha": "^5.2.0", - "terser": "^5.14.2", + "terser": "^5.15.1", "typescript": "^4.7.4" }, "files": [ diff --git a/src/lexer-world.wit b/src/lexer-world.wit new file mode 100644 index 0000000..a18ac71 --- /dev/null +++ b/src/lexer-world.wit @@ -0,0 +1,175 @@ +/** + * Module name + * + * To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. + * + * For dynamic import expressions, this field will be empty if not a valid JS string. + * + * @example + * const [imports1, exports1] = parse(String.raw`import './\u0061\u0062.js'`); + * imports1[0].n; + * // Returns "./ab.js" + * + * const [imports2, exports2] = parse(`import("./ab.js")`); + * imports2[0].n; + * // Returns "./ab.js" + * + * const [imports3, exports3] = parse(`import("./" + "ab.js")`); + * imports3[0].n; + * // Returns undefined + */ +record import { + /** + * Module name + * + * To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. + * + * For dynamic import expressions, this field will be empty if not a valid JS string. + * + * @example + * const [imports1, exports1] = parse(String.raw`import './\u0061\u0062.js'`); + * imports1[0].n; + * // Returns "./ab.js" + * + * const [imports2, exports2] = parse(`import("./ab.js")`); + * imports2[0].n; + * // Returns "./ab.js" + * + * const [imports3, exports3] = parse(`import("./" + "ab.js")`); + * imports3[0].n; + * // Returns undefined + */ + n: option, + + /** + * Start of module specifier + * + * @example + * const source = `import { a } from 'asdf'`; + * const [imports, exports] = parse(source); + * source.substring(imports[0].s, imports[0].e); + * // Returns "asdf" + */ + s: s32, + + /** + * End of module specifier + */ + e: s32, + + /** + * Start of import statement + * + * @example + * const source = `import { a } from 'asdf'`; + * const [imports, exports] = parse(source); + * source.substring(imports[0].ss, imports[0].se); + * // Returns "import { a } from 'asdf';" + */ + ss: s32, + + /** + * End of import statement + */ + se: s32, + + /** + * If this import statement is a dynamic import, this is the start value. + * Otherwise this is `-1`. + */ + d: s32, + + /** + * If this import has an import assertion, this is the start value. + * Otherwise this is `-1`. + */ + a: s32, +} + +record export { + /** + * Exported name + * + * @example + * const source = `export default []`; + * const [imports, exports] = parse(source); + * exports[0].n; + * // Returns "default" + * + * @example + * const source = `export const asdf = 42`; + * const [imports, exports] = parse(source); + * exports[0].n; + * // Returns "asdf" + */ + n: string, + + /** + * Local name, or undefined. + * + * @example + * const source = `export default []`; + * const [imports, exports] = parse(source); + * exports[0].ln; + * // Returns undefined + * + * @example + * const asdf = 42; + * const source = `export { asdf as a }`; + * const [imports, exports] = parse(source); + * exports[0].ln; + * // Returns "asdf" + */ + ln: option, + + /** + * Start of exported name + * + * @example + * const source = `export default []`; + * const [imports, exports] = parse(source); + * source.substring(exports[0].s, exports[0].e); + * // Returns "default" + * + * @example + * const source = `export { 42 as asdf }`; + * const [imports, exports] = parse(source); + * source.substring(exports[0].s, exports[0].e); + * // Returns "asdf" + */ + s: s32, + + /** + * End of exported name + */ + e: s32, + + /** + * Start of local name, or -1. + * + * @example + * const asdf = 42; + * const source = `export { asdf as a }`; + * const [imports, exports] = parse(source); + * source.substring(exports[0].ls, exports[0].le); + * // Returns "asdf" + */ + ls: s32, + + /** + * End of local name, or -1. + */ + le: s32, +} + +type parse-result = result, list, bool>, string> + +/// +/// Outputs the list of exports and locations of import specifiers, +/// including dynamic import and import meta handling. +/// +/// @param source Source code to parser +/// @param name Optional sourcename +/// @returns Tuple contaning imports list and exports list. +/// Test +parse: func(source: string, name: option) -> parse-result diff --git a/src/lexer.c b/src/lexer.c old mode 100755 new mode 100644 index 075cd50..a23926d --- a/src/lexer.c +++ b/src/lexer.c @@ -1,8 +1,62 @@ #include "lexer.h" -#include -#include +#include + +// Bump allocator + +static void* heap_ptr = 0; +static void* heap_top = 0; +static const size_t PAGE_SIZE = 64 * 1024; + +__attribute__((weak, export_name("cabi_realloc"))) +void* cabi_realloc(void *ptr, size_t orig_size, size_t align, size_t new_size) { + if (heap_top == 0) { + heap_top = (void*)(__builtin_wasm_memory_size(0) * PAGE_SIZE); + heap_ptr = &__heap_base; + } + uint32_t align_diff = (uint32_t)heap_ptr % align; + if (align_diff) heap_ptr += align - align_diff; + heap_ptr += new_size; + if (heap_ptr > heap_top) { + uint32_t pages = (heap_ptr - heap_top) / PAGE_SIZE + 1; + heap_top = (void*)__builtin_wasm_memory_grow(0, (uintptr_t)pages); + } + return heap_ptr - new_size; +} + +__attribute__((weak, export_name("cabi_post_parse"))) +void __wasm_export_lexer_world_parse_post_return(int32_t arg0) { + heap_ptr = &__heap_base; +} + +// Strings + +#define LEN(STR) (sizeof(STR) / sizeof(STR[0]) - 1) + +static inline void cpy(char16_t* out, char16_t* in, size_t len) { + for (size_t i = 0; i < len; i++) { + out[i] = in[i]; + } +} + +size_t write_num(char16_t* out, uint32_t num) { + char16_t* ptr = out, *ptr1 = out, tmp_char; + uint32_t tmp_num; + do { + tmp_num = num; + num /= 10; + *ptr++ = "0123456789"[tmp_num - num * 10]; + } while (num); + size_t len = ptr - ptr1 + 1; + while (ptr1 < ptr) { + tmp_char = *ptr; + *ptr-- = *ptr1; + *ptr1++ = tmp_char; + } + return len; +} + +static const char16_t PARSE_ERROR_[] = { 'P', 'a', 'r', 's', 'e', ' ', 'e', 'r', 'r', 'o', 'r' }; -// NOTE: MESSING WITH THESE REQUIRES MANUAL ASM DICTIONARY CONSTRUCTION (via lexer.emcc.js base64 decoding) static const char16_t XPORT[] = { 'x', 'p', 'o', 'r', 't' }; static const char16_t MPORT[] = { 'm', 'p', 'o', 'r', 't' }; static const char16_t LASS[] = { 'l', 'a', 's', 's' }; @@ -29,28 +83,172 @@ static const char16_t CONTIN[] = { 'c', 'o', 'n', 't', 'i', 'n' }; static const char16_t SYNC[] = {'s', 'y', 'n', 'c'}; static const char16_t UNCTION[] = {'u', 'n', 'c', 't', 'i', 'o', 'n'}; -// Note: parsing is based on the _assumption_ that the source is already valid -bool parse () { +// Static vars + +static char16_t* source; +static uint32_t source_len = 0; + +static char16_t* name; +static size_t name_len = 0; + +static char16_t* parse_error; +static bool has_error = false; + +static bool facade; +static bool last_slash_was_division; +static uint16_t open_token_depth; +static char16_t* last_token_pos; +static char16_t* pos; +static char16_t* end; +static OpenToken* open_token_stack; +static uint16_t dynamic_import_stack_depth; +static lexer_world_import_t** dynamic_import_stack; +static bool next_brace_is_class; + +static lexer_world_import_t* imports_base; +static lexer_world_export_t* exports_base; +static lexer_world_import_t* imports_head; +static lexer_world_export_t* exports_head; + +static const size_t MAX_IMPORTS = 1024; +static const size_t MAX_EXPORTS = 8192; +static const size_t MAX_DYNAMIC_IMPORT_NESTING = 512; +static const size_t MAX_BRACE_DEPTH = 1024; + +// Output data + +void add_import (const char16_t* statement_start, + const char16_t* start, + const char16_t* end, + const char16_t* dynamic) { + imports_head->ss = statement_start - source; + imports_head->s = start - source; + imports_head->e = end - source; + imports_head->a = 0; + if (dynamic == STANDARD_IMPORT) { + imports_head->d = -1; + imports_head->se = end - source + 1; + imports_head->n.is_some = true; + imports_head->n.val.len = end - start; + imports_head->n.val.ptr = (char16_t*)start; + } else if (dynamic == IMPORT_META) { + imports_head->d = -2; + imports_head->se = end - source; + imports_head->n.is_some = false; + } else { + imports_head->d = dynamic - source; + imports_head->se = 0; + imports_head->n.is_some = false; + } + if (imports_head - imports_base < MAX_IMPORTS) + imports_head++; +} + +void add_export (const char16_t* start, + const char16_t* end, + const char16_t* local_start, + const char16_t* local_end) { + exports_head->s = start - source; + exports_head->e = end - source; + exports_head->n.len = end - start; + exports_head->n.ptr = (char16_t*)start; + if (local_start == NULL || local_end == NULL) { + exports_head->ls = exports_head->le = -1; + } else { + exports_head->ls = local_start - source; + exports_head->le = local_end - source; + exports_head->ln.is_some = true; + exports_head->ln.val.len = local_end - local_start; + exports_head->ln.val.ptr = (char16_t*)local_start; + } + if (exports_head - exports_base < MAX_IMPORTS) + exports_head++; +} + +// Error handler + +static lexer_world_parse_result_t* ret; + +// `Parse error (${name || '@'}:${line}:${col})` +void syntax_error () { + has_error = true; + char16_t* err = cabi_realloc(0, 0, 2, 50); + size_t len = LEN(PARSE_ERROR_); + cpy(err, (char16_t*)PARSE_ERROR_, len); + if (name_len) { + cpy(err + len, name, name_len); + len += name_len; + } else { + err[len++] = '@'; + } + err[len++] = ':'; + int32_t line = 1, col = 1; + char16_t* err_pos = pos; + char16_t ch; + pos = source - 1; + while (++pos < err_pos) { + ch = *pos; + if (ch == '\n') { + line++; + col = 1; + } else { + col++; + } + } + len += write_num(&err[len], line); + err[len++] = ':'; + len += write_num(&err[len], col); + lexer_world_parse_result_t result = { + .is_err = true, + .val = { + .err = { + .ptr = err, + .len = len + } + } + }; + *ret = result; + pos = end + 1; +} + +// Main + +void lexer_world_parse(lexer_world_string_t* input, lexer_world_option_string_t* name_option, lexer_world_parse_result_t* _ret) { + source_len = input->len; + source = input->ptr; + ret = _ret; + if (name_option->is_some) { + name = name_option->val.ptr; + name_len = name_option->val.len; + } else { + name_len = 0; + } + // stack allocations - // these are done here to avoid data section \0\0\0 repetition bloat - // (while gzip fixes this, still better to have ~10KiB ungzipped over ~20KiB) - OpenToken openTokenStack_[1024]; - Import* dynamicImportStack_[512]; + OpenToken open_token_stack_[MAX_BRACE_DEPTH]; + lexer_world_import_t* dynamic_import_stack_[MAX_DYNAMIC_IMPORT_NESTING]; + lexer_world_import_t imports_[MAX_IMPORTS]; + lexer_world_export_t exports_[MAX_EXPORTS]; + + // TODO: Exact stack sizing based on the above! facade = true; - dynamicImportStackDepth = 0; - openTokenDepth = 0; - lastTokenPos = (char16_t*)EMPTY_CHAR; - lastSlashWasDivision = false; - parse_error = 0; + dynamic_import_stack_depth = 0; + open_token_depth = 0; + last_token_pos = (char16_t*)EMPTY_CHAR; + last_slash_was_division = false; has_error = false; - openTokenStack = &openTokenStack_[0]; - dynamicImportStack = &dynamicImportStack_[0]; - nextBraceIsClass = false; + open_token_stack = &open_token_stack_[0]; + dynamic_import_stack = &dynamic_import_stack_[0]; + imports_base = &imports_[0]; + exports_base = &exports_[0]; + imports_head = imports_base; + exports_head = exports_base; + next_brace_is_class = false; pos = (char16_t*)(source - 1); char16_t ch = '\0'; - end = pos + sourceLen; + end = pos + source_len; // start with a pure "module-only" parser while (pos++ < end) { @@ -61,30 +259,30 @@ bool parse () { switch (ch) { case 'e': - if (openTokenDepth == 0 && keywordStart(pos) && memcmp(pos + 1, &XPORT[0], 5 * 2) == 0) { - tryParseExportStatement(); + if (open_token_depth == 0 && keyword_start(pos) && memcmp(pos + 1, &XPORT[0], 5 * 2) == 0) { + try_parse_export_statement(); // export might have been a non-pure declaration if (!facade) { - lastTokenPos = pos; - goto mainparse; + last_token_pos = pos; + goto main_parse; } } break; case 'i': - if (keywordStart(pos) && memcmp(pos + 1, &MPORT[0], 5 * 2) == 0) - tryParseImportStatement(); + if (keyword_start(pos) && memcmp(pos + 1, &MPORT[0], 5 * 2) == 0) + try_parse_import_statement(); break; case ';': break; case '/': { char16_t next_ch = *(pos + 1); if (next_ch == '/') { - lineComment(); + line_comment(); // dont update lastToken continue; } else if (next_ch == '*') { - blockComment(true); + block_comment(true); // dont update lastToken continue; } @@ -94,15 +292,15 @@ bool parse () { // as soon as we hit a non-module token, we go to main parser facade = false; pos--; - goto mainparse; // oh yeahhh + goto main_parse; // oh yeahhh } - lastTokenPos = pos; + last_token_pos = pos; } if (has_error) - return false; + return; - mainparse: while (pos++ < end) { + main_parse: while (pos++ < end) { ch = *pos; if (ch == 32 || ch < 14 && ch > 8) @@ -110,70 +308,66 @@ bool parse () { switch (ch) { case 'e': - if (openTokenDepth == 0 && keywordStart(pos) && memcmp(pos + 1, &XPORT[0], 5 * 2) == 0) - tryParseExportStatement(); + if (open_token_depth == 0 && keyword_start(pos) && memcmp(pos + 1, &XPORT[0], 5 * 2) == 0) + try_parse_export_statement(); break; case 'i': - if (keywordStart(pos) && memcmp(pos + 1, &MPORT[0], 5 * 2) == 0) - tryParseImportStatement(); + if (keyword_start(pos) && memcmp(pos + 1, &MPORT[0], 5 * 2) == 0) + try_parse_import_statement(); break; case 'c': - if (keywordStart(pos) && memcmp(pos + 1, &LASS[0], 4 * 2) == 0 && isBrOrWs(*(pos + 5))) - nextBraceIsClass = true; + if (keyword_start(pos) && memcmp(pos + 1, &LASS[0], 4 * 2) == 0 && is_br_or_ws(*(pos + 5))) + next_brace_is_class = true; break; case '(': - openTokenStack[openTokenDepth].token = AnyParen; - openTokenStack[openTokenDepth++].pos = lastTokenPos; + open_token_stack[open_token_depth].token = AnyParen; + open_token_stack[open_token_depth++].pos = last_token_pos; break; case ')': - if (openTokenDepth == 0) - return syntaxError(), false; - openTokenDepth--; - if (dynamicImportStackDepth > 0 && dynamicImportStack[dynamicImportStackDepth - 1]->dynamic == openTokenStack[openTokenDepth].pos) { - Import* cur_dynamic_import = dynamicImportStack[dynamicImportStackDepth - 1]; - if (cur_dynamic_import->end == 0) - cur_dynamic_import->end = pos; - cur_dynamic_import->statement_end = pos + 1; - dynamicImportStackDepth--; + if (open_token_depth == 0) + return syntax_error(); + open_token_depth--; + if (dynamic_import_stack_depth > 0 && source + dynamic_import_stack[dynamic_import_stack_depth - 1]->d == open_token_stack[open_token_depth].pos) { + lexer_world_import_t* cur_dynamic_import = dynamic_import_stack[dynamic_import_stack_depth - 1]; + if (cur_dynamic_import->e == -1) + cur_dynamic_import->e = pos - source; + cur_dynamic_import->se = pos - source + 1; + dynamic_import_stack_depth--; } break; case '{': // dynamic import followed by { is not a dynamic import (so remove) // this is a sneaky way to get around { import () {} } v { import () } // block / object ambiguity without a parser (assuming source is valid) - if (*lastTokenPos == ')' && import_write_head && import_write_head->end == lastTokenPos) { - import_write_head = import_write_head_last; - if (import_write_head) - import_write_head->next = NULL; - else - first_import = NULL; + if (*last_token_pos == ')' && imports_head > imports_base && source + (imports_head - 1)->e == last_token_pos) { + imports_head--; } - openTokenStack[openTokenDepth].token = nextBraceIsClass ? ClassBrace : AnyBrace; - openTokenStack[openTokenDepth++].pos = lastTokenPos; - nextBraceIsClass = false; + open_token_stack[open_token_depth].token = next_brace_is_class ? ClassBrace : AnyBrace; + open_token_stack[open_token_depth++].pos = last_token_pos; + next_brace_is_class = false; break; case '}': - if (openTokenDepth == 0) - return syntaxError(), false; - if (openTokenStack[--openTokenDepth].token == TemplateBrace) { - templateString(); + if (open_token_depth == 0) + return syntax_error(); + if (open_token_stack[--open_token_depth].token == TemplateBrace) { + template_string(); } break; case '\'': - stringLiteral(ch); + string_literal(ch); break; case '"': - stringLiteral(ch); + string_literal(ch); break; case '/': { char16_t next_ch = *(pos + 1); if (next_ch == '/') { - lineComment(); + line_comment(); // dont update lastToken continue; } else if (next_ch == '*') { - blockComment(true); + block_comment(true); // dont update lastToken continue; } @@ -182,98 +376,120 @@ bool parse () { // - what token came previously (lastToken) // - if a closing brace or paren, what token came before the corresponding // opening brace or paren (lastOpenTokenIndex) - char16_t lastToken = *lastTokenPos; - if (isExpressionPunctuator(lastToken) && - !(lastToken == '.' && (*(lastTokenPos - 1) >= '0' && *(lastTokenPos - 1) <= '9')) && - !(lastToken == '+' && *(lastTokenPos - 1) == '+') && !(lastToken == '-' && *(lastTokenPos - 1) == '-') || - lastToken == ')' && isParenKeyword(openTokenStack[openTokenDepth].pos) || - lastToken == '}' && (isExpressionTerminator(openTokenStack[openTokenDepth].pos) || openTokenStack[openTokenDepth].token == ClassBrace) || - isExpressionKeyword(lastTokenPos) || - lastToken == '/' && lastSlashWasDivision || + char16_t lastToken = *last_token_pos; + if (is_expression_punctuator(lastToken) && + !(lastToken == '.' && (*(last_token_pos - 1) >= '0' && *(last_token_pos - 1) <= '9')) && + !(lastToken == '+' && *(last_token_pos - 1) == '+') && !(lastToken == '-' && *(last_token_pos - 1) == '-') || + lastToken == ')' && is_paren_keyword(open_token_stack[open_token_depth].pos) || + lastToken == '}' && (is_expression_terminator(open_token_stack[open_token_depth].pos) || open_token_stack[open_token_depth].token == ClassBrace) || + is_expression_keyword(last_token_pos) || + lastToken == '/' && last_slash_was_division || !lastToken) { - regularExpression(); - lastSlashWasDivision = false; + regular_expression(); + last_slash_was_division = false; } else { // Final check - if the last token was "break x" or "continue x" - while (lastTokenPos > source && !isBrOrWsOrPunctuatorNotDot(*(--lastTokenPos))); - if (isWsNotBr(*lastTokenPos)) { - while (lastTokenPos > source && isWsNotBr(*(--lastTokenPos))); - if (isBreakOrContinue(lastTokenPos)) { - regularExpression(); - lastSlashWasDivision = false; + while (last_token_pos > source && !is_br_or_ws_or_punctuator_not_dot(*(--last_token_pos))); + if (is_ws_not_br(*last_token_pos)) { + while (last_token_pos > source && is_ws_not_br(*(--last_token_pos))); + if (is_break_or_continue(last_token_pos)) { + regular_expression(); + last_slash_was_division = false; break; } } - lastSlashWasDivision = true; + last_slash_was_division = true; } } break; } case '`': - openTokenStack[openTokenDepth].pos = lastTokenPos; - openTokenStack[openTokenDepth++].token = Template; - templateString(); + open_token_stack[open_token_depth].pos = last_token_pos; + open_token_stack[open_token_depth++].token = Template; + template_string(); break; } - lastTokenPos = pos; + last_token_pos = pos; } - if (openTokenDepth || has_error || dynamicImportStackDepth) - return false; + if (has_error) + return; - // succeess - return true; + if (open_token_depth || dynamic_import_stack_depth) + return syntax_error(); + + lexer_world_parse_result_t result = { + .is_err = false, + .val = { + .ok = { + .f0 = { + .ptr = imports_base, + .len = imports_head - imports_base + }, + .f1 = { + .ptr = exports_base, + .len = exports_head - exports_base + }, + .f2 = facade + } + } + }; + *_ret = result; } -void tryParseImportStatement () { - char16_t* startPos = pos; +void try_parse_import_statement () { + char16_t* start_pos = pos; pos += 6; - char16_t ch = commentWhitespace(true); + char16_t ch = comment_whitespace(true); switch (ch) { // dynamic import case '(': - openTokenStack[openTokenDepth].token = ImportParen; - openTokenStack[openTokenDepth++].pos = pos; - if (*lastTokenPos == '.') + open_token_stack[open_token_depth].token = ImportParen; + open_token_stack[open_token_depth++].pos = pos; + if (*last_token_pos == '.') return; // dynamic import indicated by positive d - char16_t* dynamicPos = pos; + char16_t* dynamic_pos = pos; // try parse a string, to record a safe dynamic import string pos++; - ch = commentWhitespace(true); - addImport(startPos, pos, 0, dynamicPos); - dynamicImportStack[dynamicImportStackDepth++] = import_write_head; + ch = comment_whitespace(true); + add_import(start_pos, pos, 0, dynamic_pos); + dynamic_import_stack[dynamic_import_stack_depth++] = imports_head - 1; if (ch == '\'') { - stringLiteral(ch); + string_literal(ch); } else if (ch == '"') { - stringLiteral(ch); + string_literal(ch); } else { pos--; return; } pos++; - char16_t* endPos = pos; - ch = commentWhitespace(true); + char16_t* end_pos = pos; + ch = comment_whitespace(true); if (ch == ',') { pos++; - ch = commentWhitespace(true); - import_write_head->end = endPos; - import_write_head->assert_index = pos; - import_write_head->safe = true; + ch = comment_whitespace(true); + (imports_head - 1)->e = end_pos - source; + (imports_head - 1)->a = pos - source; + (imports_head - 1)->n.is_some = true; + (imports_head - 1)->n.val.ptr = source + (imports_head - 1)->d + 1; + (imports_head - 1)->n.val.len = end_pos - source - (imports_head - 1)->d - 1; pos--; } else if (ch == ')') { - openTokenDepth--; - import_write_head->end = endPos; - import_write_head->statement_end = pos + 1; - import_write_head->safe = true; - dynamicImportStackDepth--; + open_token_depth--; + (imports_head - 1)->e = end_pos - source; + (imports_head - 1)->se = pos + 1 - source; + (imports_head - 1)->n.is_some = true; + (imports_head - 1)->n.val.ptr = source + (imports_head - 1)->d + 1; + (imports_head - 1)->n.val.len = end_pos - source - (imports_head - 1)->d - 1; + dynamic_import_stack_depth--; } else { pos--; @@ -282,15 +498,15 @@ void tryParseImportStatement () { // import.meta case '.': pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); // import.meta indicated by d == -2 - if (ch == 'm' && memcmp(pos + 1, &ETA[0], 3 * 2) == 0 && *lastTokenPos != '.') - addImport(startPos, startPos, pos + 4, IMPORT_META); + if (ch == 'm' && memcmp(pos + 1, &ETA[0], 3 * 2) == 0 && *last_token_pos != '.') + add_import(start_pos, start_pos, pos + 4, IMPORT_META); return; default: // no space after "import" -> not an import keyword - if (pos == startPos + 6) { + if (pos == start_pos + 6) { pos--; break; } @@ -298,34 +514,34 @@ void tryParseImportStatement () { case '\'': case '*': { // import statement only permitted at base-level - if (openTokenDepth != 0) { + if (open_token_depth != 0) { pos--; return; } while (pos < end) { ch = *pos; - if (isQuote(ch)) { - readImportString(startPos, ch); + if (is_quote(ch)) { + read_import_string(start_pos, ch); return; } pos++; } - syntaxError(); + syntax_error(); break; } case '{': { // import statement only permitted at base-level - if (openTokenDepth != 0) { + if (open_token_depth != 0) { pos--; return; } while (pos < end) { - ch = commentWhitespace(true); + ch = comment_whitespace(true); - if (isQuote(ch)) { - stringLiteral(ch); + if (is_quote(ch)) { + string_literal(ch); } else if (ch == '}') { pos++; break; @@ -334,47 +550,46 @@ void tryParseImportStatement () { pos++; } - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (memcmp(pos, &FROM[0], 4 * 2) != 0) { - syntaxError(); + syntax_error(); break; } pos += 4; - ch = commentWhitespace(true); + ch = comment_whitespace(true); - if (!isQuote(ch)) { - return syntaxError(); - } + if (!is_quote(ch)) + return syntax_error(); - readImportString(startPos, ch); + read_import_string(start_pos, ch); break; } } } -void tryParseExportStatement () { - char16_t* sStartPos = pos; - Export* prev_export_write_head = export_write_head; +void try_parse_export_statement () { + char16_t* s_start_pos = pos; + lexer_world_export_t* prev_exports_head = exports_head; pos += 6; - char16_t* curPos = pos; + char16_t* cur_pos = pos; - char16_t ch = commentWhitespace(true); + char16_t ch = comment_whitespace(true); - if (pos == curPos && !isPunctuator(ch)) + if (pos == cur_pos && !is_punctuator(ch)) return; if (ch == '{') { pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); while (true) { - char16_t* startPos = pos; + char16_t* start_pos = pos; - if (!isQuote(ch)) { - ch = readToWsOrPunctuator(ch); + if (!is_quote(ch)) { + ch = read_to_ws_or_punctuator(ch); } // export { "identifer" as } from // export { "@notid" as } from @@ -386,56 +601,56 @@ void tryParseExportStatement () { // export { "identifer" } from // export { "%notid" } from else { - stringLiteral(ch); + string_literal(ch); pos++; } - char16_t* endPos = pos; - commentWhitespace(true); - ch = readExportAs(startPos, endPos); + char16_t* end_pos = pos; + comment_whitespace(true); + ch = read_export_as(start_pos, end_pos); // , if (ch == ',') { pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); } if (ch == '}') break; - if (pos == startPos) - return syntaxError(); + if (pos == start_pos) + return syntax_error(); if (pos > end) - return syntaxError(); + return syntax_error(); } pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); } // export * // export * as X else if (ch == '*') { pos++; - commentWhitespace(true); - ch = readExportAs(pos, pos); - ch = commentWhitespace(true); + comment_whitespace(true); + ch = read_export_as(pos, pos); + ch = comment_whitespace(true); } else { facade = false; switch (ch) { // export default ... case 'd': { - const char16_t* startPos = pos; + const char16_t* start_pos = pos; pos += 7; ch = commentWhitespace(true); bool localName = false; // export default async? function*? name? (){} - if (ch == 'a' && keywordStart(pos) && memcmp(pos + 1, &SYNC[0], 4 * 2) == 0 && isWsNotBr(*(pos + 5))) { + if (ch == 'a' && keyword_start(pos) && memcmp(pos + 1, &SYNC[0], 4 * 2) == 0 && is_ws_not_br(*(pos + 5))) { pos += 5; - ch = commentWhitespace(false); + ch = comment_whitespace(false); } - if (ch == 'f' && keywordStart(pos) && memcmp(pos + 1, &UNCTION[0], 7 * 2) == 0 && (isBrOrWs(*(pos + 8)) || *(pos + 8) == '*' || *(pos + 8) == '(')) { + if (ch == 'f' && keyword_start(pos) && memcmp(pos + 1, &UNCTION[0], 7 * 2) == 0 && (is_br_or_ws(*(pos + 8)) || *(pos + 8) == '*' || *(pos + 8) == '(')) { pos += 8; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch == '*') { pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); } if (ch == '(') { addExport(startPos, startPos + 7, NULL, NULL); @@ -445,9 +660,9 @@ void tryParseExportStatement () { localName = true; } // export default class name? {} - if (ch == 'c' && keywordStart(pos) && memcmp(pos + 1, &LASS[0], 4 * 2) == 0 && (isBrOrWs(*(pos + 5)) || *(pos + 5) == '{')) { + if (ch == 'c' && keyword_start(pos) && memcmp(pos + 1, &LASS[0], 4 * 2) == 0 && (is_br_or_ws(*(pos + 5)) || *(pos + 5) == '{')) { pos += 5; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch == '{') { addExport(startPos, startPos + 7, NULL, NULL); pos = (char16_t*)(startPos + 6); @@ -470,29 +685,29 @@ void tryParseExportStatement () { // export async? function*? name () { case 'a': pos += 5; - commentWhitespace(true); + comment_whitespace(true); // fallthrough case 'f': pos += 8; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch == '*') { pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); } - const char16_t* startPos = pos; - ch = readToWsOrPunctuator(ch); - addExport(startPos, pos, startPos, pos); + const char16_t* start_pos = pos; + ch = read_to_ws_or_punctuator(ch); + add_export(start_pos, pos, start_pos, pos); pos--; return; // export class name ... case 'c': - if (memcmp(pos + 1, &LASS[0], 4 * 2) == 0 && isBrOrWsOrPunctuatorNotDot(*(pos + 5))) { + if (memcmp(pos + 1, &LASS[0], 4 * 2) == 0 && is_br_or_ws_or_punctuator_not_dot(*(pos + 5))) { pos += 5; - ch = commentWhitespace(true); - const char16_t* startPos = pos; - ch = readToWsOrPunctuator(ch); - addExport(startPos, pos, startPos, pos); + ch = comment_whitespace(true); + const char16_t* start_pos = pos; + ch = read_to_ws_or_punctuator(ch); + add_export(start_pos, pos, start_pos, pos); pos--; return; } @@ -505,20 +720,19 @@ void tryParseExportStatement () { // destructured initializations not currently supported (skipped for { or [) // also, lexing names after variable equals is skipped (export var p = function () { ... }, q = 5 skips "q") pos += 2; - facade = false; do { pos++; - ch = commentWhitespace(true); - const char16_t* startPos = pos; - ch = readToWsOrPunctuator(ch); + ch = comment_whitespace(true); + const char16_t* start_pos = pos; + ch = read_to_ws_or_punctuator(ch); // dont yet handle [ { destructurings if (ch == '{' || ch == '[') { break; } - if (pos == startPos) + if (pos == start_pos) return; - addExport(startPos, pos, startPos, pos); - ch = commentWhitespace(true); + add_export(start_pos, pos, start_pos, pos); + ch = comment_whitespace(true); if (ch == '=') { break; } @@ -534,11 +748,11 @@ void tryParseExportStatement () { // from ... if (ch == 'f' && memcmp(pos + 1, &FROM[1], 3 * 2) == 0) { pos += 4; - readImportString(sStartPos, commentWhitespace(true)); + read_import_string(s_start_pos, comment_whitespace(true)); // There were no local names. - for (Export* exprt = prev_export_write_head == NULL ? first_export : prev_export_write_head->next; exprt != NULL; exprt = exprt->next) { - exprt->local_start = exprt->local_end = NULL; + for (lexer_world_export_t* expt = prev_exports_head == exports_base ? exports_base : prev_exports_head + 1; expt != exports_head; expt++) { + expt->ls = expt->le = -1; } } else { @@ -546,18 +760,18 @@ void tryParseExportStatement () { } } -char16_t readExportAs (char16_t* startPos, char16_t* endPos) { +char16_t read_export_as (char16_t* start_pos, char16_t* end_pos) { char16_t ch = *pos; - char16_t* localStartPos = startPos == endPos ? NULL : startPos; - char16_t* localEndPos = startPos == endPos ? NULL : endPos; + char16_t* local_start_pos = start_pos == end_pos ? NULL : start_pos; + char16_t* local_end_pos = start_pos == end_pos ? NULL : end_pos; if (ch == 'a') { pos += 2; - ch = commentWhitespace(true); - startPos = pos; + ch = comment_whitespace(true); + start_pos = pos; - if (!isQuote(ch)) { - ch = readToWsOrPunctuator(ch); + if (!is_quote(ch)) { + ch = read_to_ws_or_punctuator(ch); } // export { mod as "identifer" } from // export { mod as "@notid" } from @@ -567,142 +781,142 @@ char16_t readExportAs (char16_t* startPos, char16_t* endPos) { // export { mod as "not~id" } from // export { mod as "%notid" } from else { - stringLiteral(ch); + string_literal(ch); pos++; } - endPos = pos; + end_pos = pos; - ch = commentWhitespace(true); + ch = comment_whitespace(true); } - if (pos != startPos) - addExport(startPos, endPos, localStartPos, localEndPos); + if (pos != start_pos) + add_export(start_pos, end_pos, local_start_pos, local_end_pos); return ch; } -void readImportString (const char16_t* ss, char16_t ch) { - const char16_t* startPos = pos + 1; +void read_import_string (const char16_t* ss, char16_t ch) { + const char16_t* start_pos = pos + 1; if (ch == '\'') { - stringLiteral(ch); + string_literal(ch); } else if (ch == '"') { - stringLiteral(ch); + string_literal(ch); } else { - syntaxError(); + syntax_error(); return; } - addImport(ss, startPos, pos, STANDARD_IMPORT); + add_import(ss, start_pos, pos, STANDARD_IMPORT); pos++; - ch = commentWhitespace(false); + ch = comment_whitespace(false); if (ch != 'a' || memcmp(pos + 1, &SSERT[0], 5 * 2) != 0) { pos--; return; } - char16_t* assertIndex = pos; + char16_t* assert_idx = pos; pos += 6; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch != '{') { - pos = assertIndex; + pos = assert_idx; return; } - const char16_t* assertStart = pos; + const char16_t* assert_start = pos; do { pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch == '\'') { - stringLiteral(ch); + string_literal(ch); pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); } else if (ch == '"') { - stringLiteral(ch); + string_literal(ch); pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); } else { - ch = readToWsOrPunctuator(ch); + ch = read_to_ws_or_punctuator(ch); } if (ch != ':') { - pos = assertIndex; + pos = assert_idx; return; } pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch == '\'') { - stringLiteral(ch); + string_literal(ch); } else if (ch == '"') { - stringLiteral(ch); + string_literal(ch); } else { - pos = assertIndex; + pos = assert_idx; return; } pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch == ',') { pos++; - ch = commentWhitespace(true); + ch = comment_whitespace(true); if (ch == '}') break; continue; } if (ch == '}') break; - pos = assertIndex; + pos = assert_idx; return; } while (true); - import_write_head->assert_index = assertStart; - import_write_head->statement_end = pos + 1; + (imports_head - 1)->a = assert_start - source; + (imports_head - 1)->se = pos + 1 - source; } -char16_t commentWhitespace (bool br) { +char16_t comment_whitespace (bool br) { char16_t ch; do { ch = *pos; if (ch == '/') { char16_t next_ch = *(pos + 1); if (next_ch == '/') - lineComment(); + line_comment(); else if (next_ch == '*') - blockComment(br); + block_comment(br); else return ch; } - else if (br ? !isBrOrWs(ch) : !isWsNotBr(ch)) { + else if (br ? !is_br_or_ws(ch) : !is_ws_not_br(ch)) { return ch; } } while (pos++ < end); return ch; } -void templateString () { +void template_string () { while (pos++ < end) { char16_t ch = *pos; if (ch == '$' && *(pos + 1) == '{') { pos++; - openTokenStack[openTokenDepth].token = TemplateBrace; - openTokenStack[openTokenDepth++].pos = pos; + open_token_stack[open_token_depth].token = TemplateBrace; + open_token_stack[open_token_depth++].pos = pos; return; } if (ch == '`') { - if (openTokenStack[--openTokenDepth].token != Template) - syntaxError(); + if (open_token_stack[--open_token_depth].token != Template) + syntax_error(); return; } if (ch == '\\') pos++; } - syntaxError(); + syntax_error(); } -void blockComment (bool br) { +void block_comment (bool br) { pos++; while (pos++ < end) { char16_t ch = *pos; - if (!br && isBr(ch)) + if (!br && is_br(ch)) return; if (ch == '*' && *(pos + 1) == '/') { pos++; @@ -711,7 +925,7 @@ void blockComment (bool br) { } } -void lineComment () { +void line_comment () { while (pos++ < end) { char16_t ch = *pos; if (ch == '\n' || ch == '\r') @@ -719,7 +933,7 @@ void lineComment () { } } -void stringLiteral (char16_t quote) { +void string_literal (char16_t quote) { while (pos++ < end) { char16_t ch = *pos; if (ch == quote) @@ -729,13 +943,13 @@ void stringLiteral (char16_t quote) { if (ch == '\r' && *(pos + 1) == '\n') pos++; } - else if (isBr(ch)) + else if (is_br(ch)) break; } - syntaxError(); + syntax_error(); } -char16_t regexCharacterClass () { +static inline char16_t regexCharacterClass () { while (pos++ < end) { char16_t ch = *pos; if (ch == ']') @@ -745,11 +959,11 @@ char16_t regexCharacterClass () { else if (ch == '\n' || ch == '\r') break; } - syntaxError(); + syntax_error(); return '\0'; } -void regularExpression () { +void regular_expression () { while (pos++ < end) { char16_t ch = *pos; if (ch == '/') @@ -761,12 +975,12 @@ void regularExpression () { else if (ch == '\n' || ch == '\r') break; } - syntaxError(); + syntax_error(); } -char16_t readToWsOrPunctuator (char16_t ch) { +char16_t read_to_ws_or_punctuator (char16_t ch) { do { - if (isBrOrWs(ch) || isPunctuator(ch)) + if (is_br_or_ws(ch) || is_punctuator(ch)) return ch; } while (ch = *(++pos)); return ch; @@ -774,52 +988,52 @@ char16_t readToWsOrPunctuator (char16_t ch) { // Note: non-asii BR and whitespace checks omitted for perf / footprint // if there is a significant user need this can be reconsidered -bool isBr (char16_t c) { +bool is_br (char16_t c) { return c == '\r' || c == '\n'; } -bool isWsNotBr (char16_t c) { +bool is_ws_not_br (char16_t c) { return c == 9 || c == 11 || c == 12 || c == 32 || c == 160; } -bool isBrOrWs (char16_t c) { +bool is_br_or_ws (char16_t c) { return c > 8 && c < 14 || c == 32 || c == 160; } -bool isBrOrWsOrPunctuatorNotDot (char16_t c) { - return c > 8 && c < 14 || c == 32 || c == 160 || isPunctuator(c) && c != '.'; +bool is_br_or_ws_or_punctuator_not_dot (char16_t c) { + return c > 8 && c < 14 || c == 32 || c == 160 || is_punctuator(c) && c != '.'; } -bool isQuote (char16_t ch) { +bool is_quote (char16_t ch) { return ch == '\'' || ch == '"'; } -bool keywordStart (char16_t* pos) { - return pos == source || isBrOrWsOrPunctuatorNotDot(*(pos - 1)); +bool keyword_start (char16_t* pos) { + return pos == source || is_br_or_ws_or_punctuator_not_dot(*(pos - 1)); } -bool readPrecedingKeyword1 (char16_t* pos, char16_t c1) { +bool read_preceding_keyword_1 (char16_t* pos, char16_t c1) { if (pos < source) return false; - return *pos == c1 && (pos == source || isBrOrWsOrPunctuatorNotDot(*(pos - 1))); + return *pos == c1 && (pos == source || is_br_or_ws_or_punctuator_not_dot(*(pos - 1))); } -bool readPrecedingKeywordn (char16_t* pos, const char16_t* compare, size_t n) { +bool read_preceding_keyword_n (char16_t* pos, const char16_t* compare, size_t n) { if (pos - n + 1 < source) return false; - return memcmp(pos - n + 1, compare, n * 2) == 0 && (pos - n + 1 == source || isBrOrWsOrPunctuatorNotDot(*(pos - n))); + return memcmp(pos - n + 1, compare, n * 2) == 0 && (pos - n + 1 == source || is_br_or_ws_or_punctuator_not_dot(*(pos - n))); } // Detects one of case, debugger, delete, do, else, in, instanceof, new, // return, throw, typeof, void, yield ,await -bool isExpressionKeyword (char16_t* pos) { +bool is_expression_keyword (char16_t* pos) { switch (*pos) { case 'd': switch (*(pos - 1)) { case 'i': // void - return readPrecedingKeywordn(pos - 2, &VO[0], 2); + return read_preceding_keyword_n(pos - 2, &VO[0], 2); case 'l': // yield - return readPrecedingKeywordn(pos - 2, &YIE[0], 3); + return read_preceding_keyword_n(pos - 2, &YIE[0], 3); default: return false; } @@ -829,19 +1043,19 @@ bool isExpressionKeyword (char16_t* pos) { switch (*(pos - 2)) { case 'l': // else - return readPrecedingKeyword1(pos - 3, 'e'); + return read_preceding_keyword_1(pos - 3, 'e'); case 'a': // case - return readPrecedingKeyword1(pos - 3, 'c'); + return read_preceding_keyword_1(pos - 3, 'c'); default: return false; } case 't': // delete - return readPrecedingKeywordn(pos - 2, &DELE[0], 4); + return read_preceding_keyword_n(pos - 2, &DELE[0], 4); case 'u': // continue - return readPrecedingKeywordn(pos - 2, &CONTIN[0], 6); + return read_preceding_keyword_n(pos - 2, &CONTIN[0], 6); default: return false; } @@ -851,36 +1065,36 @@ bool isExpressionKeyword (char16_t* pos) { switch (*(pos - 3)) { case 'c': // instanceof - return readPrecedingKeywordn(pos - 4, &INSTAN[0], 6); + return read_preceding_keyword_n(pos - 4, &INSTAN[0], 6); case 'p': // typeof - return readPrecedingKeywordn(pos - 4, &TY[0], 2); + return read_preceding_keyword_n(pos - 4, &TY[0], 2); default: return false; } case 'k': // break - return readPrecedingKeywordn(pos - 1, &BREA[0], 4); + return read_preceding_keyword_n(pos - 1, &BREA[0], 4); case 'n': // in, return - return readPrecedingKeyword1(pos - 1, 'i') || readPrecedingKeywordn(pos - 1, &RETUR[0], 5); + return read_preceding_keyword_1(pos - 1, 'i') || read_preceding_keyword_n(pos - 1, &RETUR[0], 5); case 'o': // do - return readPrecedingKeyword1(pos - 1, 'd'); + return read_preceding_keyword_1(pos - 1, 'd'); case 'r': // debugger - return readPrecedingKeywordn(pos - 1, &DEBUGGE[0], 7); + return read_preceding_keyword_n(pos - 1, &DEBUGGE[0], 7); case 't': // await - return readPrecedingKeywordn(pos - 1, &AWAI[0], 4); + return read_preceding_keyword_n(pos - 1, &AWAI[0], 4); case 'w': switch (*(pos - 1)) { case 'e': // new - return readPrecedingKeyword1(pos - 2, 'n'); + return read_preceding_keyword_1(pos - 2, 'n'); case 'o': // throw - return readPrecedingKeywordn(pos - 2, &THR[0], 3); + return read_preceding_keyword_n(pos - 2, &THR[0], 3); default: return false; } @@ -888,13 +1102,13 @@ bool isExpressionKeyword (char16_t* pos) { return false; } -bool isParenKeyword (char16_t* curPos) { - return readPrecedingKeywordn(curPos, &WHILE[0], 5) || - readPrecedingKeywordn(curPos, &FOR[0], 3) || - readPrecedingKeywordn(curPos, &IF[0], 2); +bool is_paren_keyword (char16_t* cur_pos) { + return read_preceding_keyword_n(cur_pos, &WHILE[0], 5) || + read_preceding_keyword_n(cur_pos, &FOR[0], 3) || + read_preceding_keyword_n(cur_pos, &IF[0], 2); } -bool isPunctuator (char16_t ch) { +bool is_punctuator (char16_t ch) { // 23 possible punctuator endings: !%&()*+,-./:;<=>?[]^{}|~ return ch == '!' || ch == '%' || ch == '&' || ch > 39 && ch < 48 || ch > 57 && ch < 64 || @@ -902,52 +1116,40 @@ bool isPunctuator (char16_t ch) { ch > 122 && ch < 127; } -bool isExpressionPunctuator (char16_t ch) { +bool is_expression_punctuator (char16_t ch) { // 20 possible expression endings: !%&(*+,-.:;<=>?[^{|~ return ch == '!' || ch == '%' || ch == '&' || ch > 39 && ch < 47 && ch != 41 || ch > 57 && ch < 64 || ch == '[' || ch == '^' || ch > 122 && ch < 127 && ch != '}'; } -bool isBreakOrContinue (char16_t* curPos) { - switch (*curPos) { +bool is_break_or_continue (char16_t* cur_pos) { + switch (*cur_pos) { case 'k': - return readPrecedingKeywordn(curPos - 1, &BREA[0], 4); + return read_preceding_keyword_n(cur_pos - 1, &BREA[0], 4); case 'e': - if (*(curPos - 1) == 'u') - return readPrecedingKeywordn(curPos - 2, &CONTIN[0], 6); + if (*(cur_pos - 1) == 'u') + return read_preceding_keyword_n(cur_pos - 2, &CONTIN[0], 6); } return false; } -bool isExpressionTerminator (char16_t* curPos) { +bool is_expression_terminator (char16_t* cur_pos) { // detects: // => ; ) finally catch else class X // as all of these followed by a { will indicate a statement brace - switch (*curPos) { + switch (*cur_pos) { case '>': - return *(curPos - 1) == '='; + return *(cur_pos - 1) == '='; case ';': case ')': return true; case 'h': - return readPrecedingKeywordn(curPos - 1, &CATC[0], 4); + return read_preceding_keyword_n(cur_pos - 1, &CATC[0], 4); case 'y': - return readPrecedingKeywordn(curPos - 1, &FINALL[0], 6); + return read_preceding_keyword_n(cur_pos - 1, &FINALL[0], 6); case 'e': - return readPrecedingKeywordn(curPos - 1, &ELS[0], 3); + return read_preceding_keyword_n(cur_pos - 1, &ELS[0], 3); } return false; } - -void bail (uint32_t error) { - has_error = true; - parse_error = error; - pos = end + 1; -} - -void syntaxError () { - has_error = true; - parse_error = pos - source; - pos = end + 1; -} diff --git a/src/lexer.h b/src/lexer.h old mode 100755 new mode 100644 index 4f5d00b..c7b1584 --- a/src/lexer.h +++ b/src/lexer.h @@ -1,31 +1,12 @@ -#include -#include -#include +#include "../obj/lexer_world.h" -typedef unsigned short char16_t; -extern unsigned char __heap_base; +extern void* __heap_base; +void *sbrk(intptr_t increment); const char16_t* STANDARD_IMPORT = (char16_t*)0x1; const char16_t* IMPORT_META = (char16_t*)0x2; const char16_t __empty_char = '\0'; const char16_t* EMPTY_CHAR = &__empty_char; -const char16_t* source = (void*)&__heap_base; - -void setSource (void* ptr) { - source = ptr; -} - -struct Import { - const char16_t* start; - const char16_t* end; - const char16_t* statement_start; - const char16_t* statement_end; - const char16_t* assert_index; - const char16_t* dynamic; - bool safe; - struct Import* next; -}; -typedef struct Import Import; // Paren = odd, Brace = even enum OpenTokenState { @@ -44,218 +25,53 @@ struct OpenToken { }; typedef struct OpenToken OpenToken; -struct Export { - const char16_t* start; - const char16_t* end; - const char16_t* local_start; - const char16_t* local_end; - struct Export* next; -}; -typedef struct Export Export; - -Import* first_import = NULL; -Export* first_export = NULL; -Import* import_read_head = NULL; -Export* export_read_head = NULL; -Import* import_write_head = NULL; -Import* import_write_head_last = NULL; -Export* export_write_head = NULL; -void* analysis_base; -void* analysis_head; - -bool facade; -bool lastSlashWasDivision; -uint16_t openTokenDepth; -char16_t* lastTokenPos; -char16_t* pos; -char16_t* end; -OpenToken* openTokenStack; -uint16_t dynamicImportStackDepth; -Import** dynamicImportStack; -bool nextBraceIsClass; - -// Memory Structure: -// -> source -// -> analysis starts after source -uint32_t parse_error; -bool has_error = false; -uint32_t sourceLen = 0; - -void bail (uint32_t err); - -// allocateSource -const char16_t* sa (uint32_t utf16Len) { - sourceLen = utf16Len; - const char16_t* sourceEnd = source + utf16Len + 1; - // ensure source is null terminated - *(char16_t*)(source + utf16Len) = '\0'; - analysis_base = (void*)sourceEnd; - analysis_head = analysis_base; - first_import = NULL; - import_write_head = NULL; - import_read_head = NULL; - first_export = NULL; - export_write_head = NULL; - export_read_head = NULL; - return source; -} - -void addImport (const char16_t* statement_start, const char16_t* start, const char16_t* end, const char16_t* dynamic) { - Import* import = (Import*)(analysis_head); - analysis_head = analysis_head + sizeof(Import); - if (import_write_head == NULL) - first_import = import; - else - import_write_head->next = import; - import_write_head_last = import_write_head; - import_write_head = import; - import->statement_start = statement_start; - if (dynamic == IMPORT_META) - import->statement_end = end; - else if (dynamic == STANDARD_IMPORT) - import->statement_end = end + 1; - else - import->statement_end = 0; - import->start = start; - import->end = end; - import->assert_index = 0; - import->dynamic = dynamic; - import->safe = dynamic == STANDARD_IMPORT; - import->next = NULL; -} - -void addExport (const char16_t* start, const char16_t* end, const char16_t* local_start, const char16_t* local_end) { - Export* export = (Export*)(analysis_head); - analysis_head = analysis_head + sizeof(Export); - if (export_write_head == NULL) - first_export = export; - else - export_write_head->next = export; - export_write_head = export; - export->start = start; - export->end = end; - export->local_start = local_start; - export->local_end = local_end; - export->next = NULL; -} - -// getErr -uint32_t e () { - return parse_error; -} - -// getImportStart -uint32_t is () { - return import_read_head->start - source; -} -// getImportEnd -uint32_t ie () { - return import_read_head->end == 0 ? -1 : import_read_head->end - source; -} -// getImportStatementStart -uint32_t ss () { - return import_read_head->statement_start - source; -} -// getImportStatementEnd -uint32_t se () { - return import_read_head->statement_end == 0 ? -1 : import_read_head->statement_end - source; -} -// getAssertIndex -uint32_t ai () { - return import_read_head->assert_index == 0 ? -1 : import_read_head->assert_index - source; -} -// getImportDynamic -uint32_t id () { - const char16_t* dynamic = import_read_head->dynamic; - if (dynamic == STANDARD_IMPORT) - return -1; - else if (dynamic == IMPORT_META) - return -2; - return import_read_head->dynamic - source; -} -// getImportSafeString -uint32_t ip () { - return import_read_head->safe; -} -// getExportStart -uint32_t es () { - return export_read_head->start - source; -} -// getExportEnd -uint32_t ee () { - return export_read_head->end - source; -} -// getExportLocalStart -int32_t els () { - return export_read_head->local_start ? export_read_head->local_start - source : -1; -} -// getExportLocalEnd -int32_t ele () { - return export_read_head->local_end ? export_read_head->local_end - source : -1; -} -// readImport -bool ri () { - if (import_read_head == NULL) - import_read_head = first_import; - else - import_read_head = import_read_head->next; - if (import_read_head == NULL) - return false; - return true; -} -// readExport -bool re () { - if (export_read_head == NULL) - export_read_head = first_export; - else - export_read_head = export_read_head->next; - if (export_read_head == NULL) - return false; - return true; -} -bool f () { - return facade; -} +void add_import (const char16_t* statement_start, + const char16_t* start, + const char16_t* end, + const char16_t* dynamic); -bool parse (); +void add_export (const char16_t* start, + const char16_t* end, + const char16_t* local_start, + const char16_t* local_end); -void tryParseImportStatement (); -void tryParseExportStatement (); +void try_parse_import_statement (); +void try_parse_export_statement (); -void readImportString (const char16_t* ss, char16_t ch); -char16_t readExportAs (char16_t* startPos, char16_t* endPos); +void read_import_string (const char16_t* ss, char16_t ch); +char16_t read_export_as (char16_t* startPos, char16_t* endPos); -char16_t commentWhitespace (bool br); -void regularExpression (); -void templateString (); -void blockComment (bool br); -void lineComment (); -void stringLiteral (char16_t quote); +char16_t comment_whitespace (bool br); +void regular_expression (); +void template_string (); +void block_comment (bool br); +void line_comment (); +void string_literal (char16_t quote); -char16_t readToWsOrPunctuator (char16_t ch); +char16_t read_to_ws_or_punctuator (char16_t ch); -bool isQuote (char16_t ch); +bool is_quote (char16_t ch); -bool isBr (char16_t c); -bool isWsNotBr (char16_t c); -bool isBrOrWs (char16_t c); -bool isBrOrWsOrPunctuator (char16_t c); -bool isBrOrWsOrPunctuatorNotDot (char16_t c); +bool is_br (char16_t c); +bool is_ws_not_br (char16_t c); +bool is_br_or_ws (char16_t c); +bool is_br_or_ws_not_punctuator (char16_t c); +bool is_br_or_ws_or_punctuator_not_dot (char16_t c); -bool readPrecedingKeyword1(char16_t* pos, char16_t c1); -bool readPrecedingKeywordn(char16_t* pos, const char16_t* compare, size_t n); +bool read_preceding_keyword_1 (char16_t* pos, char16_t c1); +bool read_preceding_keyword_n (char16_t* pos, const char16_t* compare, size_t n); -bool isBreakOrContinue (char16_t* curPos); +bool is_break_or_continue (char16_t* curPos); -bool keywordStart (char16_t* pos); -bool isExpressionKeyword (char16_t* pos); -bool isParenKeyword (char16_t* pos); -bool isPunctuator (char16_t charCode); -bool isExpressionPunctuator (char16_t charCode); -bool isExpressionTerminator (char16_t* pos); +bool keyword_start (char16_t* pos); +bool is_expression_keyword (char16_t* pos); +bool is_paren_keyword (char16_t* pos); +bool is_punctuator (char16_t charCode); +bool is_expression_punctuator (char16_t charCode); +bool is_expression_terminator (char16_t* pos); -void nextChar (char16_t ch); -void nextCharSurrogate (char16_t ch); -char16_t readChar (); +void next_char (char16_t ch); +void next_char_surrogate (char16_t ch); +char16_t read_char (); void syntaxError (); diff --git a/src/lexer.js b/src/lexer.js new file mode 100644 index 0000000..5f97a51 --- /dev/null +++ b/src/lexer.js @@ -0,0 +1,85 @@ +const isLE = new Uint8Array(new Uint16Array([ + 1 +]).buffer)[0] === 1; +/** + * Outputs the list of exports and locations of import specifiers, + * including dynamic import and import meta handling. + * + * @param source Source code to parser + * @param name Optional sourcename + * @returns Tuple contaning imports list and exports list. + */ export function parse(source, name = '@') { + if (!wasm) // actually returns a promise if init hasn't resolved (not type safe). + // casting to avoid a breaking type change. + return init.then(()=>parse(source)); + const len = source.length + 1; + // need 2 bytes per code point plus analysis space so we double again + const extraMem = (wasm.__heap_base.value || wasm.__heap_base) + len * 4 - wasm.memory.buffer.byteLength; + if (extraMem > 0) wasm.memory.grow(Math.ceil(extraMem / 65536)); + const addr = wasm.sa(len - 1); + (isLE ? copyLE : copyBE)(source, new Uint16Array(wasm.memory.buffer, addr, len)); + if (!wasm.parse()) throw Object.assign(new Error(`Parse error ${name}:${source.slice(0, wasm.e()).split('\n').length}:${wasm.e() - source.lastIndexOf('\n', wasm.e() - 1)}`), { + idx: wasm.e() + }); + const imports = [], exports = []; + while(wasm.ri()){ + const s = wasm.is(), e = wasm.ie(), a = wasm.ai(), d = wasm.id(), ss = wasm.ss(), se = wasm.se(); + let n; + if (wasm.ip()) n = decode(source.slice(d === -1 ? s - 1 : s, d === -1 ? e + 1 : e)); + imports.push({ + n, + s, + e, + ss, + se, + d, + a + }); + } + while(wasm.re()){ + const s = wasm.es(), e = wasm.ee(), ls = wasm.els(), le = wasm.ele(); + const n = source.slice(s, e), ch = n[0]; + const ln = ls < 0 ? undefined : source.slice(ls, le), lch = ln ? ln[0] : ''; + exports.push({ + s, + e, + ls, + le, + n: ch === '"' || ch === "'" ? decode(n) : n, + ln: lch === '"' || lch === "'" ? decode(ln) : ln + }); + } + function decode(str) { + try { + return (0, eval)(str) // eval(undefined) -> undefined + ; + } catch (e) {} + } + return [ + imports, + exports, + !!wasm.f() + ]; +} +function copyBE(src, outBuf16) { + const len = src.length; + let i = 0; + while(i < len){ + const ch = src.charCodeAt(i); + outBuf16[i++] = (ch & 0xff) << 8 | ch >>> 8; + } +} +function copyLE(src, outBuf16) { + const len = src.length; + let i = 0; + while(i < len)outBuf16[i] = src.charCodeAt(i++); +} +let wasm; +/** + * Wait for init to resolve before calling `parse`. + */ export const init = WebAssembly.compile(((binary)=>typeof Buffer !== 'undefined' ? Buffer.from(binary, 'base64') : Uint8Array.from(atob(binary), (x)=>x.charCodeAt(0)))('WASM_BINARY')).then(WebAssembly.instantiate).then(({ exports })=>{ + wasm = exports; +}); + + +//# sourceMappingURL=lexer.js.map \ No newline at end of file diff --git a/test/_unit.cjs b/test/_unit.cjs index c50cad5..9a7c274 100755 --- a/test/_unit.cjs +++ b/test/_unit.cjs @@ -72,6 +72,7 @@ suite('Invalid syntax', () => { const source = `import(\n"./statehash\\u1011.js"\r)`; const [imports] = parse(source); assert.strictEqual(imports.length, 1); + console.log(imports); assert.strictEqual(imports[0].n, './statehashထ.js'); assert.strictEqual(source.slice(imports[0].s, imports[0].e), '"./statehash\\u1011.js"'); }); From fcf893b79e7ff51940cb5c0fc87ddf31515ee445 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 15 Nov 2022 12:26:33 -0800 Subject: [PATCH 02/18] world update --- chompfile.toml | 12 +- src/lexer-world.wit | 328 ++++++++++++++++++++++---------------------- src/lexer.c | 30 ++-- 3 files changed, 188 insertions(+), 182 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index 5c1193e..11c54a0 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -5,7 +5,7 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] WASI_PATH = '/opt/wasi-sdk' WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' -WITPACK = '../witpack/src/witpack.js' +WT = '../witjs/src/wct.js' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' WIT_COMPONENT = '../wit-bindgen/target/debug/wit-component' @@ -22,7 +22,7 @@ run = 'babel dist/lexer.js | terser -c -m -o dist/lexer.cjs' targets = ['dist/lexer.js', 'dist/lexer.d.ts'] deps = ['obj/lexer-component.wasm', 'obj/preamble'] run = ''' - ${{ WITPACK }} host obj/lexer-component.wasm -o dist --name=lexer --valid-lifting-optimization --optimize --minify -b9000 -q + ${{ WT }} host obj/lexer-component.wasm -o dist --name=lexer --valid-lifting-optimization --optimize --minify -b9000 -q cat obj/preamble > dist/tmp ; cat dist/lexer.js >> dist/tmp ; mv dist/tmp dist/lexer.js ''' @@ -30,7 +30,7 @@ run = ''' target = 'dist/lexer.asm.js' dep = 'obj/lexer-component.wasm' run = ''' - ${{ WITPACK }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q + ${{ WT }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q cat obj/preamble.asm > dist/tmp ; cat dist/lexer.asm.js >> dist/tmp ; mv dist/tmp dist/lexer.asm.js ''' @@ -50,7 +50,7 @@ target = 'obj/lexer-component.wasm' stdio = 'stderr-only' deps = ['obj/lexer.wasm', 'src/lexer-world.wit'] run = ''' - ${{ WIT_COMPONENT }} $DEP --encoding utf16 --interface src/lexer-world.wit -o $TARGET + ${{ WIT_COMPONENT }} $DEP --encoding utf16 --world src/lexer-world.wit -o $TARGET ''' [[task]] @@ -67,9 +67,9 @@ run = """ [[task]] targets = ['obj/lexer_world.c', 'obj/lexer_world.h'] dep = 'src/lexer-world.wit' -stdio = 'stderr-only' +# stdio = 'stderr-only' run = ''' - ${{ WIT_BINDGEN }} guest c --name lexer_world --string-encoding utf16 --default src/lexer-world.wit --no-helpers --out-dir obj + ${{ WIT_BINDGEN }} guest c --name lexer_world --string-encoding utf16 src/lexer-world.wit --no-helpers --out-dir obj ''' [[task]] diff --git a/src/lexer-world.wit b/src/lexer-world.wit index a18ac71..56ce558 100644 --- a/src/lexer-world.wit +++ b/src/lexer-world.wit @@ -1,175 +1,181 @@ -/** - * Module name - * - * To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. - * - * For dynamic import expressions, this field will be empty if not a valid JS string. - * - * @example - * const [imports1, exports1] = parse(String.raw`import './\u0061\u0062.js'`); - * imports1[0].n; - * // Returns "./ab.js" - * - * const [imports2, exports2] = parse(`import("./ab.js")`); - * imports2[0].n; - * // Returns "./ab.js" - * - * const [imports3, exports3] = parse(`import("./" + "ab.js")`); - * imports3[0].n; - * // Returns undefined - */ -record import { - /** - * Module name - * - * To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. - * - * For dynamic import expressions, this field will be empty if not a valid JS string. - * - * @example - * const [imports1, exports1] = parse(String.raw`import './\u0061\u0062.js'`); - * imports1[0].n; - * // Returns "./ab.js" - * - * const [imports2, exports2] = parse(`import("./ab.js")`); - * imports2[0].n; - * // Returns "./ab.js" - * - * const [imports3, exports3] = parse(`import("./" + "ab.js")`); - * imports3[0].n; - * // Returns undefined - */ - n: option, +world lexer { + default export lexer +} - /** - * Start of module specifier - * - * @example - * const source = `import { a } from 'asdf'`; - * const [imports, exports] = parse(source); - * source.substring(imports[0].s, imports[0].e); - * // Returns "asdf" - */ - s: s32, +interface lexer { + /// + /// Outputs the list of exports and locations of import specifiers, + /// including dynamic import and import meta handling. + /// + /// @param source Source code to parser + /// @param name Optional sourcename + /// @returns Tuple contaning imports list and exports list. + /// Test + parse: func(source: string, name: option) -> parse-result - /** - * End of module specifier - */ - e: s32, + /// + /// Module name + /// + /// To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. + /// + /// For dynamic import expressions, this field will be empty if not a valid JS string. + /// + /// @example + /// const [imports1, exports1] = parse(String.raw`import './\u0061\u0062.js'`); + /// imports1[0].n; + /// // Returns "./ab.js" + /// + /// const [imports2, exports2] = parse(`import("./ab.js")`); + /// imports2[0].n; + /// // Returns "./ab.js" + /// + /// const [imports3, exports3] = parse(`import("./" + "ab.js")`); + /// imports3[0].n; + /// // Returns undefined + /// + record %import { + /// + /// Module name + /// + /// To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible. + /// + /// For dynamic import expressions, this field will be empty if not a valid JS string. + /// + /// @example + /// const [imports1, exports1] = parse(String.raw`import './\u0061\u0062.js'`); + /// imports1[0].n; + /// // Returns "./ab.js" + /// + /// const [imports2, exports2] = parse(`import("./ab.js")`); + /// imports2[0].n; + /// // Returns "./ab.js" + /// + /// const [imports3, exports3] = parse(`import("./" + "ab.js")`); + /// imports3[0].n; + /// // Returns undefined + /// + n: option, - /** - * Start of import statement - * - * @example - * const source = `import { a } from 'asdf'`; - * const [imports, exports] = parse(source); - * source.substring(imports[0].ss, imports[0].se); - * // Returns "import { a } from 'asdf';" - */ - ss: s32, + /// + /// Start of module specifier + /// + /// @example + /// const source = `import { a } from 'asdf'`; + /// const [imports, exports] = parse(source); + /// source.substring(imports[0].s, imports[0].e); + /// // Returns "asdf" + /// + s: s32, - /** - * End of import statement - */ - se: s32, + /// + /// End of module specifier + /// + e: s32, - /** - * If this import statement is a dynamic import, this is the start value. - * Otherwise this is `-1`. - */ - d: s32, + /// + /// Start of import statement + /// + /// @example + /// const source = `import { a } from 'asdf'`; + /// const [imports, exports] = parse(source); + /// source.substring(imports[0].ss, imports[0].se); + /// // Returns "import { a } from 'asdf';" + /// + ss: s32, - /** - * If this import has an import assertion, this is the start value. - * Otherwise this is `-1`. - */ - a: s32, -} + /// + /// End of import statement + /// + se: s32, -record export { - /** - * Exported name - * - * @example - * const source = `export default []`; - * const [imports, exports] = parse(source); - * exports[0].n; - * // Returns "default" - * - * @example - * const source = `export const asdf = 42`; - * const [imports, exports] = parse(source); - * exports[0].n; - * // Returns "asdf" - */ - n: string, + /// + /// If this import statement is a dynamic import, this is the start value. + /// Otherwise this is `-1`. + /// + d: s32, - /** - * Local name, or undefined. - * - * @example - * const source = `export default []`; - * const [imports, exports] = parse(source); - * exports[0].ln; - * // Returns undefined - * - * @example - * const asdf = 42; - * const source = `export { asdf as a }`; - * const [imports, exports] = parse(source); - * exports[0].ln; - * // Returns "asdf" - */ - ln: option, + /// + /// If this import has an import assertion, this is the start value. + /// Otherwise this is `-1`. + /// + a: s32, + } - /** - * Start of exported name - * - * @example - * const source = `export default []`; - * const [imports, exports] = parse(source); - * source.substring(exports[0].s, exports[0].e); - * // Returns "default" - * - * @example - * const source = `export { 42 as asdf }`; - * const [imports, exports] = parse(source); - * source.substring(exports[0].s, exports[0].e); - * // Returns "asdf" - */ - s: s32, + record %export { + /// + /// Exported name + /// + /// @example + /// const source = `export default []`; + /// const [imports, exports] = parse(source); + /// exports[0].n; + /// // Returns "default" + /// + /// @example + /// const source = `export const asdf = 42`; + /// const [imports, exports] = parse(source); + /// exports[0].n; + /// // Returns "asdf" + /// + n: string, - /** - * End of exported name - */ - e: s32, + /// + /// Local name, or undefined. + /// + /// @example + /// const source = `export default []`; + /// const [imports, exports] = parse(source); + /// exports[0].ln; + /// // Returns undefined + /// + /// @example + /// const asdf = 42; + /// const source = `export { asdf as a }`; + /// const [imports, exports] = parse(source); + /// exports[0].ln; + /// // Returns "asdf" + /// + ln: option, - /** - * Start of local name, or -1. - * - * @example - * const asdf = 42; - * const source = `export { asdf as a }`; - * const [imports, exports] = parse(source); - * source.substring(exports[0].ls, exports[0].le); - * // Returns "asdf" - */ - ls: s32, + /// + /// Start of exported name + /// + /// @example + /// const source = `export default []`; + /// const [imports, exports] = parse(source); + /// source.substring(exports[0].s, exports[0].e); + /// // Returns "default" + /// + /// @example + /// const source = `export { 42 as asdf }`; + /// const [imports, exports] = parse(source); + /// source.substring(exports[0].s, exports[0].e); + /// // Returns "asdf" + /// + s: s32, - /** - * End of local name, or -1. - */ - le: s32, -} + /// + /// End of exported name + /// + e: s32, -type parse-result = result, list, bool>, string> + /// + /// Start of local name, or -1. + /// + /// @example + /// const asdf = 42; + /// const source = `export { asdf as a }`; + /// const [imports, exports] = parse(source); + /// source.substring(exports[0].ls, exports[0].le); + /// // Returns "asdf" + /// + ls: s32, -/// -/// Outputs the list of exports and locations of import specifiers, -/// including dynamic import and import meta handling. -/// -/// @param source Source code to parser -/// @param name Optional sourcename -/// @returns Tuple contaning imports list and exports list. -/// Test -parse: func(source: string, name: option) -> parse-result + /// + /// End of local name, or -1. + /// + le: s32, + } + + type parse-result = result, list<%export>, bool>, string> +} diff --git a/src/lexer.c b/src/lexer.c index a23926d..a31133f 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -102,13 +102,13 @@ static char16_t* pos; static char16_t* end; static OpenToken* open_token_stack; static uint16_t dynamic_import_stack_depth; -static lexer_world_import_t** dynamic_import_stack; +static lexer_import_t** dynamic_import_stack; static bool next_brace_is_class; -static lexer_world_import_t* imports_base; -static lexer_world_export_t* exports_base; -static lexer_world_import_t* imports_head; -static lexer_world_export_t* exports_head; +static lexer_import_t* imports_base; +static lexer_export_t* exports_base; +static lexer_import_t* imports_head; +static lexer_export_t* exports_head; static const size_t MAX_IMPORTS = 1024; static const size_t MAX_EXPORTS = 8192; @@ -167,7 +167,7 @@ void add_export (const char16_t* start, // Error handler -static lexer_world_parse_result_t* ret; +static lexer_parse_result_t* ret; // `Parse error (${name || '@'}:${line}:${col})` void syntax_error () { @@ -198,7 +198,7 @@ void syntax_error () { len += write_num(&err[len], line); err[len++] = ':'; len += write_num(&err[len], col); - lexer_world_parse_result_t result = { + lexer_parse_result_t result = { .is_err = true, .val = { .err = { @@ -213,7 +213,7 @@ void syntax_error () { // Main -void lexer_world_parse(lexer_world_string_t* input, lexer_world_option_string_t* name_option, lexer_world_parse_result_t* _ret) { +void lexer_parse(lexer_world_string_t* input, lexer_option_string_t* name_option, lexer_parse_result_t* _ret) { source_len = input->len; source = input->ptr; ret = _ret; @@ -226,9 +226,9 @@ void lexer_world_parse(lexer_world_string_t* input, lexer_world_option_string_t* // stack allocations OpenToken open_token_stack_[MAX_BRACE_DEPTH]; - lexer_world_import_t* dynamic_import_stack_[MAX_DYNAMIC_IMPORT_NESTING]; - lexer_world_import_t imports_[MAX_IMPORTS]; - lexer_world_export_t exports_[MAX_EXPORTS]; + lexer_import_t* dynamic_import_stack_[MAX_DYNAMIC_IMPORT_NESTING]; + lexer_import_t imports_[MAX_IMPORTS]; + lexer_export_t exports_[MAX_EXPORTS]; // TODO: Exact stack sizing based on the above! @@ -328,7 +328,7 @@ void lexer_world_parse(lexer_world_string_t* input, lexer_world_option_string_t* return syntax_error(); open_token_depth--; if (dynamic_import_stack_depth > 0 && source + dynamic_import_stack[dynamic_import_stack_depth - 1]->d == open_token_stack[open_token_depth].pos) { - lexer_world_import_t* cur_dynamic_import = dynamic_import_stack[dynamic_import_stack_depth - 1]; + lexer_import_t* cur_dynamic_import = dynamic_import_stack[dynamic_import_stack_depth - 1]; if (cur_dynamic_import->e == -1) cur_dynamic_import->e = pos - source; cur_dynamic_import->se = pos - source + 1; @@ -419,7 +419,7 @@ void lexer_world_parse(lexer_world_string_t* input, lexer_world_option_string_t* if (open_token_depth || dynamic_import_stack_depth) return syntax_error(); - lexer_world_parse_result_t result = { + lexer_parse_result_t result = { .is_err = false, .val = { .ok = { @@ -571,7 +571,7 @@ void try_parse_import_statement () { void try_parse_export_statement () { char16_t* s_start_pos = pos; - lexer_world_export_t* prev_exports_head = exports_head; + lexer_export_t* prev_exports_head = exports_head; pos += 6; @@ -751,7 +751,7 @@ void try_parse_export_statement () { read_import_string(s_start_pos, comment_whitespace(true)); // There were no local names. - for (lexer_world_export_t* expt = prev_exports_head == exports_base ? exports_base : prev_exports_head + 1; expt != exports_head; expt++) { + for (lexer_export_t* expt = prev_exports_head == exports_base ? exports_base : prev_exports_head + 1; expt != exports_head; expt++) { expt->ls = expt->le = -1; } } From cd66a4d26c1bf51a8e4c607cddb31211a82fbe10 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 15 Nov 2022 17:25:44 -0800 Subject: [PATCH 03/18] all working --- chompfile.toml | 16 +++++++++++++--- src/lexer.c | 4 ++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index 11c54a0..7811e64 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -5,7 +5,7 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] WASI_PATH = '/opt/wasi-sdk' WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' -WT = '../witjs/src/wct.js' +WT = '../wasm-component-tools/src/wct.js' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' WIT_COMPONENT = '../wit-bindgen/target/debug/wit-component' @@ -65,11 +65,21 @@ run = """ """ [[task]] -targets = ['obj/lexer_world.c', 'obj/lexer_world.h'] +targets = ['obj/lexer.c', 'obj/lexer_world.h'] dep = 'src/lexer-world.wit' # stdio = 'stderr-only' run = ''' - ${{ WIT_BINDGEN }} guest c --name lexer_world --string-encoding utf16 src/lexer-world.wit --no-helpers --out-dir obj + ${{ WIT_BINDGEN }} guest c --string-encoding utf16 src/lexer-world.wit --no-helpers --out-dir obj + mv obj/lexer.h obj/lexer_world.h +''' + +[[task]] +target = 'obj/lexer_world.c' +dep = 'obj/lexer.c' +engine = 'node' +run = ''' + import { readFile, writeFile } from 'fs/promises'; + await writeFile(process.env.TARGET, (await readFile(process.env.DEP, 'utf8')).replace('lexer.h', 'lexer_world.h')); ''' [[task]] diff --git a/src/lexer.c b/src/lexer.c index a31133f..f4da27b 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -24,7 +24,7 @@ void* cabi_realloc(void *ptr, size_t orig_size, size_t align, size_t new_size) { } __attribute__((weak, export_name("cabi_post_parse"))) -void __wasm_export_lexer_world_parse_post_return(int32_t arg0) { +void __wasm_export_lexer_parse_post_return(int32_t arg0) { heap_ptr = &__heap_base; } @@ -213,7 +213,7 @@ void syntax_error () { // Main -void lexer_parse(lexer_world_string_t* input, lexer_option_string_t* name_option, lexer_parse_result_t* _ret) { +void lexer_parse(lexer_string_t* input, lexer_option_string_t* name_option, lexer_parse_result_t* _ret) { source_len = input->len; source = input->ptr; ret = _ret; From cb4376cc0fcc57a55581039320f12b21ffb0ca75 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 16 Nov 2022 09:07:29 -0800 Subject: [PATCH 04/18] fixes --- chompfile.toml | 6 +++--- src/lexer.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index 7811e64..b7e2dd3 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -5,7 +5,7 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] WASI_PATH = '/opt/wasi-sdk' WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' -WT = '../wasm-component-tools/src/wct.js' +JSCT = '../wasm-component-tools/src/jsct.js' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' WIT_COMPONENT = '../wit-bindgen/target/debug/wit-component' @@ -22,7 +22,7 @@ run = 'babel dist/lexer.js | terser -c -m -o dist/lexer.cjs' targets = ['dist/lexer.js', 'dist/lexer.d.ts'] deps = ['obj/lexer-component.wasm', 'obj/preamble'] run = ''' - ${{ WT }} host obj/lexer-component.wasm -o dist --name=lexer --valid-lifting-optimization --optimize --minify -b9000 -q + ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer --valid-lifting-optimization --optimize --minify -b9000 -q cat obj/preamble > dist/tmp ; cat dist/lexer.js >> dist/tmp ; mv dist/tmp dist/lexer.js ''' @@ -30,7 +30,7 @@ run = ''' target = 'dist/lexer.asm.js' dep = 'obj/lexer-component.wasm' run = ''' - ${{ WT }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q + ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q cat obj/preamble.asm > dist/tmp ; cat dist/lexer.asm.js >> dist/tmp ; mv dist/tmp dist/lexer.asm.js ''' diff --git a/src/lexer.c b/src/lexer.c index f4da27b..6f148a3 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -55,7 +55,7 @@ size_t write_num(char16_t* out, uint32_t num) { return len; } -static const char16_t PARSE_ERROR_[] = { 'P', 'a', 'r', 's', 'e', ' ', 'e', 'r', 'r', 'o', 'r' }; +static const char16_t PARSE_ERROR_[] = { 'P', 'a', 'r', 's', 'e', ' ', 'e', 'r', 'r', 'o', 'r', ' ' }; static const char16_t XPORT[] = { 'x', 'p', 'o', 'r', 't' }; static const char16_t MPORT[] = { 'm', 'p', 'o', 'r', 't' }; @@ -173,7 +173,7 @@ static lexer_parse_result_t* ret; void syntax_error () { has_error = true; char16_t* err = cabi_realloc(0, 0, 2, 50); - size_t len = LEN(PARSE_ERROR_); + size_t len = LEN(PARSE_ERROR_) + 1; cpy(err, (char16_t*)PARSE_ERROR_, len); if (name_len) { cpy(err + len, name, name_len); From 60e0a49617eb9e1bc246b416959c9f854c9ccc94 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 16 Nov 2022 16:23:28 -0800 Subject: [PATCH 05/18] latest --- chompfile.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index b7e2dd3..de05e11 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -5,9 +5,8 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] WASI_PATH = '/opt/wasi-sdk' WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' -JSCT = '../wasm-component-tools/src/jsct.js' +JSCT = 'jsct' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' -WIT_COMPONENT = '../wit-bindgen/target/debug/wit-component' [[task]] name = 'build' @@ -50,7 +49,7 @@ target = 'obj/lexer-component.wasm' stdio = 'stderr-only' deps = ['obj/lexer.wasm', 'src/lexer-world.wit'] run = ''' - ${{ WIT_COMPONENT }} $DEP --encoding utf16 --world src/lexer-world.wit -o $TARGET + ${{ WASM_TOOLS }} component new $DEP --encoding utf16 --wit src/lexer-world.wit -o $TARGET ''' [[task]] From c5185c3819deff0e5a20f5345e8e4f4cc0fdb072 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 17 Nov 2022 10:55:41 -0800 Subject: [PATCH 06/18] latest js-component-tools --- chompfile.toml | 6 +++--- package.json | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index de05e11..be9c7e6 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -5,7 +5,7 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] WASI_PATH = '/opt/wasi-sdk' WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' -JSCT = 'jsct' +JSCT = './node_modules/.bin/jsct' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' [[task]] @@ -19,7 +19,7 @@ run = 'babel dist/lexer.js | terser -c -m -o dist/lexer.cjs' [[task]] targets = ['dist/lexer.js', 'dist/lexer.d.ts'] -deps = ['obj/lexer-component.wasm', 'obj/preamble'] +deps = ['obj/lexer-component.wasm', 'obj/preamble', 'npm:install'] run = ''' ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer --valid-lifting-optimization --optimize --minify -b9000 -q cat obj/preamble > dist/tmp ; cat dist/lexer.js >> dist/tmp ; mv dist/tmp dist/lexer.js @@ -27,7 +27,7 @@ run = ''' [[task]] target = 'dist/lexer.asm.js' -dep = 'obj/lexer-component.wasm' +deps = ['obj/lexer-component.wasm', 'npm:install'] run = ''' ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q cat obj/preamble.asm > dist/tmp ; cat dist/lexer.asm.js >> dist/tmp ; mv dist/tmp dist/lexer.asm.js diff --git a/package.json b/package.json index c52697c..1d97640 100755 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@swc/cli": "^0.1.57", "@swc/core": "^1.2.224", "@types/node": "^18.7.1", + "js-component-tools": "^0.1.3", "kleur": "^2.0.2", "mocha": "^5.2.0", "terser": "^5.15.1", From 156e9c0d7bb946dd75ffd7e488dfc5b4e3e8a743 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 17 Nov 2022 11:00:30 -0800 Subject: [PATCH 07/18] fixup rebase --- src/lexer.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index 6f148a3..2a349ed 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -638,7 +638,7 @@ void try_parse_export_statement () { case 'd': { const char16_t* start_pos = pos; pos += 7; - ch = commentWhitespace(true); + ch = comment_whitespace(true); bool localName = false; // export default async? function*? name? (){} if (ch == 'a' && keyword_start(pos) && memcmp(pos + 1, &SYNC[0], 4 * 2) == 0 && is_ws_not_br(*(pos + 5))) { @@ -653,8 +653,8 @@ void try_parse_export_statement () { ch = comment_whitespace(true); } if (ch == '(') { - addExport(startPos, startPos + 7, NULL, NULL); - pos = (char16_t*)(startPos + 6); + add_export(start_pos, start_pos + 7, NULL, NULL); + pos = (char16_t*)(start_pos + 6); return; } localName = true; @@ -664,21 +664,21 @@ void try_parse_export_statement () { pos += 5; ch = comment_whitespace(true); if (ch == '{') { - addExport(startPos, startPos + 7, NULL, NULL); - pos = (char16_t*)(startPos + 6); + add_export(start_pos, start_pos + 7, NULL, NULL); + pos = (char16_t*)(start_pos + 6); return; } localName = true; } - const char16_t* localStartPos = pos; - ch = readToWsOrPunctuator(ch); - if (localName && pos > localStartPos) { - addExport(startPos, startPos + 7, localStartPos, pos); + const char16_t* localstart_pos = pos; + ch = read_to_ws_or_punctuator(ch); + if (localName && pos > localstart_pos) { + add_export(start_pos, start_pos + 7, localstart_pos, pos); pos--; } else { - addExport(startPos, startPos + 7, NULL, NULL); - pos = (char16_t*)(startPos + 6); + add_export(start_pos, start_pos + 7, NULL, NULL); + pos = (char16_t*)(start_pos + 6); } return; } From 3cfbeba607fd03abbfb6c2041be2503d9f7d22a4 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 25 Dec 2022 01:17:11 +0200 Subject: [PATCH 08/18] wat --- chompfile.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index be9c7e6..b6f1b9c 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -5,7 +5,7 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] WASI_PATH = '/opt/wasi-sdk' WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' -JSCT = './node_modules/.bin/jsct' +JSCT = '../js-component-tools/dist/cli.mjs' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' [[task]] @@ -20,8 +20,9 @@ run = 'babel dist/lexer.js | terser -c -m -o dist/lexer.cjs' [[task]] targets = ['dist/lexer.js', 'dist/lexer.d.ts'] deps = ['obj/lexer-component.wasm', 'obj/preamble', 'npm:install'] +# -b9000 --optimize --minify -q run = ''' - ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer --valid-lifting-optimization --optimize --minify -b9000 -q + ${{ WIT_BINDGEN }} host js obj/lexer-component.wasm --out-dir dist --name=lexer cat obj/preamble > dist/tmp ; cat dist/lexer.js >> dist/tmp ; mv dist/tmp dist/lexer.js ''' @@ -29,7 +30,7 @@ run = ''' target = 'dist/lexer.asm.js' deps = ['obj/lexer-component.wasm', 'npm:install'] run = ''' - ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q + ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-binary-optimization --optimize --minify --asm -q cat obj/preamble.asm > dist/tmp ; cat dist/lexer.asm.js >> dist/tmp ; mv dist/tmp dist/lexer.asm.js ''' From eba4a88a65918c565af5b77f16f7b10c6d6202f2 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 25 Dec 2022 01:52:18 +0200 Subject: [PATCH 09/18] latest components tooling --- .github/workflows/build.yml | 14 +++++- chompfile.toml | 14 +++--- package.json | 2 +- src/lexer.c | 92 ++++++++++++++++++------------------- 4 files changed, 63 insertions(+), 59 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6ffefb..68eaf1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,8 +7,8 @@ on: branches: main env: - WASI_VERSION: 12 - WASI_VERSION_FULL: "12.0" + WASI_VERSION: 17 + WASI_VERSION_FULL: "17.0" EMCC_VERSION: "1.40.1-fastcomp" jobs: @@ -19,6 +19,16 @@ jobs: - name: Check out Git repository uses: actions/checkout@v2 + - name: Install Rust + run: rustup update stable --no-self-update && rustup default stable + - name: Install wasm32-unknown-unknown target + run: rustup target add wasm32-unknown-unknown + - name: Install wasm32-wasi target + run: rustup target add wasm32-wasi + + - name: Install WIT Bindgen + run: cargo install --git https://github.com/bytecodealliance/wit-bindgen wit-bindgen-cli + - name: Install wasi-sdk shell: bash run: | diff --git a/chompfile.toml b/chompfile.toml index b6f1b9c..15d280e 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -4,8 +4,7 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] [env-default] WASI_PATH = '/opt/wasi-sdk' -WASM_TOOLS = '../wasm-tools/target/debug/wasm-tools' -JSCT = '../js-component-tools/dist/cli.mjs' +JSCT = 'jsct' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' [[task]] @@ -20,9 +19,8 @@ run = 'babel dist/lexer.js | terser -c -m -o dist/lexer.cjs' [[task]] targets = ['dist/lexer.js', 'dist/lexer.d.ts'] deps = ['obj/lexer-component.wasm', 'obj/preamble', 'npm:install'] -# -b9000 --optimize --minify -q run = ''' - ${{ WIT_BINDGEN }} host js obj/lexer-component.wasm --out-dir dist --name=lexer + ${{ JSCT }} transpile obj/lexer-component.wasm --out-dir dist --name=lexer --valid-lifting-optimization -b9000 --optimize --minify -q cat obj/preamble > dist/tmp ; cat dist/lexer.js >> dist/tmp ; mv dist/tmp dist/lexer.js ''' @@ -30,7 +28,7 @@ run = ''' target = 'dist/lexer.asm.js' deps = ['obj/lexer-component.wasm', 'npm:install'] run = ''' - ${{ JSCT }} host obj/lexer-component.wasm -o dist --name=lexer.asm --valid-binary-optimization --optimize --minify --asm -q + ${{ JSCT }} transpile obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q cat obj/preamble.asm > dist/tmp ; cat dist/lexer.asm.js >> dist/tmp ; mv dist/tmp dist/lexer.asm.js ''' @@ -50,7 +48,7 @@ target = 'obj/lexer-component.wasm' stdio = 'stderr-only' deps = ['obj/lexer.wasm', 'src/lexer-world.wit'] run = ''' - ${{ WASM_TOOLS }} component new $DEP --encoding utf16 --wit src/lexer-world.wit -o $TARGET + ${{ JSCT }} new $DEP --encoding utf16 --wit src/lexer-world.wit -o $TARGET ''' [[task]] @@ -122,12 +120,12 @@ run = 'mocha -b -u tdd test/*.cjs' [[task]] target = 'obj/lexer.wat' dep = 'obj/lexer.wasm' -run = '${{ WASM_TOOLS }} parse -t $DEP -o $TARGET' +run = '${{ jsct }} parse -t $DEP -o $TARGET' [[task]] target = 'obj/lexer-component.core.wat' dep = 'obj/lexer-component.core.wasm' -run = '${{ WASM_TOOLS }} parse -t $DEP -o $TARGET' +run = '${{ jsct }} parse -t $DEP -o $TARGET' [[task]] name = 'footprint' diff --git a/package.json b/package.json index 1d97640..434fae6 100755 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@swc/cli": "^0.1.57", "@swc/core": "^1.2.224", "@types/node": "^18.7.1", - "js-component-tools": "^0.1.3", + "js-component-tools": "^0.2.2", "kleur": "^2.0.2", "mocha": "^5.2.0", "terser": "^5.15.1", diff --git a/src/lexer.c b/src/lexer.c index 2a349ed..91f5d89 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -167,21 +167,22 @@ void add_export (const char16_t* start, // Error handler -static lexer_parse_result_t* ret; +static lexer_tuple3_list_import_list_export_bool_t* ret; +static lexer_string_t* err; // `Parse error (${name || '@'}:${line}:${col})` void syntax_error () { has_error = true; - char16_t* err = cabi_realloc(0, 0, 2, 50); + char16_t* err_msg = cabi_realloc(0, 0, 2, 50); size_t len = LEN(PARSE_ERROR_) + 1; - cpy(err, (char16_t*)PARSE_ERROR_, len); + cpy(err_msg, (char16_t*)PARSE_ERROR_, len); if (name_len) { - cpy(err + len, name, name_len); + cpy(err_msg + len, name, name_len); len += name_len; } else { - err[len++] = '@'; + err_msg[len++] = '@'; } - err[len++] = ':'; + err_msg[len++] = ':'; int32_t line = 1, col = 1; char16_t* err_pos = pos; char16_t ch; @@ -195,31 +196,24 @@ void syntax_error () { col++; } } - len += write_num(&err[len], line); - err[len++] = ':'; - len += write_num(&err[len], col); - lexer_parse_result_t result = { - .is_err = true, - .val = { - .err = { - .ptr = err, - .len = len - } - } - }; - *ret = result; + len += write_num(&err_msg[len], line); + err_msg[len++] = ':'; + len += write_num(&err_msg[len], col); + err->ptr = err_msg; + err->len = len; pos = end + 1; } // Main -void lexer_parse(lexer_string_t* input, lexer_option_string_t* name_option, lexer_parse_result_t* _ret) { +bool lexer_parse(lexer_string_t* input, lexer_string_t* maybe_name, lexer_tuple3_list_import_list_export_bool_t *_ret, lexer_string_t *_err) { source_len = input->len; source = input->ptr; ret = _ret; - if (name_option->is_some) { - name = name_option->val.ptr; - name_len = name_option->val.len; + err = _err; + if (maybe_name != NULL) { + name = maybe_name->ptr; + name_len = maybe_name->len; } else { name_len = 0; } @@ -298,7 +292,7 @@ void lexer_parse(lexer_string_t* input, lexer_option_string_t* name_option, lexe } if (has_error) - return; + return false; main_parse: while (pos++ < end) { ch = *pos; @@ -324,8 +318,10 @@ void lexer_parse(lexer_string_t* input, lexer_option_string_t* name_option, lexe open_token_stack[open_token_depth++].pos = last_token_pos; break; case ')': - if (open_token_depth == 0) - return syntax_error(); + if (open_token_depth == 0) { + syntax_error(); + return false; + } open_token_depth--; if (dynamic_import_stack_depth > 0 && source + dynamic_import_stack[dynamic_import_stack_depth - 1]->d == open_token_stack[open_token_depth].pos) { lexer_import_t* cur_dynamic_import = dynamic_import_stack[dynamic_import_stack_depth - 1]; @@ -347,8 +343,10 @@ void lexer_parse(lexer_string_t* input, lexer_option_string_t* name_option, lexe next_brace_is_class = false; break; case '}': - if (open_token_depth == 0) - return syntax_error(); + if (open_token_depth == 0) { + syntax_error(); + return false; + } if (open_token_stack[--open_token_depth].token == TemplateBrace) { template_string(); } @@ -414,28 +412,26 @@ void lexer_parse(lexer_string_t* input, lexer_option_string_t* name_option, lexe } if (has_error) - return; + return false; - if (open_token_depth || dynamic_import_stack_depth) - return syntax_error(); - - lexer_parse_result_t result = { - .is_err = false, - .val = { - .ok = { - .f0 = { - .ptr = imports_base, - .len = imports_head - imports_base - }, - .f1 = { - .ptr = exports_base, - .len = exports_head - exports_base - }, - .f2 = facade - } - } + if (open_token_depth || dynamic_import_stack_depth) { + syntax_error(); + return false; + } + + lexer_tuple3_list_import_list_export_bool_t result = { + .f0 = { + .ptr = imports_base, + .len = imports_head - imports_base + }, + .f1 = { + .ptr = exports_base, + .len = exports_head - exports_base + }, + .f2 = facade }; - *_ret = result; + *ret = result; + return true; } void try_parse_import_statement () { From f8d8d56c7c8ee0b33ac7031631bda8237cf23586 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 25 Dec 2022 01:56:24 +0200 Subject: [PATCH 10/18] remove emscripten install --- .github/workflows/build.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68eaf1e..6003588 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,14 +50,6 @@ jobs: # print clang version ./wasi-sdk-${WASI_VERSION_FULL}/bin/clang --version - - name: Install Emscripten - run: | - git clone https://github.com/emscripten-core/emsdk.git - cd ./emsdk - ./emsdk install ${{ env.EMCC_VERSION }} - ./emsdk activate ${{ env.EMCC_VERSION }} - ./fastcomp/emscripten/emcc -v - - name: npm Install run: npm install From 007a8bc9eae8b71b2b6fffd3a94d7eaad5ad476c Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 25 Dec 2022 03:08:14 +0200 Subject: [PATCH 11/18] fixup tests --- .github/workflows/build.yml | 6 ------ src/lexer.c | 20 ++++++++++++-------- test/_unit.cjs | 6 +++--- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6003588..bdbc2e9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,6 @@ on: env: WASI_VERSION: 17 WASI_VERSION_FULL: "17.0" - EMCC_VERSION: "1.40.1-fastcomp" jobs: build: @@ -50,17 +49,12 @@ jobs: # print clang version ./wasi-sdk-${WASI_VERSION_FULL}/bin/clang --version - - name: npm Install - run: npm install - - name: Setup Chomp uses: guybedford/chomp-action@v1 - name: Compile to Wasm & Test Wasm env: WASI_PATH: './wasi-sdk-${{ env.WASI_VERSION_FULL }}' - EMSDK_PATH: './emsdk' - EMSCRIPTEN_BIN: './emsdk/fastcomp/emscripten/emcc' run: chomp test - name: Footprint diff --git a/src/lexer.c b/src/lexer.c index 91f5d89..472ae3f 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -170,8 +170,7 @@ void add_export (const char16_t* start, static lexer_tuple3_list_import_list_export_bool_t* ret; static lexer_string_t* err; -// `Parse error (${name || '@'}:${line}:${col})` -void syntax_error () { +void error (int32_t line, int32_t col) { has_error = true; char16_t* err_msg = cabi_realloc(0, 0, 2, 50); size_t len = LEN(PARSE_ERROR_) + 1; @@ -183,6 +182,16 @@ void syntax_error () { err_msg[len++] = '@'; } err_msg[len++] = ':'; + len += write_num(&err_msg[len], line); + err_msg[len++] = ':'; + len += write_num(&err_msg[len], col); + err->ptr = err_msg; + err->len = len * 2; + pos = end + 1; +} + +// `Parse error (${name || '@'}:${line}:${col})` +void syntax_error () { int32_t line = 1, col = 1; char16_t* err_pos = pos; char16_t ch; @@ -196,12 +205,7 @@ void syntax_error () { col++; } } - len += write_num(&err_msg[len], line); - err_msg[len++] = ':'; - len += write_num(&err_msg[len], col); - err->ptr = err_msg; - err->len = len; - pos = end + 1; + error(line, col); } // Main diff --git a/test/_unit.cjs b/test/_unit.cjs index 9a7c274..defe8c4 100755 --- a/test/_unit.cjs +++ b/test/_unit.cjs @@ -39,13 +39,13 @@ function assertExportIs(source, actual, expected) { suite('Invalid syntax', () => { beforeEach(async () => await init); - test(`Template string default bracket`, () => { + test(`Export default bracket`, () => { const source = `export default{};`; const [, [expt]] = parse(source); assert.strictEqual(source.slice(expt.s, expt.e), 'default'); assert.strictEqual(source.slice(expt.ls, expt.le), ''); assert.strictEqual(expt.n, 'default'); - assert.strictEqual(expt.ln, undefined); + assert.strictEqual(expt.ln, null); }); test(`Template string default`, () => { @@ -55,7 +55,7 @@ suite('Invalid syntax', () => { assert.strictEqual(source.slice(expt.s, expt.e), 'default'); assert.strictEqual(source.slice(expt.ls, expt.le), ''); assert.strictEqual(expt.n, 'default'); - assert.strictEqual(expt.ln, undefined); + assert.strictEqual(expt.ln, null); }); test('Class fn ASI', () => { From 2f1a09a5717308487a041242cdac11fe6475d1d7 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 1 Jan 2023 02:33:44 +0200 Subject: [PATCH 12/18] further test fixes --- chompfile.toml | 62 ++++++++++++++------ src/lexer.c | 141 +++++++++++++++++++++++++++++++++++++++++++- src/readstring.js | 145 ++++++++++++++++++++++++++++++++++++++++++++++ test/_unit.cjs | 2 +- 4 files changed, 330 insertions(+), 20 deletions(-) create mode 100644 src/readstring.js diff --git a/chompfile.toml b/chompfile.toml index 15d280e..37d52fb 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -1,8 +1,9 @@ version = 0.1 -extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint'] +extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint', 'chomp@0.1:terser'] [env-default] +WASM_TOOLS = 'wasm-tools' WASI_PATH = '/opt/wasi-sdk' JSCT = 'jsct' WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' @@ -18,37 +19,64 @@ run = 'babel dist/lexer.js | terser -c -m -o dist/lexer.cjs' [[task]] targets = ['dist/lexer.js', 'dist/lexer.d.ts'] -deps = ['obj/lexer-component.wasm', 'obj/preamble', 'npm:install'] +deps = ['obj/lexer-component.wasm', 'package.json', 'npm:install'] +engine = 'node' run = ''' - ${{ JSCT }} transpile obj/lexer-component.wasm --out-dir dist --name=lexer --valid-lifting-optimization -b9000 --optimize --minify -q - cat obj/preamble > dist/tmp ; cat dist/lexer.js >> dist/tmp ; mv dist/tmp dist/lexer.js + import { transpile } from 'js-component-tools'; + import { readFile, writeFile } from 'node:fs/promises'; + + const { files } = await transpile(await readFile('obj/lexer-component.wasm'), { + outDir: 'dist', + name: 'lexer', + validLiftingOptimization: true, + base64Cutoff: 9000, + optimize: true, + minify: true + }); + + const { version } = JSON.parse(await readFile('package.json')); + + await writeFile('dist/lexer.js', `/* es-module-lexer ${version} */\n${files['dist/lexer.js'].replace(/n:(\w+)/, 'n:eval($1)')}`); + await writeFile('dist/lexer.d.ts', files['dist/lexer.d.ts']); ''' [[task]] target = 'dist/lexer.asm.js' -deps = ['obj/lexer-component.wasm', 'npm:install'] +deps = ['obj/lexer-component.wasm', 'package.json', 'npm:install', 'obj/readstring.min.js'] +engine = 'node' run = ''' - ${{ JSCT }} transpile obj/lexer-component.wasm -o dist --name=lexer.asm --valid-lifting-optimization --optimize --minify --asm -q - cat obj/preamble.asm > dist/tmp ; cat dist/lexer.asm.js >> dist/tmp ; mv dist/tmp dist/lexer.asm.js + import { transpile } from 'js-component-tools'; + import { readFile, writeFile } from 'node:fs/promises'; + + const { files } = await transpile(await readFile('obj/lexer-component.wasm'), { + outDir: 'dist', + name: 'lexer.asm', + validLiftingOptimization: true, + optimize: true, + minify: true, + asm: true + }); + + const { version } = JSON.parse(await readFile('package.json')); + const readstring = await readFile('obj/readstring.min.js', 'utf8'); + + await writeFile('dist/lexer.asm.js', `/* es-module-lexer asm.js ${version} */\n${readstring}${ + files['dist/lexer.asm.js'].replace(/n:(\w)/, 'n:readString($1)') + }`); ''' [[task]] -targets = ['obj/preamble', 'obj/preamble.asm'] -dep = 'package.json' -engine = 'node' -run = ''' -import { readFile, writeFile } from 'fs/promises'; -const { version } = JSON.parse(await readFile('package.json')); -await writeFile('obj/preamble', `/* es-module-lexer ${version} */\n`); -await writeFile('obj/preamble.asm', `/* es-module-lexer asm.js ${version} */\n`); -''' +target = 'obj/readstring.min.js' +dep = 'src/readstring.js' +template = 'terser' [[task]] target = 'obj/lexer-component.wasm' stdio = 'stderr-only' deps = ['obj/lexer.wasm', 'src/lexer-world.wit'] +# Switch back to jsct after next release with encoding fix run = ''' - ${{ JSCT }} new $DEP --encoding utf16 --wit src/lexer-world.wit -o $TARGET + ${{ WASM_TOOLS }} component new $DEP --encoding utf16 --wit src/lexer-world.wit -o $TARGET ''' [[task]] diff --git a/src/lexer.c b/src/lexer.c index 472ae3f..8681c62 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -949,7 +949,7 @@ void string_literal (char16_t quote) { syntax_error(); } -static inline char16_t regexCharacterClass () { +static inline char16_t regex_character_class () { while (pos++ < end) { char16_t ch = *pos; if (ch == ']') @@ -969,7 +969,7 @@ void regular_expression () { if (ch == '/') return; if (ch == '[') - ch = regexCharacterClass(); + ch = regex_character_class(); else if (ch == '\\') pos++; else if (ch == '\n' || ch == '\r') @@ -1153,3 +1153,140 @@ bool is_expression_terminator (char16_t* cur_pos) { } return false; } + + +// /* +// * Ported from Acorn +// * +// * MIT License +// * Copyright (C) 2012-2020 by various contributors (see AUTHORS) +// * Permission is hereby granted, free of charge, to any person obtaining a copy +// * of this software and associated documentation files (the "Software"), to deal +// * in the Software without restriction, including without limitation the rights +// * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// * copies of the Software, and to permit persons to whom the Software is +// * furnished to do so, subject to the following conditions: +// * The above copyright notice and this permission notice shall be included in +// * all copies or substantial portions of the Software. +// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// * THE SOFTWARE. +// */ +// static char16_t* str_pos; +// function readString (start, quote) { +// acornPos = start; +// let out = '', chunkStart = acornPos; +// for (;;) { +// if (acornPos >= source.length) syntaxError(); +// const ch = source.charCodeAt(acornPos); +// if (ch === quote) break; +// if (ch === 92) { // '\' +// out += source.slice(chunkStart, acornPos); +// out += readEscapedChar(); +// chunkStart = acornPos; +// } +// else if (ch === 0x2028 || ch === 0x2029) { +// ++acornPos; +// } +// else { +// if (isBr(ch)) syntaxError(); +// ++acornPos; +// } +// } +// out += source.slice(chunkStart, acornPos++); +// return out; +// } + +// // Used to read escaped characters + +// function readEscapedChar () { +// let ch = source.charCodeAt(++acornPos); +// ++acornPos; +// switch (ch) { +// case 110: return '\n'; // 'n' -> '\n' +// case 114: return '\r'; // 'r' -> '\r' +// case 120: return String.fromCharCode(readHexChar(2)); // 'x' +// case 117: return readCodePointToString(); // 'u' +// case 116: return '\t'; // 't' -> '\t' +// case 98: return '\b'; // 'b' -> '\b' +// case 118: return '\u000b'; // 'v' -> '\u000b' +// case 102: return '\f'; // 'f' -> '\f' +// case 13: if (source.charCodeAt(acornPos) === 10) ++acornPos; // '\r\n' +// case 10: // ' \n' +// return ''; +// case 56: +// case 57: +// syntaxError(); +// default: +// if (ch >= 48 && ch <= 55) { +// let octalStr = source.substr(acornPos - 1, 3).match(/^[0-7]+/)[0]; +// let octal = parseInt(octalStr, 8); +// if (octal > 255) { +// octalStr = octalStr.slice(0, -1); +// octal = parseInt(octalStr, 8); +// } +// acornPos += octalStr.length - 1; +// ch = source.charCodeAt(acornPos); +// if (octalStr !== '0' || ch === 56 || ch === 57) +// syntaxError(); +// return String.fromCharCode(octal); +// } +// if (isBr(ch)) { +// // Unicode new line characters after \ get removed from output in both +// // template literals and strings +// return ''; +// } +// return String.fromCharCode(ch); +// } +// } + +// // Used to read character escape sequences ('\x', '\u', '\U'). + +// function readHexChar (len) { +// const start = acornPos; +// let total = 0, lastCode = 0; +// for (let i = 0; i < len; ++i, ++acornPos) { +// let code = source.charCodeAt(acornPos), val; + +// if (code === 95) { +// if (lastCode === 95 || i === 0) syntaxError(); +// lastCode = code; +// continue; +// } + +// if (code >= 97) val = code - 97 + 10; // a +// else if (code >= 65) val = code - 65 + 10; // A +// else if (code >= 48 && code <= 57) val = code - 48; // 0-9 +// else break; +// if (val >= 16) break; +// lastCode = code; +// total = total * 16 + val; +// } + +// if (lastCode === 95 || acornPos - start !== len) syntaxError(); + +// return total; +// } + +// // Read a string value, interpreting backslash-escapes. + +// function readCodePointToString () { +// const ch = source.charCodeAt(acornPos); +// let code; +// if (ch === 123) { // '{' +// ++acornPos; +// code = readHexChar(source.indexOf('}', acornPos) - acornPos); +// ++acornPos; +// if (code > 0x10FFFF) syntaxError(); +// } else { +// code = readHexChar(4); +// } +// // UTF-16 Decoding +// if (code <= 0xFFFF) return String.fromCharCode(code); +// code -= 0x10000; +// return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00); +// } diff --git a/src/readstring.js b/src/readstring.js new file mode 100644 index 0000000..645b846 --- /dev/null +++ b/src/readstring.js @@ -0,0 +1,145 @@ +/* + * Ported from Acorn + * + * MIT License + * Copyright (C) 2012-2020 by various contributors (see AUTHORS) + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +let acornPos, source; +function readString (str) { + source = str.trim(); + acornPos = 1; + const quote = source.charCodeAt(0); + let out = '', chunkStart = acornPos; + for (;;) { + if (acornPos >= source.length) syntaxError(); + const ch = source.charCodeAt(acornPos); + if (ch === quote) break; + if (ch === 92) { // '\' + out += source.slice(chunkStart, acornPos); + out += readEscapedChar(); + chunkStart = acornPos; + } + else if (ch === 0x2028 || ch === 0x2029) { + ++acornPos; + } + else { + if (isBr(ch)) syntaxError(); + ++acornPos; + } + } + out += source.slice(chunkStart, acornPos++); + return out; +} + +// Used to read escaped characters + +function readEscapedChar () { + let ch = source.charCodeAt(++acornPos); + ++acornPos; + switch (ch) { + case 110: return '\n'; // 'n' -> '\n' + case 114: return '\r'; // 'r' -> '\r' + case 120: return String.fromCharCode(readHexChar(2)); // 'x' + case 117: return readCodePointToString(); // 'u' + case 116: return '\t'; // 't' -> '\t' + case 98: return '\b'; // 'b' -> '\b' + case 118: return '\u000b'; // 'v' -> '\u000b' + case 102: return '\f'; // 'f' -> '\f' + case 13: if (source.charCodeAt(acornPos) === 10) ++acornPos; // '\r\n' + case 10: // ' \n' + return ''; + case 56: + case 57: + syntaxError(); + default: + if (ch >= 48 && ch <= 55) { + let octalStr = source.substr(acornPos - 1, 3).match(/^[0-7]+/)[0]; + let octal = parseInt(octalStr, 8); + if (octal > 255) { + octalStr = octalStr.slice(0, -1); + octal = parseInt(octalStr, 8); + } + acornPos += octalStr.length - 1; + ch = source.charCodeAt(acornPos); + if (octalStr !== '0' || ch === 56 || ch === 57) + syntaxError(); + return String.fromCharCode(octal); + } + if (isBr(ch)) { + // Unicode new line characters after \ get removed from output in both + // template literals and strings + return ''; + } + return String.fromCharCode(ch); + } +} + +// Used to read character escape sequences ('\x', '\u', '\U'). + +function readHexChar (len) { + const start = acornPos; + let total = 0, lastCode = 0; + for (let i = 0; i < len; ++i, ++acornPos) { + let code = source.charCodeAt(acornPos), val; + + if (code === 95) { + if (lastCode === 95 || i === 0) syntaxError(); + lastCode = code; + continue; + } + + if (code >= 97) val = code - 97 + 10; // a + else if (code >= 65) val = code - 65 + 10; // A + else if (code >= 48 && code <= 57) val = code - 48; // 0-9 + else break; + if (val >= 16) break; + lastCode = code; + total = total * 16 + val; + } + + if (lastCode === 95 || acornPos - start !== len) syntaxError(); + + return total; +} + +// Read a string value, interpreting backslash-escapes. + +function readCodePointToString () { + const ch = source.charCodeAt(acornPos); + let code; + if (ch === 123) { // '{' + ++acornPos; + code = readHexChar(source.indexOf('}', acornPos) - acornPos); + ++acornPos; + if (code > 0x10FFFF) syntaxError(); + } else { + code = readHexChar(4); + } + // UTF-16 Decoding + if (code <= 0xFFFF) return String.fromCharCode(code); + code -= 0x10000; + return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00); +} + +function isBr (c) { + return c === 13/*\r*/ || c === 10/*\n*/; +} + +function syntaxError () { + throw Object.assign(new Error(`Parse error @:${source.slice(0, acornPos).split('\n').length}:${acornPos - source.lastIndexOf('\n', acornPos - 1)}`), { idx: acornPos }); +} diff --git a/test/_unit.cjs b/test/_unit.cjs index defe8c4..59bb10e 100755 --- a/test/_unit.cjs +++ b/test/_unit.cjs @@ -72,7 +72,6 @@ suite('Invalid syntax', () => { const source = `import(\n"./statehash\\u1011.js"\r)`; const [imports] = parse(source); assert.strictEqual(imports.length, 1); - console.log(imports); assert.strictEqual(imports[0].n, './statehashထ.js'); assert.strictEqual(source.slice(imports[0].s, imports[0].e), '"./statehash\\u1011.js"'); }); @@ -81,6 +80,7 @@ suite('Invalid syntax', () => { test('Basic nested dynamic import support', () => { const source = `await import (await import ('foo'))`; const [imports] = parse(source); + console.log(imports); assert.strictEqual(imports.length, 2); assert.strictEqual(source.slice(imports[0].ss, imports[0].d), 'import '); assert.strictEqual(source.slice(imports[0].ss, imports[0].se), 'import (await import (\'foo\'))'); From fdd3f12df7acea64163921ef73cd0f0b825377d3 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 1 Jan 2023 02:34:52 +0200 Subject: [PATCH 13/18] fixup --- src/lexer.c | 137 ---------------------------------------------------- 1 file changed, 137 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index 8681c62..49409e7 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -1153,140 +1153,3 @@ bool is_expression_terminator (char16_t* cur_pos) { } return false; } - - -// /* -// * Ported from Acorn -// * -// * MIT License -// * Copyright (C) 2012-2020 by various contributors (see AUTHORS) -// * Permission is hereby granted, free of charge, to any person obtaining a copy -// * of this software and associated documentation files (the "Software"), to deal -// * in the Software without restriction, including without limitation the rights -// * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// * copies of the Software, and to permit persons to whom the Software is -// * furnished to do so, subject to the following conditions: -// * The above copyright notice and this permission notice shall be included in -// * all copies or substantial portions of the Software. -// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// * THE SOFTWARE. -// */ -// static char16_t* str_pos; -// function readString (start, quote) { -// acornPos = start; -// let out = '', chunkStart = acornPos; -// for (;;) { -// if (acornPos >= source.length) syntaxError(); -// const ch = source.charCodeAt(acornPos); -// if (ch === quote) break; -// if (ch === 92) { // '\' -// out += source.slice(chunkStart, acornPos); -// out += readEscapedChar(); -// chunkStart = acornPos; -// } -// else if (ch === 0x2028 || ch === 0x2029) { -// ++acornPos; -// } -// else { -// if (isBr(ch)) syntaxError(); -// ++acornPos; -// } -// } -// out += source.slice(chunkStart, acornPos++); -// return out; -// } - -// // Used to read escaped characters - -// function readEscapedChar () { -// let ch = source.charCodeAt(++acornPos); -// ++acornPos; -// switch (ch) { -// case 110: return '\n'; // 'n' -> '\n' -// case 114: return '\r'; // 'r' -> '\r' -// case 120: return String.fromCharCode(readHexChar(2)); // 'x' -// case 117: return readCodePointToString(); // 'u' -// case 116: return '\t'; // 't' -> '\t' -// case 98: return '\b'; // 'b' -> '\b' -// case 118: return '\u000b'; // 'v' -> '\u000b' -// case 102: return '\f'; // 'f' -> '\f' -// case 13: if (source.charCodeAt(acornPos) === 10) ++acornPos; // '\r\n' -// case 10: // ' \n' -// return ''; -// case 56: -// case 57: -// syntaxError(); -// default: -// if (ch >= 48 && ch <= 55) { -// let octalStr = source.substr(acornPos - 1, 3).match(/^[0-7]+/)[0]; -// let octal = parseInt(octalStr, 8); -// if (octal > 255) { -// octalStr = octalStr.slice(0, -1); -// octal = parseInt(octalStr, 8); -// } -// acornPos += octalStr.length - 1; -// ch = source.charCodeAt(acornPos); -// if (octalStr !== '0' || ch === 56 || ch === 57) -// syntaxError(); -// return String.fromCharCode(octal); -// } -// if (isBr(ch)) { -// // Unicode new line characters after \ get removed from output in both -// // template literals and strings -// return ''; -// } -// return String.fromCharCode(ch); -// } -// } - -// // Used to read character escape sequences ('\x', '\u', '\U'). - -// function readHexChar (len) { -// const start = acornPos; -// let total = 0, lastCode = 0; -// for (let i = 0; i < len; ++i, ++acornPos) { -// let code = source.charCodeAt(acornPos), val; - -// if (code === 95) { -// if (lastCode === 95 || i === 0) syntaxError(); -// lastCode = code; -// continue; -// } - -// if (code >= 97) val = code - 97 + 10; // a -// else if (code >= 65) val = code - 65 + 10; // A -// else if (code >= 48 && code <= 57) val = code - 48; // 0-9 -// else break; -// if (val >= 16) break; -// lastCode = code; -// total = total * 16 + val; -// } - -// if (lastCode === 95 || acornPos - start !== len) syntaxError(); - -// return total; -// } - -// // Read a string value, interpreting backslash-escapes. - -// function readCodePointToString () { -// const ch = source.charCodeAt(acornPos); -// let code; -// if (ch === 123) { // '{' -// ++acornPos; -// code = readHexChar(source.indexOf('}', acornPos) - acornPos); -// ++acornPos; -// if (code > 0x10FFFF) syntaxError(); -// } else { -// code = readHexChar(4); -// } -// // UTF-16 Decoding -// if (code <= 0xFFFF) return String.fromCharCode(code); -// code -= 0x10000; -// return String.fromCharCode((code >> 10) + 0xD800, (code & 1023) + 0xDC00); -// } From fc355cde8a2600cb6af383f8760bb3344d474302 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 1 Jan 2023 22:42:50 +0200 Subject: [PATCH 14/18] fixup string exports --- chompfile.toml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index 37d52fb..fad760e 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -36,7 +36,11 @@ run = ''' const { version } = JSON.parse(await readFile('package.json')); - await writeFile('dist/lexer.js', `/* es-module-lexer ${version} */\n${files['dist/lexer.js'].replace(/n:(\w+)/, 'n:eval($1)')}`); + await writeFile('dist/lexer.js', `/* es-module-lexer ${version} */\n${ + files['dist/lexer.js'] + .replace(/n:(\w+)/, 'n:$1?eval($1):$1') + .replace(/n:([^e])/g, `n:$1&&($1[0]==='"'||$1[0]==="'")?eval($1):$1`) + }`); await writeFile('dist/lexer.d.ts', files['dist/lexer.d.ts']); ''' @@ -61,7 +65,9 @@ run = ''' const readstring = await readFile('obj/readstring.min.js', 'utf8'); await writeFile('dist/lexer.asm.js', `/* es-module-lexer asm.js ${version} */\n${readstring}${ - files['dist/lexer.asm.js'].replace(/n:(\w)/, 'n:readString($1)') + files['dist/lexer.asm.js'] + .replace(/n:(\w)/, 'n:$1?readString($1):$1') + .replace(/n:([a-qs-zA-Z])/g, `n:$1&&($1[0]==='"'||$1[0]==="'")?readString($1):$1`) }`); ''' From b32875b93239874cceaa49cd97c3b0657db1f2ec Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 2 Jan 2023 00:04:35 +0200 Subject: [PATCH 15/18] finalize tests --- chompfile.toml | 2 + src/lexer.c | 21 ++-- test/_unit.cjs | 307 ++++++++++++++++++++++++------------------------- 3 files changed, 167 insertions(+), 163 deletions(-) diff --git a/chompfile.toml b/chompfile.toml index fad760e..02fc262 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -38,6 +38,7 @@ run = ''' await writeFile('dist/lexer.js', `/* es-module-lexer ${version} */\n${ files['dist/lexer.js'] + .replace(/parse\((\w),(\w)\)\{/, `parse($1,$2){$1+='\\0';`) .replace(/n:(\w+)/, 'n:$1?eval($1):$1') .replace(/n:([^e])/g, `n:$1&&($1[0]==='"'||$1[0]==="'")?eval($1):$1`) }`); @@ -66,6 +67,7 @@ run = ''' await writeFile('dist/lexer.asm.js', `/* es-module-lexer asm.js ${version} */\n${readstring}${ files['dist/lexer.asm.js'] + .replace(/parse\((\w),(\w)\)\{/, `parse($1,$2){$1+='\\0';`) .replace(/n:(\w)/, 'n:$1?readString($1):$1') .replace(/n:([a-qs-zA-Z])/g, `n:$1&&($1[0]==='"'||$1[0]==="'")?readString($1):$1`) }`); diff --git a/src/lexer.c b/src/lexer.c index 49409e7..af38671 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -46,10 +46,10 @@ size_t write_num(char16_t* out, uint32_t num) { num /= 10; *ptr++ = "0123456789"[tmp_num - num * 10]; } while (num); - size_t len = ptr - ptr1 + 1; + size_t len = ptr - ptr1; while (ptr1 < ptr) { - tmp_char = *ptr; - *ptr-- = *ptr1; + tmp_char = *--ptr; + *ptr = *ptr1; *ptr1++ = tmp_char; } return len; @@ -124,13 +124,13 @@ void add_import (const char16_t* statement_start, imports_head->ss = statement_start - source; imports_head->s = start - source; imports_head->e = end - source; - imports_head->a = 0; + imports_head->a = -1; if (dynamic == STANDARD_IMPORT) { imports_head->d = -1; imports_head->se = end - source + 1; imports_head->n.is_some = true; - imports_head->n.val.len = end - start; - imports_head->n.val.ptr = (char16_t*)start; + imports_head->n.val.len = end - start + 2; + imports_head->n.val.ptr = (char16_t*)start - 1; } else if (dynamic == IMPORT_META) { imports_head->d = -2; imports_head->se = end - source; @@ -154,6 +154,7 @@ void add_export (const char16_t* start, exports_head->n.ptr = (char16_t*)start; if (local_start == NULL || local_end == NULL) { exports_head->ls = exports_head->le = -1; + exports_head->ln.is_some = false; } else { exports_head->ls = local_start - source; exports_head->le = local_end - source; @@ -186,7 +187,7 @@ void error (int32_t line, int32_t col) { err_msg[len++] = ':'; len += write_num(&err_msg[len], col); err->ptr = err_msg; - err->len = len * 2; + err->len = len; pos = end + 1; } @@ -246,7 +247,7 @@ bool lexer_parse(lexer_string_t* input, lexer_string_t* maybe_name, lexer_tuple3 pos = (char16_t*)(source - 1); char16_t ch = '\0'; - end = pos + source_len; + end = pos + source_len - 1; // start with a pure "module-only" parser while (pos++ < end) { @@ -459,6 +460,7 @@ void try_parse_import_statement () { ch = comment_whitespace(true); add_import(start_pos, pos, 0, dynamic_pos); dynamic_import_stack[dynamic_import_stack_depth++] = imports_head - 1; + (imports_head - 1)->e = -1; if (ch == '\'') { string_literal(ch); } @@ -751,8 +753,9 @@ void try_parse_export_statement () { read_import_string(s_start_pos, comment_whitespace(true)); // There were no local names. - for (lexer_export_t* expt = prev_exports_head == exports_base ? exports_base : prev_exports_head + 1; expt != exports_head; expt++) { + for (lexer_export_t* expt = prev_exports_head == exports_base ? exports_base : prev_exports_head; expt != exports_head; expt++) { expt->ls = expt->le = -1; + expt->ln.is_some = false; } } else { diff --git a/test/_unit.cjs b/test/_unit.cjs index 59bb10e..3eef704 100755 --- a/test/_unit.cjs +++ b/test/_unit.cjs @@ -24,7 +24,7 @@ function assertExportIs(source, actual, expected) { } else { assert.strictEqual(source.substring(actual.s, actual.e), expected.n, `export.s, export.e: ${source.substring(actual.s, actual.e)} != ${expected.n}`); } - if (expected.ln === undefined) { + if (expected.ln === null) { assert.strictEqual(actual.ls, -1, `export.ls: ${actual.ls} != -1`); assert.strictEqual(actual.le, -1, `export.le: ${actual.le} != -1`); } else if (source[actual.ls] === '"' || source[actual.ls] === "'") { @@ -80,7 +80,6 @@ suite('Invalid syntax', () => { test('Basic nested dynamic import support', () => { const source = `await import (await import ('foo'))`; const [imports] = parse(source); - console.log(imports); assert.strictEqual(imports.length, 2); assert.strictEqual(source.slice(imports[0].ss, imports[0].d), 'import '); assert.strictEqual(source.slice(imports[0].ss, imports[0].se), 'import (await import (\'foo\'))'); @@ -160,11 +159,11 @@ export { d as a, p as b, z as c, r as d, q }`; test('Invalid export', () => { try { const source = `export { a = };`; - parse(source); + parse(source, 'test.js'); assert(false, 'Should error'); } catch (err) { - assert.strictEqual(err.idx, 11); + assert.strictEqual(err.payload, 'Parse error test.js:1:12'); } }); }); @@ -191,9 +190,9 @@ suite('Lexer', () => { assert.strictEqual(imports[0].n, './abc.js'); assert.strictEqual(imports[1].n, './𠈄.js'); assert.strictEqual(imports[2].n, './𠈄.js'); - assert.strictEqual(imports[3].n, undefined); + assert.strictEqual(imports[3].n, null); assert.strictEqual(imports[4].n, './𠈄.js'); - assert.strictEqual(imports[5].n, undefined); + assert.strictEqual(imports[5].n, null); }) test('Regexp case', () => { @@ -407,7 +406,7 @@ suite('Lexer', () => { assert.strictEqual(source.slice(ss, se), 'export { hello as default } from "test-dep"'); assert.strictEqual(exports.length, 1); - assertExportIs(source, exports[0], { n: 'default', ln: undefined }); + assertExportIs(source, exports[0], { n: 'default', ln: null }); }); test('import.meta', () => { @@ -658,8 +657,8 @@ function x() { const [imports, exports] = parse(source); assert.strictEqual(imports.length, 2); assert.strictEqual(exports.length, 2); - assertExportIs(source, exports[0], { n: 'X', ln: undefined }); - assertExportIs(source, exports[1], { n: 'yy', ln: undefined }); + assertExportIs(source, exports[0], { n: 'X', ln: null }); + assertExportIs(source, exports[1], { n: 'yy', ln: null }); }); suite('Import From', () => { @@ -800,13 +799,13 @@ function x() { const [imports, exports] = parse(source); assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 7); - assertExportIs(source, exports[0], { n: 'x', ln: undefined }); - assertExportIs(source, exports[1], { n: 'x1', ln: undefined }); - assertExportIs(source, exports[2], { n: 'x2', ln: undefined }); - assertExportIs(source, exports[3], { n: 'foo', ln: undefined }); - assertExportIs(source, exports[4], { n: 'bar', ln: undefined }); - assertExportIs(source, exports[5], { n: 'zoo', ln: undefined }); - assertExportIs(source, exports[6], { n: 'LionCombobox', ln: undefined }); + assertExportIs(source, exports[0], { n: 'x', ln: null }); + assertExportIs(source, exports[1], { n: 'x1', ln: null }); + assertExportIs(source, exports[2], { n: 'x2', ln: null }); + assertExportIs(source, exports[3], { n: 'foo', ln: null }); + assertExportIs(source, exports[4], { n: 'bar', ln: null }); + assertExportIs(source, exports[5], { n: 'zoo', ln: null }); + assertExportIs(source, exports[6], { n: 'LionCombobox', ln: null }); }); if (!js) @@ -825,15 +824,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: 'foo0', ln: undefined }); - assertExportIs(source, exports[1], { n: 'foo1', ln: undefined }); - assertExportIs(source, exports[2], { n: 'foo2', ln: undefined }); - assertExportIs(source, exports[3], { n: 'foo3', ln: undefined }); - assertExportIs(source, exports[4], { n: 'foo4', ln: undefined }); - assertExportIs(source, exports[5], { n: 'foo5', ln: undefined }); - assertExportIs(source, exports[6], { n: 'foo6', ln: undefined }); - assertExportIs(source, exports[7], { n: 'foo7', ln: undefined }); - assertExportIs(source, exports[8], { n: 'foo8', ln: undefined }); + assertExportIs(source, exports[0], { n: 'foo0', ln: null }); + assertExportIs(source, exports[1], { n: 'foo1', ln: null }); + assertExportIs(source, exports[2], { n: 'foo2', ln: null }); + assertExportIs(source, exports[3], { n: 'foo3', ln: null }); + assertExportIs(source, exports[4], { n: 'foo4', ln: null }); + assertExportIs(source, exports[5], { n: 'foo5', ln: null }); + assertExportIs(source, exports[6], { n: 'foo6', ln: null }); + assertExportIs(source, exports[7], { n: 'foo7', ln: null }); + assertExportIs(source, exports[8], { n: 'foo8', ln: null }); }); if (!js) @@ -852,15 +851,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: 'foo0', ln: undefined }); - assertExportIs(source, exports[1], { n: 'foo1', ln: undefined }); - assertExportIs(source, exports[2], { n: 'foo2', ln: undefined }); - assertExportIs(source, exports[3], { n: 'foo3', ln: undefined }); - assertExportIs(source, exports[4], { n: 'foo4', ln: undefined }); - assertExportIs(source, exports[5], { n: 'foo5', ln: undefined }); - assertExportIs(source, exports[6], { n: 'foo6', ln: undefined }); - assertExportIs(source, exports[7], { n: 'foo7', ln: undefined }); - assertExportIs(source, exports[8], { n: 'foo8', ln: undefined }); + assertExportIs(source, exports[0], { n: 'foo0', ln: null }); + assertExportIs(source, exports[1], { n: 'foo1', ln: null }); + assertExportIs(source, exports[2], { n: 'foo2', ln: null }); + assertExportIs(source, exports[3], { n: 'foo3', ln: null }); + assertExportIs(source, exports[4], { n: 'foo4', ln: null }); + assertExportIs(source, exports[5], { n: 'foo5', ln: null }); + assertExportIs(source, exports[6], { n: 'foo6', ln: null }); + assertExportIs(source, exports[7], { n: 'foo7', ln: null }); + assertExportIs(source, exports[8], { n: 'foo8', ln: null }); }); if (!js) @@ -874,10 +873,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: 'foo0', ln: undefined }); - assertExportIs(source, exports[1], { n: 'foo1', ln: undefined }); - assertExportIs(source, exports[2], { n: 'foo2', ln: undefined }); - assertExportIs(source, exports[3], { n: 'foo3', ln: undefined }); + assertExportIs(source, exports[0], { n: 'foo0', ln: null }); + assertExportIs(source, exports[1], { n: 'foo1', ln: null }); + assertExportIs(source, exports[2], { n: 'foo2', ln: null }); + assertExportIs(source, exports[3], { n: 'foo3', ln: null }); }); if (!js) @@ -891,10 +890,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: 'foo0', ln: undefined }); - assertExportIs(source, exports[1], { n: 'foo1', ln: undefined }); - assertExportIs(source, exports[2], { n: 'foo2', ln: undefined }); - assertExportIs(source, exports[3], { n: 'foo3', ln: undefined }); + assertExportIs(source, exports[0], { n: 'foo0', ln: null }); + assertExportIs(source, exports[1], { n: 'foo1', ln: null }); + assertExportIs(source, exports[2], { n: 'foo2', ln: null }); + assertExportIs(source, exports[3], { n: 'foo3', ln: null }); }); if (!js) @@ -906,8 +905,8 @@ function x() { assert.strictEqual(imports.length, 2); assert.strictEqual(exports.length, 2); - assertExportIs(source, exports[0], { n: 'foo0', ln: undefined }); - assertExportIs(source, exports[1], { n: 'foo1', ln: undefined }); + assertExportIs(source, exports[0], { n: 'foo0', ln: null }); + assertExportIs(source, exports[1], { n: 'foo1', ln: null }); }); if (!js) @@ -926,15 +925,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: '~123', ln: undefined }); - assertExportIs(source, exports[1], { n: 'ab cd', ln: undefined }); - assertExportIs(source, exports[2], { n: 'not identifier', ln: undefined }); - assertExportIs(source, exports[3], { n: '-notidentifier', ln: undefined }); - assertExportIs(source, exports[4], { n: '%notidentifier', ln: undefined }); - assertExportIs(source, exports[5], { n: '@notidentifier', ln: undefined }); - assertExportIs(source, exports[6], { n: ' notidentifier', ln: undefined }); - assertExportIs(source, exports[7], { n: 'notidentifier ', ln: undefined }); - assertExportIs(source, exports[8], { n: ' notidentifier ', ln: undefined }); + assertExportIs(source, exports[0], { n: '~123', ln: null }); + assertExportIs(source, exports[1], { n: 'ab cd', ln: null }); + assertExportIs(source, exports[2], { n: 'not identifier', ln: null }); + assertExportIs(source, exports[3], { n: '-notidentifier', ln: null }); + assertExportIs(source, exports[4], { n: '%notidentifier', ln: null }); + assertExportIs(source, exports[5], { n: '@notidentifier', ln: null }); + assertExportIs(source, exports[6], { n: ' notidentifier', ln: null }); + assertExportIs(source, exports[7], { n: 'notidentifier ', ln: null }); + assertExportIs(source, exports[8], { n: ' notidentifier ', ln: null }); }); if (!js) @@ -953,15 +952,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: '~123', ln: undefined }); - assertExportIs(source, exports[1], { n: 'ab cd', ln: undefined }); - assertExportIs(source, exports[2], { n: 'not identifier', ln: undefined }); - assertExportIs(source, exports[3], { n: '-notidentifier', ln: undefined }); - assertExportIs(source, exports[4], { n: '%notidentifier', ln: undefined }); - assertExportIs(source, exports[5], { n: '@notidentifier', ln: undefined }); - assertExportIs(source, exports[6], { n: ' notidentifier', ln: undefined }); - assertExportIs(source, exports[7], { n: 'notidentifier ', ln: undefined }); - assertExportIs(source, exports[8], { n: ' notidentifier ', ln: undefined }); + assertExportIs(source, exports[0], { n: '~123', ln: null }); + assertExportIs(source, exports[1], { n: 'ab cd', ln: null }); + assertExportIs(source, exports[2], { n: 'not identifier', ln: null }); + assertExportIs(source, exports[3], { n: '-notidentifier', ln: null }); + assertExportIs(source, exports[4], { n: '%notidentifier', ln: null }); + assertExportIs(source, exports[5], { n: '@notidentifier', ln: null }); + assertExportIs(source, exports[6], { n: ' notidentifier', ln: null }); + assertExportIs(source, exports[7], { n: 'notidentifier ', ln: null }); + assertExportIs(source, exports[8], { n: ' notidentifier ', ln: null }); }); if (!js) @@ -975,10 +974,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: undefined }); - assertExportIs(source, exports[1], { n: String.raw` quote" `, ln: undefined }); - assertExportIs(source, exports[2], { n: String.raw` quote\" `, ln: undefined }); - assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: undefined }); + assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: null }); + assertExportIs(source, exports[1], { n: String.raw` quote" `, ln: null }); + assertExportIs(source, exports[2], { n: String.raw` quote\" `, ln: null }); + assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: null }); }); if (!js) @@ -992,10 +991,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: undefined }); - assertExportIs(source, exports[1], { n: String.raw` quote' `, ln: undefined }); - assertExportIs(source, exports[2], { n: String.raw` quote\' `, ln: undefined }); - assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: undefined }); + assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: null }); + assertExportIs(source, exports[1], { n: String.raw` quote' `, ln: null }); + assertExportIs(source, exports[2], { n: String.raw` quote\' `, ln: null }); + assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: null }); }); if (!js) @@ -1014,15 +1013,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: '~123', ln: undefined }); - assertExportIs(source, exports[1], { n: 'ab cd', ln: undefined }); - assertExportIs(source, exports[2], { n: 'not identifier', ln: undefined }); - assertExportIs(source, exports[3], { n: '-notidentifier', ln: undefined }); - assertExportIs(source, exports[4], { n: '%notidentifier', ln: undefined }); - assertExportIs(source, exports[5], { n: '@notidentifier', ln: undefined }); - assertExportIs(source, exports[6], { n: ' notidentifier', ln: undefined }); - assertExportIs(source, exports[7], { n: 'notidentifier ', ln: undefined }); - assertExportIs(source, exports[8], { n: ' notidentifier ', ln: undefined }); + assertExportIs(source, exports[0], { n: '~123', ln: null }); + assertExportIs(source, exports[1], { n: 'ab cd', ln: null }); + assertExportIs(source, exports[2], { n: 'not identifier', ln: null }); + assertExportIs(source, exports[3], { n: '-notidentifier', ln: null }); + assertExportIs(source, exports[4], { n: '%notidentifier', ln: null }); + assertExportIs(source, exports[5], { n: '@notidentifier', ln: null }); + assertExportIs(source, exports[6], { n: ' notidentifier', ln: null }); + assertExportIs(source, exports[7], { n: 'notidentifier ', ln: null }); + assertExportIs(source, exports[8], { n: ' notidentifier ', ln: null }); }); if (!js) @@ -1041,15 +1040,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: '~123', ln: undefined }); - assertExportIs(source, exports[1], { n: 'ab cd', ln: undefined }); - assertExportIs(source, exports[2], { n: 'not identifier', ln: undefined }); - assertExportIs(source, exports[3], { n: '-notidentifier', ln: undefined }); - assertExportIs(source, exports[4], { n: '%notidentifier', ln: undefined }); - assertExportIs(source, exports[5], { n: '@notidentifier', ln: undefined }); - assertExportIs(source, exports[6], { n: ' notidentifier', ln: undefined }); - assertExportIs(source, exports[7], { n: 'notidentifier ', ln: undefined }); - assertExportIs(source, exports[8], { n: ' notidentifier ', ln: undefined }); + assertExportIs(source, exports[0], { n: '~123', ln: null }); + assertExportIs(source, exports[1], { n: 'ab cd', ln: null }); + assertExportIs(source, exports[2], { n: 'not identifier', ln: null }); + assertExportIs(source, exports[3], { n: '-notidentifier', ln: null }); + assertExportIs(source, exports[4], { n: '%notidentifier', ln: null }); + assertExportIs(source, exports[5], { n: '@notidentifier', ln: null }); + assertExportIs(source, exports[6], { n: ' notidentifier', ln: null }); + assertExportIs(source, exports[7], { n: 'notidentifier ', ln: null }); + assertExportIs(source, exports[8], { n: ' notidentifier ', ln: null }); }); if (!js) @@ -1063,10 +1062,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: undefined }); - assertExportIs(source, exports[1], { n: String.raw` quote" `, ln: undefined }); - assertExportIs(source, exports[2], { n: String.raw` quote\" `, ln: undefined }); - assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: undefined }); + assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: null }); + assertExportIs(source, exports[1], { n: String.raw` quote" `, ln: null }); + assertExportIs(source, exports[2], { n: String.raw` quote\" `, ln: null }); + assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: null }); }); if (!js) @@ -1080,10 +1079,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: undefined }); - assertExportIs(source, exports[1], { n: String.raw` quote' `, ln: undefined }); - assertExportIs(source, exports[2], { n: String.raw` quote\' `, ln: undefined }); - assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: undefined }); + assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: null }); + assertExportIs(source, exports[1], { n: String.raw` quote' `, ln: null }); + assertExportIs(source, exports[2], { n: String.raw` quote\' `, ln: null }); + assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: null }); }); if (!js) @@ -1102,15 +1101,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: '~123', ln: undefined }); - assertExportIs(source, exports[1], { n: 'ab cd', ln: undefined }); - assertExportIs(source, exports[2], { n: 'not identifier', ln: undefined }); - assertExportIs(source, exports[3], { n: '-notidentifier', ln: undefined }); - assertExportIs(source, exports[4], { n: '%notidentifier', ln: undefined }); - assertExportIs(source, exports[5], { n: '@notidentifier', ln: undefined }); - assertExportIs(source, exports[6], { n: ' notidentifier', ln: undefined }); - assertExportIs(source, exports[7], { n: 'notidentifier ', ln: undefined }); - assertExportIs(source, exports[8], { n: ' notidentifier ', ln: undefined }); + assertExportIs(source, exports[0], { n: '~123', ln: null }); + assertExportIs(source, exports[1], { n: 'ab cd', ln: null }); + assertExportIs(source, exports[2], { n: 'not identifier', ln: null }); + assertExportIs(source, exports[3], { n: '-notidentifier', ln: null }); + assertExportIs(source, exports[4], { n: '%notidentifier', ln: null }); + assertExportIs(source, exports[5], { n: '@notidentifier', ln: null }); + assertExportIs(source, exports[6], { n: ' notidentifier', ln: null }); + assertExportIs(source, exports[7], { n: 'notidentifier ', ln: null }); + assertExportIs(source, exports[8], { n: ' notidentifier ', ln: null }); }); if (!js) @@ -1129,15 +1128,15 @@ function x() { assert.strictEqual(imports.length, 9); assert.strictEqual(exports.length, 9); - assertExportIs(source, exports[0], { n: '~123', ln: undefined }); - assertExportIs(source, exports[1], { n: 'ab cd', ln: undefined }); - assertExportIs(source, exports[2], { n: 'not identifier', ln: undefined }); - assertExportIs(source, exports[3], { n: '-notidentifier', ln: undefined }); - assertExportIs(source, exports[4], { n: '%notidentifier', ln: undefined }); - assertExportIs(source, exports[5], { n: '@notidentifier', ln: undefined }); - assertExportIs(source, exports[6], { n: ' notidentifier', ln: undefined }); - assertExportIs(source, exports[7], { n: 'notidentifier ', ln: undefined }); - assertExportIs(source, exports[8], { n: ' notidentifier ', ln: undefined }); + assertExportIs(source, exports[0], { n: '~123', ln: null }); + assertExportIs(source, exports[1], { n: 'ab cd', ln: null }); + assertExportIs(source, exports[2], { n: 'not identifier', ln: null }); + assertExportIs(source, exports[3], { n: '-notidentifier', ln: null }); + assertExportIs(source, exports[4], { n: '%notidentifier', ln: null }); + assertExportIs(source, exports[5], { n: '@notidentifier', ln: null }); + assertExportIs(source, exports[6], { n: ' notidentifier', ln: null }); + assertExportIs(source, exports[7], { n: 'notidentifier ', ln: null }); + assertExportIs(source, exports[8], { n: ' notidentifier ', ln: null }); }); if (!js) @@ -1151,10 +1150,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: undefined, a: true}); - assertExportIs(source, exports[1], { n: String.raw` quote" `, ln: undefined, a: true}); - assertExportIs(source, exports[2], { n: String.raw` quote\" `, ln: undefined, a: true}); - assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: undefined, a: true}); + assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: null, a: true}); + assertExportIs(source, exports[1], { n: String.raw` quote" `, ln: null, a: true}); + assertExportIs(source, exports[2], { n: String.raw` quote\" `, ln: null, a: true}); + assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: null, a: true}); }); if (!js) @@ -1168,10 +1167,10 @@ function x() { assert.strictEqual(imports.length, 4); assert.strictEqual(exports.length, 4); - assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: undefined }); - assertExportIs(source, exports[1], { n: String.raw` quote' `, ln: undefined }); - assertExportIs(source, exports[2], { n: String.raw` quote\' `, ln: undefined }); - assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: undefined }); + assertExportIs(source, exports[0], { n: String.raw` slash\ `, ln: null }); + assertExportIs(source, exports[1], { n: String.raw` quote' `, ln: null }); + assertExportIs(source, exports[2], { n: String.raw` quote\' `, ln: null }); + assertExportIs(source, exports[3], { n: String.raw` quote' `, ln: null }); }); if (!js) @@ -1187,12 +1186,12 @@ function x() { assert.strictEqual(imports.length, 6); assert.strictEqual(exports.length, 6); - assertExportIs(source, exports[0], { n: ' right-curlybrace} ', ln: undefined }); - assertExportIs(source, exports[1], { n: ' {left-curlybrace ', ln: undefined }); - assertExportIs(source, exports[2], { n: ' {curlybrackets} ', ln: undefined }); - assertExportIs(source, exports[3], { n: ' right-curlybrace} ', ln: undefined }); - assertExportIs(source, exports[4], { n: ' {left-curlybrace ', ln: undefined }); - assertExportIs(source, exports[5], { n: ' {curlybrackets} ', ln: undefined }); + assertExportIs(source, exports[0], { n: ' right-curlybrace} ', ln: null }); + assertExportIs(source, exports[1], { n: ' {left-curlybrace ', ln: null }); + assertExportIs(source, exports[2], { n: ' {curlybrackets} ', ln: null }); + assertExportIs(source, exports[3], { n: ' right-curlybrace} ', ln: null }); + assertExportIs(source, exports[4], { n: ' {left-curlybrace ', ln: null }); + assertExportIs(source, exports[5], { n: ' {curlybrackets} ', ln: null }); }); if (!js) @@ -1208,12 +1207,12 @@ function x() { assert.strictEqual(imports.length, 6); assert.strictEqual(exports.length, 6); - assertExportIs(source, exports[0], { n: ' right-curlybrace} ', ln: undefined }); - assertExportIs(source, exports[1], { n: ' {left-curlybrace ', ln: undefined }); - assertExportIs(source, exports[2], { n: ' {curlybrackets} ', ln: undefined }); - assertExportIs(source, exports[3], { n: ' right-curlybrace} ', ln: undefined }); - assertExportIs(source, exports[4], { n: ' {left-curlybrace ', ln: undefined }); - assertExportIs(source, exports[5], { n: ' {curlybrackets} ', ln: undefined }); + assertExportIs(source, exports[0], { n: ' right-curlybrace} ', ln: null }); + assertExportIs(source, exports[1], { n: ' {left-curlybrace ', ln: null }); + assertExportIs(source, exports[2], { n: ' {curlybrackets} ', ln: null }); + assertExportIs(source, exports[3], { n: ' right-curlybrace} ', ln: null }); + assertExportIs(source, exports[4], { n: ' {left-curlybrace ', ln: null }); + assertExportIs(source, exports[5], { n: ' {curlybrackets} ', ln: null }); }); if (!js) @@ -1229,12 +1228,12 @@ function x() { assert.strictEqual(imports.length, 6); assert.strictEqual(exports.length, 6); - assertExportIs(source, exports[0], { n: ' right-curlybrace} ', ln: undefined }); - assertExportIs(source, exports[1], { n: ' {left-curlybrace ', ln: undefined }); - assertExportIs(source, exports[2], { n: ' {curlybrackets} ', ln: undefined }); - assertExportIs(source, exports[3], { n: ' right-curlybrace} ', ln: undefined }); - assertExportIs(source, exports[4], { n: ' {left-curlybrace ', ln: undefined }); - assertExportIs(source, exports[5], { n: ' {curlybrackets} ', ln: undefined }); + assertExportIs(source, exports[0], { n: ' right-curlybrace} ', ln: null }); + assertExportIs(source, exports[1], { n: ' {left-curlybrace ', ln: null }); + assertExportIs(source, exports[2], { n: ' {curlybrackets} ', ln: null }); + assertExportIs(source, exports[3], { n: ' right-curlybrace} ', ln: null }); + assertExportIs(source, exports[4], { n: ' {left-curlybrace ', ln: null }); + assertExportIs(source, exports[5], { n: ' {curlybrackets} ', ln: null }); }); if (!js) @@ -1253,13 +1252,13 @@ function x() { assert.strictEqual(imports.length, 3); assert.strictEqual(exports.length, 7); - assertExportIs(source, exports[0], { n: 'foo', ln: undefined }); - assertExportIs(source, exports[1], { n: 'foo2', ln: undefined }); - assertExportIs(source, exports[2], { n: ' {left-curlybrace ', ln: undefined }); - assertExportIs(source, exports[3], { n: '@notidentifier', ln: undefined }); - assertExportIs(source, exports[4], { n: 'identifier', ln: undefined }); - assertExportIs(source, exports[5], { n: "z'", ln: undefined }); - assertExportIs(source, exports[6], { n: "p as 'z' from 'asdf'", ln: undefined }); + assertExportIs(source, exports[0], { n: 'foo', ln: null }); + assertExportIs(source, exports[1], { n: 'foo2', ln: null }); + assertExportIs(source, exports[2], { n: ' {left-curlybrace ', ln: null }); + assertExportIs(source, exports[3], { n: '@notidentifier', ln: null }); + assertExportIs(source, exports[4], { n: 'identifier', ln: null }); + assertExportIs(source, exports[5], { n: "z'", ln: null }); + assertExportIs(source, exports[6], { n: "p as 'z' from 'asdf'", ln: null }); }); }); @@ -1333,16 +1332,16 @@ function x() { assert.strictEqual(exports.length, 12); assertExportIs(source, exports[0], { n: 'default', ln: 'example'}); assertExportIs(source, exports[1], { n: 'a', ln: 'a' }); - assertExportIs(source, exports[2], { n: 'default', ln: undefined }); + assertExportIs(source, exports[2], { n: 'default', ln: null }); assertExportIs(source, exports[3], { n: 'default', ln: 'example1'}); - assertExportIs(source, exports[4], { n: 'default' }); + assertExportIs(source, exports[4], { n: 'default', ln: null }); assertExportIs(source, exports[5], { n: 'default', ln: 'className'}); - assertExportIs(source, exports[6], { n: 'default' }); + assertExportIs(source, exports[6], { n: 'default', ln: null }); assertExportIs(source, exports[7], { n: 'default', ln: 'generatorFunctionName'}); - assertExportIs(source, exports[8], { n: 'default' }); - assertExportIs(source, exports[9], { n: 'default', ln: undefined }); - assertExportIs(source, exports[10], { n: 'default', ln: undefined }); - assertExportIs(source, exports[11], { n: 'default', ln: undefined }); + assertExportIs(source, exports[8], { n: 'default', ln: null }); + assertExportIs(source, exports[9], { n: 'default', ln: null }); + assertExportIs(source, exports[10], { n: 'default', ln: null }); + assertExportIs(source, exports[11], { n: 'default', ln: null }); }); }); From e95368a54990e335d3b2b992a73d8a72ca85e567 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 2 Jan 2023 00:05:54 +0200 Subject: [PATCH 16/18] fixup chomp action --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bdbc2e9..ffd73d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,6 +51,8 @@ jobs: - name: Setup Chomp uses: guybedford/chomp-action@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Compile to Wasm & Test Wasm env: From c5dda4802c0ac1165084743b4c65f83d57023832 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 1 Jan 2023 15:25:29 -0800 Subject: [PATCH 17/18] fixup wit-bindgen ref --- chompfile.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chompfile.toml b/chompfile.toml index 02fc262..7cc338d 100644 --- a/chompfile.toml +++ b/chompfile.toml @@ -6,7 +6,7 @@ extensions = ['chomp@0.1:npm', 'chomp@0.1:footprint', 'chomp@0.1:terser'] WASM_TOOLS = 'wasm-tools' WASI_PATH = '/opt/wasi-sdk' JSCT = 'jsct' -WIT_BINDGEN = '../wit-bindgen/target/debug/wit-bindgen' +WIT_BINDGEN = 'wit-bindgen' [[task]] name = 'build' From 042460ac0cf9a36cadd65783e8286d1719f29696 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 1 Jan 2023 15:26:00 -0800 Subject: [PATCH 18/18] install wasm-tools --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ffd73d0..c741aaf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,9 @@ jobs: - name: Install WIT Bindgen run: cargo install --git https://github.com/bytecodealliance/wit-bindgen wit-bindgen-cli + + - name: Install Wasm Tools + run: cargo install wasm-tools - name: Install wasi-sdk shell: bash