gofront 1.6.1 → 1.7.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 +35 -39
- package/package.json +37 -4
- package/src/backend/js/expressions.js +323 -37
- package/src/backend/js/index.js +137 -49
- package/src/backend/js/runtime.js +570 -6
- package/src/backend/js/statements.js +36 -6
- package/src/backend/js/stdlib/errors.js +5 -0
- package/src/backend/js/stdlib/index.js +12 -0
- package/src/backend/js/stdlib/json.js +972 -0
- package/src/backend/js/stdlib/simd.js +24 -0
- package/src/backend/wasm/boundary.js +18 -0
- package/src/backend/wasm/emit-builtins.js +17 -0
- package/src/backend/wasm/emit-exprs.js +11 -0
- package/src/backend/wasm/emit.js +9 -0
- package/src/backend/wasm/encode.js +85 -0
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/stdlib/index.js +3 -0
- package/src/backend/wasm/stdlib/simd.js +234 -0
- package/src/backend/wasm/types.js +21 -0
- package/src/backend/wasm/wat.js +37 -0
- package/src/dts-parser.js +71 -14
- package/src/lower/boxing.js +1 -6
- package/src/lower/embedding.js +2 -1
- package/src/parser/struct-tag.js +144 -0
- package/src/parser/types.js +63 -6
- package/src/typechecker/assignability.js +45 -0
- package/src/typechecker/expressions.js +66 -14
- package/src/typechecker/index.js +35 -8
- package/src/typechecker/resolve.js +151 -2
- package/src/typechecker/statements.js +68 -9
- package/src/typechecker/stdlib/extended.js +37 -0
- package/src/typechecker/stdlib/simd.js +73 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +18 -3
- package/src/vendor.js +86 -11
package/src/typechecker/index.js
CHANGED
|
@@ -57,6 +57,7 @@ export class TypeChecker {
|
|
|
57
57
|
this._typeSwitchDepth = 0; // for rejecting fallthrough in type switch
|
|
58
58
|
this._deferCount = 0; // tracks defer usage in current function body
|
|
59
59
|
this._imports = []; // tracked imports for unused-import detection
|
|
60
|
+
this._constExprs = new Map(); // name -> AST initializer expr
|
|
60
61
|
this._setupGlobals();
|
|
61
62
|
// Register error as a named type (interface)
|
|
62
63
|
this.types.set("error", ERROR);
|
|
@@ -220,7 +221,11 @@ export class TypeChecker {
|
|
|
220
221
|
const type = spec.type
|
|
221
222
|
? this.resolveTypeNode(spec.type, this.globals)
|
|
222
223
|
: ANY;
|
|
223
|
-
for (
|
|
224
|
+
for (let i = 0; i < spec.names.length; i++) {
|
|
225
|
+
const name = spec.names[i];
|
|
226
|
+
const expr = spec.value?.[i] ?? spec.value?.[0] ?? null;
|
|
227
|
+
this.globals.defineConst(name, type, expr);
|
|
228
|
+
}
|
|
224
229
|
}
|
|
225
230
|
}
|
|
226
231
|
|
|
@@ -510,7 +515,8 @@ export class TypeChecker {
|
|
|
510
515
|
const struct = type.underlying;
|
|
511
516
|
if (struct?.kind === "struct" && struct._embeds) {
|
|
512
517
|
for (const embed of struct._embeds) {
|
|
513
|
-
const
|
|
518
|
+
const inner = embed.kind === "pointer" ? embed.base : embed;
|
|
519
|
+
const base = inner.kind === "named" ? inner.underlying : inner;
|
|
514
520
|
if (base?.kind !== "struct" || !base.methods) continue;
|
|
515
521
|
for (const [mName, mType] of base.methods.entries()) {
|
|
516
522
|
if (!struct.methods.has(mName)) struct.methods.set(mName, mType);
|
|
@@ -899,14 +905,31 @@ export class TypeChecker {
|
|
|
899
905
|
let type = spec.type ? this.resolveTypeNode(spec.type, scope) : null;
|
|
900
906
|
if (spec.value) {
|
|
901
907
|
const valTypes = spec.value.map((v) => this.checkExpr(v, scope));
|
|
902
|
-
|
|
908
|
+
const isTuple = valTypes.length === 1 && valTypes[0]?.kind === "tuple";
|
|
909
|
+
const valTypesFlat = isTuple ? valTypes[0].types : valTypes;
|
|
910
|
+
if (spec.names.length !== valTypesFlat.length) {
|
|
911
|
+
const vars =
|
|
912
|
+
spec.names.length === 1
|
|
913
|
+
? "1 variable"
|
|
914
|
+
: `${spec.names.length} variables`;
|
|
915
|
+
const vals = isTuple
|
|
916
|
+
? `returns ${valTypesFlat.length} values`
|
|
917
|
+
: valTypesFlat.length === 1
|
|
918
|
+
? "1 value"
|
|
919
|
+
: `${valTypesFlat.length} values`;
|
|
920
|
+
this.err(`assignment mismatch: ${vars} but ${vals}`, spec);
|
|
921
|
+
}
|
|
903
922
|
for (let i = 0; i < spec.names.length; i++) {
|
|
904
|
-
const vt =
|
|
905
|
-
|
|
923
|
+
const vt = valTypesFlat[i] ?? ANY;
|
|
924
|
+
const varType = type ?? defaultType(vt) ?? ANY;
|
|
925
|
+
if (type)
|
|
926
|
+
this.assertAssignable(type, vt, spec.value[i] ?? spec.value[0]);
|
|
927
|
+
scope.defineLocal(spec.names[i], varType);
|
|
906
928
|
}
|
|
929
|
+
} else {
|
|
930
|
+
if (!type) type = ANY;
|
|
931
|
+
for (const name of spec.names) scope.defineLocal(name, type);
|
|
907
932
|
}
|
|
908
|
-
if (!type) type = ANY;
|
|
909
|
-
for (const name of spec.names) scope.defineLocal(name, type);
|
|
910
933
|
}
|
|
911
934
|
this._sharedAllocOk = savedSharedAlloc;
|
|
912
935
|
}
|
|
@@ -918,7 +941,11 @@ export class TypeChecker {
|
|
|
918
941
|
const type = spec.type
|
|
919
942
|
? this.resolveTypeNode(spec.type, scope)
|
|
920
943
|
: (valTypes[0] ?? ANY);
|
|
921
|
-
for (
|
|
944
|
+
for (let i = 0; i < spec.names.length; i++) {
|
|
945
|
+
const name = spec.names[i];
|
|
946
|
+
const expr = spec.value?.[i] ?? spec.value?.[0] ?? null;
|
|
947
|
+
scope.defineConst(name, type, expr);
|
|
948
|
+
}
|
|
922
949
|
}
|
|
923
950
|
}
|
|
924
951
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Type resolution methods — resolveTypeNode, resolveType, fieldType, generics.
|
|
2
2
|
// Installed as a mixin on TypeChecker.prototype.
|
|
3
3
|
|
|
4
|
+
import { parseJsonTag } from "../parser/struct-tag.js";
|
|
4
5
|
import {
|
|
5
6
|
ANY,
|
|
6
7
|
BASIC_TYPES,
|
|
@@ -209,10 +210,28 @@ export const resolveMethods = {
|
|
|
209
210
|
_resolveStructType(node, scope) {
|
|
210
211
|
const fields = new Map();
|
|
211
212
|
const embeds = [];
|
|
213
|
+
const structFields = [];
|
|
212
214
|
for (const f of node.fields) {
|
|
213
215
|
const ft = this.resolveTypeNode(f.type, scope);
|
|
216
|
+
structFields.push({
|
|
217
|
+
names: f.names,
|
|
218
|
+
type: ft,
|
|
219
|
+
tag: f.tag ?? null,
|
|
220
|
+
embedded: Boolean(f.embedded),
|
|
221
|
+
astType: f.type,
|
|
222
|
+
});
|
|
214
223
|
if (f.embedded) {
|
|
215
|
-
const
|
|
224
|
+
const inner = ft.kind === "pointer" ? ft.base : ft;
|
|
225
|
+
const embedName = (
|
|
226
|
+
f.astType?.name ??
|
|
227
|
+
f.type?.name ??
|
|
228
|
+
f.type?.base?.name ??
|
|
229
|
+
(inner.kind === "named" ? inner.name : "")
|
|
230
|
+
)
|
|
231
|
+
.split(".")
|
|
232
|
+
.pop();
|
|
233
|
+
if (embedName) fields.set(embedName, ft);
|
|
234
|
+
const base = inner.kind === "named" ? inner.underlying : inner;
|
|
216
235
|
if (base?.kind === "struct") {
|
|
217
236
|
for (const [k, v] of base.fields.entries()) fields.set(k, v);
|
|
218
237
|
}
|
|
@@ -221,7 +240,13 @@ export const resolveMethods = {
|
|
|
221
240
|
for (const n of f.names) fields.set(n, ft);
|
|
222
241
|
}
|
|
223
242
|
}
|
|
224
|
-
return {
|
|
243
|
+
return {
|
|
244
|
+
kind: "struct",
|
|
245
|
+
fields,
|
|
246
|
+
methods: new Map(),
|
|
247
|
+
_embeds: embeds,
|
|
248
|
+
_structFields: structFields,
|
|
249
|
+
};
|
|
225
250
|
},
|
|
226
251
|
|
|
227
252
|
_resolveInterfaceType(node, scope) {
|
|
@@ -434,6 +459,7 @@ export const resolveMethods = {
|
|
|
434
459
|
case "namespace":
|
|
435
460
|
return this._fieldTypeNamespace(base, field, node);
|
|
436
461
|
case "shared":
|
|
462
|
+
case "simd":
|
|
437
463
|
if (base.methods?.has(field)) return base.methods.get(field);
|
|
438
464
|
return this.err(`No method '${field}' on ${typeStr(baseType)}`, node);
|
|
439
465
|
case "slice":
|
|
@@ -537,4 +563,127 @@ export const resolveMethods = {
|
|
|
537
563
|
}
|
|
538
564
|
return this.err(`No member '${field}' in namespace ${base.name}`, node);
|
|
539
565
|
},
|
|
566
|
+
|
|
567
|
+
resolveJsonFields(structType) {
|
|
568
|
+
return resolveJsonFields(structType);
|
|
569
|
+
},
|
|
540
570
|
};
|
|
571
|
+
|
|
572
|
+
function isExported(name) {
|
|
573
|
+
if (!name || typeof name !== "string") return false;
|
|
574
|
+
const code = name.charCodeAt(0);
|
|
575
|
+
return code >= 65 && code <= 90;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function _processJsonField(f, depth, path, visited, queue, candidates) {
|
|
579
|
+
const tagInfo = parseJsonTag(f.tag);
|
|
580
|
+
if (tagInfo.ignored) return;
|
|
581
|
+
|
|
582
|
+
if (f.embedded) {
|
|
583
|
+
const isPtr = f.type.kind === "pointer" || f.type.kind === "PointerType";
|
|
584
|
+
const inner = isPtr ? f.type.base : f.type;
|
|
585
|
+
const embedStruct = inner.kind === "named" ? inner.underlying : inner;
|
|
586
|
+
const embedName = (
|
|
587
|
+
f.astType?.name ??
|
|
588
|
+
f.astType?.base?.name ??
|
|
589
|
+
inner.name ??
|
|
590
|
+
""
|
|
591
|
+
)
|
|
592
|
+
.split(".")
|
|
593
|
+
.pop();
|
|
594
|
+
|
|
595
|
+
if (tagInfo.name !== null) {
|
|
596
|
+
if (isExported(embedName)) {
|
|
597
|
+
candidates.push({
|
|
598
|
+
jsonKey: tagInfo.name,
|
|
599
|
+
goName: embedName,
|
|
600
|
+
type: f.type,
|
|
601
|
+
omitempty: tagInfo.omitempty,
|
|
602
|
+
depth,
|
|
603
|
+
tagged: true,
|
|
604
|
+
isEmbedded: true,
|
|
605
|
+
path: [...path, { name: embedName, isPtr, typeName: embedName }],
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
} else if (embedStruct?.kind === "struct") {
|
|
609
|
+
if (!visited.has(embedStruct)) {
|
|
610
|
+
const nextVisited = new Set(visited);
|
|
611
|
+
nextVisited.add(embedStruct);
|
|
612
|
+
queue.push({
|
|
613
|
+
struct: embedStruct,
|
|
614
|
+
depth: depth + 1,
|
|
615
|
+
path: [...path, { name: embedName, isPtr, typeName: embedName }],
|
|
616
|
+
visited: nextVisited,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
for (const name of f.names) {
|
|
624
|
+
if (!isExported(name)) continue;
|
|
625
|
+
candidates.push({
|
|
626
|
+
jsonKey: tagInfo.name !== null ? tagInfo.name : name,
|
|
627
|
+
goName: name,
|
|
628
|
+
type: f.type,
|
|
629
|
+
omitempty: tagInfo.omitempty,
|
|
630
|
+
quoted: tagInfo.options.includes("string"),
|
|
631
|
+
depth,
|
|
632
|
+
tagged: tagInfo.name !== null,
|
|
633
|
+
path: [...path, { name, isPtr: false }],
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function _resolveCandidateCollisions(candidates) {
|
|
639
|
+
const byKey = new Map();
|
|
640
|
+
for (const cand of candidates) {
|
|
641
|
+
if (!byKey.has(cand.jsonKey)) byKey.set(cand.jsonKey, []);
|
|
642
|
+
byKey.get(cand.jsonKey).push(cand);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const winningFields = [];
|
|
646
|
+
for (const [, group] of byKey.entries()) {
|
|
647
|
+
let minDepth = Infinity;
|
|
648
|
+
for (const c of group) {
|
|
649
|
+
if (c.depth < minDepth) minDepth = c.depth;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const atMin = group.filter((c) => c.depth === minDepth);
|
|
653
|
+
if (atMin.length === 1) {
|
|
654
|
+
winningFields.push(atMin[0]);
|
|
655
|
+
} else {
|
|
656
|
+
const tagged = atMin.filter((c) => c.tagged);
|
|
657
|
+
if (tagged.length === 1) winningFields.push(tagged[0]);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
winningFields.sort((a, b) => candidates.indexOf(a) - candidates.indexOf(b));
|
|
662
|
+
return winningFields;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export function resolveJsonFields(structType) {
|
|
666
|
+
if (!structType) return [];
|
|
667
|
+
const underlying =
|
|
668
|
+
structType.kind === "named" ? structType.underlying : structType;
|
|
669
|
+
if (underlying?.kind !== "struct") return [];
|
|
670
|
+
if (underlying._jsonFields) return underlying._jsonFields;
|
|
671
|
+
|
|
672
|
+
const queue = [
|
|
673
|
+
{ struct: underlying, depth: 0, path: [], visited: new Set([underlying]) },
|
|
674
|
+
];
|
|
675
|
+
const candidates = [];
|
|
676
|
+
|
|
677
|
+
while (queue.length > 0) {
|
|
678
|
+
const { struct: curr, depth, path, visited } = queue.shift();
|
|
679
|
+
if (curr?.kind !== "struct") continue;
|
|
680
|
+
|
|
681
|
+
for (const f of curr._structFields || []) {
|
|
682
|
+
_processJsonField(f, depth, path, visited, queue, candidates);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const winning = _resolveCandidateCollisions(candidates);
|
|
687
|
+
underlying._jsonFields = winning;
|
|
688
|
+
return winning;
|
|
689
|
+
}
|
|
@@ -83,6 +83,29 @@ export const statementCheckMethods = {
|
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
85
|
|
|
86
|
+
_checkDefineCommaOkMap(stmt, scope) {
|
|
87
|
+
if (
|
|
88
|
+
stmt.lhs.length !== 2 ||
|
|
89
|
+
stmt.rhs.length !== 1 ||
|
|
90
|
+
stmt.rhs[0].kind !== "IndexExpr"
|
|
91
|
+
)
|
|
92
|
+
return false;
|
|
93
|
+
const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
|
|
94
|
+
const resolvedBase =
|
|
95
|
+
mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
|
|
96
|
+
if (resolvedBase?.kind !== "map") return false;
|
|
97
|
+
this.checkExpr(stmt.rhs[0].index, scope);
|
|
98
|
+
stmt._commaOkMap = true;
|
|
99
|
+
stmt.rhs[0]._mapValueType = resolvedBase.value;
|
|
100
|
+
this._declareDefineVars(stmt, [resolvedBase.value, BOOL], scope);
|
|
101
|
+
const allRedecl = stmt.lhs.every((e) => e._redecl || (e.name ?? e) === "_");
|
|
102
|
+
if (allRedecl && stmt.lhs.length > 0) {
|
|
103
|
+
this.err("no new variables on left side of :=", stmt.lhs[0]);
|
|
104
|
+
}
|
|
105
|
+
stmt._rhsTypes = [resolvedBase.value, BOOL];
|
|
106
|
+
return true;
|
|
107
|
+
},
|
|
108
|
+
|
|
86
109
|
_checkDefineStmt(stmt, scope) {
|
|
87
110
|
// comma-ok type assertion: n, ok := x.(T)
|
|
88
111
|
if (
|
|
@@ -92,10 +115,24 @@ export const statementCheckMethods = {
|
|
|
92
115
|
) {
|
|
93
116
|
stmt.rhs[0]._commaOk = true;
|
|
94
117
|
}
|
|
118
|
+
if (this._checkDefineCommaOkMap(stmt, scope)) return;
|
|
95
119
|
const rhs = stmt.rhs.map((e) => this.checkExpr(e, scope));
|
|
96
|
-
const
|
|
97
|
-
|
|
120
|
+
const isTuple = rhs.length === 1 && rhs[0]?.kind === "tuple";
|
|
121
|
+
const rhsFlat = isTuple ? rhs[0].types : rhs;
|
|
98
122
|
if (this._checkDefineCommaOkAssert(stmt, rhs, scope)) return;
|
|
123
|
+
if (
|
|
124
|
+
stmt.rhs[0]?.kind !== "RangeExpr" &&
|
|
125
|
+
stmt.lhs.length !== rhsFlat.length
|
|
126
|
+
) {
|
|
127
|
+
const vars =
|
|
128
|
+
stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
|
|
129
|
+
const vals = isTuple
|
|
130
|
+
? `returns ${rhsFlat.length} values`
|
|
131
|
+
: rhsFlat.length === 1
|
|
132
|
+
? "1 value"
|
|
133
|
+
: `${rhsFlat.length} values`;
|
|
134
|
+
this.err(`assignment mismatch: ${vars} but ${vals}`, stmt);
|
|
135
|
+
}
|
|
99
136
|
this._declareDefineVars(stmt, rhsFlat, scope);
|
|
100
137
|
const allRedecl = stmt.lhs.every((e) => e._redecl || (e.name ?? e) === "_");
|
|
101
138
|
if (allRedecl && stmt.lhs.length > 0) {
|
|
@@ -110,15 +147,15 @@ export const statementCheckMethods = {
|
|
|
110
147
|
stmt.rhs.length !== 1 ||
|
|
111
148
|
stmt.rhs[0].kind !== "IndexExpr"
|
|
112
149
|
)
|
|
113
|
-
return
|
|
150
|
+
return null;
|
|
114
151
|
const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
|
|
115
152
|
const resolvedBase =
|
|
116
153
|
mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
|
|
117
|
-
if (resolvedBase?.kind !== "map") return
|
|
154
|
+
if (resolvedBase?.kind !== "map") return null;
|
|
118
155
|
this.checkExpr(stmt.rhs[0].index, scope);
|
|
119
156
|
stmt._commaOkMap = true;
|
|
120
157
|
stmt.rhs[0]._mapValueType = resolvedBase.value;
|
|
121
|
-
return
|
|
158
|
+
return [resolvedBase.value, BOOL];
|
|
122
159
|
},
|
|
123
160
|
|
|
124
161
|
_checkConstAssignment(stmt, scope) {
|
|
@@ -163,12 +200,34 @@ export const statementCheckMethods = {
|
|
|
163
200
|
},
|
|
164
201
|
|
|
165
202
|
_checkAssignStmt(stmt, scope) {
|
|
166
|
-
|
|
203
|
+
const commaOkMapTypes = this._checkAssignCommaOkMap(stmt, scope);
|
|
167
204
|
if (this._isCommaOkTypeAssertAssign(stmt)) stmt.rhs[0]._commaOk = true;
|
|
168
205
|
this._checkConstAssignment(stmt, scope);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
206
|
+
let rhsFlat;
|
|
207
|
+
if (commaOkMapTypes) {
|
|
208
|
+
rhsFlat = commaOkMapTypes;
|
|
209
|
+
} else {
|
|
210
|
+
const rhs = stmt.rhs.map((e) => this.checkExpr(e, scope));
|
|
211
|
+
const isTuple = rhs.length === 1 && rhs[0]?.kind === "tuple";
|
|
212
|
+
rhsFlat = isTuple ? rhs[0].types : rhs;
|
|
213
|
+
if (
|
|
214
|
+
!stmt.rhs[0]?._commaOk &&
|
|
215
|
+
stmt.rhs[0]?.kind !== "RangeExpr" &&
|
|
216
|
+
stmt.lhs.length !== rhsFlat.length
|
|
217
|
+
) {
|
|
218
|
+
const vars =
|
|
219
|
+
stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
|
|
220
|
+
const vals = isTuple
|
|
221
|
+
? `returns ${rhsFlat.length} values`
|
|
222
|
+
: rhsFlat.length === 1
|
|
223
|
+
? "1 value"
|
|
224
|
+
: `${rhsFlat.length} values`;
|
|
225
|
+
this.err(`assignment mismatch: ${vars} but ${vals}`, stmt);
|
|
226
|
+
}
|
|
227
|
+
if (stmt.rhs[0]?._commaOk) {
|
|
228
|
+
rhsFlat = [rhs[0], BOOL];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
172
231
|
const lhsTypes = stmt.lhs.map((e) => this._checkAssignLhsType(e, scope));
|
|
173
232
|
this._checkAssignTypes(stmt, rhsFlat, lhsTypes);
|
|
174
233
|
},
|
|
@@ -853,6 +853,43 @@ export function setupExtendedGlobals(globals, types) {
|
|
|
853
853
|
types.set("encoding/base64.Encoding", BASE64_ENCODING_T);
|
|
854
854
|
types.set("base64.Encoding", BASE64_ENCODING_T);
|
|
855
855
|
|
|
856
|
+
// ── encoding/json ─────────────────────────────────────────────
|
|
857
|
+
const RAW_MESSAGE_T = {
|
|
858
|
+
kind: "named",
|
|
859
|
+
name: "json.RawMessage",
|
|
860
|
+
underlying: BYTE_SLICE,
|
|
861
|
+
methods: new Map(),
|
|
862
|
+
};
|
|
863
|
+
globals.define("json", {
|
|
864
|
+
kind: "namespace",
|
|
865
|
+
name: "json",
|
|
866
|
+
members: {
|
|
867
|
+
Marshal: {
|
|
868
|
+
kind: "func",
|
|
869
|
+
params: [ANY],
|
|
870
|
+
returns: [BYTE_SLICE, ERROR],
|
|
871
|
+
},
|
|
872
|
+
MarshalIndent: {
|
|
873
|
+
kind: "func",
|
|
874
|
+
params: [ANY, STRING, STRING],
|
|
875
|
+
returns: [BYTE_SLICE, ERROR],
|
|
876
|
+
},
|
|
877
|
+
Unmarshal: {
|
|
878
|
+
kind: "func",
|
|
879
|
+
params: [BYTE_SLICE, ANY],
|
|
880
|
+
returns: [ERROR],
|
|
881
|
+
},
|
|
882
|
+
Valid: {
|
|
883
|
+
kind: "func",
|
|
884
|
+
params: [BYTE_SLICE],
|
|
885
|
+
returns: [BOOL],
|
|
886
|
+
},
|
|
887
|
+
RawMessage: RAW_MESSAGE_T,
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
types.set("encoding/json.RawMessage", RAW_MESSAGE_T);
|
|
891
|
+
types.set("json.RawMessage", RAW_MESSAGE_T);
|
|
892
|
+
|
|
856
893
|
for (const name of [
|
|
857
894
|
"append",
|
|
858
895
|
"len",
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/typechecker/stdlib/simd.js
|
|
2
|
+
// `gofront/simd` — WebAssembly SIMD types and vector operations.
|
|
3
|
+
//
|
|
4
|
+
// F32x4 is a 128-bit 4-wide float32 vector with Go value semantics:
|
|
5
|
+
// - Lowered to WASM v128 in wasm packages.
|
|
6
|
+
// - Lowered to a frozen 4-number array with Math.fround in js packages.
|
|
7
|
+
|
|
8
|
+
import { FLOAT32, INT } from "../types.js";
|
|
9
|
+
|
|
10
|
+
export function setupSimdGlobals(globals, types) {
|
|
11
|
+
const f32x4Type = {
|
|
12
|
+
kind: "named",
|
|
13
|
+
name: "simd.F32x4",
|
|
14
|
+
underlying: {
|
|
15
|
+
kind: "simd",
|
|
16
|
+
simd: "F32x4",
|
|
17
|
+
elem: FLOAT32,
|
|
18
|
+
lanes: 4,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
f32x4Type.underlying.methods = new Map([
|
|
23
|
+
["Add", { kind: "func", params: [f32x4Type], returns: [f32x4Type] }],
|
|
24
|
+
["Sub", { kind: "func", params: [f32x4Type], returns: [f32x4Type] }],
|
|
25
|
+
["Mul", { kind: "func", params: [f32x4Type], returns: [f32x4Type] }],
|
|
26
|
+
["Div", { kind: "func", params: [f32x4Type], returns: [f32x4Type] }],
|
|
27
|
+
["Min", { kind: "func", params: [f32x4Type], returns: [f32x4Type] }],
|
|
28
|
+
["Max", { kind: "func", params: [f32x4Type], returns: [f32x4Type] }],
|
|
29
|
+
["Dot3", { kind: "func", params: [f32x4Type], returns: [FLOAT32] }],
|
|
30
|
+
["Dot4", { kind: "func", params: [f32x4Type], returns: [FLOAT32] }],
|
|
31
|
+
["Lane", { kind: "func", params: [INT], returns: [FLOAT32] }],
|
|
32
|
+
[
|
|
33
|
+
"WithLane",
|
|
34
|
+
{ kind: "func", params: [INT, FLOAT32], returns: [f32x4Type] },
|
|
35
|
+
],
|
|
36
|
+
["Splat", { kind: "func", params: [FLOAT32], returns: [f32x4Type] }],
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
types.set("simd.F32x4", f32x4Type);
|
|
40
|
+
types.set("F32x4", f32x4Type);
|
|
41
|
+
|
|
42
|
+
const sharedFloat32 = types.get("shared.Float32") ?? {
|
|
43
|
+
kind: "named",
|
|
44
|
+
name: "shared.Float32",
|
|
45
|
+
underlying: { kind: "shared", elem: FLOAT32, shared: "Float32" },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const members = {
|
|
49
|
+
F32x4: f32x4Type,
|
|
50
|
+
NewF32x4: {
|
|
51
|
+
kind: "func",
|
|
52
|
+
params: [FLOAT32, FLOAT32, FLOAT32, FLOAT32],
|
|
53
|
+
returns: [f32x4Type],
|
|
54
|
+
},
|
|
55
|
+
Splat: {
|
|
56
|
+
kind: "func",
|
|
57
|
+
params: [FLOAT32],
|
|
58
|
+
returns: [f32x4Type],
|
|
59
|
+
},
|
|
60
|
+
LoadF32x4: {
|
|
61
|
+
kind: "func",
|
|
62
|
+
params: [sharedFloat32, INT],
|
|
63
|
+
returns: [f32x4Type],
|
|
64
|
+
},
|
|
65
|
+
StoreF32x4: {
|
|
66
|
+
kind: "func",
|
|
67
|
+
params: [sharedFloat32, INT, f32x4Type],
|
|
68
|
+
returns: [],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
globals.define("simd", { kind: "namespace", name: "simd", members });
|
|
73
|
+
}
|
|
@@ -10,11 +10,13 @@
|
|
|
10
10
|
import { setupCoreGlobals } from "./stdlib/core.js";
|
|
11
11
|
import { setupExtendedGlobals } from "./stdlib/extended.js";
|
|
12
12
|
import { setupSharedGlobals } from "./stdlib/shared.js";
|
|
13
|
+
import { setupSimdGlobals } from "./stdlib/simd.js";
|
|
13
14
|
import { setupWebGlobals } from "./stdlib/web.js";
|
|
14
15
|
|
|
15
16
|
export function setupGlobals(globals, types) {
|
|
16
17
|
setupCoreGlobals(globals, types);
|
|
17
18
|
setupExtendedGlobals(globals, types);
|
|
18
19
|
setupSharedGlobals(globals, types);
|
|
20
|
+
setupSimdGlobals(globals, types);
|
|
19
21
|
setupWebGlobals(globals, types);
|
|
20
22
|
}
|
package/src/typechecker/types.js
CHANGED
|
@@ -126,6 +126,7 @@ export const WASM_SUPPORTED_STDLIB = new Set([
|
|
|
126
126
|
"time",
|
|
127
127
|
"testing",
|
|
128
128
|
"gofront/shared",
|
|
129
|
+
"gofront/simd",
|
|
129
130
|
"sync",
|
|
130
131
|
"crypto/rand",
|
|
131
132
|
"crypto/sha256",
|
|
@@ -139,12 +140,14 @@ export const STDLIB_PACKAGES = new Set([
|
|
|
139
140
|
"os",
|
|
140
141
|
"gom",
|
|
141
142
|
"regexp",
|
|
143
|
+
"encoding/json",
|
|
142
144
|
]);
|
|
143
145
|
|
|
144
146
|
// Per-namespace member subset the WASM backend implements. `null` means every
|
|
145
147
|
// member the checker knows about. Members outside the set are rejected in
|
|
146
148
|
// wasm/both packages with "'strings.Split' is not yet available in wasm packages".
|
|
147
149
|
export const WASM_STDLIB_MEMBERS = new Map([
|
|
150
|
+
["json", new Set()],
|
|
148
151
|
[
|
|
149
152
|
"math",
|
|
150
153
|
new Set([
|
|
@@ -839,8 +842,13 @@ export function typeStr(t) {
|
|
|
839
842
|
return t.name || "interface{...}";
|
|
840
843
|
case "namespace":
|
|
841
844
|
return t.name || "namespace{...}";
|
|
842
|
-
case "func":
|
|
843
|
-
|
|
845
|
+
case "func": {
|
|
846
|
+
const retStr =
|
|
847
|
+
t.returns.length > 1
|
|
848
|
+
? `(${t.returns.map(typeStr).join(", ")})`
|
|
849
|
+
: typeStr(t.returns[0] ?? VOID);
|
|
850
|
+
return `func(${t.params.map(typeStr).join(", ")}) ${retStr}`;
|
|
851
|
+
}
|
|
844
852
|
case "tuple":
|
|
845
853
|
return `(${t.types.map(typeStr).join(", ")})`;
|
|
846
854
|
case "named":
|
|
@@ -887,6 +895,7 @@ export class Scope {
|
|
|
887
895
|
this.parent = parent;
|
|
888
896
|
this.symbols = new Map();
|
|
889
897
|
this._consts = new Set(); // names declared as const in this scope
|
|
898
|
+
this._constExprs = new Map(); // name -> AST initializer expr
|
|
890
899
|
this._locals = new Set(); // names declared as local variables (var / :=)
|
|
891
900
|
this._used = new Set(); // names referenced in this scope
|
|
892
901
|
}
|
|
@@ -897,9 +906,15 @@ export class Scope {
|
|
|
897
906
|
this.symbols.set(name, type);
|
|
898
907
|
if (name !== "_") this._locals.add(name);
|
|
899
908
|
}
|
|
900
|
-
defineConst(name, type) {
|
|
909
|
+
defineConst(name, type, expr = null) {
|
|
901
910
|
this.symbols.set(name, type);
|
|
902
911
|
this._consts.add(name);
|
|
912
|
+
if (expr) this._constExprs.set(name, expr);
|
|
913
|
+
}
|
|
914
|
+
lookupConstExpr(name) {
|
|
915
|
+
if (this._constExprs.has(name)) return this._constExprs.get(name);
|
|
916
|
+
if (this.parent) return this.parent.lookupConstExpr(name);
|
|
917
|
+
return null;
|
|
903
918
|
}
|
|
904
919
|
isConst(name) {
|
|
905
920
|
if (this._consts.has(name)) return true;
|