gofront 1.7.0 → 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.
Files changed (128) hide show
  1. package/README.md +13 -13
  2. package/package.json +8 -37
  3. package/src/backend/js/expressions.js +1341 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +375 -57
  6. package/src/backend/js/runtime.js +1460 -133
  7. package/src/backend/js/statements.js +900 -86
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +2 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +8 -3
  14. package/src/backend/js/stdlib/json.js +247 -52
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +10 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +436 -80
  29. package/src/backend/wasm/emit-builtins.js +303 -13
  30. package/src/backend/wasm/emit-exprs.js +1397 -203
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +512 -77
  34. package/src/backend/wasm/encode.js +43 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/prescan.js +397 -0
  41. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  42. package/src/backend/wasm/stdlib/core.js +1574 -199
  43. package/src/backend/wasm/stdlib/index.js +0 -5
  44. package/src/backend/wasm/stdlib/io.js +1567 -150
  45. package/src/backend/wasm/stdlib/packages.js +2 -0
  46. package/src/backend/wasm/stdlib/simd.js +293 -28
  47. package/src/backend/wasm/stdlib/time.js +1041 -12
  48. package/src/backend/wasm/types.js +200 -15
  49. package/src/backend/wasm/variadic.js +93 -0
  50. package/src/backend/wasm/wat.js +47 -20
  51. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  52. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  53. package/src/{vendor.js → cli/vendor.js} +3 -4
  54. package/src/compiler.js +107 -269
  55. package/src/index.js +15 -169
  56. package/src/{lexer.js → lexer/lexer.js} +102 -55
  57. package/src/{tokens.js → lexer/tokens.js} +1 -0
  58. package/src/link/sourcemap.js +97 -0
  59. package/src/link/stdlib.js +515 -0
  60. package/src/link/wasm.js +294 -0
  61. package/src/lower/bounds.js +394 -0
  62. package/src/lower/boxing.js +1 -1
  63. package/src/lower/captures.js +176 -32
  64. package/src/lower/index.js +26 -18
  65. package/src/lower/json-codecs.js +1398 -0
  66. package/src/lower/ownership.js +22 -1
  67. package/src/lower/range.js +86 -1
  68. package/src/lower/reachability.js +132 -0
  69. package/src/parser/declarations.js +58 -13
  70. package/src/parser/expressions.js +29 -7
  71. package/src/parser/index.js +2 -1
  72. package/src/parser/statements.js +37 -22
  73. package/src/{templ-parser.js → parser/templ.js} +138 -15
  74. package/src/parser/types.js +69 -9
  75. package/src/resolver.js +22 -1
  76. package/src/typechecker/assignability.js +176 -17
  77. package/src/typechecker/const-fold.js +356 -0
  78. package/src/typechecker/constants.js +256 -0
  79. package/src/typechecker/expressions.js +931 -122
  80. package/src/typechecker/index.js +459 -67
  81. package/src/typechecker/init-order.js +330 -0
  82. package/src/typechecker/resolve.js +451 -56
  83. package/src/typechecker/statements.js +90 -9
  84. package/src/typechecker/stdlib/core.js +333 -55
  85. package/src/typechecker/stdlib/excluded.js +898 -0
  86. package/src/typechecker/stdlib/extended.js +322 -26
  87. package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
  88. package/src/typechecker/stdlib/simd.js +65 -15
  89. package/src/typechecker/termination.js +1 -1
  90. package/src/typechecker/types.js +149 -25
  91. package/stdlib/bytes/iter.go +192 -0
  92. package/stdlib/bytes/search.go +240 -0
  93. package/stdlib/bytes/split.go +218 -0
  94. package/stdlib/cmp/cmp.go +65 -0
  95. package/stdlib/encoding/json/errors.go +31 -0
  96. package/stdlib/encoding/json/indent.go +71 -0
  97. package/stdlib/encoding/json/number.go +12 -0
  98. package/stdlib/encoding/json/stream.go +173 -0
  99. package/stdlib/errors/overlay.go +12 -0
  100. package/stdlib/hash/hash.go +53 -0
  101. package/stdlib/internal/jsonrt/dec.go +738 -0
  102. package/stdlib/internal/jsonrt/enc.go +366 -0
  103. package/stdlib/internal/jsonrt/indent.go +135 -0
  104. package/stdlib/internal/jsonrt/number.go +27 -0
  105. package/stdlib/internal/jsonrt/reader.go +73 -0
  106. package/stdlib/internal/jsonrt/scanner.go +649 -0
  107. package/stdlib/io/io.go +346 -0
  108. package/stdlib/iter/iter.go +18 -0
  109. package/stdlib/maps/iter.go +65 -0
  110. package/stdlib/math/extra.go +270 -0
  111. package/stdlib/slices/extra.go +73 -0
  112. package/stdlib/slices/iter.go +113 -0
  113. package/stdlib/sort/sort.go +561 -0
  114. package/stdlib/strconv/quote.go +341 -0
  115. package/stdlib/strings/extra.go +35 -0
  116. package/stdlib/strings/iter.go +122 -0
  117. package/stdlib/sync/extra.go +105 -0
  118. package/stdlib/unicode/unicode.go +112 -0
  119. package/stdlib/unicode/utf8/bytes.go +239 -0
  120. package/src/backend/wasm/const-fold.js +0 -205
  121. package/src/backend/wasm/emit-stdlib.js +0 -13
  122. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  123. /package/src/{colors.js → cli/colors.js} +0 -0
  124. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  125. /package/src/{minifier.js → cli/minifier.js} +0 -0
  126. /package/src/{project-config.js → cli/project-config.js} +0 -0
  127. /package/src/{pwa.js → cli/pwa.js} +0 -0
  128. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -0,0 +1,256 @@
1
+ // TypeChecker constant evaluation — installed as a mixin on TypeChecker.prototype.
2
+ //
3
+ // Every constant expression gets its exact value from the shared folder
4
+ // (src/typechecker/const-fold.js) and carries it on the AST node as `_const`, so
5
+ // both backends emit the same value. Typed constant overflow and constant
6
+ // division by zero are reported here.
7
+
8
+ import {
9
+ constFromLit,
10
+ constIntValue,
11
+ convertConst,
12
+ foldBinary,
13
+ foldMinMax,
14
+ foldUnary,
15
+ INT_RANGES,
16
+ intConst,
17
+ MATH_INT_CONSTS,
18
+ } from "./const-fold.js";
19
+ import { defaultType, isAny, typeStr, VOID } from "./types.js";
20
+
21
+ // Basic type names usable as conversion functions (`int32(x)`).
22
+ const BASIC_CONVERSIONS = new Set([
23
+ ...Object.keys(INT_RANGES),
24
+ "float32",
25
+ "float64",
26
+ "string",
27
+ "bool",
28
+ ]);
29
+
30
+ // Node kinds that can be constant expressions.
31
+ const CONST_KINDS = new Set([
32
+ "BasicLit",
33
+ "Ident",
34
+ "ParenExpr",
35
+ "UnaryExpr",
36
+ "BinaryExpr",
37
+ "CallExpr",
38
+ "TypeConversion",
39
+ "SelectorExpr",
40
+ ]);
41
+
42
+ const NEGATIVE_SHIFT_ERROR = "invalid shift count: negative shift count";
43
+
44
+ /** @type {ThisType<import('./index.js').TypeChecker>} */
45
+ export const constantCheckMethods = {
46
+ // The value of a constant expression, or null when `expr` is not one (or
47
+ // is an overflowing or erroneous one).
48
+ _constValue(expr, scope = null) {
49
+ const raw = this._constRaw(expr, scope ?? this.globals);
50
+ return raw && !raw.overflow && !raw.error ? raw : null;
51
+ },
52
+
53
+ // Integer value of an integer (or untyped integral float) constant as a
54
+ // Number, or null.
55
+ _constIntValue(expr, scope = null) {
56
+ const v = this._constValue(expr, scope);
57
+ if (!v || (v.k === "FLOAT" && v.t)) return null;
58
+ const i = constIntValue(v);
59
+ return i === null ? null : Number(i);
60
+ },
61
+
62
+ // Raw evaluation result, memoized on checked nodes: a value, a value with
63
+ // `overflow`, `{ error }`, or null.
64
+ _constRaw(expr, scope) {
65
+ if (!expr || typeof expr !== "object" || !CONST_KINDS.has(expr.kind))
66
+ return null;
67
+ if (expr._constRaw !== undefined) return expr._constRaw;
68
+ if (expr._constBusy) return null; // cyclic constant
69
+ expr._constBusy = true;
70
+ let raw;
71
+ try {
72
+ raw = this._evalConst(expr, scope);
73
+ } catch (err) {
74
+ raw = { error: err.message };
75
+ } finally {
76
+ expr._constBusy = false;
77
+ }
78
+ // Conversions are only resolved once the node has been checked.
79
+ if (expr._type !== undefined) expr._constRaw = raw;
80
+ return raw;
81
+ },
82
+
83
+ // Called by checkExpr after typing `expr`: attaches `_const` and reports
84
+ // overflow / division by zero once per node.
85
+ _noteConst(expr, scope) {
86
+ if (!CONST_KINDS.has(expr.kind)) return;
87
+ const raw = this._constRaw(expr, scope);
88
+ expr._const = raw && !raw.overflow && !raw.error ? raw : null;
89
+ if (!raw || expr._constReported) return;
90
+ if (raw.error) {
91
+ expr._constReported = true;
92
+ // checkShiftCount already reports a negative count at its operand.
93
+ if (raw.error === NEGATIVE_SHIFT_ERROR) return;
94
+ this.err(raw.error, expr);
95
+ } else if (raw.overflow) {
96
+ expr._constReported = true;
97
+ this.err(`constant ${raw.v} overflows ${raw.typeName ?? raw.t}`, expr);
98
+ }
99
+ },
100
+
101
+ _evalConst(expr, scope) {
102
+ switch (expr.kind) {
103
+ case "BasicLit":
104
+ return constFromLit(expr);
105
+ case "ParenExpr":
106
+ return this._constValue(expr.expr, scope);
107
+ case "UnaryExpr": {
108
+ const x = this._constValue(expr.operand, scope);
109
+ return x ? foldUnary(expr.op, x) : null;
110
+ }
111
+ case "BinaryExpr": {
112
+ const a = this._constValue(expr.left, scope);
113
+ const b = a ? this._constValue(expr.right, scope) : null;
114
+ return a && b ? foldBinary(expr.op, a, b) : null;
115
+ }
116
+ case "Ident":
117
+ return this._evalConstIdent(expr, scope);
118
+ case "SelectorExpr":
119
+ return this._evalConstSelector(expr, scope);
120
+ case "TypeConversion": {
121
+ const v = this._constValue(expr.expr, scope);
122
+ const target = v ? this.resolveTypeNode(expr.targetType, scope) : null;
123
+ return target ? this._convertConstTo(v, target) : null;
124
+ }
125
+ default:
126
+ return this._evalConstCall(expr, scope);
127
+ }
128
+ },
129
+
130
+ _evalConstIdent(expr, scope) {
131
+ const owner = scope.lookupScope(expr.name);
132
+ if (!owner?._consts.has(expr.name)) return null;
133
+ const init = owner.lookupConstExpr(expr.name);
134
+ const v = init
135
+ ? this._constValue(init, owner)
136
+ : (this._importedConsts?.get(expr.name) ?? null);
137
+ if (!v) return null;
138
+ const declared = owner.symbols.get(expr.name);
139
+ if (!declared || declared.kind === "untyped" || declared.kind === "any")
140
+ return v;
141
+ const typed = this._convertConstTo(v, declared);
142
+ return typed?.overflow ? null : typed;
143
+ },
144
+
145
+ // `pkg.K` (an imported GoFront package) and the integer `math` constants.
146
+ _evalConstSelector(expr, scope) {
147
+ if (expr.expr?.kind !== "Ident") return null;
148
+ const ns = scope.lookup(expr.expr.name);
149
+ if (ns?.kind !== "namespace") return null;
150
+ if (ns._gofront) return ns._consts?.get(expr.field) ?? null;
151
+ const m = ns.name === "math" ? MATH_INT_CONSTS[expr.field] : undefined;
152
+ return m === undefined ? null : intConst(m);
153
+ },
154
+
155
+ // Conversions, `len` of a constant string, and `min`/`max` of constants.
156
+ _evalConstCall(expr, scope) {
157
+ const target = this._constConversionTarget(expr, scope);
158
+ if (target) {
159
+ const v = this._constValue(expr.args[0], scope);
160
+ return v ? this._convertConstTo(v, target) : null;
161
+ }
162
+ const fn = expr.func;
163
+ if (fn?.kind !== "Ident" || scope.lookup(fn.name)?.kind !== "builtin")
164
+ return null;
165
+ const vals = (expr.args ?? []).map((a) => this._constValue(a, scope));
166
+ if (vals.length === 0 || vals.some((v) => !v)) return null;
167
+ if (fn.name === "min" || fn.name === "max")
168
+ return foldMinMax(fn.name === "min", vals);
169
+ // GoFront strings use UTF-16 code units on both backends.
170
+ const isLen = fn.name === "len" && vals[0].k === "STRING";
171
+ return isLen ? intConst(BigInt(vals[0].v.length)) : null;
172
+ },
173
+
174
+ // Target type of a conversion call (`MyInt(x)`, `int32(x)`), or null.
175
+ _constConversionTarget(expr, scope) {
176
+ if (expr.args?.length !== 1) return null;
177
+ if (expr._isTypeConversion) return expr._conversionTargetType ?? null;
178
+ const fn = expr.func;
179
+ if (fn?.kind !== "Ident" || !BASIC_CONVERSIONS.has(fn.name)) return null;
180
+ const owner = scope.lookupScope(fn.name);
181
+ return !owner || owner === this.universe
182
+ ? { kind: "basic", name: fn.name }
183
+ : null;
184
+ },
185
+
186
+ // Converts a constant to Go type `t` (basic or named over a basic type).
187
+ _convertConstTo(v, t) {
188
+ const u = this.resolveType(t?.kind === "named" ? t.underlying : t);
189
+ if (u?.kind !== "basic") return null;
190
+ const r = convertConst(v, u.name);
191
+ return r?.overflow ? { ...r, typeName: typeStr(t) } : r;
192
+ },
193
+
194
+ // `var x int8 = 200`: an untyped integer constant must fit the target. An
195
+ // interface target takes the constant's default type (`var v any = 1 << 64`).
196
+ // A null `ctx` reports an operand converted to the other operand's type.
197
+ _checkConstOverflow(target, source, node, ctx) {
198
+ if (source?.kind !== "untyped" || !node?._const) return false;
199
+ const base = this.resolveType(
200
+ target?.kind === "named" ? target.underlying : target,
201
+ );
202
+ if (base?.kind === "interface" || isAny(base)) target = defaultType(source);
203
+ const i = constIntValue(node._const);
204
+ const range = i === null ? null : this._intRange(target);
205
+ if (!range) return false;
206
+ if (i >= range[0] && i <= range[1]) {
207
+ node._intConst = true; // the JS backend's precision warning reads this
208
+ return false;
209
+ }
210
+ const isInt = node._const.k === "INT";
211
+ const text = isInt ? String(i) : String(node._const.v);
212
+ const what = `untyped ${isInt ? "int" : "float"} constant`;
213
+ const shown =
214
+ node.kind === "BasicLit"
215
+ ? `${node.value} (${what})`
216
+ : `${text} (${what} ${text})`;
217
+ this.err(
218
+ ctx
219
+ ? `cannot use ${shown} as ${typeStr(target)} value in ${ctx} (overflows)`
220
+ : `${shown} overflows ${typeStr(target)}`,
221
+ node,
222
+ );
223
+ return true;
224
+ },
225
+
226
+ // `x := 1 << 63`, `println(1 << 64)`: the constant takes its default type.
227
+ _checkConstDefault(source, node, ctx) {
228
+ return this._checkConstOverflow(defaultType(source), source, node, ctx);
229
+ },
230
+
231
+ // print/println arguments take their default type.
232
+ _checkPrintArgs(name, expr, argTypes) {
233
+ for (let i = 0; i < expr.args.length; i++)
234
+ this._checkConstDefault(
235
+ argTypes[i],
236
+ expr.args[i],
237
+ `argument to built-in ${name}`,
238
+ );
239
+ return VOID;
240
+ },
241
+
242
+ // `x + 300` with `x int8`: the untyped operand is converted to the other
243
+ // operand's type.
244
+ _checkConstOperands(op, lt, rt, expr) {
245
+ if (op === "<<" || op === ">>" || op === "&&" || op === "||") return;
246
+ if (rt?.kind === "untyped" && lt?.kind !== "untyped")
247
+ this._checkConstOverflow(lt, rt, expr.right, null);
248
+ else if (lt?.kind === "untyped" && rt?.kind !== "untyped")
249
+ this._checkConstOverflow(rt, lt, expr.left, null);
250
+ },
251
+
252
+ _intRange(t) {
253
+ const u = this.resolveType(t?.kind === "named" ? t.underlying : t);
254
+ return u?.kind === "basic" ? (INT_RANGES[u.name] ?? null) : null;
255
+ },
256
+ };