gofront 1.8.0 → 1.9.1
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 +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -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 +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -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 +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- 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 +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- 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 +287 -575
- 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,20 @@ 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
|
+
registerLocalTypeOwners,
|
|
40
|
+
renameLocalTypes,
|
|
41
|
+
withOriginalTypeNames,
|
|
38
42
|
} from "./link/stdlib.js";
|
|
39
43
|
import {
|
|
40
44
|
linkIntoBundle,
|
|
41
45
|
registerPackageUnit,
|
|
42
|
-
registerWasmUnit,
|
|
43
46
|
wasmMarker,
|
|
44
47
|
} from "./link/wasm.js";
|
|
45
|
-
import { lower } from "./lower/index.js";
|
|
48
|
+
import { lower, takesElementAddress } from "./lower/index.js";
|
|
46
49
|
import { Parser } from "./parser/index.js";
|
|
47
50
|
import { TemplParser } from "./parser/templ.js";
|
|
48
51
|
import {
|
|
@@ -60,10 +63,21 @@ import { WASM_SUPPORTED_STDLIB } from "./typechecker/types.js";
|
|
|
60
63
|
// Map<filePath, { mtime: number, ast: object }>
|
|
61
64
|
const _parseCache = new Map();
|
|
62
65
|
|
|
66
|
+
// A cached AST keeps everything the build attached to it (checked types,
|
|
67
|
+
// lowered stdlib programs), so a build of a Go-source stdlib importer pins
|
|
68
|
+
// megabytes. The cache is therefore bounded; the least recently used file goes
|
|
69
|
+
// first.
|
|
70
|
+
const PARSE_CACHE_LIMIT = 16;
|
|
71
|
+
|
|
63
72
|
export function clearParseCache() {
|
|
64
73
|
_parseCache.clear();
|
|
65
74
|
}
|
|
66
75
|
|
|
76
|
+
// Drops one file, e.g. a throwaway source that was deleted after its build.
|
|
77
|
+
export function evictParseCache(filePath) {
|
|
78
|
+
_parseCache.delete(resolve(filePath));
|
|
79
|
+
}
|
|
80
|
+
|
|
67
81
|
export function parseCacheSize() {
|
|
68
82
|
return _parseCache.size;
|
|
69
83
|
}
|
|
@@ -71,9 +85,14 @@ export function parseCacheSize() {
|
|
|
71
85
|
// ── Helpers ──────────────────────────────────────────────────
|
|
72
86
|
|
|
73
87
|
function parseGoFrontFile(filePath) {
|
|
88
|
+
const key = resolve(filePath);
|
|
74
89
|
const mtime = statSync(filePath).mtimeMs;
|
|
75
|
-
const cached = _parseCache.get(
|
|
76
|
-
if (cached && cached.mtime === mtime)
|
|
90
|
+
const cached = _parseCache.get(key);
|
|
91
|
+
if (cached && cached.mtime === mtime) {
|
|
92
|
+
_parseCache.delete(key);
|
|
93
|
+
_parseCache.set(key, cached);
|
|
94
|
+
return cached.ast;
|
|
95
|
+
}
|
|
77
96
|
|
|
78
97
|
const source = readFileSync(filePath, "utf8");
|
|
79
98
|
const filename = basename(filePath);
|
|
@@ -88,7 +107,9 @@ function parseGoFrontFile(filePath) {
|
|
|
88
107
|
ast._path = resolve(filePath);
|
|
89
108
|
// compileFiles overwrites `target` with the resolved one; keep the directive.
|
|
90
109
|
ast._declaredTarget = ast.target;
|
|
91
|
-
_parseCache.set(
|
|
110
|
+
_parseCache.set(key, { mtime, ast });
|
|
111
|
+
if (_parseCache.size > PARSE_CACHE_LIMIT)
|
|
112
|
+
_parseCache.delete(_parseCache.keys().next().value);
|
|
92
113
|
return ast;
|
|
93
114
|
}
|
|
94
115
|
|
|
@@ -107,7 +128,7 @@ export function gwFilesIn(dir, { includeTests = false } = {}) {
|
|
|
107
128
|
// wasm-opt when requested and produce the wasm source map.
|
|
108
129
|
function finishWasm(options, merged, pkgTarget, outputDir) {
|
|
109
130
|
const wantMap = Boolean(options.sourceMap && !options.isDependency);
|
|
110
|
-
let { wasm, wat, sourceMapInfo } = linkIntoBundle(
|
|
131
|
+
let { wasm, wat, sourceMapInfo, stringTable } = linkIntoBundle(
|
|
111
132
|
options,
|
|
112
133
|
merged,
|
|
113
134
|
pkgTarget,
|
|
@@ -132,7 +153,7 @@ function finishWasm(options, merged, pkgTarget, outputDir) {
|
|
|
132
153
|
wasmOptInfo = opt;
|
|
133
154
|
if (opt.sourceMap) wasmSourceMap = opt.sourceMap;
|
|
134
155
|
}
|
|
135
|
-
return { wasm, wat, wasmOptInfo, wasmSourceMap };
|
|
156
|
+
return { wasm, wat, wasmOptInfo, wasmSourceMap, stringTable };
|
|
136
157
|
}
|
|
137
158
|
|
|
138
159
|
// Prints non-fatal diagnostics (e.g. JS Number precision of a constant).
|
|
@@ -184,12 +205,6 @@ function resolveImports(
|
|
|
184
205
|
"js: imports are not allowed in wasm packages",
|
|
185
206
|
impNode,
|
|
186
207
|
);
|
|
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
208
|
} else if (path === "gofront/shared" && pkgTarget === "both") {
|
|
194
209
|
checker.err(
|
|
195
210
|
"'gofront/shared' requires a wasm-only package (//gofront:target wasm): a both package has no linear memory on the JS side",
|
|
@@ -287,6 +302,9 @@ function resolveImports(
|
|
|
287
302
|
isDependency: true,
|
|
288
303
|
});
|
|
289
304
|
if (!alreadyBundled) preambles.push(dep);
|
|
305
|
+
// A dependency that hands out `&xs[i]` makes whole-element
|
|
306
|
+
// stores in this package write in place too.
|
|
307
|
+
if (dep.elemAddrTaken) checker.depElemAddrTaken = true;
|
|
290
308
|
|
|
291
309
|
// Target import rules for local packages
|
|
292
310
|
const impNode = {
|
|
@@ -374,120 +392,29 @@ export function resolvePackageTarget(declared, pkgDir, options = {}) {
|
|
|
374
392
|
}
|
|
375
393
|
|
|
376
394
|
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);
|
|
395
|
+
const { outputFile = null, dumpTokens = false, dumpAst = false } = options;
|
|
441
396
|
|
|
442
|
-
if (
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
397
|
+
if (dumpTokens || dumpAst) {
|
|
398
|
+
let source;
|
|
399
|
+
try {
|
|
400
|
+
source = readFileSync(inputPath, "utf8");
|
|
401
|
+
} catch (e) {
|
|
402
|
+
throw new Error(`cannot read '${inputPath}': ${e.message}`);
|
|
403
|
+
}
|
|
404
|
+
const tokens = new Lexer(source, basename(inputPath)).tokenize();
|
|
405
|
+
if (dumpTokens) return { tokens };
|
|
406
|
+
const ast = new Parser(tokens, basename(inputPath), source).parse();
|
|
407
|
+
ast._source = source;
|
|
408
|
+
ast._path = resolve(inputPath);
|
|
409
|
+
return { ast };
|
|
451
410
|
}
|
|
452
411
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
412
|
+
return compileFiles([resolve(inputPath)], {
|
|
413
|
+
...options,
|
|
414
|
+
outputDir:
|
|
415
|
+
options.outputDir ??
|
|
416
|
+
(outputFile ? dirname(resolve(outputFile)) : resolve(".")),
|
|
457
417
|
});
|
|
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
418
|
}
|
|
492
419
|
|
|
493
420
|
export function compileDir(dir, options = {}) {
|
|
@@ -518,6 +445,8 @@ export function compileFiles(files, options = {}) {
|
|
|
518
445
|
jsRootPrograms: options.jsRootPrograms ?? [],
|
|
519
446
|
rootDir: options.rootDir ?? fromDir,
|
|
520
447
|
bundledDirs: options.bundledDirs ?? new Set(),
|
|
448
|
+
typeOwners: options.typeOwners ?? new Map(),
|
|
449
|
+
jsonBuild: options.jsonBuild ?? { js: false, wasm: false },
|
|
521
450
|
};
|
|
522
451
|
const outputDir =
|
|
523
452
|
options.outputDir ??
|
|
@@ -545,6 +474,15 @@ export function compileFiles(files, options = {}) {
|
|
|
545
474
|
);
|
|
546
475
|
const pkgName = pkgNames[0];
|
|
547
476
|
|
|
477
|
+
// Local packages share one flat JS module: a type name another package
|
|
478
|
+
// already declared is renamed here (`Pt` -> `Pt$$b`).
|
|
479
|
+
const renamedTypes = mangleLocalTypes(
|
|
480
|
+
programs,
|
|
481
|
+
resolve(fromDir),
|
|
482
|
+
pkgName,
|
|
483
|
+
options.typeOwners,
|
|
484
|
+
);
|
|
485
|
+
|
|
548
486
|
// Validate target directives across files in this package
|
|
549
487
|
const declaredTargets = new Map();
|
|
550
488
|
for (const p of programs) {
|
|
@@ -571,6 +509,11 @@ export function compileFiles(files, options = {}) {
|
|
|
571
509
|
);
|
|
572
510
|
for (const p of programs) p.target = pkgTarget;
|
|
573
511
|
|
|
512
|
+
// The test runner's driver is a source that exists only in memory: it is
|
|
513
|
+
// parsed and checked with the package but never written next to it.
|
|
514
|
+
const driver = testDriverProgram(options, programs, pkgTarget, fromDir);
|
|
515
|
+
if (driver) programs.push(driver);
|
|
516
|
+
|
|
574
517
|
// ── 2. Resolve imports ────────────────────────────────────────
|
|
575
518
|
const checker = new TypeChecker();
|
|
576
519
|
checker.target = pkgTarget;
|
|
@@ -593,7 +536,7 @@ export function compileFiles(files, options = {}) {
|
|
|
593
536
|
|
|
594
537
|
// ── 3. Type-check ─────────────────────────────────────────────
|
|
595
538
|
const errors = checker.checkAll(programs);
|
|
596
|
-
const codecs =
|
|
539
|
+
const codecs = checkGeneratedGo(
|
|
597
540
|
programs,
|
|
598
541
|
checker,
|
|
599
542
|
preambles,
|
|
@@ -621,11 +564,12 @@ export function compileFiles(files, options = {}) {
|
|
|
621
564
|
const codegen = new CodeGen(checker, jsImports, bundledPackages, {
|
|
622
565
|
target: pkgTarget,
|
|
623
566
|
strict: isStrict,
|
|
567
|
+
natives: options.natives,
|
|
624
568
|
});
|
|
625
569
|
const mainJs =
|
|
626
570
|
emitTarget === "wasm"
|
|
627
571
|
? wasmMarker(pkgName)
|
|
628
|
-
: codegen.generateAll(programs, {
|
|
572
|
+
: codegen.generateAll([...programs, ...codecs], {
|
|
629
573
|
isTest: options.isTest ?? false,
|
|
630
574
|
});
|
|
631
575
|
logWarnings(codegen.warnings);
|
|
@@ -638,8 +582,8 @@ export function compileFiles(files, options = {}) {
|
|
|
638
582
|
};
|
|
639
583
|
|
|
640
584
|
const merged = mergeCompilationChunks([...preambles, mainChunk]);
|
|
641
|
-
finishStdlibJs(merged, options, programs, emitTarget);
|
|
642
|
-
const { wasm, wat, wasmOptInfo, wasmSourceMap } = finishWasm(
|
|
585
|
+
finishStdlibJs(merged, options, [...programs, ...codecs], emitTarget);
|
|
586
|
+
const { wasm, wat, wasmOptInfo, wasmSourceMap, stringTable } = finishWasm(
|
|
643
587
|
options,
|
|
644
588
|
merged,
|
|
645
589
|
pkgTarget,
|
|
@@ -658,15 +602,22 @@ export function compileFiles(files, options = {}) {
|
|
|
658
602
|
return {
|
|
659
603
|
pkgName,
|
|
660
604
|
target: pkgTarget,
|
|
605
|
+
elemAddrTaken:
|
|
606
|
+
checker.depElemAddrTaken === true ||
|
|
607
|
+
programs.some((p) => takesElementAddress(p.decls)),
|
|
661
608
|
js: merged.js,
|
|
662
609
|
css: allCss,
|
|
663
610
|
wasm,
|
|
664
611
|
wat,
|
|
665
612
|
wasmOptInfo,
|
|
666
613
|
wasmSourceMap,
|
|
667
|
-
|
|
614
|
+
stringTable,
|
|
615
|
+
programs: driver ? programs.filter((p) => p !== driver) : programs,
|
|
668
616
|
exportedSymbols: checker.getExportedSymbols(),
|
|
669
|
-
exportedTypes:
|
|
617
|
+
exportedTypes: withOriginalTypeNames(
|
|
618
|
+
checker.getExportedTypes(),
|
|
619
|
+
renamedTypes,
|
|
620
|
+
),
|
|
670
621
|
exportedConsts: checker.getExportedConsts(),
|
|
671
622
|
sourceFiles: merged.sourceFiles,
|
|
672
623
|
sourcesContent: merged.sourcesContent,
|
|
@@ -674,6 +625,38 @@ export function compileFiles(files, options = {}) {
|
|
|
674
625
|
};
|
|
675
626
|
}
|
|
676
627
|
|
|
628
|
+
// Parses the in-memory driver source `options.testDriver(programs, target)`
|
|
629
|
+
// returns (null: no driver) as a `_test.go` file of the package.
|
|
630
|
+
function testDriverProgram(options, programs, pkgTarget, fromDir) {
|
|
631
|
+
if (!options.testDriver || options.isDependency) return null;
|
|
632
|
+
const source = options.testDriver(programs, pkgTarget);
|
|
633
|
+
if (!source) return null;
|
|
634
|
+
const filename = "gofront_testmain_test.go";
|
|
635
|
+
const ast = new Parser(
|
|
636
|
+
new Lexer(source, filename).tokenize(),
|
|
637
|
+
filename,
|
|
638
|
+
source,
|
|
639
|
+
).parse();
|
|
640
|
+
ast._source = source;
|
|
641
|
+
ast._path = join(fromDir, filename);
|
|
642
|
+
ast._declaredTarget = ast.target;
|
|
643
|
+
ast.target = pkgTarget;
|
|
644
|
+
return ast;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// The renamed ASTs must not be reused from the parse cache by a later build.
|
|
648
|
+
function mangleLocalTypes(programs, pkgKey, pkgName, owners) {
|
|
649
|
+
const renamed = mangleCollidingTypes(programs, pkgKey, pkgName, owners);
|
|
650
|
+
const foreign = new Set();
|
|
651
|
+
for (const [name, owner] of owners ?? [])
|
|
652
|
+
if (owner !== pkgKey) foreign.add(name);
|
|
653
|
+
const locals = renameLocalTypes(programs, foreign);
|
|
654
|
+
registerLocalTypeOwners(programs, pkgKey, owners);
|
|
655
|
+
if (renamed.size > 0 || locals)
|
|
656
|
+
for (const p of programs) _parseCache.delete(p._path);
|
|
657
|
+
return renamed;
|
|
658
|
+
}
|
|
659
|
+
|
|
677
660
|
export function compileWasm(source, options = {}) {
|
|
678
661
|
const filename = options.filename ?? "main.go";
|
|
679
662
|
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 &&
|
|
@@ -551,6 +637,7 @@ export class Lexer {
|
|
|
551
637
|
}
|
|
552
638
|
if (ch === "'") {
|
|
553
639
|
this.push(T.INT, this.readRuneLiteral(), l, c);
|
|
640
|
+
this.tokens[this.tokens.length - 1].rune = true;
|
|
554
641
|
continue;
|
|
555
642
|
}
|
|
556
643
|
|
|
@@ -584,6 +671,7 @@ export class Lexer {
|
|
|
584
671
|
this.tokens.target = this.target;
|
|
585
672
|
this.tokens.targetDirective = this.targetDirective;
|
|
586
673
|
this.tokens.intrinsics = this.intrinsics;
|
|
674
|
+
this.tokens.natives = this.natives;
|
|
587
675
|
this.tokens.embedDirectives = this.embedDirectives;
|
|
588
676
|
return this.tokens;
|
|
589
677
|
}
|
|
@@ -617,8 +705,10 @@ export class Lexer {
|
|
|
617
705
|
|
|
618
706
|
_readAmpOp(l, c) {
|
|
619
707
|
if (this.match("&")) this.push(T.AND, "&&", l, c);
|
|
620
|
-
else if (this.match("^"))
|
|
621
|
-
|
|
708
|
+
else if (this.match("^")) {
|
|
709
|
+
if (this.match("=")) this.push(T.AND_NOT_ASSIGN, "&^=", l, c);
|
|
710
|
+
else this.push(T.AND_NOT, "&^", l, c);
|
|
711
|
+
} else if (this.match("=")) this.push(T.AMP_ASSIGN, "&=", l, c);
|
|
622
712
|
else this.push(T.AMP, "&", l, c);
|
|
623
713
|
}
|
|
624
714
|
|
|
@@ -660,7 +750,12 @@ export class Lexer {
|
|
|
660
750
|
case ".":
|
|
661
751
|
if (/[0-9]/.test(this.peek() ?? "")) {
|
|
662
752
|
const { n } = this._readLeadingDotFloat();
|
|
663
|
-
this.
|
|
753
|
+
if (this.src[this.pos] === "i") {
|
|
754
|
+
this.advance();
|
|
755
|
+
this.push(T.IMAG, n, l, c);
|
|
756
|
+
} else {
|
|
757
|
+
this.push(T.FLOAT, n, l, c);
|
|
758
|
+
}
|
|
664
759
|
} else if (this.peek() === "." && this.peek(1) === ".") {
|
|
665
760
|
this.advance();
|
|
666
761
|
this.advance();
|