bireactive 0.3.0 → 0.3.2
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 +14 -7
- package/dist/automerge/doc-cell.d.ts +20 -0
- package/dist/automerge/doc-cell.js +80 -0
- package/dist/automerge/index.d.ts +3 -0
- package/dist/automerge/index.js +12 -0
- package/dist/automerge/reconcile.d.ts +5 -0
- package/dist/automerge/reconcile.js +63 -0
- package/dist/core/_counts.d.ts +48 -0
- package/dist/core/_counts.js +51 -0
- package/dist/core/cell.d.ts +148 -112
- package/dist/core/cell.js +945 -768
- package/dist/core/debug.d.ts +25 -0
- package/dist/core/debug.js +121 -0
- package/dist/core/derived-geometry.js +4 -7
- package/dist/core/index.d.ts +9 -2
- package/dist/core/index.js +8 -1
- package/dist/core/lenses/aggregates.d.ts +42 -52
- package/dist/core/lenses/aggregates.js +225 -116
- package/dist/core/lenses/geometry.d.ts +22 -4
- package/dist/core/lenses/geometry.js +59 -27
- package/dist/core/lenses/index.d.ts +6 -6
- package/dist/core/lenses/index.js +6 -6
- package/dist/core/lenses/memory.js +4 -17
- package/dist/core/lenses/numerical.d.ts +100 -0
- package/dist/core/lenses/{typed-factor.js → numerical.js} +136 -34
- package/dist/core/lenses/point-cloud.d.ts +67 -0
- package/dist/core/lenses/{closed-form-policies.js → point-cloud.js} +226 -84
- package/dist/core/lenses/snap.d.ts +18 -0
- package/dist/core/lenses/snap.js +138 -0
- package/dist/core/lenses/text.d.ts +40 -0
- package/dist/core/lenses/text.js +202 -0
- package/dist/core/lifecycle.js +3 -6
- package/dist/core/linalg.js +5 -11
- package/dist/core/optic.d.ts +13 -0
- package/dist/core/optic.js +39 -0
- package/dist/core/optics.d.ts +10 -0
- package/dist/core/optics.js +26 -0
- package/dist/core/store.d.ts +9 -0
- package/dist/core/store.js +77 -0
- package/dist/core/traits.d.ts +4 -7
- package/dist/core/traits.js +8 -12
- package/dist/core/values/anchor.js +0 -4
- package/dist/core/values/arr.d.ts +110 -0
- package/dist/core/values/arr.js +336 -0
- package/dist/core/values/audio.d.ts +8 -9
- package/dist/core/values/audio.js +11 -28
- package/dist/core/values/bool.d.ts +11 -11
- package/dist/core/values/bool.js +12 -22
- package/dist/core/values/box.d.ts +15 -20
- package/dist/core/values/box.js +20 -33
- package/dist/core/values/canvas.d.ts +18 -25
- package/dist/core/values/canvas.js +32 -66
- package/dist/core/values/color.d.ts +5 -7
- package/dist/core/values/color.js +5 -11
- package/dist/core/values/field.d.ts +6 -7
- package/dist/core/values/field.js +10 -35
- package/dist/core/values/flags.d.ts +1 -2
- package/dist/core/values/flags.js +1 -17
- package/dist/core/values/gpu.d.ts +6 -10
- package/dist/core/values/gpu.js +8 -22
- package/dist/core/values/matrix.d.ts +2 -4
- package/dist/core/values/matrix.js +2 -12
- package/dist/core/values/num.d.ts +19 -28
- package/dist/core/values/num.js +23 -41
- package/dist/core/values/pose.d.ts +2 -4
- package/dist/core/values/pose.js +3 -12
- package/dist/core/values/range.d.ts +18 -26
- package/dist/core/values/range.js +22 -39
- package/dist/core/values/reg/ambiguity.d.ts +8 -0
- package/dist/core/values/reg/ambiguity.js +131 -0
- package/dist/core/values/reg/engine.d.ts +91 -0
- package/dist/core/values/reg/engine.js +373 -0
- package/dist/core/values/reg/nfa.d.ts +42 -0
- package/dist/core/values/reg/nfa.js +391 -0
- package/dist/core/values/reg/regex.d.ts +7 -0
- package/dist/core/values/reg/regex.js +318 -0
- package/dist/core/values/reg/types.d.ts +60 -0
- package/dist/core/values/reg/types.js +3 -0
- package/dist/core/values/reg.d.ts +250 -0
- package/dist/core/values/reg.js +649 -0
- package/dist/core/values/str.d.ts +16 -60
- package/dist/core/values/str.js +133 -315
- package/dist/core/values/template.js +1 -24
- package/dist/core/values/transform.d.ts +3 -5
- package/dist/core/values/transform.js +3 -12
- package/dist/core/values/tri.d.ts +9 -10
- package/dist/core/values/tri.js +9 -15
- package/dist/core/values/vec.d.ts +9 -24
- package/dist/core/values/vec.js +9 -64
- package/dist/formats/lens.js +6 -9
- package/dist/index.d.ts +0 -11
- package/dist/index.js +1 -11
- package/dist/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx-dev-runtime.js +5 -0
- package/dist/jsx-runtime.d.ts +54 -0
- package/dist/jsx-runtime.js +219 -0
- package/dist/schema/lens.js +5 -5
- package/dist/shapes/drag-behaviors.d.ts +56 -0
- package/dist/shapes/drag-behaviors.js +102 -0
- package/dist/shapes/drag-spec.d.ts +52 -0
- package/dist/shapes/drag-spec.js +112 -0
- package/dist/shapes/index.d.ts +3 -1
- package/dist/shapes/index.js +3 -1
- package/dist/shapes/interaction.d.ts +2 -3
- package/dist/shapes/interaction.js +77 -56
- package/dist/shapes/label.js +6 -0
- package/dist/shapes/layout.d.ts +47 -1
- package/dist/shapes/layout.js +59 -1
- package/package.json +22 -1
- package/dist/coll.d.ts +0 -74
- package/dist/coll.js +0 -210
- package/dist/core/lenses/closed-form-policies.d.ts +0 -57
- package/dist/core/lenses/decompositions.d.ts +0 -14
- package/dist/core/lenses/decompositions.js +0 -224
- package/dist/core/lenses/domain-aggregates.d.ts +0 -42
- package/dist/core/lenses/domain-aggregates.js +0 -245
- package/dist/core/lenses/typed-factor.d.ts +0 -40
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { altAll, CharSet as CS, chr, EPS, repeat, seq, seqAll, star, } from "./engine.js";
|
|
2
|
+
/** Thrown when a pattern uses a construct outside the regular subset. */
|
|
3
|
+
export class RegError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "RegError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const LINE_TERMINATORS = CS.of([
|
|
10
|
+
[0x0a, 0x0a],
|
|
11
|
+
[0x0d, 0x0d],
|
|
12
|
+
[0x2028, 0x2029],
|
|
13
|
+
]);
|
|
14
|
+
// JS \s
|
|
15
|
+
const WHITESPACE = CS.of([
|
|
16
|
+
[0x09, 0x0d],
|
|
17
|
+
[0x20, 0x20],
|
|
18
|
+
[0xa0, 0xa0],
|
|
19
|
+
[0x1680, 0x1680],
|
|
20
|
+
[0x2000, 0x200a],
|
|
21
|
+
[0x2028, 0x2029],
|
|
22
|
+
[0x202f, 0x202f],
|
|
23
|
+
[0x205f, 0x205f],
|
|
24
|
+
[0x3000, 0x3000],
|
|
25
|
+
[0xfeff, 0xfeff],
|
|
26
|
+
]);
|
|
27
|
+
const DIGIT = CS.range(0x30, 0x39);
|
|
28
|
+
const WORD = CS.of([
|
|
29
|
+
[0x30, 0x39],
|
|
30
|
+
[0x41, 0x5a],
|
|
31
|
+
[0x5f, 0x5f],
|
|
32
|
+
[0x61, 0x7a],
|
|
33
|
+
]);
|
|
34
|
+
/** Compile a `RegExp` to an engine `Re`, or throw `RegError`. */
|
|
35
|
+
export function compileRegex(re) {
|
|
36
|
+
const flags = { ignoreCase: re.flags.includes("i"), dotAll: re.flags.includes("s") };
|
|
37
|
+
const p = new Parser(re.source, flags);
|
|
38
|
+
const r = p.parseAlternation();
|
|
39
|
+
if (!p.atEnd())
|
|
40
|
+
throw new RegError(`unexpected "${p.peek()}" at ${p.pos} in /${re.source}/`);
|
|
41
|
+
return r;
|
|
42
|
+
}
|
|
43
|
+
class Parser {
|
|
44
|
+
src;
|
|
45
|
+
flags;
|
|
46
|
+
pos = 0;
|
|
47
|
+
constructor(src, flags) {
|
|
48
|
+
this.src = src;
|
|
49
|
+
this.flags = flags;
|
|
50
|
+
}
|
|
51
|
+
atEnd() {
|
|
52
|
+
return this.pos >= this.src.length;
|
|
53
|
+
}
|
|
54
|
+
peek() {
|
|
55
|
+
return this.src[this.pos] ?? "";
|
|
56
|
+
}
|
|
57
|
+
next() {
|
|
58
|
+
return this.src[this.pos++] ?? "";
|
|
59
|
+
}
|
|
60
|
+
mkChr(set) {
|
|
61
|
+
return chr(this.flags.ignoreCase ? set.ignoreCase() : set);
|
|
62
|
+
}
|
|
63
|
+
parseAlternation() {
|
|
64
|
+
const branches = [this.parseConcat()];
|
|
65
|
+
while (this.peek() === "|") {
|
|
66
|
+
this.next();
|
|
67
|
+
branches.push(this.parseConcat());
|
|
68
|
+
}
|
|
69
|
+
return branches.length === 1 ? branches[0] : altAll(branches);
|
|
70
|
+
}
|
|
71
|
+
parseConcat() {
|
|
72
|
+
const parts = [];
|
|
73
|
+
while (!this.atEnd() && this.peek() !== "|" && this.peek() !== ")") {
|
|
74
|
+
parts.push(this.parseQuantified());
|
|
75
|
+
}
|
|
76
|
+
return seqAll(parts);
|
|
77
|
+
}
|
|
78
|
+
parseQuantified() {
|
|
79
|
+
const atom = this.parseAtom();
|
|
80
|
+
return this.applyQuantifier(atom);
|
|
81
|
+
}
|
|
82
|
+
applyQuantifier(atom) {
|
|
83
|
+
const c = this.peek();
|
|
84
|
+
let out;
|
|
85
|
+
if (c === "*") {
|
|
86
|
+
this.next();
|
|
87
|
+
out = star(atom);
|
|
88
|
+
}
|
|
89
|
+
else if (c === "+") {
|
|
90
|
+
this.next();
|
|
91
|
+
out = seq(atom, star(atom));
|
|
92
|
+
}
|
|
93
|
+
else if (c === "?") {
|
|
94
|
+
this.next();
|
|
95
|
+
out = altAll([atom, EPS]);
|
|
96
|
+
}
|
|
97
|
+
else if (c === "{") {
|
|
98
|
+
const saved = this.pos;
|
|
99
|
+
const bounds = this.tryParseBraces();
|
|
100
|
+
if (bounds === null) {
|
|
101
|
+
this.pos = saved;
|
|
102
|
+
return atom; // a literal "{" — handled as a char by parseAtom next round
|
|
103
|
+
}
|
|
104
|
+
out = repeat(atom, bounds[0], bounds[1]);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
return atom;
|
|
108
|
+
}
|
|
109
|
+
if (this.peek() === "?")
|
|
110
|
+
this.next(); // lazy marker: parsed, ignored
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/** Parse `{n}` / `{n,}` / `{n,m}` after the `{`. Returns `null` if it isn't a
|
|
114
|
+
* well-formed quantifier (then `{` is a literal). */
|
|
115
|
+
tryParseBraces() {
|
|
116
|
+
if (this.peek() !== "{")
|
|
117
|
+
return null;
|
|
118
|
+
this.next();
|
|
119
|
+
const lo = this.readInt();
|
|
120
|
+
if (lo === null)
|
|
121
|
+
return null;
|
|
122
|
+
let hi = lo;
|
|
123
|
+
if (this.peek() === ",") {
|
|
124
|
+
this.next();
|
|
125
|
+
hi = this.peek() === "}" ? undefined : (this.readInt() ?? undefined);
|
|
126
|
+
}
|
|
127
|
+
if (this.peek() !== "}")
|
|
128
|
+
return null;
|
|
129
|
+
this.next();
|
|
130
|
+
return [lo, hi];
|
|
131
|
+
}
|
|
132
|
+
readInt() {
|
|
133
|
+
let s = "";
|
|
134
|
+
while (/[0-9]/.test(this.peek()))
|
|
135
|
+
s += this.next();
|
|
136
|
+
return s === "" ? null : Number.parseInt(s, 10);
|
|
137
|
+
}
|
|
138
|
+
parseAtom() {
|
|
139
|
+
const c = this.peek();
|
|
140
|
+
if (c === "(")
|
|
141
|
+
return this.parseGroup();
|
|
142
|
+
if (c === "[")
|
|
143
|
+
return this.parseClass();
|
|
144
|
+
if (c === ".") {
|
|
145
|
+
this.next();
|
|
146
|
+
return this.mkChr(this.flags.dotAll ? CS.full() : LINE_TERMINATORS.complement());
|
|
147
|
+
}
|
|
148
|
+
if (c === "^" || c === "$") {
|
|
149
|
+
throw new RegError(`anchors (^ $) are not supported: they aren't regular operators`);
|
|
150
|
+
}
|
|
151
|
+
if (c === "\\")
|
|
152
|
+
return this.parseEscape(false);
|
|
153
|
+
if (c === "*" || c === "+" || c === "?") {
|
|
154
|
+
throw new RegError(`nothing to repeat before "${c}" at ${this.pos}`);
|
|
155
|
+
}
|
|
156
|
+
this.next();
|
|
157
|
+
return this.mkChr(CS.char(c.charCodeAt(0)));
|
|
158
|
+
}
|
|
159
|
+
parseGroup() {
|
|
160
|
+
this.next(); // (
|
|
161
|
+
if (this.peek() === "?") {
|
|
162
|
+
this.next();
|
|
163
|
+
const k = this.peek();
|
|
164
|
+
if (k === ":") {
|
|
165
|
+
this.next();
|
|
166
|
+
}
|
|
167
|
+
else if (k === "<" && (this.src[this.pos + 1] === "=" || this.src[this.pos + 1] === "!")) {
|
|
168
|
+
throw new RegError("lookbehind (?<=…)/(?<!…) is not supported");
|
|
169
|
+
}
|
|
170
|
+
else if (k === "<") {
|
|
171
|
+
// named capture (?<name>…): grouping only, name is irrelevant here
|
|
172
|
+
this.next();
|
|
173
|
+
while (!this.atEnd() && this.peek() !== ">")
|
|
174
|
+
this.next();
|
|
175
|
+
if (this.peek() !== ">")
|
|
176
|
+
throw new RegError("malformed named group");
|
|
177
|
+
this.next();
|
|
178
|
+
}
|
|
179
|
+
else if (k === "=" || k === "!") {
|
|
180
|
+
throw new RegError("lookahead (?=…)/(?!…) is not supported");
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
throw new RegError(`unsupported group "(?${k}" at ${this.pos}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const inner = this.parseAlternation();
|
|
187
|
+
if (this.peek() !== ")")
|
|
188
|
+
throw new RegError(`unbalanced "(" — expected ")" at ${this.pos}`);
|
|
189
|
+
this.next();
|
|
190
|
+
return inner;
|
|
191
|
+
}
|
|
192
|
+
parseClass() {
|
|
193
|
+
this.next(); // [
|
|
194
|
+
let negated = false;
|
|
195
|
+
if (this.peek() === "^") {
|
|
196
|
+
this.next();
|
|
197
|
+
negated = true;
|
|
198
|
+
}
|
|
199
|
+
let set = CS.empty();
|
|
200
|
+
const add = (s) => {
|
|
201
|
+
set = set.union(s);
|
|
202
|
+
};
|
|
203
|
+
while (!this.atEnd() && this.peek() !== "]") {
|
|
204
|
+
// a class member: either an escape, or a char, possibly a range a-b
|
|
205
|
+
let lo;
|
|
206
|
+
if (this.peek() === "\\") {
|
|
207
|
+
const e = this.parseEscape(true);
|
|
208
|
+
lo = e; // either a single-char CharSet or a class-escape CharSet
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
lo = this.next().charCodeAt(0);
|
|
212
|
+
}
|
|
213
|
+
if (typeof lo !== "number") {
|
|
214
|
+
add(lo); // class escape (\d etc.) — can't be a range endpoint
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (this.peek() === "-" && this.src[this.pos + 1] !== "]" && this.pos + 1 < this.src.length) {
|
|
218
|
+
this.next(); // -
|
|
219
|
+
let hi;
|
|
220
|
+
if (this.peek() === "\\") {
|
|
221
|
+
const e = this.parseEscape(true);
|
|
222
|
+
if (typeof e !== "number") {
|
|
223
|
+
// range with a class escape on the right is invalid; treat literally
|
|
224
|
+
add(CS.char(lo));
|
|
225
|
+
add(CS.char(0x2d));
|
|
226
|
+
add(e);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
hi = e;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
hi = this.next().charCodeAt(0);
|
|
233
|
+
}
|
|
234
|
+
add(CS.range(lo, hi));
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
add(CS.char(lo));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (this.peek() !== "]")
|
|
241
|
+
throw new RegError(`unterminated character class`);
|
|
242
|
+
this.next();
|
|
243
|
+
return this.mkChr(negated ? set.complement() : set);
|
|
244
|
+
}
|
|
245
|
+
parseEscape(inClass) {
|
|
246
|
+
this.next(); // backslash
|
|
247
|
+
const c = this.next();
|
|
248
|
+
const set = (s) => (inClass ? s : this.mkChr(s));
|
|
249
|
+
const cp = (code) => inClass ? code : this.mkChr(CS.char(code));
|
|
250
|
+
switch (c) {
|
|
251
|
+
case "d":
|
|
252
|
+
return set(DIGIT);
|
|
253
|
+
case "D":
|
|
254
|
+
return set(DIGIT.complement());
|
|
255
|
+
case "w":
|
|
256
|
+
return set(WORD);
|
|
257
|
+
case "W":
|
|
258
|
+
return set(WORD.complement());
|
|
259
|
+
case "s":
|
|
260
|
+
return set(WHITESPACE);
|
|
261
|
+
case "S":
|
|
262
|
+
return set(WHITESPACE.complement());
|
|
263
|
+
case "t":
|
|
264
|
+
return cp(0x09);
|
|
265
|
+
case "n":
|
|
266
|
+
return cp(0x0a);
|
|
267
|
+
case "r":
|
|
268
|
+
return cp(0x0d);
|
|
269
|
+
case "f":
|
|
270
|
+
return cp(0x0c);
|
|
271
|
+
case "v":
|
|
272
|
+
return cp(0x0b);
|
|
273
|
+
case "0":
|
|
274
|
+
return cp(0x00);
|
|
275
|
+
case "b":
|
|
276
|
+
if (inClass)
|
|
277
|
+
return cp(0x08); // backspace inside a class
|
|
278
|
+
throw new RegError("word boundary \\b is not supported");
|
|
279
|
+
case "B":
|
|
280
|
+
throw new RegError("non-word-boundary \\B is not supported");
|
|
281
|
+
case "x": {
|
|
282
|
+
const h = this.src.slice(this.pos, this.pos + 2);
|
|
283
|
+
if (!/^[0-9a-fA-F]{2}$/.test(h))
|
|
284
|
+
throw new RegError("malformed \\xHH escape");
|
|
285
|
+
this.pos += 2;
|
|
286
|
+
return cp(Number.parseInt(h, 16));
|
|
287
|
+
}
|
|
288
|
+
case "u":
|
|
289
|
+
return cp(this.parseUnicodeEscape());
|
|
290
|
+
default:
|
|
291
|
+
if (/[1-9]/.test(c))
|
|
292
|
+
throw new RegError(`backreference \\${c} is not supported`);
|
|
293
|
+
if (c === "k")
|
|
294
|
+
throw new RegError("named backreference \\k<…> is not supported");
|
|
295
|
+
return cp(c.charCodeAt(0)); // escaped literal: \. \\ \( etc.
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
parseUnicodeEscape() {
|
|
299
|
+
if (this.peek() === "{") {
|
|
300
|
+
this.next();
|
|
301
|
+
let h = "";
|
|
302
|
+
while (this.peek() !== "}" && !this.atEnd())
|
|
303
|
+
h += this.next();
|
|
304
|
+
if (this.peek() !== "}")
|
|
305
|
+
throw new RegError("malformed \\u{…} escape");
|
|
306
|
+
this.next();
|
|
307
|
+
const v = Number.parseInt(h, 16);
|
|
308
|
+
if (Number.isNaN(v))
|
|
309
|
+
throw new RegError("malformed \\u{…} escape");
|
|
310
|
+
return v;
|
|
311
|
+
}
|
|
312
|
+
const h = this.src.slice(this.pos, this.pos + 4);
|
|
313
|
+
if (!/^[0-9a-fA-F]{4}$/.test(h))
|
|
314
|
+
throw new RegError("malformed \\uHHHH escape");
|
|
315
|
+
this.pos += 4;
|
|
316
|
+
return Number.parseInt(h, 16);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type Digit = "𝖣";
|
|
2
|
+
export type Lower = "𝖫";
|
|
3
|
+
export type Upper = "𝖴";
|
|
4
|
+
export type Space = "𝖶";
|
|
5
|
+
export type ClassAtom = Digit | Lower | Upper | Space;
|
|
6
|
+
export type Atom = ClassAtom | (string & {});
|
|
7
|
+
type Digits = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
|
8
|
+
type Lowers = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
|
|
9
|
+
type Uppers = Uppercase<Lowers>;
|
|
10
|
+
type Spaces = " " | "\t" | "\n" | "\r";
|
|
11
|
+
/** Fold one character to its atom (alphanumerics into a class; symbols stay
|
|
12
|
+
* singletons). */
|
|
13
|
+
export type Classify<C extends string> = C extends Digits ? Digit : C extends Lowers ? Lower : C extends Uppers ? Upper : C extends Spaces ? Space : C;
|
|
14
|
+
export type FirstChar<S extends string> = S extends `${infer H}${string}` ? H : never;
|
|
15
|
+
export type LastChar<S extends string> = S extends `${infer H}${infer R}` ? R extends "" ? H : LastChar<R> : never;
|
|
16
|
+
/** A boundary: ∅ (a leaf that only matches ""), ⊤ (unknown — the regex
|
|
17
|
+
* escape), a finite union of atoms, or a complement (everything but some
|
|
18
|
+
* atoms — e.g. `until(",")` ends on anything-but-comma). */
|
|
19
|
+
export type Bound = {
|
|
20
|
+
readonly k: "none";
|
|
21
|
+
} | {
|
|
22
|
+
readonly k: "all";
|
|
23
|
+
} | {
|
|
24
|
+
readonly k: "set";
|
|
25
|
+
readonly a: Atom;
|
|
26
|
+
} | {
|
|
27
|
+
readonly k: "co";
|
|
28
|
+
readonly a: Atom;
|
|
29
|
+
};
|
|
30
|
+
export type NoneBound = {
|
|
31
|
+
readonly k: "none";
|
|
32
|
+
};
|
|
33
|
+
export type AnyBound = {
|
|
34
|
+
readonly k: "all";
|
|
35
|
+
};
|
|
36
|
+
export type SetBound<A extends Atom> = {
|
|
37
|
+
readonly k: "set";
|
|
38
|
+
readonly a: A;
|
|
39
|
+
};
|
|
40
|
+
export type CoBound<A extends Atom> = {
|
|
41
|
+
readonly k: "co";
|
|
42
|
+
readonly a: A;
|
|
43
|
+
};
|
|
44
|
+
type IsNever<T> = [T] extends [never] ? true : false;
|
|
45
|
+
/** Provably disjoint? Returns `false` whenever overlap can't be ruled out, so
|
|
46
|
+
* acceptance is always sound (the runtime check is the complete authority). */
|
|
47
|
+
export type Disjoint<A extends Bound, B extends Bound> = A extends NoneBound ? true : B extends NoneBound ? true : A extends AnyBound ? false : B extends AnyBound ? false : A extends SetBound<infer SA> ? B extends SetBound<infer SB> ? IsNever<SA & SB> : B extends CoBound<infer CB> ? IsNever<Exclude<SA, CB>> : false : A extends CoBound<infer CA> ? B extends SetBound<infer SB> ? IsNever<Exclude<SB, CA>> : false : false;
|
|
48
|
+
/** *Provably* overlapping? The compile-time adjacency guard rejects only when
|
|
49
|
+
* this is `true`. An unknown boundary (`all`, from the `copy`/`of` escape
|
|
50
|
+
* hatch) returns `false` — the escape hatch opts out of *compile-time*
|
|
51
|
+
* checking, never of the complete runtime check. */
|
|
52
|
+
export type Overlaps<A extends Bound, B extends Bound> = A extends NoneBound ? false : B extends NoneBound ? false : A extends AnyBound ? false : B extends AnyBound ? false : A extends SetBound<infer SA> ? B extends SetBound<infer SB> ? IsNever<SA & SB> extends true ? false : true : B extends CoBound<infer CB> ? IsNever<Exclude<SA, CB>> extends true ? false : true : false : A extends CoBound<infer CA> ? B extends SetBound<infer SB> ? IsNever<Exclude<SB, CA>> extends true ? false : true : true : false;
|
|
53
|
+
/** Conservative union (may over-approximate to ⊤, which only makes the
|
|
54
|
+
* downstream disjointness test stricter — never unsound). */
|
|
55
|
+
export type Union<A extends Bound, B extends Bound> = A extends NoneBound ? B : B extends NoneBound ? A : A extends SetBound<infer SA> ? B extends SetBound<infer SB> ? SetBound<SA | SB> : AnyBound : AnyBound;
|
|
56
|
+
export type DigitBound = SetBound<Digit>;
|
|
57
|
+
export type LetterBound = SetBound<Lower | Upper>;
|
|
58
|
+
export type WordBound = SetBound<Digit | Lower | Upper | "_">;
|
|
59
|
+
export type LitBound<C extends string> = C extends "" ? NoneBound : SetBound<Classify<C>>;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { Cell, type Optic, type Writable } from "../cell.js";
|
|
2
|
+
import { Arr } from "./arr.js";
|
|
3
|
+
import { type Re } from "./reg/engine.js";
|
|
4
|
+
import type { AnyBound, Bound, Classify, CoBound, DigitBound, FirstChar, LastChar, LetterBound, LitBound, NoneBound, Overlaps, Union, WordBound } from "./reg/types.js";
|
|
5
|
+
import { Str } from "./str.js";
|
|
6
|
+
import { type Codec } from "./template.js";
|
|
7
|
+
/** A parsed star: its elements plus the literal separators between them
|
|
8
|
+
* (`seps.length === items.length - 1`), kept so `print` round-trips. */
|
|
9
|
+
export interface StarVal<V = RegVal> {
|
|
10
|
+
readonly items: readonly V[];
|
|
11
|
+
readonly seps: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
/** A parsed alternation: which branch matched, and that branch's value. */
|
|
14
|
+
export interface AltVal<V = RegVal> {
|
|
15
|
+
readonly branch: number;
|
|
16
|
+
readonly val: V;
|
|
17
|
+
}
|
|
18
|
+
/** The abstract value of a `Reg`. Wide by nature — `match`'s static type
|
|
19
|
+
* refines it; `bind` gives typed per-capture handles for editing. */
|
|
20
|
+
export type RegVal = string | number | boolean | null | readonly RegVal[] | StarVal | AltVal;
|
|
21
|
+
/** @internal — the grammar AST (also consumed by `reg/nfa.ts`). */
|
|
22
|
+
export type Node = LitNode | CopyNode | OfNode | SeqNode | AltNode | OptNode | StarNode;
|
|
23
|
+
/** @internal */
|
|
24
|
+
export interface LitNode {
|
|
25
|
+
readonly kind: "lit";
|
|
26
|
+
readonly text: string;
|
|
27
|
+
}
|
|
28
|
+
/** @internal */
|
|
29
|
+
export interface CopyNode {
|
|
30
|
+
readonly kind: "copy";
|
|
31
|
+
readonly re: RegExp;
|
|
32
|
+
readonly engine: Re;
|
|
33
|
+
readonly name?: string;
|
|
34
|
+
}
|
|
35
|
+
/** @internal */
|
|
36
|
+
export interface OfNode {
|
|
37
|
+
readonly kind: "of";
|
|
38
|
+
readonly re: RegExp;
|
|
39
|
+
readonly engine: Re;
|
|
40
|
+
readonly codec: Codec<unknown>;
|
|
41
|
+
readonly name?: string;
|
|
42
|
+
}
|
|
43
|
+
/** @internal */
|
|
44
|
+
export interface SeqNode {
|
|
45
|
+
readonly kind: "seq";
|
|
46
|
+
readonly parts: readonly Node[];
|
|
47
|
+
}
|
|
48
|
+
/** @internal */
|
|
49
|
+
export interface AltNode {
|
|
50
|
+
readonly kind: "alt";
|
|
51
|
+
readonly branches: readonly Node[];
|
|
52
|
+
}
|
|
53
|
+
/** @internal */
|
|
54
|
+
export interface OptNode {
|
|
55
|
+
readonly kind: "opt";
|
|
56
|
+
readonly part: Node;
|
|
57
|
+
}
|
|
58
|
+
/** @internal */
|
|
59
|
+
export interface StarNode {
|
|
60
|
+
readonly kind: "star";
|
|
61
|
+
readonly part: Node;
|
|
62
|
+
readonly sep?: Node;
|
|
63
|
+
readonly joiner: string;
|
|
64
|
+
/** Minimum element count for an *unseparated* star (0 = Kleene, 1 = plus).
|
|
65
|
+
* Separated stars are always ≥1 (split semantics). */
|
|
66
|
+
readonly min: 0 | 1;
|
|
67
|
+
/** Resourceful alignment: derive a stable identity from an element's string.
|
|
68
|
+
* Element handles then follow that identity across reorders. */
|
|
69
|
+
readonly key?: (item: string) => string;
|
|
70
|
+
readonly name?: string;
|
|
71
|
+
}
|
|
72
|
+
/** Source span `[start, end)` of a named capture, recovered by `spans`. */
|
|
73
|
+
export type Span = readonly [start: number, end: number];
|
|
74
|
+
declare const SILENT: unique symbol;
|
|
75
|
+
/** The value of a `lit`: dropped from `seq` tuples. */
|
|
76
|
+
export type Silent = {
|
|
77
|
+
readonly [SILENT]: true;
|
|
78
|
+
};
|
|
79
|
+
type AnyReg = Reg<any, any, any, any>;
|
|
80
|
+
type ValOf<R> = R extends Reg<infer V, any, any, any> ? V : never;
|
|
81
|
+
type NullOf<R> = R extends Reg<any, infer N, any, any> ? N : never;
|
|
82
|
+
type FirstOf<R> = R extends Reg<any, any, infer F, any> ? F : never;
|
|
83
|
+
type LastOf<R> = R extends Reg<any, any, any, infer L> ? L : never;
|
|
84
|
+
type And<A extends boolean, B extends boolean> = A extends true ? B : false;
|
|
85
|
+
type Or<A extends boolean, B extends boolean> = A extends true ? true : B;
|
|
86
|
+
/** A value as it contributes to a `seq` tuple: `lit` drops out; a nested seq
|
|
87
|
+
* tuple splices in; everything else is one slot. */
|
|
88
|
+
type AsTuple<V> = [V] extends [Silent] ? [] : V extends readonly unknown[] ? V : [V];
|
|
89
|
+
/** The value of an optional: inner value when present, `null` when absent; a
|
|
90
|
+
* silent inner (no value) records presence as `true`. */
|
|
91
|
+
type OptVal<V> = ([V] extends [Silent] ? true : V) | null;
|
|
92
|
+
/** Boundary of `until(c)`: a single-char delimiter excludes exactly one class
|
|
93
|
+
* (`CoBound`); a multi-char delimiter excludes several at runtime but the
|
|
94
|
+
* single-atom type algebra can't name that, so it degrades to `AnyBound`
|
|
95
|
+
* (sound: defers the adjacency check to construction time). */
|
|
96
|
+
type UntilBound<C extends string> = C extends `${infer _H}${infer R}` ? R extends "" ? CoBound<Classify<C>> : AnyBound : AnyBound;
|
|
97
|
+
/** The brand a bad adjacency degrades the argument list to, so the call site
|
|
98
|
+
* fails with this message. */
|
|
99
|
+
type AdjErr = {
|
|
100
|
+
readonly __ambiguous: "adjacent parts overlap — insert a lit() delimiter or use disjoint character classes";
|
|
101
|
+
};
|
|
102
|
+
/** Options for `bind` / `view`. */
|
|
103
|
+
export interface BindOpts {
|
|
104
|
+
strict?: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** A bound named handle: a `string` capture is a `Writable<Str>`; a `star`
|
|
107
|
+
* capture is an editable `Arr<string>`. */
|
|
108
|
+
export type Handle = Writable<Str> | Arr<string>;
|
|
109
|
+
/** Schema tag for the typed `bind` overload: `"str"` → scalar, `"arr"` → star. */
|
|
110
|
+
export type HandleKind = "str" | "arr";
|
|
111
|
+
/** The concrete handle type for a schema tag. */
|
|
112
|
+
export type HandleOf<K extends HandleKind> = K extends "arr" ? Arr<string> : Writable<Str>;
|
|
113
|
+
/** An immutable bidirectional string-lens description. Build with the typed
|
|
114
|
+
* leaf builders and combinators, then `bind`/`view` onto a `Cell<string>`.
|
|
115
|
+
*
|
|
116
|
+
* The four type parameters are phantom: `V` is the parsed value, `N` whether
|
|
117
|
+
* it accepts "", and `F`/`L` the character classes its match can begin/end
|
|
118
|
+
* with. `F`/`L`/`N` drive the compile-time ambiguity checks; they have no
|
|
119
|
+
* runtime presence. */
|
|
120
|
+
export declare class Reg<V = RegVal, N extends boolean = boolean, F extends Bound = AnyBound, L extends Bound = AnyBound> {
|
|
121
|
+
#private;
|
|
122
|
+
/** @internal */
|
|
123
|
+
readonly root: Node;
|
|
124
|
+
/** @internal — use the static builders. */
|
|
125
|
+
constructor(root: Node);
|
|
126
|
+
/** A fixed delimiter: matched and printed, never surfaced as a value. */
|
|
127
|
+
static lit<T extends string>(text: T): Reg<Silent, T extends "" ? true : false, LitBound<FirstChar<T>>, LitBound<LastChar<T>>>;
|
|
128
|
+
/** Text up to (but not including) the delimiter `c` — i.e. `[^c]*`. Nullable
|
|
129
|
+
* (an empty field is allowed); the natural companion of `star(lit(c))`. */
|
|
130
|
+
static until<C extends string>(c: C): Reg<string, true, UntilBound<C>, UntilBound<C>>;
|
|
131
|
+
/** One or more digits, `\d+`, as a string. */
|
|
132
|
+
static digits(): Reg<string, false, DigitBound, DigitBound>;
|
|
133
|
+
/** One or more digits, decoded as a `number` (a quotient lens — leading
|
|
134
|
+
* zeros are not preserved). */
|
|
135
|
+
static int(): Reg<number, false, DigitBound, DigitBound>;
|
|
136
|
+
/** One or more ASCII letters, `[A-Za-z]+`. */
|
|
137
|
+
static letters(): Reg<string, false, LetterBound, LetterBound>;
|
|
138
|
+
/** One or more word characters, `\w+` (letters, digits, underscore). */
|
|
139
|
+
static word(): Reg<string, false, WordBound, WordBound>;
|
|
140
|
+
/** The escape hatch: capture the text matched by an arbitrary regular `re`.
|
|
141
|
+
* Non-regular constructs (anchors, lookaround, backreferences) throw. The
|
|
142
|
+
* boundary is opaque to the type system (`AnyBound`), so adjacency can't be
|
|
143
|
+
* checked at compile time — the construction-time check still applies. */
|
|
144
|
+
static copy(re: RegExp): Reg<string, boolean, AnyBound, AnyBound>;
|
|
145
|
+
/** Typed escape hatch: `re` recognizes, `codec` decodes/encodes. */
|
|
146
|
+
static of<T>(re: RegExp, codec: Codec<T>): Reg<T, boolean, AnyBound, AnyBound>;
|
|
147
|
+
/** Unambiguous concatenation; every boundary is checked here and throws on
|
|
148
|
+
* ambiguity. (For compile-time adjacency checking, prefer the fluent
|
|
149
|
+
* `a.then(b).then(c)`, which validates each link.) */
|
|
150
|
+
static seq<T extends readonly AnyReg[]>(...parts: T): Reg<SeqVal<T>, SeqNull<T>, SeqFirst<T>, SeqLast<T>>;
|
|
151
|
+
/** Ordered union; branches must be first-disjoint (checked). */
|
|
152
|
+
static alt<T extends readonly AnyReg[]>(...branches: T): Reg<AltOf<T>, AltNull<T>, AltFirst<T>, AltLast<T>>;
|
|
153
|
+
/** Optional (`part` or nothing). `part` must be non-nullable. The value is
|
|
154
|
+
* the inner value when present and `null` when absent; an optional with no
|
|
155
|
+
* value of its own (e.g. `lit(...).optional()`) records presence as `true`. */
|
|
156
|
+
static opt<V2, F2 extends Bound, L2 extends Bound>(part: Reg<V2, false, F2, L2>): Reg<OptVal<V2>, true, F2, L2>;
|
|
157
|
+
/** `seq(this, ...next)`. A provably-overlapping boundary between `this` and
|
|
158
|
+
* the next part is a *type* error — so `a.then(b).then(c)` statically checks
|
|
159
|
+
* every link. Interior boundaries of a multi-arg call, and the `copy`/`of`
|
|
160
|
+
* escapes, are checked at construction (throws). */
|
|
161
|
+
then<T extends readonly AnyReg[]>(this: Reg<V, N, F, L>, ...next: T extends readonly [infer H extends AnyReg, ...AnyReg[]] ? Overlaps<L, FirstOf<H>> extends true ? readonly AdjErr[] : T : T): Reg<SeqVal<[Reg<V, N, F, L>, ...T]>, SeqNull<[Reg<V, N, F, L>, ...T]>, SeqFirst<[Reg<V, N, F, L>, ...T]>, SeqLast<[Reg<V, N, F, L>, ...T]>>;
|
|
162
|
+
/** `alt(this, other)`. */
|
|
163
|
+
or<V2, N2 extends boolean, F2 extends Bound, L2 extends Bound>(this: Reg<V, N, F, L>, other: Reg<V2, N2, F2, L2>): Reg<AltVal<V> | AltValB<V2>, Or<N, N2>, Union<F, F2>, Union<L, L2>>;
|
|
164
|
+
/** `opt(this)`. Only available when `this` is non-nullable. */
|
|
165
|
+
optional(this: Reg<V, false, F, L>): Reg<OptVal<V>, true, F, L>;
|
|
166
|
+
/** Iterate zero-or-more, optionally separated by `sep`; binds to an `Arr`.
|
|
167
|
+
* A separated star is a *split* (≥1 piece, like `Str.split`). Pass
|
|
168
|
+
* `opts.key` for resourceful alignment across reorders. */
|
|
169
|
+
star<Vs = never, Ns extends boolean = boolean, Fs extends Bound = Bound, Ls extends Bound = Bound>(this: Reg<V, N, F, L>, sep?: Reg<Vs, Ns, Fs, Ls>, opts?: {
|
|
170
|
+
key?: (item: string) => string;
|
|
171
|
+
}): Reg<StarVal<V>, true, F, L>;
|
|
172
|
+
/** Iterate one-or-more (forbids the empty list when unseparated). */
|
|
173
|
+
plus<Vs = never, Ns extends boolean = boolean, Fs extends Bound = Bound, Ls extends Bound = Bound>(this: Reg<V, N, F, L>, sep?: Reg<Vs, Ns, Fs, Ls>, opts?: {
|
|
174
|
+
key?: (item: string) => string;
|
|
175
|
+
}): Reg<StarVal<V>, N, F, L>;
|
|
176
|
+
/** Name this capture so `bind` exposes it as a handle (`copy`/`of`/`star`). */
|
|
177
|
+
as(name: string): Reg<V, N, F, L>;
|
|
178
|
+
/** Attach a codec to a `copy` leaf, turning it into a typed `of` capture. */
|
|
179
|
+
map<T>(codec: Codec<T>): Reg<T, N, F, L>;
|
|
180
|
+
/** Parse `s` fully (must consume to the end); `null` if it doesn't match.
|
|
181
|
+
* Single-pass and linear. */
|
|
182
|
+
match(s: string): V | null;
|
|
183
|
+
/** Reflective print: render a value back to source text. */
|
|
184
|
+
print(v: V): string;
|
|
185
|
+
/** Does `s` fully match? Linear. */
|
|
186
|
+
test(s: string): boolean;
|
|
187
|
+
/** Source spans of each named capture, keyed by name — the `get`/`put`
|
|
188
|
+
* correspondence made visible. Empty if `s` doesn't fully match. */
|
|
189
|
+
spans(s: string): Record<string, Span>;
|
|
190
|
+
/** This grammar as a first-class, composable `Optic<string, V>`: `get`
|
|
191
|
+
* parses (falling back to the default value off-language), `put` reprints
|
|
192
|
+
* and round-trip-guards (an off-language source or a non-round-tripping
|
|
193
|
+
* value leaves the source untouched). Drops straight into `compose(...)`
|
|
194
|
+
* and `cell.through(...)`, so it chains with `atKey`/`iso` and string
|
|
195
|
+
* lenses like `caseFold`. */
|
|
196
|
+
optic(): Optic<string, V>;
|
|
197
|
+
/** The whole abstract value as a writable lens over `source`. */
|
|
198
|
+
view(source: Cell<string>): Writable<Cell<V>>;
|
|
199
|
+
/** Bind named captures (`.as`) to editable handles over `source`: a string
|
|
200
|
+
* capture → `Writable<Str>`, a `star` capture → `Arr<string>`.
|
|
201
|
+
*
|
|
202
|
+
* Pass `opts.schema` (`{ name: "str" | "arr" }`) for known keys and
|
|
203
|
+
* per-handle types without casts. */
|
|
204
|
+
bind<S extends Record<string, HandleKind>>(source: Cell<string>, opts: {
|
|
205
|
+
schema: S;
|
|
206
|
+
}): {
|
|
207
|
+
[K in keyof S]: HandleOf<S[K]>;
|
|
208
|
+
};
|
|
209
|
+
bind(source: Cell<string>, opts?: BindOpts): Record<string, Handle>;
|
|
210
|
+
}
|
|
211
|
+
type SeqVal<T extends readonly AnyReg[]> = T extends readonly [
|
|
212
|
+
infer H extends AnyReg,
|
|
213
|
+
...infer R extends readonly AnyReg[]
|
|
214
|
+
] ? [...AsTuple<ValOf<H>>, ...SeqVal<R>] : [];
|
|
215
|
+
type SeqNull<T extends readonly AnyReg[]> = T extends readonly [
|
|
216
|
+
infer H extends AnyReg,
|
|
217
|
+
...infer R extends readonly AnyReg[]
|
|
218
|
+
] ? And<NullOf<H>, SeqNull<R>> : true;
|
|
219
|
+
type SeqFirst<T extends readonly AnyReg[]> = T extends readonly [
|
|
220
|
+
infer H extends AnyReg,
|
|
221
|
+
...infer R extends readonly AnyReg[]
|
|
222
|
+
] ? NullOf<H> extends true ? Union<FirstOf<H>, SeqFirst<R>> : FirstOf<H> : NoneBound;
|
|
223
|
+
type SeqLast<T extends readonly AnyReg[]> = T extends readonly [
|
|
224
|
+
...infer R extends readonly AnyReg[],
|
|
225
|
+
infer Last extends AnyReg
|
|
226
|
+
] ? NullOf<Last> extends true ? Union<LastOf<Last>, SeqLast<R>> : LastOf<Last> : NoneBound;
|
|
227
|
+
type AltOf<T extends readonly AnyReg[]> = T extends readonly [
|
|
228
|
+
infer H extends AnyReg,
|
|
229
|
+
...infer R extends readonly AnyReg[]
|
|
230
|
+
] ? R extends readonly [] ? AltVal<ValOf<H>> : AltVal<ValOf<H>> | AltShift<AltOf<R>> : never;
|
|
231
|
+
type AltShift<U> = U extends AltVal<infer W> ? AltValB<W> : never;
|
|
232
|
+
/** `AltVal` whose branch is ≥1 (used to widen `or`/`alt` unions structurally;
|
|
233
|
+
* the runtime tag is the real index). */
|
|
234
|
+
export type AltValB<V> = {
|
|
235
|
+
readonly branch: number;
|
|
236
|
+
readonly val: V;
|
|
237
|
+
};
|
|
238
|
+
type AltNull<T extends readonly AnyReg[]> = T extends readonly [
|
|
239
|
+
infer H extends AnyReg,
|
|
240
|
+
...infer R extends readonly AnyReg[]
|
|
241
|
+
] ? Or<NullOf<H>, AltNull<R>> : false;
|
|
242
|
+
type AltFirst<T extends readonly AnyReg[]> = T extends readonly [
|
|
243
|
+
infer H extends AnyReg,
|
|
244
|
+
...infer R extends readonly AnyReg[]
|
|
245
|
+
] ? Union<FirstOf<H>, AltFirst<R>> : NoneBound;
|
|
246
|
+
type AltLast<T extends readonly AnyReg[]> = T extends readonly [
|
|
247
|
+
infer H extends AnyReg,
|
|
248
|
+
...infer R extends readonly AnyReg[]
|
|
249
|
+
] ? Union<LastOf<H>, AltLast<R>> : NoneBound;
|
|
250
|
+
export {};
|