gofront 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +34 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +3 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
package/src/lower/ownership.js
CHANGED
|
@@ -32,6 +32,22 @@ export function nodeWritesVar(node, name) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// The variable whose address `node` takes without an explicit `&`, or null:
|
|
36
|
+
// a pointer-receiver method call or method value (`v.M()`, `v.M` with
|
|
37
|
+
// `func (*T) M`) takes `&v`, and slicing an array (`a[:]`) aliases it.
|
|
38
|
+
export function implicitAddrRoot(node) {
|
|
39
|
+
if (node.kind === "SelectorExpr" && node._type?._ptrRecv === true)
|
|
40
|
+
return rootIdentName(node.expr);
|
|
41
|
+
if (node.kind === "SliceExpr" && isArrayTyped(node.expr?._type))
|
|
42
|
+
return rootIdentName(node.expr);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isArrayTyped(t) {
|
|
47
|
+
while (t?.kind === "named") t = t.underlying;
|
|
48
|
+
return t?.kind === "array";
|
|
49
|
+
}
|
|
50
|
+
|
|
35
51
|
// True when `node` may modify (or take the address of) the value held by variable `name`.
|
|
36
52
|
export function nodeMutatesVar(node, name, addrOnly = false) {
|
|
37
53
|
if (!node || typeof node !== "object") return false;
|
|
@@ -43,11 +59,13 @@ export function nodeMutatesVar(node, name, addrOnly = false) {
|
|
|
43
59
|
rootIdentName(node.operand) === name
|
|
44
60
|
)
|
|
45
61
|
return true;
|
|
62
|
+
if (addrOnly && implicitAddrRoot(node) === name) return true;
|
|
46
63
|
if (!addrOnly && nodeWritesVar(node, name)) return true;
|
|
47
64
|
return someChild(node, (child) => nodeMutatesVar(child, name, addrOnly));
|
|
48
65
|
}
|
|
49
66
|
|
|
50
67
|
// Single-pass collector of all mutated or address-taken variables in an AST node.
|
|
68
|
+
// With `addrOnly`, only address-taken ones, implicit address-taking included.
|
|
51
69
|
export function collectMutatedVars(
|
|
52
70
|
node,
|
|
53
71
|
mutated = new Set(),
|
|
@@ -62,7 +80,10 @@ export function collectMutatedVars(
|
|
|
62
80
|
if (node.kind === "UnaryExpr" && node.op === "&") {
|
|
63
81
|
const root = rootIdentName(node.operand);
|
|
64
82
|
if (root) mutated.add(root);
|
|
65
|
-
} else if (
|
|
83
|
+
} else if (addrOnly) {
|
|
84
|
+
const root = implicitAddrRoot(node);
|
|
85
|
+
if (root) mutated.add(root);
|
|
86
|
+
} else {
|
|
66
87
|
if (node.kind === "AssignStmt") {
|
|
67
88
|
for (const e of node.lhs) {
|
|
68
89
|
const root = rootIdentName(e);
|
package/src/lower/range.js
CHANGED
|
@@ -1,9 +1,94 @@
|
|
|
1
1
|
// src/lower/range.js
|
|
2
2
|
// Classifies for-range loop shapes and variable scoping/redelcarations.
|
|
3
3
|
|
|
4
|
-
import { isNumeric } from "../typechecker/types.js";
|
|
4
|
+
import { isNumeric, iteratorYieldParams } from "../typechecker/types.js";
|
|
5
5
|
import { someChild } from "./walk.js";
|
|
6
6
|
|
|
7
|
+
let iterStateCounter = 0;
|
|
8
|
+
|
|
9
|
+
export function isIteratorRangeFor(stmt) {
|
|
10
|
+
if (stmt?.kind !== "ForStmt") return false;
|
|
11
|
+
const rangeExpr =
|
|
12
|
+
stmt.cond?.kind === "RangeExpr" ? stmt.cond : stmt.init?.rhs?.[0];
|
|
13
|
+
if (rangeExpr?.kind !== "RangeExpr") return false;
|
|
14
|
+
if (rangeExpr._isIterator) return true;
|
|
15
|
+
const iterType = rangeExpr.expr?._type;
|
|
16
|
+
return Boolean(iteratorYieldParams(iterType));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function buildYieldParamsAndAssignments(lhs, yieldTypes, isAssign) {
|
|
20
|
+
const params = [];
|
|
21
|
+
const assignStmts = [];
|
|
22
|
+
for (let i = 0; i < yieldTypes.length; i++) {
|
|
23
|
+
const yType = yieldTypes[i] ?? { kind: "basic", name: "any" };
|
|
24
|
+
const lhsElem = lhs[i];
|
|
25
|
+
if (!isAssign && lhsElem?.name && lhsElem.name !== "_") {
|
|
26
|
+
params.push({ name: lhsElem.name, type: yType });
|
|
27
|
+
} else {
|
|
28
|
+
const paramName = `__yield_p${i}`;
|
|
29
|
+
params.push({ name: paramName, type: yType });
|
|
30
|
+
if (isAssign && lhsElem && lhsElem.name !== "_") {
|
|
31
|
+
assignStmts.push({
|
|
32
|
+
kind: "AssignStmt",
|
|
33
|
+
lhs: [lhsElem],
|
|
34
|
+
rhs: [{ kind: "Ident", name: paramName, _type: yType }],
|
|
35
|
+
op: "=",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { params, assignStmts };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getOrCreateIteratorFuncLit(stmt) {
|
|
44
|
+
if (stmt._yieldFuncLit) return stmt._yieldFuncLit;
|
|
45
|
+
|
|
46
|
+
const rangeExpr =
|
|
47
|
+
stmt.cond?.kind === "RangeExpr" ? stmt.cond : stmt.init?.rhs?.[0];
|
|
48
|
+
const iterType = rangeExpr?.expr?._type;
|
|
49
|
+
const info = iteratorYieldParams(iterType);
|
|
50
|
+
const yieldTypes = rangeExpr?._yieldParams ?? info?.yieldParams ?? [];
|
|
51
|
+
|
|
52
|
+
if (!stmt._iterStateName) {
|
|
53
|
+
stmt._iterStateName = `__iterState$${++iterStateCounter}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const isAssign = stmt.init?.kind === "AssignStmt";
|
|
57
|
+
const lhs = stmt.init?.lhs ?? [];
|
|
58
|
+
const { params, assignStmts } = buildYieldParamsAndAssignments(
|
|
59
|
+
lhs,
|
|
60
|
+
yieldTypes,
|
|
61
|
+
isAssign,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const rawStmts = stmt.body?.stmts ?? stmt.body?.list ?? [];
|
|
65
|
+
const bodyStmts =
|
|
66
|
+
assignStmts.length > 0 ? [...assignStmts, ...rawStmts] : rawStmts;
|
|
67
|
+
|
|
68
|
+
const funcLit = {
|
|
69
|
+
kind: "FuncLit",
|
|
70
|
+
params,
|
|
71
|
+
returnType: { kind: "basic", name: "bool" },
|
|
72
|
+
body: {
|
|
73
|
+
kind: "Block",
|
|
74
|
+
stmts: bodyStmts,
|
|
75
|
+
list: bodyStmts,
|
|
76
|
+
},
|
|
77
|
+
_type: {
|
|
78
|
+
kind: "func",
|
|
79
|
+
params: params.map((p) => p.type),
|
|
80
|
+
returns: [{ kind: "basic", name: "bool" }],
|
|
81
|
+
},
|
|
82
|
+
_isYieldClosure: true,
|
|
83
|
+
_iterStateName: stmt._iterStateName,
|
|
84
|
+
_forStmt: stmt,
|
|
85
|
+
_line: stmt._line,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
stmt._yieldFuncLit = funcLit;
|
|
89
|
+
return funcLit;
|
|
90
|
+
}
|
|
91
|
+
|
|
7
92
|
export function isRangeFor(stmt) {
|
|
8
93
|
if (!stmt?.init) return false;
|
|
9
94
|
const init = stmt.init;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// src/lower/reachability.js
|
|
2
|
+
// Reachability for Go-source stdlib packages: only declarations reachable
|
|
3
|
+
// from user code are emitted, so an imported but unused package costs zero
|
|
4
|
+
// bytes (JS output and dev-mode WASM, where Binaryen does not run).
|
|
5
|
+
//
|
|
6
|
+
// Stdlib top-level names are mangled (`Compare$$cmp`) and user references to
|
|
7
|
+
// them are plain identifiers after type-checking, so reachability is a
|
|
8
|
+
// closure over names. A method is reachable when its receiver type is and
|
|
9
|
+
// its name is selected somewhere reachable, or is called implicitly.
|
|
10
|
+
|
|
11
|
+
import { forEachChild } from "./walk.js";
|
|
12
|
+
|
|
13
|
+
// Methods the runtime or emitter-backed stdlib call without a selector in
|
|
14
|
+
// GoFront code (fmt verbs, errors.Is/As/Unwrap, encoding/json).
|
|
15
|
+
const IMPLICIT_METHODS = new Set([
|
|
16
|
+
"Error",
|
|
17
|
+
"String",
|
|
18
|
+
"GoString",
|
|
19
|
+
"Format",
|
|
20
|
+
"Unwrap",
|
|
21
|
+
"Is",
|
|
22
|
+
"As",
|
|
23
|
+
"MarshalJSON",
|
|
24
|
+
"UnmarshalJSON",
|
|
25
|
+
"MarshalText",
|
|
26
|
+
"UnmarshalText",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function declNames(d) {
|
|
30
|
+
if (d.kind === "FuncDecl" || d.kind === "TypeDecl") return [d.name];
|
|
31
|
+
if (d.kind === "VarDecl" || d.kind === "ConstDecl")
|
|
32
|
+
return (d.decls ?? []).flatMap((spec) => spec.names ?? []);
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Names (identifiers and type names) and selected fields used under `node`.
|
|
37
|
+
function collectRefs(node, names, fields) {
|
|
38
|
+
if (!node || typeof node !== "object") return;
|
|
39
|
+
if (Array.isArray(node)) {
|
|
40
|
+
for (const n of node) collectRefs(n, names, fields);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (node._stdName) names.add(node._stdName); // `cmp.Compare` in user code
|
|
44
|
+
if (
|
|
45
|
+
node.kind === "Ident" ||
|
|
46
|
+
node.kind === "TypeName" ||
|
|
47
|
+
node.kind === "GenericTypeName"
|
|
48
|
+
)
|
|
49
|
+
names.add(node.name);
|
|
50
|
+
else if (node.kind === "SelectorExpr") fields.add(node.field);
|
|
51
|
+
forEachChild(node, (child) => collectRefs(child, names, fields));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function indexStdlib(stdlibPrograms) {
|
|
55
|
+
const byName = new Map(); // name -> decls
|
|
56
|
+
const methods = []; // { decl, recv }
|
|
57
|
+
const inits = new Map(); // program -> init decls
|
|
58
|
+
for (const p of stdlibPrograms) {
|
|
59
|
+
for (const d of p.decls ?? []) {
|
|
60
|
+
if (d.kind === "MethodDecl")
|
|
61
|
+
methods.push({ decl: d, recv: d.recvType?.name });
|
|
62
|
+
else if (d.kind === "FuncDecl" && d.name === "init")
|
|
63
|
+
inits.set(p, [...(inits.get(p) ?? []), d]);
|
|
64
|
+
for (const n of declNames(d)) {
|
|
65
|
+
if (!byName.has(n)) byName.set(n, []);
|
|
66
|
+
byName.get(n).push(d);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { byName, methods, inits };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const JSON_NAMES = [
|
|
74
|
+
"SyntaxError$$internal$jsonrt",
|
|
75
|
+
"UnmarshalTypeError$$internal$jsonrt",
|
|
76
|
+
"InvalidUnmarshalError$$internal$jsonrt",
|
|
77
|
+
"UnsupportedValueError$$internal$jsonrt",
|
|
78
|
+
"UnsupportedTypeError$$internal$jsonrt",
|
|
79
|
+
"MarshalerError$$internal$jsonrt",
|
|
80
|
+
"CheckValid$$internal$jsonrt",
|
|
81
|
+
"Number$$internal$jsonrt",
|
|
82
|
+
"OptEscapeHTML$$internal$jsonrt",
|
|
83
|
+
"OptUseNumber$$internal$jsonrt",
|
|
84
|
+
"OptDisallowUnknownFields$$internal$jsonrt",
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
function addExtraRoots(names) {
|
|
88
|
+
if (names.has("strconv")) names.add("NumError$$strconv");
|
|
89
|
+
if (names.has("json")) {
|
|
90
|
+
for (const n of JSON_NAMES) names.add(n);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns the set of declarations of `stdlibPrograms` reachable from
|
|
96
|
+
* `rootPrograms` (all user code that is emitted). A program's `init` is
|
|
97
|
+
* kept when anything else in that program is reached.
|
|
98
|
+
*/
|
|
99
|
+
export function reachableStdlibDecls(rootPrograms, stdlibPrograms) {
|
|
100
|
+
const { byName, methods, inits } = indexStdlib(stdlibPrograms);
|
|
101
|
+
const reached = new Set();
|
|
102
|
+
const names = new Set();
|
|
103
|
+
const fields = new Set(IMPLICIT_METHODS);
|
|
104
|
+
collectRefs(rootPrograms, names, fields);
|
|
105
|
+
addExtraRoots(names);
|
|
106
|
+
|
|
107
|
+
const pending = [...names];
|
|
108
|
+
const reach = (d) => {
|
|
109
|
+
if (reached.has(d)) return;
|
|
110
|
+
reached.add(d);
|
|
111
|
+
const local = new Set();
|
|
112
|
+
collectRefs(d, local, fields);
|
|
113
|
+
for (const n of local) {
|
|
114
|
+
if (names.has(n)) continue;
|
|
115
|
+
names.add(n);
|
|
116
|
+
pending.push(n);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
let changed = true;
|
|
120
|
+
while (changed) {
|
|
121
|
+
while (pending.length > 0)
|
|
122
|
+
for (const d of byName.get(pending.pop()) ?? []) reach(d);
|
|
123
|
+
const size = reached.size;
|
|
124
|
+
for (const m of methods)
|
|
125
|
+
if (names.has(m.recv) && fields.has(m.decl.name)) reach(m.decl);
|
|
126
|
+
for (const p of stdlibPrograms)
|
|
127
|
+
if (p.decls.some((d) => reached.has(d)))
|
|
128
|
+
for (const d of inits.get(p) ?? []) reach(d);
|
|
129
|
+
changed = reached.size !== size || pending.length > 0;
|
|
130
|
+
}
|
|
131
|
+
return reached;
|
|
132
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T } from "../tokens.js";
|
|
1
|
+
import { T } from "../lexer/tokens.js";
|
|
2
2
|
|
|
3
3
|
export const declarationParseMethods = {
|
|
4
4
|
parseTopDecl() {
|
|
@@ -56,7 +56,11 @@ export const declarationParseMethods = {
|
|
|
56
56
|
typeParams = this.parseTypeParamList();
|
|
57
57
|
}
|
|
58
58
|
const { params, returnType } = this.parseSignature();
|
|
59
|
-
|
|
59
|
+
// `//gofront:intrinsic name` on the line before a body-less func: the
|
|
60
|
+
// backend provides the implementation; every pass sees an empty body.
|
|
61
|
+
const intrinsic = this.tokens?.intrinsics?.get(_line - 1) ?? null;
|
|
62
|
+
const bodyless = intrinsic !== null && !this.check(T.LBRACE);
|
|
63
|
+
const body = bodyless ? { kind: "Block", stmts: [] } : this.parseBlock();
|
|
60
64
|
this.semi();
|
|
61
65
|
return {
|
|
62
66
|
kind: "FuncDecl",
|
|
@@ -65,6 +69,7 @@ export const declarationParseMethods = {
|
|
|
65
69
|
params,
|
|
66
70
|
returnType,
|
|
67
71
|
body,
|
|
72
|
+
...(intrinsic && { intrinsic, _bodyless: bodyless }),
|
|
68
73
|
_line,
|
|
69
74
|
};
|
|
70
75
|
},
|
|
@@ -91,17 +96,40 @@ export const declarationParseMethods = {
|
|
|
91
96
|
// Type param lists contain IDENT followed by a constraint (another IDENT or keyword),
|
|
92
97
|
// not expressions like integers or operators.
|
|
93
98
|
_looksLikeTypeParamList() {
|
|
94
|
-
|
|
99
|
+
let i = this.pos + 1; // skip [
|
|
95
100
|
const src = this.tokens;
|
|
96
|
-
// First token must be an IDENT (the type param name)
|
|
97
101
|
if (!src[i] || src[i].type !== T.IDENT) return false;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
while (
|
|
103
|
+
src[i] &&
|
|
104
|
+
src[i].type === T.IDENT &&
|
|
105
|
+
src[i + 1] &&
|
|
106
|
+
src[i + 1].type === T.COMMA
|
|
107
|
+
) {
|
|
108
|
+
i += 2;
|
|
109
|
+
}
|
|
110
|
+
if (!src[i] || src[i].type !== T.IDENT) return false;
|
|
111
|
+
const afterName = src[i + 1];
|
|
112
|
+
if (!afterName) return false;
|
|
113
|
+
if (afterName.type === T.IDENT) return true; // T any, T Stringer
|
|
114
|
+
if (afterName.type === T.TILDE) return true; // T ~int
|
|
115
|
+
if (afterName.type === T.INTERFACE) return true; // T interface{...}
|
|
116
|
+
return false;
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
// `Name[…]` followed by `,` or `)` in a result list is an instantiated
|
|
120
|
+
// generic type; a result name is followed by its type (`a []int`).
|
|
121
|
+
_isGenericResultType() {
|
|
122
|
+
if (this.peek2()?.type !== T.LBRACKET) return false;
|
|
123
|
+
const src = this.tokens;
|
|
124
|
+
let depth = 0;
|
|
125
|
+
for (let i = this.pos + 1; i < src.length; i++) {
|
|
126
|
+
const t = src[i].type;
|
|
127
|
+
if (t === T.LBRACKET) depth++;
|
|
128
|
+
else if (t === T.RBRACKET && --depth === 0) {
|
|
129
|
+
const next = src[i + 1]?.type;
|
|
130
|
+
return next === T.COMMA || next === T.RPAREN;
|
|
131
|
+
} else if (t === T.EOF || t === T.LBRACE) return false;
|
|
132
|
+
}
|
|
105
133
|
return false;
|
|
106
134
|
},
|
|
107
135
|
|
|
@@ -195,7 +223,12 @@ export const declarationParseMethods = {
|
|
|
195
223
|
let isNamed = false;
|
|
196
224
|
do {
|
|
197
225
|
// Named returns: only treat IDENT as a name if peek2 is an actual type token
|
|
198
|
-
|
|
226
|
+
// (`Box[int]` is an instantiated type, not a name followed by `[int]`).
|
|
227
|
+
if (
|
|
228
|
+
this.check(T.IDENT) &&
|
|
229
|
+
this.looksLikeType(this.peek2()) &&
|
|
230
|
+
!this._isGenericResultType()
|
|
231
|
+
) {
|
|
199
232
|
names.push(this.advance().value);
|
|
200
233
|
isNamed = true;
|
|
201
234
|
} else {
|
|
@@ -204,6 +237,17 @@ export const declarationParseMethods = {
|
|
|
204
237
|
types.push(this.parseType());
|
|
205
238
|
} while (this.match(T.COMMA));
|
|
206
239
|
this.expect(T.RPAREN);
|
|
240
|
+
// Grouped names `(r, theta float64)`: a bare identifier before a
|
|
241
|
+
// named entry is a name sharing that entry's type.
|
|
242
|
+
if (isNamed) {
|
|
243
|
+
for (let i = types.length - 2; i >= 0; i--) {
|
|
244
|
+
if (names[i] !== null) continue;
|
|
245
|
+
if (types[i]?.kind !== "TypeName" || types[i].typeArgs?.length)
|
|
246
|
+
continue;
|
|
247
|
+
names[i] = types[i].name;
|
|
248
|
+
types[i] = { ...types[i + 1] };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
207
251
|
const typeNode =
|
|
208
252
|
types.length === 1 ? types[0] : { kind: "TupleType", types };
|
|
209
253
|
// Attach named return info so typechecker can inject them into scope
|
|
@@ -261,6 +305,7 @@ export const declarationParseMethods = {
|
|
|
261
305
|
},
|
|
262
306
|
|
|
263
307
|
parseVarSpec() {
|
|
308
|
+
const at = this.peek();
|
|
264
309
|
const names = this.parseIdentList();
|
|
265
310
|
let type = null;
|
|
266
311
|
let value = null;
|
|
@@ -272,7 +317,7 @@ export const declarationParseMethods = {
|
|
|
272
317
|
type = this.parseType();
|
|
273
318
|
}
|
|
274
319
|
if (this.match(T.ASSIGN)) value = this.parseExprList();
|
|
275
|
-
return { names, type, value };
|
|
320
|
+
return { names, type, value, _line: at.line, _col: at.col };
|
|
276
321
|
},
|
|
277
322
|
|
|
278
323
|
parseConstDecl() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Parser expression methods — installed as a mixin on Parser.prototype.
|
|
2
2
|
|
|
3
|
-
import { T } from "../tokens.js";
|
|
3
|
+
import { T } from "../lexer/tokens.js";
|
|
4
4
|
|
|
5
5
|
// Binary operator precedence (higher = binds tighter, matching Go spec)
|
|
6
6
|
const PREC = {
|
|
@@ -108,9 +108,9 @@ function _isTypeIntroducerToken(tt) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
export const expressionParserMethods = {
|
|
111
|
-
parseExprList() {
|
|
112
|
-
const exprs = [this.parseExpr()];
|
|
113
|
-
while (this.match(T.COMMA)) exprs.push(this.parseExpr());
|
|
111
|
+
parseExprList(noCompLit = false) {
|
|
112
|
+
const exprs = [this.parseExpr(0, noCompLit)];
|
|
113
|
+
while (this.match(T.COMMA)) exprs.push(this.parseExpr(0, noCompLit));
|
|
114
114
|
return exprs;
|
|
115
115
|
},
|
|
116
116
|
|
|
@@ -132,7 +132,13 @@ export const expressionParserMethods = {
|
|
|
132
132
|
const t = this.peek();
|
|
133
133
|
if (UNARY_OPS.has(t.type)) {
|
|
134
134
|
const op = this.advance().value;
|
|
135
|
-
return {
|
|
135
|
+
return {
|
|
136
|
+
kind: "UnaryExpr",
|
|
137
|
+
op,
|
|
138
|
+
operand: this.parseUnary(noCompLit),
|
|
139
|
+
_line: t.line,
|
|
140
|
+
_col: t.col,
|
|
141
|
+
};
|
|
136
142
|
}
|
|
137
143
|
if (t.type === T.AWAIT) {
|
|
138
144
|
this.advance();
|
|
@@ -166,7 +172,12 @@ export const expressionParserMethods = {
|
|
|
166
172
|
}
|
|
167
173
|
} else if (t.type === T.LBRACKET) {
|
|
168
174
|
// Check if this looks like generic type args: Ident[Type](...) or Ident[Type]{...}
|
|
169
|
-
|
|
175
|
+
// `pkg.F[int](x)` / `pkg.T[X]{...}` instantiate through a qualifier.
|
|
176
|
+
if (
|
|
177
|
+
(expr.kind === "Ident" ||
|
|
178
|
+
(expr.kind === "SelectorExpr" && expr.expr.kind === "Ident")) &&
|
|
179
|
+
this.looksLikeTypeArgList()
|
|
180
|
+
) {
|
|
170
181
|
this.advance(); // consume [
|
|
171
182
|
const typeArgs = [this.parseType()];
|
|
172
183
|
while (this.match(T.COMMA)) typeArgs.push(this.parseType());
|
|
@@ -241,6 +252,8 @@ export const expressionParserMethods = {
|
|
|
241
252
|
if (tt === T.RBRACKET) {
|
|
242
253
|
depth--;
|
|
243
254
|
if (depth === 0) break;
|
|
255
|
+
// `a[i].F` selects from an index result: a value, never a type.
|
|
256
|
+
if (src[i + 1]?.type === T.DOT) return false;
|
|
244
257
|
i++;
|
|
245
258
|
continue;
|
|
246
259
|
}
|
|
@@ -369,7 +382,16 @@ export const expressionParserMethods = {
|
|
|
369
382
|
while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
|
|
370
383
|
// Implicit composite literal: {X: 1} inside []Point{{X:1}} or map[string]Point{"a": {X:1}}
|
|
371
384
|
if (this.check(T.LBRACE)) {
|
|
372
|
-
|
|
385
|
+
const lit = this.parseCompositeLit(null); // null typeExpr = inferred
|
|
386
|
+
if (this.match(T.COLON)) {
|
|
387
|
+
// Elided key type: map[P]int{{1}: 2}
|
|
388
|
+
const val = this.check(T.LBRACE)
|
|
389
|
+
? this.parseCompositeLit(null)
|
|
390
|
+
: this.parseExpr();
|
|
391
|
+
elems.push({ kind: "KeyValueExpr", key: lit, value: val });
|
|
392
|
+
} else {
|
|
393
|
+
elems.push(lit);
|
|
394
|
+
}
|
|
373
395
|
} else {
|
|
374
396
|
const first = this.parseExpr();
|
|
375
397
|
if (this.match(T.COLON)) {
|
package/src/parser/index.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// CallExpr, SelectorExpr, IndexExpr, SliceExpr
|
|
18
18
|
// CompositeLit, KeyValueExpr, FuncLit, AwaitExpr
|
|
19
19
|
|
|
20
|
-
import { T } from "../tokens.js";
|
|
20
|
+
import { T } from "../lexer/tokens.js";
|
|
21
21
|
import { declarationParseMethods } from "./declarations.js";
|
|
22
22
|
import { expressionParserMethods } from "./expressions.js";
|
|
23
23
|
import { statementParserMethods } from "./statements.js";
|
|
@@ -131,6 +131,7 @@ export class Parser {
|
|
|
131
131
|
decls,
|
|
132
132
|
target: this.tokens?.target ?? null,
|
|
133
133
|
_targetDirective: this.tokens?.targetDirective ?? null,
|
|
134
|
+
_embedDirectives: this.tokens?.embedDirectives ?? [],
|
|
134
135
|
_filename: this.filename,
|
|
135
136
|
_source: this.source,
|
|
136
137
|
};
|
package/src/parser/statements.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Parser statement methods — installed as a mixin on Parser.prototype.
|
|
2
2
|
|
|
3
|
-
import { T } from "../tokens.js";
|
|
3
|
+
import { T } from "../lexer/tokens.js";
|
|
4
4
|
|
|
5
5
|
// Method-name dispatch for pure-delegation cases in _parseStmt.
|
|
6
6
|
const PARSE_STMT_DELEGATE = {
|
|
@@ -78,6 +78,13 @@ export const statementParserMethods = {
|
|
|
78
78
|
this.advance();
|
|
79
79
|
this.semi();
|
|
80
80
|
return { kind: "BranchStmt", keyword: "fallthrough" };
|
|
81
|
+
case T.GOTO: {
|
|
82
|
+
// Parsed so the checker can reject it with a targeted message.
|
|
83
|
+
this.advance();
|
|
84
|
+
const label = this.expect(T.IDENT).value;
|
|
85
|
+
this.semi();
|
|
86
|
+
return { kind: "BranchStmt", keyword: "goto", label };
|
|
87
|
+
}
|
|
81
88
|
case T.GO:
|
|
82
89
|
return this.err("goroutines are not supported in GoFront");
|
|
83
90
|
case T.SELECT:
|
|
@@ -88,7 +95,12 @@ export const statementParserMethods = {
|
|
|
88
95
|
if (this.check2(T.COLON)) {
|
|
89
96
|
const label = this.advance().value;
|
|
90
97
|
this.advance(); // consume COLON
|
|
91
|
-
|
|
98
|
+
// A label may precede the end of a block: the statement is empty.
|
|
99
|
+
const body =
|
|
100
|
+
this.check(T.RBRACE) || this.check(T.CASE) || this.check(T.DEFAULT)
|
|
101
|
+
? { kind: "Block", stmts: [] }
|
|
102
|
+
: this.parseStmt();
|
|
103
|
+
return { kind: "LabeledStmt", label, body };
|
|
92
104
|
}
|
|
93
105
|
return this.parseSimpleStmt();
|
|
94
106
|
default:
|
|
@@ -150,27 +162,30 @@ export const statementParserMethods = {
|
|
|
150
162
|
};
|
|
151
163
|
}
|
|
152
164
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (this.
|
|
165
|
+
let first = null;
|
|
166
|
+
if (!this.check(T.SEMICOLON)) {
|
|
167
|
+
first = this.parseSimpleStmtRaw(true);
|
|
168
|
+
|
|
169
|
+
if (this.check(T.LBRACE)) {
|
|
170
|
+
// for k, v := range expr { } — range stmt ends right before {
|
|
171
|
+
if (this.isRangeStmt(first)) {
|
|
172
|
+
return {
|
|
173
|
+
kind: "ForStmt",
|
|
174
|
+
init: first,
|
|
175
|
+
cond: null,
|
|
176
|
+
post: null,
|
|
177
|
+
body: this.parseBlock(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// for cond { }
|
|
158
181
|
return {
|
|
159
182
|
kind: "ForStmt",
|
|
160
|
-
init:
|
|
161
|
-
cond:
|
|
183
|
+
init: null,
|
|
184
|
+
cond: this.stmtToExpr(first),
|
|
162
185
|
post: null,
|
|
163
186
|
body: this.parseBlock(),
|
|
164
187
|
};
|
|
165
188
|
}
|
|
166
|
-
// for cond { }
|
|
167
|
-
return {
|
|
168
|
-
kind: "ForStmt",
|
|
169
|
-
init: null,
|
|
170
|
-
cond: this.stmtToExpr(first),
|
|
171
|
-
post: null,
|
|
172
|
-
body: this.parseBlock(),
|
|
173
|
-
};
|
|
174
189
|
}
|
|
175
190
|
|
|
176
191
|
this.expect(T.SEMICOLON);
|
|
@@ -180,7 +195,7 @@ export const statementParserMethods = {
|
|
|
180
195
|
if (!this.check(T.SEMICOLON)) cond = this.parseExpr(0, true);
|
|
181
196
|
this.expect(T.SEMICOLON);
|
|
182
197
|
let post = null;
|
|
183
|
-
if (!this.check(T.LBRACE)) post = this.parseSimpleStmtRaw();
|
|
198
|
+
if (!this.check(T.LBRACE)) post = this.parseSimpleStmtRaw(true);
|
|
184
199
|
const body = this.parseBlock();
|
|
185
200
|
return { kind: "ForStmt", init: first, cond, post, body };
|
|
186
201
|
},
|
|
@@ -290,9 +305,9 @@ export const statementParserMethods = {
|
|
|
290
305
|
// a, b, c := ... or x, y = ...
|
|
291
306
|
if (t.type === T.COMMA) {
|
|
292
307
|
const lhs = [first];
|
|
293
|
-
while (this.match(T.COMMA)) lhs.push(this.parseExpr());
|
|
308
|
+
while (this.match(T.COMMA)) lhs.push(this.parseExpr(0, noCompLit));
|
|
294
309
|
const op = this.advance(); // := or =
|
|
295
|
-
const rhs = this.parseExprList();
|
|
310
|
+
const rhs = this.parseExprList(noCompLit);
|
|
296
311
|
if (op.type === T.DEFINE) return { kind: "DefineStmt", lhs, rhs };
|
|
297
312
|
return { kind: "AssignStmt", lhs, op: "=", rhs };
|
|
298
313
|
}
|
|
@@ -300,14 +315,14 @@ export const statementParserMethods = {
|
|
|
300
315
|
// x := expr
|
|
301
316
|
if (t.type === T.DEFINE) {
|
|
302
317
|
this.advance();
|
|
303
|
-
const rhs = this.parseExprList();
|
|
318
|
+
const rhs = this.parseExprList(noCompLit);
|
|
304
319
|
return { kind: "DefineStmt", lhs: [first], rhs };
|
|
305
320
|
}
|
|
306
321
|
|
|
307
322
|
// x = expr or x += expr etc.
|
|
308
323
|
if (ASSIGN_OPS.has(t.type)) {
|
|
309
324
|
const op = this.advance().value;
|
|
310
|
-
const rhs = this.parseExprList();
|
|
325
|
+
const rhs = this.parseExprList(noCompLit);
|
|
311
326
|
return { kind: "AssignStmt", lhs: [first], op, rhs };
|
|
312
327
|
}
|
|
313
328
|
|