gofront 1.8.0 → 1.9.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 +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +203 -573
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
package/src/compiler.js
CHANGED
|
@@ -32,17 +32,19 @@ import {
|
|
|
32
32
|
WASM_MAP_URL,
|
|
33
33
|
} from "./link/sourcemap.js";
|
|
34
34
|
import {
|
|
35
|
-
|
|
35
|
+
checkGeneratedGo,
|
|
36
36
|
finishStdlibJs,
|
|
37
37
|
importStdlibPackages,
|
|
38
|
+
mangleCollidingTypes,
|
|
39
|
+
renameLocalTypes,
|
|
40
|
+
withOriginalTypeNames,
|
|
38
41
|
} from "./link/stdlib.js";
|
|
39
42
|
import {
|
|
40
43
|
linkIntoBundle,
|
|
41
44
|
registerPackageUnit,
|
|
42
|
-
registerWasmUnit,
|
|
43
45
|
wasmMarker,
|
|
44
46
|
} from "./link/wasm.js";
|
|
45
|
-
import { lower } from "./lower/index.js";
|
|
47
|
+
import { lower, takesElementAddress } from "./lower/index.js";
|
|
46
48
|
import { Parser } from "./parser/index.js";
|
|
47
49
|
import { TemplParser } from "./parser/templ.js";
|
|
48
50
|
import {
|
|
@@ -60,10 +62,21 @@ import { WASM_SUPPORTED_STDLIB } from "./typechecker/types.js";
|
|
|
60
62
|
// Map<filePath, { mtime: number, ast: object }>
|
|
61
63
|
const _parseCache = new Map();
|
|
62
64
|
|
|
65
|
+
// A cached AST keeps everything the build attached to it (checked types,
|
|
66
|
+
// lowered stdlib programs), so a build of a Go-source stdlib importer pins
|
|
67
|
+
// megabytes. The cache is therefore bounded; the least recently used file goes
|
|
68
|
+
// first.
|
|
69
|
+
const PARSE_CACHE_LIMIT = 16;
|
|
70
|
+
|
|
63
71
|
export function clearParseCache() {
|
|
64
72
|
_parseCache.clear();
|
|
65
73
|
}
|
|
66
74
|
|
|
75
|
+
// Drops one file, e.g. a throwaway source that was deleted after its build.
|
|
76
|
+
export function evictParseCache(filePath) {
|
|
77
|
+
_parseCache.delete(resolve(filePath));
|
|
78
|
+
}
|
|
79
|
+
|
|
67
80
|
export function parseCacheSize() {
|
|
68
81
|
return _parseCache.size;
|
|
69
82
|
}
|
|
@@ -71,9 +84,14 @@ export function parseCacheSize() {
|
|
|
71
84
|
// ── Helpers ──────────────────────────────────────────────────
|
|
72
85
|
|
|
73
86
|
function parseGoFrontFile(filePath) {
|
|
87
|
+
const key = resolve(filePath);
|
|
74
88
|
const mtime = statSync(filePath).mtimeMs;
|
|
75
|
-
const cached = _parseCache.get(
|
|
76
|
-
if (cached && cached.mtime === mtime)
|
|
89
|
+
const cached = _parseCache.get(key);
|
|
90
|
+
if (cached && cached.mtime === mtime) {
|
|
91
|
+
_parseCache.delete(key);
|
|
92
|
+
_parseCache.set(key, cached);
|
|
93
|
+
return cached.ast;
|
|
94
|
+
}
|
|
77
95
|
|
|
78
96
|
const source = readFileSync(filePath, "utf8");
|
|
79
97
|
const filename = basename(filePath);
|
|
@@ -88,7 +106,9 @@ function parseGoFrontFile(filePath) {
|
|
|
88
106
|
ast._path = resolve(filePath);
|
|
89
107
|
// compileFiles overwrites `target` with the resolved one; keep the directive.
|
|
90
108
|
ast._declaredTarget = ast.target;
|
|
91
|
-
_parseCache.set(
|
|
109
|
+
_parseCache.set(key, { mtime, ast });
|
|
110
|
+
if (_parseCache.size > PARSE_CACHE_LIMIT)
|
|
111
|
+
_parseCache.delete(_parseCache.keys().next().value);
|
|
92
112
|
return ast;
|
|
93
113
|
}
|
|
94
114
|
|
|
@@ -107,7 +127,7 @@ export function gwFilesIn(dir, { includeTests = false } = {}) {
|
|
|
107
127
|
// wasm-opt when requested and produce the wasm source map.
|
|
108
128
|
function finishWasm(options, merged, pkgTarget, outputDir) {
|
|
109
129
|
const wantMap = Boolean(options.sourceMap && !options.isDependency);
|
|
110
|
-
let { wasm, wat, sourceMapInfo } = linkIntoBundle(
|
|
130
|
+
let { wasm, wat, sourceMapInfo, stringTable } = linkIntoBundle(
|
|
111
131
|
options,
|
|
112
132
|
merged,
|
|
113
133
|
pkgTarget,
|
|
@@ -132,7 +152,7 @@ function finishWasm(options, merged, pkgTarget, outputDir) {
|
|
|
132
152
|
wasmOptInfo = opt;
|
|
133
153
|
if (opt.sourceMap) wasmSourceMap = opt.sourceMap;
|
|
134
154
|
}
|
|
135
|
-
return { wasm, wat, wasmOptInfo, wasmSourceMap };
|
|
155
|
+
return { wasm, wat, wasmOptInfo, wasmSourceMap, stringTable };
|
|
136
156
|
}
|
|
137
157
|
|
|
138
158
|
// Prints non-fatal diagnostics (e.g. JS Number precision of a constant).
|
|
@@ -184,12 +204,6 @@ function resolveImports(
|
|
|
184
204
|
"js: imports are not allowed in wasm packages",
|
|
185
205
|
impNode,
|
|
186
206
|
);
|
|
187
|
-
} else if (path === "gom") {
|
|
188
|
-
checker.recordBlocker("gom usage", "gom");
|
|
189
|
-
checker.err(
|
|
190
|
-
"package 'gom' is not available in wasm packages",
|
|
191
|
-
impNode,
|
|
192
|
-
);
|
|
193
207
|
} else if (path === "gofront/shared" && pkgTarget === "both") {
|
|
194
208
|
checker.err(
|
|
195
209
|
"'gofront/shared' requires a wasm-only package (//gofront:target wasm): a both package has no linear memory on the JS side",
|
|
@@ -287,6 +301,9 @@ function resolveImports(
|
|
|
287
301
|
isDependency: true,
|
|
288
302
|
});
|
|
289
303
|
if (!alreadyBundled) preambles.push(dep);
|
|
304
|
+
// A dependency that hands out `&xs[i]` makes whole-element
|
|
305
|
+
// stores in this package write in place too.
|
|
306
|
+
if (dep.elemAddrTaken) checker.depElemAddrTaken = true;
|
|
290
307
|
|
|
291
308
|
// Target import rules for local packages
|
|
292
309
|
const impNode = {
|
|
@@ -374,120 +391,29 @@ export function resolvePackageTarget(declared, pkgDir, options = {}) {
|
|
|
374
391
|
}
|
|
375
392
|
|
|
376
393
|
export function compileSingleFile(inputPath, options = {}) {
|
|
377
|
-
const {
|
|
378
|
-
sourceMap = false,
|
|
379
|
-
outputFile = null,
|
|
380
|
-
dumpTokens = false,
|
|
381
|
-
dumpAst = false,
|
|
382
|
-
} = options;
|
|
383
|
-
options = {
|
|
384
|
-
...options,
|
|
385
|
-
wasmUnits: options.wasmUnits ?? [],
|
|
386
|
-
stdlibCache: options.stdlibCache ?? new Map(),
|
|
387
|
-
jsRootPrograms: options.jsRootPrograms ?? [],
|
|
388
|
-
bundledDirs: options.bundledDirs ?? new Set(),
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
let source;
|
|
392
|
-
try {
|
|
393
|
-
source = readFileSync(inputPath, "utf8");
|
|
394
|
-
} catch (e) {
|
|
395
|
-
throw new Error(`cannot read '${inputPath}': ${e.message}`);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const tokens = new Lexer(source, basename(inputPath)).tokenize();
|
|
399
|
-
if (dumpTokens) return { tokens };
|
|
400
|
-
|
|
401
|
-
const ast = new Parser(tokens, basename(inputPath), source).parse();
|
|
402
|
-
ast._source = source;
|
|
403
|
-
ast._path = resolve(inputPath);
|
|
404
|
-
if (dumpAst) return { ast };
|
|
405
|
-
|
|
406
|
-
const outputDir =
|
|
407
|
-
options.outputDir ??
|
|
408
|
-
(outputFile ? dirname(resolve(outputFile)) : resolve("."));
|
|
409
|
-
|
|
410
|
-
const checker = new TypeChecker();
|
|
411
|
-
const jsImports = new Map();
|
|
412
|
-
const bundledPackages = new Set();
|
|
413
|
-
const preambles = [];
|
|
414
|
-
|
|
415
|
-
const pkgTarget = resolvePackageTarget(
|
|
416
|
-
ast.target ?? options.target ?? "js",
|
|
417
|
-
dirname(resolve(inputPath)),
|
|
418
|
-
options,
|
|
419
|
-
);
|
|
420
|
-
ast.target = pkgTarget;
|
|
421
|
-
checker.target = pkgTarget;
|
|
422
|
-
checker.pkgName = ast.pkg?.name ?? "main";
|
|
423
|
-
|
|
424
|
-
resolveImports(
|
|
425
|
-
[ast],
|
|
426
|
-
inputPath,
|
|
427
|
-
checker,
|
|
428
|
-
jsImports,
|
|
429
|
-
bundledPackages,
|
|
430
|
-
preambles,
|
|
431
|
-
options.bundledDirs ?? new Set(),
|
|
432
|
-
{ ...options, outputDir },
|
|
433
|
-
);
|
|
434
|
-
|
|
435
|
-
const errors = checker.check(ast);
|
|
436
|
-
checker.reportUnusedImports();
|
|
437
|
-
if (errors.length > 0) {
|
|
438
|
-
throw new Error(errors.map((e) => e.message).join("\n"));
|
|
439
|
-
}
|
|
440
|
-
for (const w of checker.warnings) log.warn(w);
|
|
394
|
+
const { outputFile = null, dumpTokens = false, dumpAst = false } = options;
|
|
441
395
|
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
396
|
+
if (dumpTokens || dumpAst) {
|
|
397
|
+
let source;
|
|
398
|
+
try {
|
|
399
|
+
source = readFileSync(inputPath, "utf8");
|
|
400
|
+
} catch (e) {
|
|
401
|
+
throw new Error(`cannot read '${inputPath}': ${e.message}`);
|
|
402
|
+
}
|
|
403
|
+
const tokens = new Lexer(source, basename(inputPath)).tokenize();
|
|
404
|
+
if (dumpTokens) return { tokens };
|
|
405
|
+
const ast = new Parser(tokens, basename(inputPath), source).parse();
|
|
406
|
+
ast._source = source;
|
|
407
|
+
ast._path = resolve(inputPath);
|
|
408
|
+
return { ast };
|
|
451
409
|
}
|
|
452
410
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
411
|
+
return compileFiles([resolve(inputPath)], {
|
|
412
|
+
...options,
|
|
413
|
+
outputDir:
|
|
414
|
+
options.outputDir ??
|
|
415
|
+
(outputFile ? dirname(resolve(outputFile)) : resolve(".")),
|
|
457
416
|
});
|
|
458
|
-
const mainJs =
|
|
459
|
-
pkgTarget === "wasm" ? wasmMarker(checker.pkgName) : cg.generate(ast);
|
|
460
|
-
logWarnings(cg.warnings);
|
|
461
|
-
|
|
462
|
-
const mainChunk = {
|
|
463
|
-
js: mainJs,
|
|
464
|
-
sourceFiles: [resolve(inputPath)],
|
|
465
|
-
sourcesContent: [source],
|
|
466
|
-
mappings: pkgTarget === "wasm" ? [] : cg.getMappings(),
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
const merged = mergeCompilationChunks([...preambles, mainChunk]);
|
|
470
|
-
finishStdlibJs(merged, options, [ast], pkgTarget);
|
|
471
|
-
const { wasm, wat, wasmOptInfo, wasmSourceMap } = finishWasm(
|
|
472
|
-
{ ...options, sourceMap },
|
|
473
|
-
merged,
|
|
474
|
-
pkgTarget,
|
|
475
|
-
outputDir,
|
|
476
|
-
);
|
|
477
|
-
|
|
478
|
-
if (sourceMap && !options.isDependency) {
|
|
479
|
-
attachSourceMap(merged, outputDir);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
return {
|
|
483
|
-
js: merged.js,
|
|
484
|
-
css: cg.getCss(),
|
|
485
|
-
target: pkgTarget,
|
|
486
|
-
wasm,
|
|
487
|
-
wat,
|
|
488
|
-
wasmOptInfo,
|
|
489
|
-
wasmSourceMap,
|
|
490
|
-
};
|
|
491
417
|
}
|
|
492
418
|
|
|
493
419
|
export function compileDir(dir, options = {}) {
|
|
@@ -518,6 +444,8 @@ export function compileFiles(files, options = {}) {
|
|
|
518
444
|
jsRootPrograms: options.jsRootPrograms ?? [],
|
|
519
445
|
rootDir: options.rootDir ?? fromDir,
|
|
520
446
|
bundledDirs: options.bundledDirs ?? new Set(),
|
|
447
|
+
typeOwners: options.typeOwners ?? new Map(),
|
|
448
|
+
jsonBuild: options.jsonBuild ?? { js: false, wasm: false },
|
|
521
449
|
};
|
|
522
450
|
const outputDir =
|
|
523
451
|
options.outputDir ??
|
|
@@ -545,6 +473,15 @@ export function compileFiles(files, options = {}) {
|
|
|
545
473
|
);
|
|
546
474
|
const pkgName = pkgNames[0];
|
|
547
475
|
|
|
476
|
+
// Local packages share one flat JS module: a type name another package
|
|
477
|
+
// already declared is renamed here (`Pt` -> `Pt$$b`).
|
|
478
|
+
const renamedTypes = mangleLocalTypes(
|
|
479
|
+
programs,
|
|
480
|
+
resolve(fromDir),
|
|
481
|
+
pkgName,
|
|
482
|
+
options.typeOwners,
|
|
483
|
+
);
|
|
484
|
+
|
|
548
485
|
// Validate target directives across files in this package
|
|
549
486
|
const declaredTargets = new Map();
|
|
550
487
|
for (const p of programs) {
|
|
@@ -571,6 +508,11 @@ export function compileFiles(files, options = {}) {
|
|
|
571
508
|
);
|
|
572
509
|
for (const p of programs) p.target = pkgTarget;
|
|
573
510
|
|
|
511
|
+
// The test runner's driver is a source that exists only in memory: it is
|
|
512
|
+
// parsed and checked with the package but never written next to it.
|
|
513
|
+
const driver = testDriverProgram(options, programs, pkgTarget, fromDir);
|
|
514
|
+
if (driver) programs.push(driver);
|
|
515
|
+
|
|
574
516
|
// ── 2. Resolve imports ────────────────────────────────────────
|
|
575
517
|
const checker = new TypeChecker();
|
|
576
518
|
checker.target = pkgTarget;
|
|
@@ -593,7 +535,7 @@ export function compileFiles(files, options = {}) {
|
|
|
593
535
|
|
|
594
536
|
// ── 3. Type-check ─────────────────────────────────────────────
|
|
595
537
|
const errors = checker.checkAll(programs);
|
|
596
|
-
const codecs =
|
|
538
|
+
const codecs = checkGeneratedGo(
|
|
597
539
|
programs,
|
|
598
540
|
checker,
|
|
599
541
|
preambles,
|
|
@@ -621,11 +563,12 @@ export function compileFiles(files, options = {}) {
|
|
|
621
563
|
const codegen = new CodeGen(checker, jsImports, bundledPackages, {
|
|
622
564
|
target: pkgTarget,
|
|
623
565
|
strict: isStrict,
|
|
566
|
+
natives: options.natives,
|
|
624
567
|
});
|
|
625
568
|
const mainJs =
|
|
626
569
|
emitTarget === "wasm"
|
|
627
570
|
? wasmMarker(pkgName)
|
|
628
|
-
: codegen.generateAll(programs, {
|
|
571
|
+
: codegen.generateAll([...programs, ...codecs], {
|
|
629
572
|
isTest: options.isTest ?? false,
|
|
630
573
|
});
|
|
631
574
|
logWarnings(codegen.warnings);
|
|
@@ -638,8 +581,8 @@ export function compileFiles(files, options = {}) {
|
|
|
638
581
|
};
|
|
639
582
|
|
|
640
583
|
const merged = mergeCompilationChunks([...preambles, mainChunk]);
|
|
641
|
-
finishStdlibJs(merged, options, programs, emitTarget);
|
|
642
|
-
const { wasm, wat, wasmOptInfo, wasmSourceMap } = finishWasm(
|
|
584
|
+
finishStdlibJs(merged, options, [...programs, ...codecs], emitTarget);
|
|
585
|
+
const { wasm, wat, wasmOptInfo, wasmSourceMap, stringTable } = finishWasm(
|
|
643
586
|
options,
|
|
644
587
|
merged,
|
|
645
588
|
pkgTarget,
|
|
@@ -658,15 +601,22 @@ export function compileFiles(files, options = {}) {
|
|
|
658
601
|
return {
|
|
659
602
|
pkgName,
|
|
660
603
|
target: pkgTarget,
|
|
604
|
+
elemAddrTaken:
|
|
605
|
+
checker.depElemAddrTaken === true ||
|
|
606
|
+
programs.some((p) => takesElementAddress(p.decls)),
|
|
661
607
|
js: merged.js,
|
|
662
608
|
css: allCss,
|
|
663
609
|
wasm,
|
|
664
610
|
wat,
|
|
665
611
|
wasmOptInfo,
|
|
666
612
|
wasmSourceMap,
|
|
667
|
-
|
|
613
|
+
stringTable,
|
|
614
|
+
programs: driver ? programs.filter((p) => p !== driver) : programs,
|
|
668
615
|
exportedSymbols: checker.getExportedSymbols(),
|
|
669
|
-
exportedTypes:
|
|
616
|
+
exportedTypes: withOriginalTypeNames(
|
|
617
|
+
checker.getExportedTypes(),
|
|
618
|
+
renamedTypes,
|
|
619
|
+
),
|
|
670
620
|
exportedConsts: checker.getExportedConsts(),
|
|
671
621
|
sourceFiles: merged.sourceFiles,
|
|
672
622
|
sourcesContent: merged.sourcesContent,
|
|
@@ -674,6 +624,34 @@ export function compileFiles(files, options = {}) {
|
|
|
674
624
|
};
|
|
675
625
|
}
|
|
676
626
|
|
|
627
|
+
// Parses the in-memory driver source `options.testDriver(programs, target)`
|
|
628
|
+
// returns (null: no driver) as a `_test.go` file of the package.
|
|
629
|
+
function testDriverProgram(options, programs, pkgTarget, fromDir) {
|
|
630
|
+
if (!options.testDriver || options.isDependency) return null;
|
|
631
|
+
const source = options.testDriver(programs, pkgTarget);
|
|
632
|
+
if (!source) return null;
|
|
633
|
+
const filename = "gofront_testmain_test.go";
|
|
634
|
+
const ast = new Parser(
|
|
635
|
+
new Lexer(source, filename).tokenize(),
|
|
636
|
+
filename,
|
|
637
|
+
source,
|
|
638
|
+
).parse();
|
|
639
|
+
ast._source = source;
|
|
640
|
+
ast._path = join(fromDir, filename);
|
|
641
|
+
ast._declaredTarget = ast.target;
|
|
642
|
+
ast.target = pkgTarget;
|
|
643
|
+
return ast;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// The renamed ASTs must not be reused from the parse cache by a later build.
|
|
647
|
+
function mangleLocalTypes(programs, pkgKey, pkgName, owners) {
|
|
648
|
+
const renamed = mangleCollidingTypes(programs, pkgKey, pkgName, owners);
|
|
649
|
+
const locals = renameLocalTypes(programs);
|
|
650
|
+
if (renamed.size > 0 || locals)
|
|
651
|
+
for (const p of programs) _parseCache.delete(p._path);
|
|
652
|
+
return renamed;
|
|
653
|
+
}
|
|
654
|
+
|
|
677
655
|
export function compileWasm(source, options = {}) {
|
|
678
656
|
const filename = options.filename ?? "main.go";
|
|
679
657
|
const tokens = new Lexer(source, filename).tokenize();
|
package/src/lexer/lexer.js
CHANGED
|
@@ -119,6 +119,31 @@ const SIMPLE_ESCAPES = {
|
|
|
119
119
|
"\\": 92,
|
|
120
120
|
};
|
|
121
121
|
|
|
122
|
+
// Decodes the raw bytes of consecutive \xNN / octal escapes in a string
|
|
123
|
+
// literal: valid UTF-8 sequences become their code point, every other byte
|
|
124
|
+
// 0x80-0xff becomes the lone surrogate U+DC00+byte so the runtime can turn it
|
|
125
|
+
// back into that byte when the string is converted to []byte.
|
|
126
|
+
function decodeRawBytes(bytes) {
|
|
127
|
+
const dec = new TextDecoder("utf-8", { fatal: true });
|
|
128
|
+
let out = "";
|
|
129
|
+
for (let i = 0; i < bytes.length; ) {
|
|
130
|
+
const b = bytes[i];
|
|
131
|
+
const n = b < 0x80 ? 1 : b >= 0xf0 ? 4 : b >= 0xe0 ? 3 : b >= 0xc0 ? 2 : 0;
|
|
132
|
+
if (n > 0 && i + n <= bytes.length) {
|
|
133
|
+
try {
|
|
134
|
+
out += dec.decode(Uint8Array.from(bytes.slice(i, i + n)));
|
|
135
|
+
i += n;
|
|
136
|
+
continue;
|
|
137
|
+
} catch {
|
|
138
|
+
// invalid sequence: fall through to the lone-byte case
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
out += String.fromCharCode(b < 0x80 ? b : 0xdc00 + b);
|
|
142
|
+
i++;
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
|
|
122
147
|
export class Lexer {
|
|
123
148
|
constructor(source, filename = null) {
|
|
124
149
|
this.src = source;
|
|
@@ -130,6 +155,7 @@ export class Lexer {
|
|
|
130
155
|
this.target = null;
|
|
131
156
|
this.targetDirective = null;
|
|
132
157
|
this.intrinsics = new Map(); // line -> name of a //gofront:intrinsic
|
|
158
|
+
this.natives = new Map(); // line -> name of a //gofront:native
|
|
133
159
|
this.embedDirectives = []; // {line, col} of each //go:embed line comment
|
|
134
160
|
}
|
|
135
161
|
|
|
@@ -185,6 +211,9 @@ export class Lexer {
|
|
|
185
211
|
this.embedDirectives.push({ line, col });
|
|
186
212
|
const intrinsic = comment.match(/^\/\/gofront:intrinsic\s+(\S+)/);
|
|
187
213
|
if (intrinsic) this.intrinsics.set(line, intrinsic[1]);
|
|
214
|
+
const native = comment.match(/^\/\/gofront:native\s+(\S+)/);
|
|
215
|
+
if (native) this.natives.set(line, native[1]);
|
|
216
|
+
if (!intrinsic && !native) this.forwardDirectives(line, start);
|
|
188
217
|
const match = comment.match(/^\/\/gofront:target\s+(\S+)/);
|
|
189
218
|
if (match) {
|
|
190
219
|
this.target = match[1];
|
|
@@ -249,7 +278,7 @@ export class Lexer {
|
|
|
249
278
|
if (parts === null) parts = [];
|
|
250
279
|
if (this.pos > start) parts.push(this.src.slice(start, this.pos));
|
|
251
280
|
this.advance(); // skip \
|
|
252
|
-
parts.push(
|
|
281
|
+
parts.push(this.readStringEscape());
|
|
253
282
|
start = this.pos;
|
|
254
283
|
} else {
|
|
255
284
|
this.advance();
|
|
@@ -258,10 +287,41 @@ export class Lexer {
|
|
|
258
287
|
throw new LexError("Unterminated string", l, c, this.filename, this.src);
|
|
259
288
|
}
|
|
260
289
|
|
|
290
|
+
// The escape after a consumed backslash in a string literal. \xNN and octal
|
|
291
|
+
// escapes are raw bytes; a run of them is decoded together so "\xc3\xa9" is
|
|
292
|
+
// "é" and "\xff" is one invalid byte (a lone surrogate, see decodeRawBytes).
|
|
293
|
+
readStringEscape() {
|
|
294
|
+
if (!this.isByteEscapeAt(this.pos))
|
|
295
|
+
return String.fromCodePoint(this.readEscape('"'));
|
|
296
|
+
const bytes = [this.readEscape('"')];
|
|
297
|
+
while (this.peek() === "\\" && this.isByteEscapeAt(this.pos + 1)) {
|
|
298
|
+
this.advance();
|
|
299
|
+
bytes.push(this.readEscape('"'));
|
|
300
|
+
}
|
|
301
|
+
return decodeRawBytes(bytes);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// A doc comment between a directive and its func carries the directive down
|
|
305
|
+
// so the parser still finds it on the line above the func.
|
|
306
|
+
forwardDirectives(line, start) {
|
|
307
|
+
const before = this.src.slice(
|
|
308
|
+
this.src.lastIndexOf("\n", start - 1) + 1,
|
|
309
|
+
start,
|
|
310
|
+
);
|
|
311
|
+
if (!/^[ \t]*$/.test(before)) return;
|
|
312
|
+
for (const map of [this.intrinsics, this.natives])
|
|
313
|
+
if (map.has(line - 1)) map.set(line, map.get(line - 1));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
isByteEscapeAt(i) {
|
|
317
|
+
const n = this.src[i];
|
|
318
|
+
return n === "x" || (n >= "0" && n <= "7");
|
|
319
|
+
}
|
|
320
|
+
|
|
261
321
|
// Reads the escape after a consumed backslash and returns its code point.
|
|
262
322
|
// `quote` is the enclosing delimiter: `\'` is valid only in runes and
|
|
263
|
-
// `\"` only in strings. `\xNN` and octal escapes give
|
|
264
|
-
//
|
|
323
|
+
// `\"` only in strings. `\xNN` and octal escapes give 0-255 (a code point in
|
|
324
|
+
// rune literals; string literals decode runs of them as UTF-8 bytes).
|
|
265
325
|
readEscape(quote) {
|
|
266
326
|
const l = this.line,
|
|
267
327
|
c = this.col;
|
|
@@ -328,6 +388,7 @@ export class Lexer {
|
|
|
328
388
|
} else if (this.peek() !== "'") {
|
|
329
389
|
code = this.src.codePointAt(this.pos);
|
|
330
390
|
this.advance();
|
|
391
|
+
if (code > 0xffff) this.pos++; // astral: skip the low surrogate
|
|
331
392
|
} else {
|
|
332
393
|
throw new LexError("Empty rune literal", l, c, this.filename, this.src);
|
|
333
394
|
}
|
|
@@ -451,12 +512,10 @@ export class Lexer {
|
|
|
451
512
|
break;
|
|
452
513
|
}
|
|
453
514
|
}
|
|
454
|
-
if (
|
|
455
|
-
|
|
456
|
-
this.src.charCodeAt(this.pos) === 46
|
|
457
|
-
) {
|
|
515
|
+
if (this.src.charCodeAt(this.pos) === 46) {
|
|
516
|
+
// `1.` is a float literal; `1..` and `1.x` keep the dot separate.
|
|
458
517
|
const next = this.src.charCodeAt(this.pos + 1);
|
|
459
|
-
if (next
|
|
518
|
+
if (this._dotContinuesNumber(next)) {
|
|
460
519
|
isFloat = true;
|
|
461
520
|
n += this.advance();
|
|
462
521
|
while (this.pos < this.src.length) {
|
|
@@ -469,6 +528,7 @@ export class Lexer {
|
|
|
469
528
|
break;
|
|
470
529
|
}
|
|
471
530
|
}
|
|
531
|
+
if (n.endsWith(".")) n += "0";
|
|
472
532
|
}
|
|
473
533
|
}
|
|
474
534
|
if (this.pos < this.src.length) {
|
|
@@ -481,6 +541,32 @@ export class Lexer {
|
|
|
481
541
|
return { n, isFloat };
|
|
482
542
|
}
|
|
483
543
|
|
|
544
|
+
// Whether the "." at the cursor belongs to the decimal literal before it.
|
|
545
|
+
// `next` is the code after the dot: a digit, an exponent (`1.e3`), an
|
|
546
|
+
// imaginary suffix (`1.i`) or anything that cannot start a selector or a
|
|
547
|
+
// range-style `..` makes `1.` a float; `1.x` and `1..` keep the dot apart.
|
|
548
|
+
_dotContinuesNumber(next) {
|
|
549
|
+
if (Number.isNaN(next)) return true;
|
|
550
|
+
if (next >= 48 && next <= 57) return true;
|
|
551
|
+
if (next === 46) return false;
|
|
552
|
+
const after = this.src.charCodeAt(this.pos + 2);
|
|
553
|
+
const identAfter =
|
|
554
|
+
(after >= 97 && after <= 122) ||
|
|
555
|
+
(after >= 65 && after <= 90) ||
|
|
556
|
+
after === 95 ||
|
|
557
|
+
(after >= 48 && after <= 57);
|
|
558
|
+
if (next === 101 || next === 69) {
|
|
559
|
+
return (after >= 48 && after <= 57) || after === 43 || after === 45;
|
|
560
|
+
}
|
|
561
|
+
if (next === 105) return !identAfter;
|
|
562
|
+
return !(
|
|
563
|
+
(next >= 97 && next <= 122) ||
|
|
564
|
+
(next >= 65 && next <= 90) ||
|
|
565
|
+
next === 95 ||
|
|
566
|
+
next > 127
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
|
|
484
570
|
_startsUnicodeIdent() {
|
|
485
571
|
return (
|
|
486
572
|
this.peek().charCodeAt(0) > 127 &&
|
|
@@ -584,6 +670,7 @@ export class Lexer {
|
|
|
584
670
|
this.tokens.target = this.target;
|
|
585
671
|
this.tokens.targetDirective = this.targetDirective;
|
|
586
672
|
this.tokens.intrinsics = this.intrinsics;
|
|
673
|
+
this.tokens.natives = this.natives;
|
|
587
674
|
this.tokens.embedDirectives = this.embedDirectives;
|
|
588
675
|
return this.tokens;
|
|
589
676
|
}
|
|
@@ -617,8 +704,10 @@ export class Lexer {
|
|
|
617
704
|
|
|
618
705
|
_readAmpOp(l, c) {
|
|
619
706
|
if (this.match("&")) this.push(T.AND, "&&", l, c);
|
|
620
|
-
else if (this.match("^"))
|
|
621
|
-
|
|
707
|
+
else if (this.match("^")) {
|
|
708
|
+
if (this.match("=")) this.push(T.AND_NOT_ASSIGN, "&^=", l, c);
|
|
709
|
+
else this.push(T.AND_NOT, "&^", l, c);
|
|
710
|
+
} else if (this.match("=")) this.push(T.AMP_ASSIGN, "&=", l, c);
|
|
622
711
|
else this.push(T.AMP, "&", l, c);
|
|
623
712
|
}
|
|
624
713
|
|
|
@@ -660,7 +749,12 @@ export class Lexer {
|
|
|
660
749
|
case ".":
|
|
661
750
|
if (/[0-9]/.test(this.peek() ?? "")) {
|
|
662
751
|
const { n } = this._readLeadingDotFloat();
|
|
663
|
-
this.
|
|
752
|
+
if (this.src[this.pos] === "i") {
|
|
753
|
+
this.advance();
|
|
754
|
+
this.push(T.IMAG, n, l, c);
|
|
755
|
+
} else {
|
|
756
|
+
this.push(T.FLOAT, n, l, c);
|
|
757
|
+
}
|
|
664
758
|
} else if (this.peek() === "." && this.peek(1) === ".") {
|
|
665
759
|
this.advance();
|
|
666
760
|
this.advance();
|