gofront 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -0,0 +1,20 @@
1
+ // The host half of strconv's fixed-point float formatting: a JS function,
2
+ // as source text, that both backends embed (src/backend/js/natives.js and the
3
+ // WASM host imports in src/backend/wasm/boundary.js).
4
+ //
5
+ // `toFixed` is exact except that it rounds a tie (a value whose decimal
6
+ // expansion ends in a 5 right after the digits kept) up, where Go rounds it
7
+ // to even; the extra digits of a second call tell the two apart. A tie is a
8
+ // multiple of 2^-(p+1), so any other value needs no second call. "" declines.
9
+ export const FIXED_FLOAT_JS = `(x, p) => {
10
+ if (!(x < 1e21)) return "";
11
+ const s = x.toFixed(p);
12
+ if (x % 1 === 0 || (x * 2 ** (p + 1)) % 1 !== 0) return s;
13
+ const t = x.toFixed(p + 30);
14
+ const dot = t.indexOf(".");
15
+ if (/^50*$/.test(t.slice(dot + 1 + p))) {
16
+ const down = t.slice(0, p === 0 ? dot : dot + 1 + p);
17
+ return (down.charCodeAt(down.length - 1) - 48) % 2 === 0 ? down : s;
18
+ }
19
+ return s;
20
+ }`;
@@ -1,188 +1,109 @@
1
- // Static type descriptors for `fmt` (Task 3.1c), shared by the JS and WASM
2
- // backends. `%T` and `%#v` need the Go type of an operand, which the runtime
3
- // values do not carry (an `int` and a `float64` holding 3 are both the number
4
- // 3, a `Color` is a raw number inside a slice). Where a format string is a
5
- // literal that needs them (`fmtWantsTypes`), codegen passes the operand's
6
- // static type next to its value as a plain JSON-compatible tree, which
7
- // `__sprintf` reads (`__ft` on JS, `fmt_arg_type` on WASM).
8
- //
9
- // Descriptor shapes (`k`): `b` basic `{ n }`, `named` `{ n, u }` (a named
10
- // non-struct type, `u` its underlying type), `struct` `{ n, f: [[name, T]] }`
11
- // (`n` is "" for an anonymous struct), `ptr` `{ e }`, `slice` `{ e }`,
12
- // `array` `{ e, len }`, `map` `{ key, val }`, `i` an interface `{ n }`,
13
- // `func` `{ p, r }`, `chan` `{ e }`. A type that is too deep or unknown is
14
- // `null`; the runtime then derives a best-effort type from the value.
1
+ // Go's `reflect` spelling of a type used as a type argument (the monomorphiser
2
+ // records it for `%T` of a type-parameter operand), shared by the JS and WASM
3
+ // backends.
15
4
 
16
- const MAX_DEPTH = 4;
17
- const FLAGS = "#+\\- 0";
18
- const VERBS = "vTtcdoObxXUeEfFgGsqp";
5
+ const qualify = (name) =>
6
+ name.split("[")[0].includes(".") ? name : `main.${name}`;
19
7
 
20
- const qualify = (name) => (name.includes(".") ? name : `main.${name}`);
8
+ const PREDECLARED = new Set([
9
+ "bool",
10
+ "string",
11
+ "int",
12
+ "int8",
13
+ "int16",
14
+ "int32",
15
+ "int64",
16
+ "uint",
17
+ "uint8",
18
+ "uint16",
19
+ "uint32",
20
+ "uint64",
21
+ "uintptr",
22
+ "float32",
23
+ "float64",
24
+ "complex64",
25
+ "complex128",
26
+ "byte",
27
+ "rune",
28
+ "error",
29
+ ]);
21
30
 
22
31
  /**
23
- * @param {any} t checker type
24
- * @param {(t: any) => any} [resolve] resolves a type reference
25
- * @param {number} [depth]
26
- * @returns {object | null}
32
+ * Go's `reflect` spelling of a type used as a type argument: `int`,
33
+ * `main.Foo`, `[]main.Foo`. Takes checker types and parsed type nodes;
34
+ * `specialized` maps the names of monomorphised instances to their spelling.
35
+ * @param {any} t
36
+ * @param {Map<string, string>} [specialized]
37
+ * @returns {string}
27
38
  */
28
- export function fmtTypeDesc(t, resolve = (x) => x, depth = 0) {
29
- t = resolve(t) ?? t;
30
- if (!t) return null;
31
- const sub = (x) => fmtTypeDesc(x, resolve, depth + 1);
32
- switch (t.kind) {
39
+ export function goTypeName(t, specialized = null) {
40
+ const go = (x) => goTypeName(x, specialized);
41
+ switch (t?.kind) {
33
42
  case "basic":
34
- if (t.name === "any") return { k: "i", n: "interface {}" };
35
- if (t.name === "nil" || t.name === "void") return null;
36
- return { k: "b", n: t.name };
43
+ return t.name === "any" ? "interface {}" : t.name;
37
44
  case "untyped":
38
- return { k: "b", n: t.base };
39
- case "interface":
40
- return { k: "i", n: t.name ? qualify(t.name) : "interface {}" };
45
+ return t.base;
46
+ case "TypeName":
47
+ case "Ident":
48
+ if (specialized?.has(t.name)) return qualify(specialized.get(t.name));
49
+ if (t.name === "any") return "interface {}";
50
+ return PREDECLARED.has(t.name) ? t.name : qualify(t.name);
51
+ case "GenericTypeName":
52
+ return qualify(`${t.name}[${t.typeArgs.map(go).join(",")}]`);
41
53
  case "named":
42
- return namedDesc(t, resolve, depth, sub);
43
- case "struct":
44
- return depth > MAX_DEPTH ? null : structDesc("", t, sub);
54
+ return qualify(instanceName(t, specialized));
55
+ case "interface":
56
+ return t.name ? qualify(t.name) : "interface {}";
45
57
  case "pointer":
46
- return depth > MAX_DEPTH ? null : { k: "ptr", e: sub(t.base) };
58
+ case "PointerType":
59
+ return `*${go(t.base)}`;
47
60
  case "slice":
48
- return depth > MAX_DEPTH ? null : { k: "slice", e: sub(t.elem) };
61
+ case "SliceType":
62
+ return `[]${go(t.elem)}`;
49
63
  case "array":
50
- return depth > MAX_DEPTH
51
- ? null
52
- : { k: "array", e: sub(t.elem), len: t.size ?? 0 };
64
+ case "ArrayType":
65
+ // A parsed array type carries its length as a literal node.
66
+ return `[${typeof t.size === "object" ? (t.size?.value ?? 0) : (t.size ?? 0)}]${go(t.elem)}`;
53
67
  case "map":
54
- return depth > MAX_DEPTH
55
- ? null
56
- : { k: "map", key: sub(t.key), val: sub(t.value) };
57
- case "func":
58
- return {
59
- k: "func",
60
- p: (t.params ?? []).map(sub),
61
- r: (t.returns ?? []).map(sub),
62
- v: Boolean(t.variadic),
63
- };
64
- case "chan":
65
- return { k: "chan", e: sub(t.elem) };
66
- default:
67
- return null;
68
- }
69
- }
70
-
71
- /**
72
- * The fields a struct declares itself, in declaration order: named fields and
73
- * one entry per embedded type. Fields promoted from an embedded struct are
74
- * not listed (`fields` also holds them for selector lookup).
75
- * @param {any} st checker struct type
76
- * @returns {[string, any][]}
77
- */
78
- export function structOwnFields(st) {
79
- if (!st._structFields) return [...(st.fields ?? [])];
80
- const own = [];
81
- for (const sf of st._structFields) {
82
- if (!sf.embedded) {
83
- for (const n of sf.names) own.push([n, st.fields.get(n)]);
84
- continue;
68
+ case "MapType":
69
+ return `map[${go(t.key)}]${go(t.value)}`;
70
+ case "func": {
71
+ // A func without results is `func()`, not `func() void`.
72
+ const rets = (t.returns ?? [])
73
+ .filter((r) => !(r?.kind === "basic" && r.name === "void"))
74
+ .map(go);
75
+ const ret =
76
+ rets.length === 0
77
+ ? ""
78
+ : rets.length === 1
79
+ ? ` ${rets[0]}`
80
+ : ` (${rets.join(", ")})`;
81
+ return `func(${(t.params ?? []).map(go).join(", ")})${ret}`;
82
+ }
83
+ case "FuncType": {
84
+ // A parsed func type: parameters and the result as written.
85
+ const rt = t.returnType;
86
+ const rets = !rt ? [] : rt.kind === "TupleType" ? rt.types : [rt];
87
+ const ret =
88
+ rets.length === 0
89
+ ? ""
90
+ : rets.length === 1
91
+ ? ` ${go(rets[0])}`
92
+ : ` (${rets.map(go).join(", ")})`;
93
+ const params = (t.params ?? []).map((p) =>
94
+ p.variadic ? `...${go(p.type)}` : go(p.type),
95
+ );
96
+ return `func(${params.join(", ")})${ret}`;
85
97
  }
86
- const inner = sf.type?.kind === "pointer" ? sf.type.base : sf.type;
87
- const name = (
88
- sf.embeddedName ??
89
- sf.astType?.name ??
90
- sf.type?.name ??
91
- sf.type?.base?.name ??
92
- (inner?.kind === "named" ? inner.name : "")
93
- )
94
- .split(".")
95
- .pop();
96
- if (name) own.push([name, st.fields.get(name) ?? sf.type]);
97
- }
98
- return own;
99
- }
100
-
101
- function structDesc(n, st, sub) {
102
- const f = [];
103
- for (const [name, ft] of structOwnFields(st)) f.push([name, sub(ft)]);
104
- return { k: "struct", n, f };
105
- }
106
-
107
- function namedDesc(t, resolve, depth, sub) {
108
- const n = qualify(t.name ?? "");
109
- const u = resolve(t.underlying) ?? t.underlying;
110
- if (!u) return { k: "named", n, u: null };
111
- if (u.kind === "struct")
112
- return depth > MAX_DEPTH
113
- ? { k: "struct", n, f: null }
114
- : structDesc(n, u, sub);
115
- if (u.kind === "interface") return { k: "i", n };
116
- return { k: "named", n, u: depth > MAX_DEPTH ? null : sub(u) };
117
- }
118
-
119
- /**
120
- * Whether a type descriptor mentions a float32 or float64. Such operands
121
- * travel with their static type: a number alone cannot tell `float64(1e6)`
122
- * (Go prints 1e+06) from an `int`, or a float32 from the float64 holding the
123
- * same bits.
124
- * @param {any} desc result of fmtTypeDesc
125
- * @returns {boolean}
126
- */
127
- export function fmtDescHasFloat(desc) {
128
- if (!desc || typeof desc !== "object") return false;
129
- switch (desc.k) {
130
- case "b":
131
- return desc.n === "float32" || desc.n === "float64";
132
- case "named":
133
- return fmtDescHasFloat(desc.u);
134
- case "struct":
135
- return (desc.f ?? []).some(([, ft]) => fmtDescHasFloat(ft));
136
- case "ptr":
137
- case "slice":
138
- case "array":
139
- return fmtDescHasFloat(desc.e);
140
- case "map":
141
- return fmtDescHasFloat(desc.key) || fmtDescHasFloat(desc.val);
142
98
  default:
143
- return false;
99
+ return "interface {}";
144
100
  }
145
101
  }
146
102
 
147
- /**
148
- * Whether a type descriptor is a slice or array (possibly named). `%s`, `%q`
149
- * and `%x` read a byte slice as text, which a plain list of numbers cannot
150
- * tell from any other slice of small numbers.
151
- * @param {any} desc result of fmtTypeDesc
152
- * @returns {boolean}
153
- */
154
- export function fmtDescIsSeq(desc) {
155
- let d = desc;
156
- while (d?.k === "named") d = d.u;
157
- return d?.k === "slice" || d?.k === "array";
158
- }
159
-
160
- /**
161
- * Whether a call with this literal format string needs operand types: `%T`,
162
- * `%#v`, an unknown verb (`%!z(int=5)`), or more operands than verbs
163
- * (`%!(EXTRA int=1)`).
164
- * @param {unknown} format
165
- * @param {number} nargs operand count, -1 when not known statically
166
- * @param {boolean} [seqArgs] some operand is a slice or array, whose element
167
- * type decides how `%s`, `%q`, `%x` and `%X` read it
168
- */
169
- export function fmtWantsTypes(format, nargs, seqArgs = false) {
170
- if (typeof format !== "string") return false;
171
- const re = new RegExp(
172
- `%([${FLAGS}]*)((?:\\[\\d+\\])?(?:\\*|\\d+)?(?:\\.(?:\\[\\d+\\])?(?:\\*|\\d*))?(?:\\[\\d+\\])?)([^]?)`,
173
- "g",
174
- );
175
- let count = 0;
176
- let indexed = false;
177
- for (const m of format.matchAll(re)) {
178
- const verb = m[3];
179
- if (verb === "%") continue;
180
- if (verb === "T" || verb === "p") return true;
181
- if (verb === "v" && m[1].includes("#")) return true;
182
- if (seqArgs && "sqxX".includes(verb)) return true;
183
- if (verb !== "" && !VERBS.includes(verb) && verb !== "w") return true;
184
- if (/[[*]/.test(m[2])) indexed = true;
185
- count++;
186
- }
187
- return nargs >= 0 && !indexed && nargs > count;
103
+ // `Box[int]` for a generic instance: the checker's type arguments on JS, the
104
+ // spelling the monomorphiser recorded on WASM.
105
+ function instanceName(t, specialized = null) {
106
+ if (t._goName) return t._goName;
107
+ if (!t.typeArgs?.length) return t.name ?? "";
108
+ return `${t.name}[${t.typeArgs.map((a) => goTypeName(a, specialized)).join(",")}]`;
188
109
  }
@@ -0,0 +1,36 @@
1
+ // JS bodies of the host side of the unicode intrinsics (unicode_is,
2
+ // unicode_to_upper, unicode_to_lower). The JS backend inlines them as the
3
+ // intrinsic bodies and the WASM boundary ships them as `env` imports, so
4
+ // both targets answer from the same tables. Each takes the names of the
5
+ // parameters it reads and returns statements ending in a `return`.
6
+
7
+ // Classes, in the order of the constants in stdlib/unicode/unicode.go: the
8
+ // test of the one-code-point string `c`.
9
+ const CLASS_TESTS = [
10
+ "/\\p{L}/u.test(c)", // Letter
11
+ "/\\p{Nd}/u.test(c)", // Digit
12
+ "/[ \\t\\n\\v\\f\\r\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]/.test(c)", // Space
13
+ "/\\p{Lu}/u.test(c)", // Upper
14
+ "/\\p{Ll}/u.test(c)", // Lower
15
+ "/\\p{P}/u.test(c)", // Punct
16
+ "/\\p{Cc}/u.test(c)", // Control
17
+ "!/\\p{Cc}/u.test(c)", // Print
18
+ "!/\\p{Cc}/u.test(c)", // Graphic
19
+ ];
20
+
21
+ export function unicodeIsJs(r, cls) {
22
+ const cases = CLASS_TESTS.map((test, i) => `case ${i}: return ${test};`).join(
23
+ " ",
24
+ );
25
+ return `const c = String.fromCodePoint(${r}); switch (${cls}) { ${cases} } return false;`;
26
+ }
27
+
28
+ // A letter whose upper case spans several code points (the German sharp s)
29
+ // stays as it is.
30
+ export function unicodeToUpperJs(r) {
31
+ return `const m = String.fromCodePoint(${r}).toUpperCase(); const u = m.codePointAt(0); return m.length > (u > 0xffff ? 2 : 1) ? ${r} : u;`;
32
+ }
33
+
34
+ export function unicodeToLowerJs(r) {
35
+ return `return String.fromCodePoint(${r}).toLowerCase().codePointAt(0);`;
36
+ }