gofront 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +34 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +3 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
package/src/index.js
CHANGED
|
@@ -14,30 +14,30 @@
|
|
|
14
14
|
// gofront init [dir] — scaffold a new GoFront project
|
|
15
15
|
|
|
16
16
|
import { statSync } from "node:fs";
|
|
17
|
-
import { dirname,
|
|
18
|
-
import { copyAssets } from "./asset-manager.js";
|
|
17
|
+
import { dirname, resolve } from "node:path";
|
|
19
18
|
import {
|
|
20
|
-
|
|
19
|
+
devReporters,
|
|
21
20
|
formatPrepSummary,
|
|
22
21
|
formatWrittenDesc,
|
|
23
22
|
GOFRONT_VERSION,
|
|
23
|
+
HELP_TEXT,
|
|
24
24
|
handleBuild,
|
|
25
25
|
handleCheck,
|
|
26
26
|
handleDev,
|
|
27
27
|
handleInit,
|
|
28
28
|
handlePrep,
|
|
29
29
|
handleTest,
|
|
30
|
-
maybeMinify,
|
|
31
30
|
parseBuildArgs,
|
|
32
31
|
parseCheckArgs,
|
|
33
32
|
parseDevArgs,
|
|
34
33
|
parseLegacyArgs,
|
|
35
34
|
parsePrepArgs,
|
|
36
35
|
parseTestArgs,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
reportBuild,
|
|
37
|
+
reportCheck,
|
|
38
|
+
runSingleShot,
|
|
39
|
+
} from "./cli/cli-core.js";
|
|
40
|
+
import { colors, log, stamp } from "./cli/colors.js";
|
|
41
41
|
|
|
42
42
|
// ── Parse CLI args ───────────────────────────────────────────
|
|
43
43
|
|
|
@@ -48,35 +48,7 @@ if (args[0] === "--version" || args[0] === "-v") {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
51
|
-
console.log(
|
|
52
|
-
`
|
|
53
|
-
GoFront — a Go-inspired language that compiles to JavaScript and WebAssembly
|
|
54
|
-
|
|
55
|
-
Usage:
|
|
56
|
-
gofront dev [dir] [options] Start dev server with live reload (default port 3000)
|
|
57
|
-
gofront build [dir] [options] Build production bundle (-o <dir>, --pwa, --minify, --emit-wat, --release, --wasm-opt, --js-only)
|
|
58
|
-
gofront check [dir|dir/...] Type-check only (dir/... recurses into every package)
|
|
59
|
-
gofront test [dir|dir/...] [--dom] Run unit tests (-v verbose, -run <regex>)
|
|
60
|
-
gofront prep [dir] [--minify] Copy static assets and bundle vendor dependencies
|
|
61
|
-
gofront init [dir] Scaffold a new GoFront project
|
|
62
|
-
gofront <file.go> Compile single file and print to stdout
|
|
63
|
-
gofront <dir> (or gofront .) Compile all *.go in directory as one bundle
|
|
64
|
-
gofront <input> -o out.js Compile and write to file (wasm packages also emit app.wasm)
|
|
65
|
-
gofront <input> --release Production build with minification and Binaryen wasm optimization
|
|
66
|
-
gofront <input> --wasm-opt Optimize wasm output with Binaryen (-O3 + GUFA)
|
|
67
|
-
gofront <input> --check Type-check only
|
|
68
|
-
gofront <input> --watch Watch for changes and recompile
|
|
69
|
-
gofront <input> -o out.js --serve Watch + serve with live reload (default port 3000)
|
|
70
|
-
gofront <input> -o out.js --serve --port 8080 Use a custom port
|
|
71
|
-
gofront <input> -o out.js --copy-assets Compile and synchronize static assets
|
|
72
|
-
gofront <input> --source-map Append inline source map to output
|
|
73
|
-
gofront <input> --minify Minify output
|
|
74
|
-
gofront <input> --minify --mangle Minify and mangle identifiers
|
|
75
|
-
gofront <file.go> --ast Dump AST (debug)
|
|
76
|
-
gofront <file.go> --tokens Dump tokens (debug)
|
|
77
|
-
gofront --version Print version
|
|
78
|
-
`.trim(),
|
|
79
|
-
);
|
|
51
|
+
console.log(HELP_TEXT);
|
|
80
52
|
process.exit(0);
|
|
81
53
|
}
|
|
82
54
|
|
|
@@ -131,19 +103,7 @@ if (args[0] === "test") {
|
|
|
131
103
|
if (args[0] === "check") {
|
|
132
104
|
const { targetDir, forceTarget } = parseCheckArgs(args.slice(1));
|
|
133
105
|
try {
|
|
134
|
-
|
|
135
|
-
const okLine = (label, t) =>
|
|
136
|
-
log.info(`${colors.cyan(label)} — ${colors.green("OK")} ${ms(t)}`);
|
|
137
|
-
if (packages) {
|
|
138
|
-
for (const pkg of packages) {
|
|
139
|
-
okLine(relative(process.cwd(), pkg.dir) || ".", pkg.elapsedMs);
|
|
140
|
-
}
|
|
141
|
-
log.info(
|
|
142
|
-
`${colors.bold(`${packages.length} packages`)} — ${colors.bold(colors.green("OK"))} ${ms(elapsedMs)}`,
|
|
143
|
-
);
|
|
144
|
-
} else {
|
|
145
|
-
okLine(targetDir, elapsedMs);
|
|
146
|
-
}
|
|
106
|
+
reportCheck(handleCheck(targetDir, { forceTarget }), targetDir);
|
|
147
107
|
process.exit(0);
|
|
148
108
|
} catch (e) {
|
|
149
109
|
log.fail(e.message);
|
|
@@ -157,13 +117,7 @@ if (args[0] === "build") {
|
|
|
157
117
|
const buildOptions = parseBuildArgs(args.slice(1));
|
|
158
118
|
try {
|
|
159
119
|
const result = await handleBuild(buildOptions.targetDir, buildOptions);
|
|
160
|
-
|
|
161
|
-
log.info(
|
|
162
|
-
line
|
|
163
|
-
.replace(/^build complete/, colors.green("build complete"))
|
|
164
|
-
.replace(/\(([\d.]+ kB)\)$/, (_, s) => colors.dim(`(${s})`)),
|
|
165
|
-
);
|
|
166
|
-
}
|
|
120
|
+
reportBuild(result);
|
|
167
121
|
process.exit(0);
|
|
168
122
|
} catch (e) {
|
|
169
123
|
log.fail(e.message);
|
|
@@ -200,22 +154,19 @@ if (args[0] === "dev") {
|
|
|
200
154
|
}
|
|
201
155
|
}
|
|
202
156
|
|
|
157
|
+
const flags = parseLegacyArgs(args);
|
|
203
158
|
const {
|
|
204
159
|
inputArg,
|
|
205
160
|
outputFile,
|
|
206
|
-
checkOnly,
|
|
207
|
-
dumpAst,
|
|
208
|
-
dumpTokens,
|
|
209
161
|
sourceMap,
|
|
210
162
|
serve: serveMode,
|
|
211
163
|
watch: watchMode,
|
|
212
164
|
copyAssets: copyAssetsFlag,
|
|
213
|
-
wasmOpt,
|
|
214
165
|
minify: minifyOutput,
|
|
215
166
|
mangle: mangleOutput,
|
|
216
167
|
port: servePort,
|
|
217
168
|
forceTarget,
|
|
218
|
-
} =
|
|
169
|
+
} = flags;
|
|
219
170
|
|
|
220
171
|
// ── Determine input mode ─────────────────────────────────────
|
|
221
172
|
|
|
@@ -228,96 +179,10 @@ try {
|
|
|
228
179
|
process.exit(1);
|
|
229
180
|
}
|
|
230
181
|
|
|
231
|
-
function copyProjectAssets(verbose) {
|
|
232
|
-
try {
|
|
233
|
-
const { copied, skipped } = copyAssets(resolve("."));
|
|
234
|
-
if (verbose && (copied > 0 || skipped > 0)) {
|
|
235
|
-
log.info(`copied ${copied} assets (${skipped} skipped)`);
|
|
236
|
-
}
|
|
237
|
-
} catch (e) {
|
|
238
|
-
log.warn(`asset copy failed: ${e.message}`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
182
|
// ── Single-shot mode ─────────────────────────────────────────
|
|
243
183
|
|
|
244
184
|
if (!watchMode) {
|
|
245
|
-
|
|
246
|
-
const startMs = performance.now();
|
|
247
|
-
try {
|
|
248
|
-
result = runCompile(inputPath, isDir, {
|
|
249
|
-
sourceMap,
|
|
250
|
-
outputFile,
|
|
251
|
-
dumpTokens,
|
|
252
|
-
dumpAst,
|
|
253
|
-
wasmOpt,
|
|
254
|
-
forceTarget,
|
|
255
|
-
});
|
|
256
|
-
} catch (e) {
|
|
257
|
-
log.fail(e.message);
|
|
258
|
-
process.exit(1);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (result.tokens) {
|
|
262
|
-
for (const tok of result.tokens) console.log(tok.toString());
|
|
263
|
-
process.exit(0);
|
|
264
|
-
}
|
|
265
|
-
if (result.ast) {
|
|
266
|
-
console.log(JSON.stringify(result.ast, null, 2));
|
|
267
|
-
process.exit(0);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (checkOnly) {
|
|
271
|
-
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
272
|
-
log.info(
|
|
273
|
-
`${colors.cyan(inputArg)} — ${colors.green("OK")} ${ms(elapsedMs)}`,
|
|
274
|
-
);
|
|
275
|
-
process.exit(0);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
let js;
|
|
279
|
-
try {
|
|
280
|
-
js = maybeMinify(result.js, {
|
|
281
|
-
minify: minifyOutput,
|
|
282
|
-
mangle: mangleOutput,
|
|
283
|
-
sourceMap,
|
|
284
|
-
});
|
|
285
|
-
} catch (e) {
|
|
286
|
-
log.fail(`minify failed: ${e.message}`);
|
|
287
|
-
process.exit(1);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const elapsedMs = (performance.now() - startMs).toFixed(0);
|
|
291
|
-
|
|
292
|
-
if (outputFile) {
|
|
293
|
-
try {
|
|
294
|
-
const written = writeCompileOutput(resolve(outputFile), result, js);
|
|
295
|
-
log.info(
|
|
296
|
-
`wrote ${formatWrittenDesc(written, outputFile)} ${ms(elapsedMs)}`,
|
|
297
|
-
);
|
|
298
|
-
if (result.wasmOptInfo && result.wasmOptInfo.originalSize > 0) {
|
|
299
|
-
const origKb = (result.wasmOptInfo.originalSize / 1024).toFixed(1);
|
|
300
|
-
const optKb = (result.wasmOptInfo.optimizedSize / 1024).toFixed(1);
|
|
301
|
-
log.info(
|
|
302
|
-
`wasm-opt: app.wasm ${origKb} kB → ${optKb} kB (-${result.wasmOptInfo.percentSaved}%) [${result.wasmOptInfo.engine}]`,
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
} catch (e) {
|
|
306
|
-
log.fail(`cannot write '${outputFile}': ${e.message}`);
|
|
307
|
-
process.exit(1);
|
|
308
|
-
}
|
|
309
|
-
} else {
|
|
310
|
-
console.log(js);
|
|
311
|
-
if (result.wasm) {
|
|
312
|
-
log.warn(
|
|
313
|
-
"wasm packages were linked but app.wasm needs -o <file> to be written",
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (copyAssetsFlag) copyProjectAssets(true);
|
|
319
|
-
|
|
320
|
-
process.exit(0);
|
|
185
|
+
process.exit(runSingleShot(inputPath, isDir, flags));
|
|
321
186
|
}
|
|
322
187
|
|
|
323
188
|
// ── Watch mode ───────────────────────────────────────────────
|
|
@@ -342,26 +207,7 @@ try {
|
|
|
342
207
|
mangle: mangleOutput,
|
|
343
208
|
copyAssets: copyAssetsFlag,
|
|
344
209
|
forceTarget,
|
|
345
|
-
|
|
346
|
-
const changeNote = changedFile ? ` — ${changedFile} changed` : "";
|
|
347
|
-
const timing = colors.dim(`(${elapsedMs}ms${changeNote})`);
|
|
348
|
-
if (outputFile) {
|
|
349
|
-
console.error(
|
|
350
|
-
`${prefix()} ${colors.green("OK")} — wrote ${formatWrittenDesc(written, outputFile)} ${timing}`,
|
|
351
|
-
);
|
|
352
|
-
} else {
|
|
353
|
-
// Clear screen then print
|
|
354
|
-
process.stdout.write("\x1Bc");
|
|
355
|
-
console.log(js);
|
|
356
|
-
console.error(`${prefix()} ${colors.green("OK")} ${timing}`);
|
|
357
|
-
}
|
|
358
|
-
if (copyAssetsFlag) copyProjectAssets(false);
|
|
359
|
-
},
|
|
360
|
-
onError: (e) => {
|
|
361
|
-
console.error(`${prefix()} ${colors.bold(colors.red("ERROR"))}`);
|
|
362
|
-
for (const line of formatDiagnostic(e.message).split("\n"))
|
|
363
|
-
console.error(` ${line}`);
|
|
364
|
-
},
|
|
210
|
+
...devReporters({ outputFile, copyAssets: copyAssetsFlag }),
|
|
365
211
|
});
|
|
366
212
|
} catch (e) {
|
|
367
213
|
log.fail(e.message);
|
|
@@ -52,8 +52,8 @@ const KEYWORDS = new Set([
|
|
|
52
52
|
"case",
|
|
53
53
|
"default",
|
|
54
54
|
"map",
|
|
55
|
-
"new",
|
|
56
55
|
"fallthrough",
|
|
56
|
+
"goto",
|
|
57
57
|
"defer",
|
|
58
58
|
"async",
|
|
59
59
|
"await",
|
|
@@ -86,6 +86,11 @@ const ASSIGN_OP = {
|
|
|
86
86
|
":": [T.DEFINE, ":=", T.COLON, ":"],
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
+
const UNICODE_LETTER = /^\p{L}$/u;
|
|
90
|
+
const UNICODE_DIGIT = /^\p{Nd}$/u;
|
|
91
|
+
const isUnicodeLetter = (cp) => UNICODE_LETTER.test(String.fromCodePoint(cp));
|
|
92
|
+
const isUnicodeDigit = (cp) => UNICODE_DIGIT.test(String.fromCodePoint(cp));
|
|
93
|
+
|
|
89
94
|
export class LexError extends Error {
|
|
90
95
|
constructor(msg, line, col, filename, sourceCode) {
|
|
91
96
|
const loc = filename ? `${filename}:${line}:${col}` : `${line}:${col}`;
|
|
@@ -103,6 +108,17 @@ export class LexError extends Error {
|
|
|
103
108
|
}
|
|
104
109
|
}
|
|
105
110
|
|
|
111
|
+
const SIMPLE_ESCAPES = {
|
|
112
|
+
a: 7,
|
|
113
|
+
b: 8,
|
|
114
|
+
f: 12,
|
|
115
|
+
n: 10,
|
|
116
|
+
r: 13,
|
|
117
|
+
t: 9,
|
|
118
|
+
v: 11,
|
|
119
|
+
"\\": 92,
|
|
120
|
+
};
|
|
121
|
+
|
|
106
122
|
export class Lexer {
|
|
107
123
|
constructor(source, filename = null) {
|
|
108
124
|
this.src = source;
|
|
@@ -113,6 +129,8 @@ export class Lexer {
|
|
|
113
129
|
this.filename = filename;
|
|
114
130
|
this.target = null;
|
|
115
131
|
this.targetDirective = null;
|
|
132
|
+
this.intrinsics = new Map(); // line -> name of a //gofront:intrinsic
|
|
133
|
+
this.embedDirectives = []; // {line, col} of each //go:embed line comment
|
|
116
134
|
}
|
|
117
135
|
|
|
118
136
|
// ── Primitives ──────────────────────────────────────────────
|
|
@@ -158,6 +176,15 @@ export class Lexer {
|
|
|
158
176
|
while (this.pos < this.src.length && this.peek() !== "\n")
|
|
159
177
|
this.advance();
|
|
160
178
|
const comment = this.src.slice(start, this.pos);
|
|
179
|
+
if (
|
|
180
|
+
/^\/\/go:embed(?:\s|$)/.test(comment) &&
|
|
181
|
+
/^[ \t]*$/.test(
|
|
182
|
+
this.src.slice(this.src.lastIndexOf("\n", start - 1) + 1, start),
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
this.embedDirectives.push({ line, col });
|
|
186
|
+
const intrinsic = comment.match(/^\/\/gofront:intrinsic\s+(\S+)/);
|
|
187
|
+
if (intrinsic) this.intrinsics.set(line, intrinsic[1]);
|
|
161
188
|
const match = comment.match(/^\/\/gofront:target\s+(\S+)/);
|
|
162
189
|
if (match) {
|
|
163
190
|
this.target = match[1];
|
|
@@ -222,29 +249,7 @@ export class Lexer {
|
|
|
222
249
|
if (parts === null) parts = [];
|
|
223
250
|
if (this.pos > start) parts.push(this.src.slice(start, this.pos));
|
|
224
251
|
this.advance(); // skip \
|
|
225
|
-
|
|
226
|
-
switch (esc) {
|
|
227
|
-
case "n":
|
|
228
|
-
parts.push("\n");
|
|
229
|
-
break;
|
|
230
|
-
case "t":
|
|
231
|
-
parts.push("\t");
|
|
232
|
-
break;
|
|
233
|
-
case "r":
|
|
234
|
-
parts.push("\r");
|
|
235
|
-
break;
|
|
236
|
-
case '"':
|
|
237
|
-
parts.push('"');
|
|
238
|
-
break;
|
|
239
|
-
case "\\":
|
|
240
|
-
parts.push("\\");
|
|
241
|
-
break;
|
|
242
|
-
case "0":
|
|
243
|
-
parts.push("\0");
|
|
244
|
-
break;
|
|
245
|
-
default:
|
|
246
|
-
parts.push(`\\${esc}`);
|
|
247
|
-
}
|
|
252
|
+
parts.push(String.fromCodePoint(this.readEscape('"')));
|
|
248
253
|
start = this.pos;
|
|
249
254
|
} else {
|
|
250
255
|
this.advance();
|
|
@@ -253,6 +258,46 @@ export class Lexer {
|
|
|
253
258
|
throw new LexError("Unterminated string", l, c, this.filename, this.src);
|
|
254
259
|
}
|
|
255
260
|
|
|
261
|
+
// Reads the escape after a consumed backslash and returns its code point.
|
|
262
|
+
// `quote` is the enclosing delimiter: `\'` is valid only in runes and
|
|
263
|
+
// `\"` only in strings. `\xNN` and octal escapes give U+0000-U+00FF (see
|
|
264
|
+
// plan §1.1: strings are JS strings, so \x80-\xff are code points, not raw bytes).
|
|
265
|
+
readEscape(quote) {
|
|
266
|
+
const l = this.line,
|
|
267
|
+
c = this.col;
|
|
268
|
+
const fail = (msg) => {
|
|
269
|
+
throw new LexError(msg, l, c, this.filename, this.src);
|
|
270
|
+
};
|
|
271
|
+
const esc = this.advance();
|
|
272
|
+
const simple = SIMPLE_ESCAPES[esc];
|
|
273
|
+
if (simple !== undefined) return simple;
|
|
274
|
+
if (esc === quote) return esc.charCodeAt(0);
|
|
275
|
+
if (esc === "x") return this.readEscapeDigits(2, 16, fail);
|
|
276
|
+
if (esc === "u" || esc === "U") {
|
|
277
|
+
const v = this.readEscapeDigits(esc === "u" ? 4 : 8, 16, fail);
|
|
278
|
+
if (v > 0x10ffff || (v >= 0xd800 && v < 0xe000))
|
|
279
|
+
fail("escape is invalid Unicode code point");
|
|
280
|
+
return v;
|
|
281
|
+
}
|
|
282
|
+
if (esc >= "0" && esc <= "7") {
|
|
283
|
+
const v = Number(esc) * 64 + this.readEscapeDigits(2, 8, fail);
|
|
284
|
+
if (v > 255) fail("octal escape value exceeds 255");
|
|
285
|
+
return v;
|
|
286
|
+
}
|
|
287
|
+
return fail(`unknown escape sequence: \\${esc}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
readEscapeDigits(n, radix, fail) {
|
|
291
|
+
let v = 0;
|
|
292
|
+
for (let i = 0; i < n; i++) {
|
|
293
|
+
const d = Number.parseInt(this.src[this.pos] ?? "", radix);
|
|
294
|
+
if (Number.isNaN(d)) fail("invalid character in escape sequence");
|
|
295
|
+
v = v * radix + d;
|
|
296
|
+
this.advance();
|
|
297
|
+
}
|
|
298
|
+
return v;
|
|
299
|
+
}
|
|
300
|
+
|
|
256
301
|
readRawString() {
|
|
257
302
|
const l = this.line,
|
|
258
303
|
c = this.col;
|
|
@@ -279,35 +324,7 @@ export class Lexer {
|
|
|
279
324
|
let code;
|
|
280
325
|
if (this.peek() === "\\") {
|
|
281
326
|
this.advance();
|
|
282
|
-
|
|
283
|
-
switch (esc) {
|
|
284
|
-
case "n":
|
|
285
|
-
code = 10;
|
|
286
|
-
break;
|
|
287
|
-
case "t":
|
|
288
|
-
code = 9;
|
|
289
|
-
break;
|
|
290
|
-
case "r":
|
|
291
|
-
code = 13;
|
|
292
|
-
break;
|
|
293
|
-
case "'":
|
|
294
|
-
code = 39;
|
|
295
|
-
break;
|
|
296
|
-
case "\\":
|
|
297
|
-
code = 92;
|
|
298
|
-
break;
|
|
299
|
-
case "0":
|
|
300
|
-
code = 0;
|
|
301
|
-
break;
|
|
302
|
-
default:
|
|
303
|
-
throw new LexError(
|
|
304
|
-
`Unknown escape in rune literal: \\${esc}`,
|
|
305
|
-
l,
|
|
306
|
-
c,
|
|
307
|
-
this.filename,
|
|
308
|
-
this.src,
|
|
309
|
-
);
|
|
310
|
-
}
|
|
327
|
+
code = this.readEscape("'");
|
|
311
328
|
} else if (this.peek() !== "'") {
|
|
312
329
|
code = this.src.codePointAt(this.pos);
|
|
313
330
|
this.advance();
|
|
@@ -392,6 +409,20 @@ export class Lexer {
|
|
|
392
409
|
return { n, isFloat: false };
|
|
393
410
|
}
|
|
394
411
|
|
|
412
|
+
// The leading "." is already consumed; reads digits and an optional exponent.
|
|
413
|
+
_readLeadingDotFloat() {
|
|
414
|
+
let n = "0.";
|
|
415
|
+
while (this.pos < this.src.length) {
|
|
416
|
+
const c = this.src.charCodeAt(this.pos);
|
|
417
|
+
if (c >= 48 && c <= 57) n += this.advance();
|
|
418
|
+
else if (c === 95) this.advance();
|
|
419
|
+
else break;
|
|
420
|
+
}
|
|
421
|
+
const c = this.src.charCodeAt(this.pos);
|
|
422
|
+
if (c === 101 || c === 69) n = this._readDecimalExponent(n);
|
|
423
|
+
return { n };
|
|
424
|
+
}
|
|
425
|
+
|
|
395
426
|
_readDecimalExponent(n) {
|
|
396
427
|
n += this.advance(); // consume e/E
|
|
397
428
|
if (this.peek() === "+" || this.peek() === "-") n += this.advance();
|
|
@@ -450,6 +481,13 @@ export class Lexer {
|
|
|
450
481
|
return { n, isFloat };
|
|
451
482
|
}
|
|
452
483
|
|
|
484
|
+
_startsUnicodeIdent() {
|
|
485
|
+
return (
|
|
486
|
+
this.peek().charCodeAt(0) > 127 &&
|
|
487
|
+
isUnicodeLetter(this.src.codePointAt(this.pos))
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
453
491
|
readIdent() {
|
|
454
492
|
const start = this.pos;
|
|
455
493
|
while (this.pos < this.src.length) {
|
|
@@ -461,6 +499,10 @@ export class Lexer {
|
|
|
461
499
|
code === 95
|
|
462
500
|
) {
|
|
463
501
|
this.pos++;
|
|
502
|
+
} else if (code > 127) {
|
|
503
|
+
const cp = this.src.codePointAt(this.pos);
|
|
504
|
+
if (!isUnicodeLetter(cp) && !isUnicodeDigit(cp)) break;
|
|
505
|
+
this.pos += cp > 0xffff ? 2 : 1;
|
|
464
506
|
} else {
|
|
465
507
|
break;
|
|
466
508
|
}
|
|
@@ -526,7 +568,7 @@ export class Lexer {
|
|
|
526
568
|
}
|
|
527
569
|
|
|
528
570
|
// Identifiers & keywords
|
|
529
|
-
if (/[a-zA-Z_]/.test(ch)) {
|
|
571
|
+
if (/[a-zA-Z_]/.test(ch) || this._startsUnicodeIdent()) {
|
|
530
572
|
const id = this.readIdent();
|
|
531
573
|
this.push(KEYWORDS.has(id) ? id : T.IDENT, id, l, c);
|
|
532
574
|
continue;
|
|
@@ -541,6 +583,8 @@ export class Lexer {
|
|
|
541
583
|
this.push(T.EOF, "", this.line, this.col);
|
|
542
584
|
this.tokens.target = this.target;
|
|
543
585
|
this.tokens.targetDirective = this.targetDirective;
|
|
586
|
+
this.tokens.intrinsics = this.intrinsics;
|
|
587
|
+
this.tokens.embedDirectives = this.embedDirectives;
|
|
544
588
|
return this.tokens;
|
|
545
589
|
}
|
|
546
590
|
_readPlusOp(l, c) {
|
|
@@ -614,7 +658,10 @@ export class Lexer {
|
|
|
614
658
|
else this.push(T.PIPE, "|", l, c);
|
|
615
659
|
break;
|
|
616
660
|
case ".":
|
|
617
|
-
if (
|
|
661
|
+
if (/[0-9]/.test(this.peek() ?? "")) {
|
|
662
|
+
const { n } = this._readLeadingDotFloat();
|
|
663
|
+
this.push(T.FLOAT, n, l, c);
|
|
664
|
+
} else if (this.peek() === "." && this.peek(1) === ".") {
|
|
618
665
|
this.advance();
|
|
619
666
|
this.advance();
|
|
620
667
|
this.push(T.ELLIPSIS, "...", l, c);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Source-map merging for the bundle: chunk concatenation with line-offset
|
|
2
|
+
// mappings, the inline JS source map and the function-level wasm map.
|
|
3
|
+
|
|
4
|
+
import { relative, resolve } from "node:path";
|
|
5
|
+
import { buildSourceMap, buildWasmSourceMap } from "../backend/js/index.js";
|
|
6
|
+
|
|
7
|
+
function normalizeChunk(c) {
|
|
8
|
+
if (typeof c === "string") {
|
|
9
|
+
return { js: c, sourceFiles: [], sourcesContent: [], mappings: [] };
|
|
10
|
+
}
|
|
11
|
+
return c;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function appendChunkMappings(dest, mappings, localToUnifiedIdx, lineOffset) {
|
|
15
|
+
if (!mappings) return;
|
|
16
|
+
for (const m of mappings) {
|
|
17
|
+
dest.push({
|
|
18
|
+
genLine: m.genLine + lineOffset,
|
|
19
|
+
srcLine: m.srcLine,
|
|
20
|
+
srcFileIdx: localToUnifiedIdx[m.srcFileIdx ?? 0] ?? 0,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function mergeCompilationChunks(chunks) {
|
|
26
|
+
const activeChunks = chunks
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.map(normalizeChunk)
|
|
29
|
+
.filter((c) => c.js.length > 0);
|
|
30
|
+
|
|
31
|
+
if (activeChunks.length === 0) {
|
|
32
|
+
return { js: "", sourceFiles: [], sourcesContent: [], mappings: [] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const unifiedSources = [];
|
|
36
|
+
const unifiedSourcesContent = [];
|
|
37
|
+
const fileIdxMap = new Map();
|
|
38
|
+
|
|
39
|
+
function getOrAddFile(filePath, content) {
|
|
40
|
+
const key = resolve(filePath);
|
|
41
|
+
let idx = fileIdxMap.get(key);
|
|
42
|
+
if (idx === undefined) {
|
|
43
|
+
idx = unifiedSources.length;
|
|
44
|
+
fileIdxMap.set(key, idx);
|
|
45
|
+
unifiedSources.push(key);
|
|
46
|
+
unifiedSourcesContent.push(content);
|
|
47
|
+
}
|
|
48
|
+
return idx;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const combinedJsParts = [];
|
|
52
|
+
const mergedMappings = [];
|
|
53
|
+
let currentLineOffset = 0;
|
|
54
|
+
|
|
55
|
+
for (const chunk of activeChunks) {
|
|
56
|
+
combinedJsParts.push(chunk.js);
|
|
57
|
+
const localToUnifiedIdx = (chunk.sourceFiles ?? []).map((file, i) =>
|
|
58
|
+
getOrAddFile(file, chunk.sourcesContent?.[i] ?? ""),
|
|
59
|
+
);
|
|
60
|
+
appendChunkMappings(
|
|
61
|
+
mergedMappings,
|
|
62
|
+
chunk.mappings,
|
|
63
|
+
localToUnifiedIdx,
|
|
64
|
+
currentLineOffset,
|
|
65
|
+
);
|
|
66
|
+
currentLineOffset += chunk.js.split("\n").length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
js: combinedJsParts.join("\n"),
|
|
71
|
+
sourceFiles: unifiedSources,
|
|
72
|
+
sourcesContent: unifiedSourcesContent,
|
|
73
|
+
mappings: mergedMappings,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function attachSourceMap(merged, outputDir) {
|
|
78
|
+
const relativeSources = merged.sourceFiles.map((f) => relative(outputDir, f));
|
|
79
|
+
const mapJson = buildSourceMap(
|
|
80
|
+
relativeSources,
|
|
81
|
+
merged.mappings,
|
|
82
|
+
merged.sourcesContent,
|
|
83
|
+
);
|
|
84
|
+
const b64 = Buffer.from(mapJson).toString("base64");
|
|
85
|
+
merged.js += `\n//# sourceMappingURL=data:application/json;base64,${b64}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const WASM_MAP_URL = "app.wasm.map";
|
|
89
|
+
|
|
90
|
+
// Function-level `app.wasm.map` (sources relative to the output dir).
|
|
91
|
+
export function buildWasmMap(info, outputDir) {
|
|
92
|
+
if (!info || info.mappings.length === 0) return null;
|
|
93
|
+
return buildWasmSourceMap(
|
|
94
|
+
info.sources.map((f) => relative(outputDir, f)),
|
|
95
|
+
info.mappings,
|
|
96
|
+
);
|
|
97
|
+
}
|