gofront 1.3.12 → 1.5.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 +90 -20
- package/package.json +2 -1
- package/src/{codegen → backend/js}/expressions.js +217 -60
- package/src/{codegen → backend/js}/index.js +47 -36
- package/src/{codegen → backend/js}/runtime.js +11 -0
- package/src/{codegen → backend/js}/statements.js +43 -48
- package/src/backend/js/stdlib/bits.js +32 -0
- package/src/{codegen → backend/js}/stdlib/index.js +3 -0
- package/src/{codegen → backend/js}/templ.js +31 -2
- package/src/backend/wasm/boundary.js +1345 -0
- package/src/backend/wasm/emit.js +5779 -0
- package/src/backend/wasm/encode.js +789 -0
- package/src/backend/wasm/glue.js +130 -0
- package/src/backend/wasm/index.js +1413 -0
- package/src/backend/wasm/types.js +420 -0
- package/src/backend/wasm/wat.js +255 -0
- package/src/cli-core.js +52 -7
- package/src/compiler.js +328 -12
- package/src/dev-server.js +56 -15
- package/src/index.js +17 -9
- package/src/lexer.js +18 -0
- package/src/lower/boxing.js +36 -0
- package/src/lower/captures.js +179 -0
- package/src/lower/embedding.js +30 -0
- package/src/lower/escape.js +169 -0
- package/src/lower/functions.js +30 -0
- package/src/lower/globals.js +324 -0
- package/src/lower/index.js +115 -0
- package/src/lower/ownership.js +142 -0
- package/src/lower/range.js +70 -0
- package/src/minifier.js +2 -0
- package/src/parser/index.js +8 -3
- package/src/resolver.js +4 -1
- package/src/templ-lexer.js +107 -3
- package/src/templ-parser.js +29 -0
- package/src/test-runner.js +74 -20
- package/src/typechecker/expressions.js +40 -1
- package/src/typechecker/index.js +301 -6
- package/src/typechecker/statements.js +7 -0
- package/src/typechecker/stdlib/core.js +17 -0
- package/src/typechecker/types.js +56 -2
- /package/src/{codegen → backend/js}/source-map.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/errors.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/fmt.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/maps.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/path.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/slices.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/sort.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/strconv.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/strings.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/testing.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/time.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Code generator: walks the typed AST and emits clean JavaScript.
|
|
2
2
|
//
|
|
3
|
-
// Split into sub-modules under
|
|
3
|
+
// Split into sub-modules under backend/js/:
|
|
4
4
|
// source-map.js — VLQ encoder and source map builder
|
|
5
5
|
// statements.js — genBlock, genStmt, genFor, genSwitch, etc.
|
|
6
6
|
// expressions.js — genExpr, genCall, genCompositeLit, helpers
|
|
@@ -17,7 +17,12 @@
|
|
|
17
17
|
// - make(map[K]V) → {}
|
|
18
18
|
// - for range → for...of with .entries()
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
computeEmbeddedStubs,
|
|
22
|
+
isReferenceType,
|
|
23
|
+
scanAddressTaken,
|
|
24
|
+
} from "../../lower/index.js";
|
|
25
|
+
import { isComplex, isNumeric } from "../../typechecker/types.js";
|
|
21
26
|
import { expressionGenMethods } from "./expressions.js";
|
|
22
27
|
import {
|
|
23
28
|
HELPER_APPEND,
|
|
@@ -27,6 +32,7 @@ import {
|
|
|
27
32
|
HELPER_ERROR,
|
|
28
33
|
HELPER_ERROR_IS,
|
|
29
34
|
HELPER_IFACE_BOX,
|
|
35
|
+
HELPER_INJECT_STYLES,
|
|
30
36
|
HELPER_LEN,
|
|
31
37
|
HELPER_PATH_CLEAN,
|
|
32
38
|
HELPER_S,
|
|
@@ -42,6 +48,8 @@ import { statementGenMethods } from "./statements.js";
|
|
|
42
48
|
import { stdlibGenMethods } from "./stdlib/index.js";
|
|
43
49
|
import { templGenMethods } from "./templ.js";
|
|
44
50
|
|
|
51
|
+
export { buildSourceMap };
|
|
52
|
+
|
|
45
53
|
// Valid Go identifiers that cannot be used as JS bindings; emitted with a `$` suffix.
|
|
46
54
|
const JS_RESERVED = new Set([
|
|
47
55
|
"arguments",
|
|
@@ -90,13 +98,17 @@ export class CodeGen {
|
|
|
90
98
|
checker = null,
|
|
91
99
|
jsImports = new Map(),
|
|
92
100
|
bundledPackages = new Set(),
|
|
101
|
+
options = {},
|
|
93
102
|
) {
|
|
94
103
|
this.checker = checker;
|
|
104
|
+
this.target = options.target ?? "js";
|
|
105
|
+
this.strict = Boolean(options.strict || this.target === "both");
|
|
95
106
|
this.out = [];
|
|
96
107
|
this.indent = 0;
|
|
97
108
|
this.structNames = new Set();
|
|
98
109
|
this.structFields = new Map();
|
|
99
110
|
this.namedWrapperNames = new Set();
|
|
111
|
+
this.collectedCss = [];
|
|
100
112
|
this.jsImports = jsImports;
|
|
101
113
|
this.bundledPackages = bundledPackages;
|
|
102
114
|
this.namedReturnVars = null; // names of current function's named return vars
|
|
@@ -191,6 +203,10 @@ export class CodeGen {
|
|
|
191
203
|
}
|
|
192
204
|
|
|
193
205
|
_callInitAndMain(initNames, program, isTest = false) {
|
|
206
|
+
if (this.collectedCss.length > 0) {
|
|
207
|
+
const uniqueCss = [...new Set(this.collectedCss)];
|
|
208
|
+
this.line(`__injectStyles(${JSON.stringify(uniqueCss.join("\n\n"))});`);
|
|
209
|
+
}
|
|
194
210
|
for (const name of initNames) this.line(`${name}();`);
|
|
195
211
|
if (
|
|
196
212
|
!isTest &&
|
|
@@ -201,6 +217,14 @@ export class CodeGen {
|
|
|
201
217
|
|
|
202
218
|
generate(program, options = {}) {
|
|
203
219
|
const isTest = options.isTest ?? false;
|
|
220
|
+
// Ensure _pkgName is set on all decls (generateAll sets it for
|
|
221
|
+
// multi-file; for single-file compiles we derive it from the AST).
|
|
222
|
+
const pkgName = program.pkg?.name;
|
|
223
|
+
if (pkgName) {
|
|
224
|
+
for (const d of program.decls) {
|
|
225
|
+
if (!d._pkgName) d._pkgName = pkgName;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
204
228
|
this._renameReservedIdents(program.decls);
|
|
205
229
|
const methods = this._collectDecls(program);
|
|
206
230
|
this._emitJsImports();
|
|
@@ -227,7 +251,7 @@ export class CodeGen {
|
|
|
227
251
|
return this.out.join("\n");
|
|
228
252
|
}
|
|
229
253
|
|
|
230
|
-
// Emits FuncDecl and
|
|
254
|
+
// Emits FuncDecl, TemplDecl, and CssDecl nodes; returns renamed init function names.
|
|
231
255
|
_emitFuncDecls(program) {
|
|
232
256
|
let initCount = 0;
|
|
233
257
|
const initNames = [];
|
|
@@ -247,6 +271,10 @@ export class CodeGen {
|
|
|
247
271
|
this._currentSrcFileIdx = d._srcFileIdx ?? 0;
|
|
248
272
|
this.genTemplDecl(d);
|
|
249
273
|
this.blank();
|
|
274
|
+
} else if (d.kind === "CssDecl") {
|
|
275
|
+
this._currentSrcFileIdx = d._srcFileIdx ?? 0;
|
|
276
|
+
this.genCssDecl(d);
|
|
277
|
+
this.blank();
|
|
250
278
|
}
|
|
251
279
|
}
|
|
252
280
|
return initNames;
|
|
@@ -306,6 +334,7 @@ export class CodeGen {
|
|
|
306
334
|
MethodDecl: "_renameMethodDeclNode",
|
|
307
335
|
FuncLit: "_renameSignature",
|
|
308
336
|
TemplDecl: "_renameSignature",
|
|
337
|
+
CssDecl: "_renameSignature",
|
|
309
338
|
VarDecl: "_renameDeclSpecs",
|
|
310
339
|
ConstDecl: "_renameDeclSpecs",
|
|
311
340
|
TypeSwitchStmt: "_renameTypeSwitchNode",
|
|
@@ -381,6 +410,7 @@ export class CodeGen {
|
|
|
381
410
|
_prependHelpers(isTest = false) {
|
|
382
411
|
const needsTesting = isTest || this._usesTesting;
|
|
383
412
|
const HELPER_MAP = [
|
|
413
|
+
[this.collectedCss.length > 0, HELPER_INJECT_STYLES],
|
|
384
414
|
[this._usesLen, HELPER_LEN],
|
|
385
415
|
[this._usesAppend, HELPER_APPEND],
|
|
386
416
|
[this._usesSliceGuard, HELPER_S],
|
|
@@ -414,12 +444,17 @@ export class CodeGen {
|
|
|
414
444
|
for (let i = 0; i < programs.length; i++) {
|
|
415
445
|
for (const decl of programs[i].decls) {
|
|
416
446
|
decl._srcFileIdx = i;
|
|
447
|
+
decl._pkgName = programs[i].pkg?.name;
|
|
417
448
|
}
|
|
418
449
|
}
|
|
419
450
|
const merged = { decls: programs.flatMap((p) => p.decls) };
|
|
420
451
|
return this.generate(merged, options);
|
|
421
452
|
}
|
|
422
453
|
|
|
454
|
+
getCss() {
|
|
455
|
+
return [...new Set(this.collectedCss)].join("\n\n");
|
|
456
|
+
}
|
|
457
|
+
|
|
423
458
|
getMappings() {
|
|
424
459
|
return this._srcMappings;
|
|
425
460
|
}
|
|
@@ -596,12 +631,13 @@ export class CodeGen {
|
|
|
596
631
|
}
|
|
597
632
|
|
|
598
633
|
_genEmbeddedMethodStubs(name, methodDecls) {
|
|
599
|
-
|
|
600
|
-
const
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
634
|
+
const stubs = computeEmbeddedStubs(name, methodDecls, this.checker);
|
|
635
|
+
for (const stub of stubs) {
|
|
636
|
+
this.blank();
|
|
637
|
+
this.line(
|
|
638
|
+
`${stub.methodName}(...__a) { return ${stub.embedName}.prototype.${stub.methodName}.call(this, ...__a); }`,
|
|
639
|
+
);
|
|
640
|
+
}
|
|
605
641
|
}
|
|
606
642
|
|
|
607
643
|
genMethod(decl, recvField = null) {
|
|
@@ -742,36 +778,11 @@ export class CodeGen {
|
|
|
742
778
|
|
|
743
779
|
// Scan AST node for _addressTaken idents on scalars and populate _boxedVars.
|
|
744
780
|
_scanAddressTaken(node) {
|
|
745
|
-
|
|
746
|
-
if (Array.isArray(node)) {
|
|
747
|
-
for (const child of node) this._scanAddressTaken(child);
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
// &x — the operand ident will have _addressTaken set by typechecker
|
|
751
|
-
if (node.kind === "Ident" && node._addressTaken) {
|
|
752
|
-
// Check if the type is a scalar (needs boxing) vs reference type (no boxing)
|
|
753
|
-
const t = node._type;
|
|
754
|
-
if (t && !this._isReferenceType(t)) {
|
|
755
|
-
this._boxedVars.add(node.name);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
// Recurse into FuncLit too — closures may take address of outer vars
|
|
759
|
-
for (const key of Object.keys(node)) {
|
|
760
|
-
if (key.startsWith("_")) continue;
|
|
761
|
-
this._scanAddressTaken(node[key]);
|
|
762
|
-
}
|
|
781
|
+
scanAddressTaken(node, this._boxedVars);
|
|
763
782
|
}
|
|
764
783
|
|
|
765
784
|
_isReferenceType(t) {
|
|
766
|
-
|
|
767
|
-
const base = t.kind === "named" ? t.underlying : t;
|
|
768
|
-
return (
|
|
769
|
-
base?.kind === "struct" ||
|
|
770
|
-
base?.kind === "slice" ||
|
|
771
|
-
base?.kind === "map" ||
|
|
772
|
-
base?.kind === "func" ||
|
|
773
|
-
base?.kind === "interface"
|
|
774
|
-
);
|
|
785
|
+
return isReferenceType(t);
|
|
775
786
|
}
|
|
776
787
|
|
|
777
788
|
// ── Variable / const declarations ────────────────────────────
|
|
@@ -312,3 +312,14 @@ var __GoFront_T = __GoFront_T || class {
|
|
|
312
312
|
}
|
|
313
313
|
};
|
|
314
314
|
var __testing_T = __GoFront_T;`;
|
|
315
|
+
|
|
316
|
+
export const HELPER_INJECT_STYLES = `var __injectStyles = __injectStyles || function(css) {
|
|
317
|
+
if (typeof document === "undefined" || !document || !document.head || !css) return;
|
|
318
|
+
let s = document.getElementById("gofront-styles");
|
|
319
|
+
if (!s) {
|
|
320
|
+
s = document.createElement("style");
|
|
321
|
+
s.id = "gofront-styles";
|
|
322
|
+
document.head.appendChild(s);
|
|
323
|
+
}
|
|
324
|
+
s.textContent += "\\n" + css;
|
|
325
|
+
};`;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
bodyRedeclares,
|
|
3
|
+
isIntRangeType,
|
|
4
|
+
isMapRangeType,
|
|
5
|
+
isRangeFor,
|
|
6
|
+
isSimpleIntRange,
|
|
7
|
+
isStringRangeType,
|
|
8
|
+
nodeAssigns,
|
|
9
|
+
} from "../../lower/index.js";
|
|
10
|
+
import { isComplex } from "../../typechecker/types.js";
|
|
4
11
|
|
|
5
12
|
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
6
13
|
|
|
@@ -55,9 +62,20 @@ export const statementGenMethods = {
|
|
|
55
62
|
case "TypeDecl":
|
|
56
63
|
this.genTypeDeclWithMethods(stmt, []);
|
|
57
64
|
break;
|
|
58
|
-
case "IncDecStmt":
|
|
59
|
-
|
|
65
|
+
case "IncDecStmt": {
|
|
66
|
+
if (
|
|
67
|
+
this.strict &&
|
|
68
|
+
this._genStrictCompound(
|
|
69
|
+
stmt.expr,
|
|
70
|
+
stmt.op === "++" ? "+" : "-",
|
|
71
|
+
"1",
|
|
72
|
+
srcLine,
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
break;
|
|
76
|
+
this.line(`${this.genExpr(stmt.expr)}${stmt.op};`, srcLine);
|
|
60
77
|
break;
|
|
78
|
+
}
|
|
61
79
|
case "ExprStmt":
|
|
62
80
|
this.line(`${this.genExpr(stmt.expr)};`);
|
|
63
81
|
break;
|
|
@@ -248,6 +266,19 @@ export const statementGenMethods = {
|
|
|
248
266
|
if (active.length === 0) {
|
|
249
267
|
if (rhs.length > 0) this.line(`${rhs[0]};`);
|
|
250
268
|
} else if (lhs.length === 1) {
|
|
269
|
+
if (
|
|
270
|
+
this.strict &&
|
|
271
|
+
stmt.op !== "=" &&
|
|
272
|
+
this._genStrictCompound(
|
|
273
|
+
stmt.lhs[0],
|
|
274
|
+
stmt.op.slice(0, -1),
|
|
275
|
+
active[0].r,
|
|
276
|
+
null,
|
|
277
|
+
active[0].l,
|
|
278
|
+
)
|
|
279
|
+
) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
251
282
|
this.line(`${active[0].l} ${stmt.op} ${active[0].r};`);
|
|
252
283
|
} else {
|
|
253
284
|
this._genAssignMulti(stmt, lhs, rhs, active, pairs);
|
|
@@ -449,28 +480,19 @@ export const statementGenMethods = {
|
|
|
449
480
|
},
|
|
450
481
|
|
|
451
482
|
isRangeFor(stmt) {
|
|
452
|
-
|
|
453
|
-
const init = stmt.init;
|
|
454
|
-
if (init.kind !== "DefineStmt" && init.kind !== "AssignStmt") return false;
|
|
455
|
-
return init.rhs?.[0]?.kind === "RangeExpr";
|
|
483
|
+
return isRangeFor(stmt);
|
|
456
484
|
},
|
|
457
485
|
|
|
458
486
|
_isIntRangeType(iterType) {
|
|
459
|
-
return
|
|
487
|
+
return isIntRangeType(iterType);
|
|
460
488
|
},
|
|
461
489
|
|
|
462
490
|
_isMapRangeType(iterType) {
|
|
463
|
-
return (
|
|
464
|
-
iterType?.kind === "map" ||
|
|
465
|
-
(iterType?.kind === "named" && iterType.underlying?.kind === "map")
|
|
466
|
-
);
|
|
491
|
+
return isMapRangeType(iterType);
|
|
467
492
|
},
|
|
468
493
|
|
|
469
494
|
_isStringRangeType(iterType) {
|
|
470
|
-
return (
|
|
471
|
-
(iterType?.kind === "basic" && iterType.name === "string") ||
|
|
472
|
-
(iterType?.kind === "untyped" && iterType.base === "string")
|
|
473
|
-
);
|
|
495
|
+
return isStringRangeType(iterType);
|
|
474
496
|
},
|
|
475
497
|
|
|
476
498
|
_rangeVarTarget(e, isAssign) {
|
|
@@ -498,17 +520,7 @@ export const statementGenMethods = {
|
|
|
498
520
|
|
|
499
521
|
// True when the body re-declares one of `names` at its top level (legal Go shadowing).
|
|
500
522
|
_bodyRedeclares(body, names) {
|
|
501
|
-
|
|
502
|
-
for (const s of body.stmts ?? []) {
|
|
503
|
-
if (s.kind === "DefineStmt" && s.lhs.some((e) => names.includes(e.name)))
|
|
504
|
-
return true;
|
|
505
|
-
if (
|
|
506
|
-
(s.kind === "VarDecl" || s.kind === "ConstDecl") &&
|
|
507
|
-
s.decls?.some((d) => d.names?.some((n) => names.includes(n)))
|
|
508
|
-
)
|
|
509
|
-
return true;
|
|
510
|
-
}
|
|
511
|
-
return false;
|
|
523
|
+
return bodyRedeclares(body, names);
|
|
512
524
|
},
|
|
513
525
|
|
|
514
526
|
// Range value variable: aliases the element unless the body writes the variable or the collection.
|
|
@@ -565,9 +577,7 @@ export const statementGenMethods = {
|
|
|
565
577
|
|
|
566
578
|
// Whether `for i := range n` can be emitted as a plain `for (let i = 0; …)` without hidden registers.
|
|
567
579
|
_isSimpleIntRange(stmt, name, isAssign) {
|
|
568
|
-
|
|
569
|
-
if (!name || name === "_") return true;
|
|
570
|
-
return !this._boxedVars.has(name) && !this._nodeAssigns(stmt.body, name);
|
|
580
|
+
return isSimpleIntRange(stmt, name, isAssign, this._boxedVars);
|
|
571
581
|
},
|
|
572
582
|
|
|
573
583
|
_genIntRangeBody(stmt, name, isAssign) {
|
|
@@ -603,22 +613,7 @@ export const statementGenMethods = {
|
|
|
603
613
|
|
|
604
614
|
// True when `node` contains an assignment or ++/-- targeting identifier `name`.
|
|
605
615
|
_nodeAssigns(node, name) {
|
|
606
|
-
|
|
607
|
-
if (Array.isArray(node))
|
|
608
|
-
return node.some((n) => this._nodeAssigns(n, name));
|
|
609
|
-
if (
|
|
610
|
-
(node.kind === "AssignStmt" &&
|
|
611
|
-
node.lhs.some((e) => e.kind === "Ident" && e.name === name)) ||
|
|
612
|
-
(node.kind === "IncDecStmt" &&
|
|
613
|
-
node.expr.kind === "Ident" &&
|
|
614
|
-
node.expr.name === name)
|
|
615
|
-
)
|
|
616
|
-
return true;
|
|
617
|
-
for (const key of Object.keys(node)) {
|
|
618
|
-
if (key.startsWith("_")) continue;
|
|
619
|
-
if (this._nodeAssigns(node[key], name)) return true;
|
|
620
|
-
}
|
|
621
|
-
return false;
|
|
616
|
+
return nodeAssigns(node, name);
|
|
622
617
|
},
|
|
623
618
|
|
|
624
619
|
_genIterFor(init, iterType, iteree, lhs, body) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/backend/js/stdlib/bits.js
|
|
2
|
+
// CodeGen for Go `math/bits` package.
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
5
|
+
|
|
6
|
+
const BITS_MAP = {
|
|
7
|
+
LeadingZeros32: ([x]) => `Math.clz32((${x}) >>> 0)`,
|
|
8
|
+
TrailingZeros32: ([x]) =>
|
|
9
|
+
`((() => { const v = (${x}) >>> 0; return v === 0 ? 32 : 31 - Math.clz32(v & -v); })())`,
|
|
10
|
+
OnesCount32: ([x]) =>
|
|
11
|
+
`((() => { let v = (${x}) >>> 0; v = v - ((v >>> 1) & 0x55555555); v = (v & 0x33333333) + ((v >>> 2) & 0x33333333); return (Math.imul((v + (v >>> 4)) & 0x0f0f0f0f, 0x01010101)) >>> 24; })())`,
|
|
12
|
+
RotateLeft32: ([x, k]) =>
|
|
13
|
+
`((() => { const s = (${k}) & 31; const v = (${x}) >>> 0; return ((v << s) | (v >>> ((32 - s) & 31))) >>> 0; })())`,
|
|
14
|
+
|
|
15
|
+
LeadingZeros64: ([x]) =>
|
|
16
|
+
`((() => { const v = BigInt.asUintN(64, BigInt(${x})); if (v === 0n) return 64; const hi = Number(v >> 32n); return hi !== 0 ? Math.clz32(hi) : 32 + Math.clz32(Number(v & 0xffffffffn)); })())`,
|
|
17
|
+
TrailingZeros64: ([x]) =>
|
|
18
|
+
`((() => { const v = BigInt.asUintN(64, BigInt(${x})); if (v === 0n) return 64; const lo = Number(v & 0xffffffffn); if (lo !== 0) return 31 - Math.clz32(lo & -lo); const hi = Number((v >> 32n) & 0xffffffffn); return 32 + (31 - Math.clz32(hi & -hi)); })())`,
|
|
19
|
+
OnesCount64: ([x]) =>
|
|
20
|
+
`((() => { let v = BigInt.asUintN(64, BigInt(${x})); let c = 0; while (v > 0n) { if (v & 1n) c++; v >>= 1n; } return c; })())`,
|
|
21
|
+
RotateLeft64: ([x, k]) =>
|
|
22
|
+
`((() => { const s = BigInt(${k}) & 63n; const v = BigInt.asUintN(64, BigInt(${x})); return BigInt.asUintN(64, (v << s) | (v >> ((64n - s) & 63n))); })())`,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** @type {ThisType<CodeGen>} */
|
|
26
|
+
export const bitsMethods = {
|
|
27
|
+
_genBits(fn, a) {
|
|
28
|
+
const args = a();
|
|
29
|
+
const gen = BITS_MAP[fn];
|
|
30
|
+
return gen ? gen(args) : undefined;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// CodeGen stdlib call generation — installed as a mixin on CodeGen.prototype.
|
|
2
2
|
// One file per Go package under ./stdlib/. This index merges them.
|
|
3
3
|
|
|
4
|
+
import { bitsMethods } from "./bits.js";
|
|
4
5
|
import { builderMethods } from "./builder.js";
|
|
5
6
|
import { bytesMethods } from "./bytes.js";
|
|
6
7
|
import { errorsMethods } from "./errors.js";
|
|
@@ -34,6 +35,7 @@ const STDLIB_METHOD_MAP = {
|
|
|
34
35
|
strconv: "_genStrconv",
|
|
35
36
|
sort: "_genSort",
|
|
36
37
|
math: "_genMath",
|
|
38
|
+
bits: "_genBits",
|
|
37
39
|
unicode: "_genUnicode",
|
|
38
40
|
os: "_genOs",
|
|
39
41
|
errors: "_genErrors",
|
|
@@ -76,6 +78,7 @@ const dispatchMethods = {
|
|
|
76
78
|
|
|
77
79
|
/** @type {ThisType<CodeGen>} */
|
|
78
80
|
export const stdlibGenMethods = {
|
|
81
|
+
...bitsMethods,
|
|
79
82
|
...builderMethods,
|
|
80
83
|
...bytesMethods,
|
|
81
84
|
...errorsMethods,
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Mounts an HTML/expression tree onto a parent DOM node by emitting plain
|
|
3
3
|
// document.createElement / appendChild / setAttribute calls.
|
|
4
4
|
|
|
5
|
-
import { Parser } from "
|
|
6
|
-
import { T, Token } from "
|
|
5
|
+
import { Parser } from "../../parser/index.js";
|
|
6
|
+
import { T, Token } from "../../tokens.js";
|
|
7
7
|
|
|
8
8
|
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
9
9
|
|
|
@@ -26,6 +26,35 @@ export const templGenMethods = {
|
|
|
26
26
|
this.line("}");
|
|
27
27
|
},
|
|
28
28
|
|
|
29
|
+
genCssDecl(decl) {
|
|
30
|
+
if (decl.params && decl.params.length > 0) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`css ${decl.name}: parameterized css declarations are not yet supported`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const className = this.getCssClassName(decl);
|
|
36
|
+
const scopedCss = `.${className} {\n${decl.cssText.trim()}\n}`;
|
|
37
|
+
this.collectedCss.push(scopedCss);
|
|
38
|
+
|
|
39
|
+
this.line(`function ${decl.name}() {`);
|
|
40
|
+
this.indented(() => {
|
|
41
|
+
this.line(`return ${JSON.stringify(className)};`);
|
|
42
|
+
});
|
|
43
|
+
this.line("}");
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
getCssClassName(decl) {
|
|
47
|
+
if (decl._className) return decl._className;
|
|
48
|
+
let h = 2166136261 >>> 0;
|
|
49
|
+
const str = `${decl._pkgName || ""}_${decl.name}_${decl.cssText}`;
|
|
50
|
+
for (let i = 0; i < str.length; i++) {
|
|
51
|
+
h = Math.imul(h ^ str.charCodeAt(i), 16777619) >>> 0;
|
|
52
|
+
}
|
|
53
|
+
const hash = (h >>> 0).toString(36);
|
|
54
|
+
decl._className = `gfc_${decl.name}_${hash}`;
|
|
55
|
+
return decl._className;
|
|
56
|
+
},
|
|
57
|
+
|
|
29
58
|
// Emit mount statements for an array of TemplNodes into parent variable `p`.
|
|
30
59
|
_genTemplNodes(nodes, p) {
|
|
31
60
|
if (!nodes || nodes.length === 0) return;
|