bireactive 0.3.1 → 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/core/_counts.js +5 -12
- package/dist/core/cell.d.ts +3 -3
- package/dist/core/cell.js +6 -7
- package/dist/core/derived-geometry.js +4 -7
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +3 -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 +5 -6
- package/dist/core/lenses/index.js +5 -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} +218 -81
- package/dist/core/lenses/snap.d.ts +1 -1
- package/dist/core/lenses/snap.js +3 -10
- 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.js +10 -15
- package/dist/core/optics.js +4 -8
- package/dist/core/store.d.ts +1 -2
- package/dist/core/store.js +7 -15
- 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 +7 -23
- 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 +17 -48
- 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/index.d.ts +0 -11
- package/dist/index.js +1 -11
- package/package.json +6 -7
- 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
|
@@ -12,15 +12,14 @@ export declare const sub: (a: V, b: V) => V;
|
|
|
12
12
|
export declare const scale: (a: V, k: number) => V;
|
|
13
13
|
export declare const lerp: (a: V, b: V, t: number) => V;
|
|
14
14
|
export declare const equals: (a: V, b: V) => boolean;
|
|
15
|
-
/**
|
|
15
|
+
/** Euclidean distance over (lo, hi). */
|
|
16
16
|
export declare const metric: (a: V, b: V) => number;
|
|
17
17
|
export declare const width: (r: V) => number;
|
|
18
18
|
export declare const center: (r: V) => number;
|
|
19
19
|
export declare const contains: (r: V, v: number) => boolean;
|
|
20
20
|
export declare const clamp: (r: V, v: number) => number;
|
|
21
|
-
/** Closest value
|
|
22
|
-
*
|
|
23
|
-
* false-side policy. */
|
|
21
|
+
/** Closest value strictly outside `[lo, hi]`, displaced past the nearest
|
|
22
|
+
* endpoint by `eps`. */
|
|
24
23
|
export declare const eject: (r: V, v: number, eps?: number) => number;
|
|
25
24
|
/** Sample at parameter `t`: `lo + t·(hi - lo)`. `t ∈ [0, 1]` stays
|
|
26
25
|
* inside the range; values outside extrapolate linearly. */
|
|
@@ -43,43 +42,36 @@ export declare class Range extends Cell<V> {
|
|
|
43
42
|
/** End endpoint. Writes preserve `lo` (end-knob semantics). */
|
|
44
43
|
get hi(): this extends WritableBrand ? Writable<Num> : Num;
|
|
45
44
|
get width(): Num;
|
|
46
|
-
/** Midpoint body-drag:
|
|
47
|
-
*
|
|
48
|
-
* variant; `.lo` / `.hi` edit the endpoints. */
|
|
45
|
+
/** Midpoint body-drag: reads the center; a write shifts the range so the
|
|
46
|
+
* center matches (width preserved). */
|
|
49
47
|
get center(): Writable<Num>;
|
|
50
|
-
/** Translate by `by`.
|
|
48
|
+
/** Translate by `by`. */
|
|
51
49
|
shift(by: Val<number>): this;
|
|
52
|
-
/** Scale
|
|
50
|
+
/** Scale about the origin. Invertible when k ≠ 0. */
|
|
53
51
|
scale(k: Val<number>): this;
|
|
54
|
-
/** Body-drag handle:
|
|
55
|
-
*
|
|
52
|
+
/** Body-drag handle: reads `lo`; a write shifts the range so `lo` matches
|
|
53
|
+
* (width preserved). */
|
|
56
54
|
get start(): Writable<Num>;
|
|
57
55
|
/** RO sample at `t`. `t ∈ [0, 1]` stays inside; outside extrapolates. */
|
|
58
56
|
sample(t: Val<number>): Num;
|
|
59
57
|
/** Bidirectional `t ↔ value` slider. Read `lo + t·(hi - lo)`; write
|
|
60
58
|
* solves for `t` and updates `t` only, leaving `lo` / `hi` put. */
|
|
61
59
|
slider(t: Writable<Num>): Writable<Num>;
|
|
62
|
-
/**
|
|
63
|
-
*
|
|
64
|
-
* (`
|
|
65
|
-
* endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
|
|
60
|
+
/** True when `v` is in `[lo, hi]`. A writable `Num` yields a `Writable<Bool>`:
|
|
61
|
+
* flipping it clamps `v` inside (`true`) or ejects it past the nearest
|
|
62
|
+
* endpoint (`false`). Literal/RO inputs yield a read-only `Bool`. */
|
|
66
63
|
contains<P extends Val<number>>(v: P): P extends WritableBrand ? Writable<Bool> : Bool;
|
|
67
|
-
/**
|
|
68
|
-
* on a single Num, see `Num#clamp(lo, hi)`. */
|
|
64
|
+
/** Read-only clamp of `v` into `[lo, hi]`. */
|
|
69
65
|
clampedRead(v: Val<number>): Num;
|
|
70
66
|
/** Inverse of `sample`: derive the `t` that would produce `v`. */
|
|
71
67
|
paramOf(v: Val<number>): Num;
|
|
72
|
-
/** Tween-builder
|
|
68
|
+
/** Tween-builder. */
|
|
73
69
|
to(this: Writable<Range>, target: V, dur: Val<number>, ease?: Easing): Tween<V>;
|
|
74
70
|
}
|
|
75
|
-
/** Range over `[at, at + dur]`,
|
|
76
|
-
* timeline-clip shape: `.lo` slides the start, `.hi` the end, `.start`
|
|
77
|
-
* body-drags (preserving width). Backed by the live `at` / `dur` Nums. */
|
|
71
|
+
/** Range over `[at, at + dur]`, backed by the live `at` and `dur` Nums. */
|
|
78
72
|
export declare function span(at: Writable<Num>, dur: Writable<Num>): Writable<Range>;
|
|
79
|
-
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* `Range.derive(...)` for reactive RO tracking, or `cell.value` to
|
|
83
|
-
* snapshot. Lock an endpoint with `Num.pin(c)`. */
|
|
73
|
+
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal (new cell) or
|
|
74
|
+
* existing writable (passed through); for read-only sources use `Range.derive`.
|
|
75
|
+
* Lock an endpoint with `Num.pin`. */
|
|
84
76
|
export declare function range(lo?: Init<Num>, hi?: Init<Num>): Writable<Range>;
|
|
85
77
|
export {};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
// range.ts — reactive numeric interval `[lo, hi]`.
|
|
2
|
-
//
|
|
3
|
-
// Home for sliders, scrollbars, and timeline clip spans. Field lenses
|
|
4
|
-
// give start-knob (`.lo`) and end-knob (`.hi`) drag; `.start` / `.center`
|
|
5
|
-
// are body-drags (shift both, preserving width — anchored at lo / midpoint
|
|
6
|
-
// respectively); `.slider(t)` is the bidirectional `t ↔ lo + t·(hi - lo)`
|
|
7
|
-
// iso. Traits: `linear`, `lerp`, `equals`, `pack` (scalar-only).
|
|
8
1
|
import { tween } from "../../animation/index.js";
|
|
9
2
|
import { Cell, cachedDerive, fieldLens, isReadonly, reader, readNow, SKIP, } from "../cell.js";
|
|
10
3
|
import { Bool } from "./bool.js";
|
|
@@ -17,15 +10,14 @@ export const lerp = (a, b, t) => ({
|
|
|
17
10
|
hi: a.hi + (b.hi - a.hi) * t,
|
|
18
11
|
});
|
|
19
12
|
export const equals = (a, b) => a === b || (a.lo === b.lo && a.hi === b.hi);
|
|
20
|
-
/**
|
|
13
|
+
/** Euclidean distance over (lo, hi). */
|
|
21
14
|
export const metric = (a, b) => Math.hypot(a.lo - b.lo, a.hi - b.hi);
|
|
22
15
|
export const width = (r) => r.hi - r.lo;
|
|
23
16
|
export const center = (r) => (r.lo + r.hi) / 2;
|
|
24
17
|
export const contains = (r, v) => v >= r.lo && v <= r.hi;
|
|
25
18
|
export const clamp = (r, v) => (v < r.lo ? r.lo : v > r.hi ? r.hi : v);
|
|
26
|
-
/** Closest value
|
|
27
|
-
*
|
|
28
|
-
* false-side policy. */
|
|
19
|
+
/** Closest value strictly outside `[lo, hi]`, displaced past the nearest
|
|
20
|
+
* endpoint by `eps`. */
|
|
29
21
|
export const eject = (r, v, eps = 1e-6) => {
|
|
30
22
|
if (!contains(r, v))
|
|
31
23
|
return v;
|
|
@@ -71,21 +63,20 @@ export class Range extends Cell {
|
|
|
71
63
|
get width() {
|
|
72
64
|
return cachedDerive(this, "width", Num, width);
|
|
73
65
|
}
|
|
74
|
-
/** Midpoint body-drag:
|
|
75
|
-
*
|
|
76
|
-
* variant; `.lo` / `.hi` edit the endpoints. */
|
|
66
|
+
/** Midpoint body-drag: reads the center; a write shifts the range so the
|
|
67
|
+
* center matches (width preserved). */
|
|
77
68
|
get center() {
|
|
78
69
|
return Num.lens(this, center, (c, src) => {
|
|
79
70
|
const half = (src.hi - src.lo) / 2;
|
|
80
71
|
return { lo: c - half, hi: c + half };
|
|
81
72
|
});
|
|
82
73
|
}
|
|
83
|
-
/** Translate by `by`.
|
|
74
|
+
/** Translate by `by`. */
|
|
84
75
|
shift(by) {
|
|
85
76
|
const f = reader(by);
|
|
86
77
|
return this.lens(v => ({ lo: v.lo + f(), hi: v.hi + f() }), n => ({ lo: n.lo - f(), hi: n.hi - f() }));
|
|
87
78
|
}
|
|
88
|
-
/** Scale
|
|
79
|
+
/** Scale about the origin. Invertible when k ≠ 0. */
|
|
89
80
|
scale(k) {
|
|
90
81
|
const kf = reader(k);
|
|
91
82
|
return this.lens(v => {
|
|
@@ -96,8 +87,8 @@ export class Range extends Cell {
|
|
|
96
87
|
return { lo: n.lo / k, hi: n.hi / k };
|
|
97
88
|
});
|
|
98
89
|
}
|
|
99
|
-
/** Body-drag handle:
|
|
100
|
-
*
|
|
90
|
+
/** Body-drag handle: reads `lo`; a write shifts the range so `lo` matches
|
|
91
|
+
* (width preserved). */
|
|
101
92
|
get start() {
|
|
102
93
|
return Num.lens(this, v => v.lo, (newLo, src) => ({ lo: newLo, hi: newLo + (src.hi - src.lo) }));
|
|
103
94
|
}
|
|
@@ -108,21 +99,19 @@ export class Range extends Cell {
|
|
|
108
99
|
/** Bidirectional `t ↔ value` slider. Read `lo + t·(hi - lo)`; write
|
|
109
100
|
* solves for `t` and updates `t` only, leaving `lo` / `hi` put. */
|
|
110
101
|
slider(t) {
|
|
111
|
-
// `this as Range` pins the tuple element type
|
|
112
|
-
//
|
|
102
|
+
// `this as Range` pins the tuple element type; polymorphic `this` breaks
|
|
103
|
+
// the mapped-tuple inference.
|
|
113
104
|
return Num.lens([this, t], ([r, tv]) => sample(r, tv), (v, [r]) => {
|
|
114
105
|
const w = r.hi - r.lo;
|
|
115
106
|
return [SKIP, w === 0 ? 0 : (v - r.lo) / w];
|
|
116
107
|
});
|
|
117
108
|
}
|
|
118
|
-
/**
|
|
119
|
-
*
|
|
120
|
-
* (`
|
|
121
|
-
* endpoint by `eps`). Literal / RO inputs yield a bare RO `Bool`. */
|
|
109
|
+
/** True when `v` is in `[lo, hi]`. A writable `Num` yields a `Writable<Bool>`:
|
|
110
|
+
* flipping it clamps `v` inside (`true`) or ejects it past the nearest
|
|
111
|
+
* endpoint (`false`). Literal/RO inputs yield a read-only `Bool`. */
|
|
122
112
|
contains(v) {
|
|
123
113
|
if (v instanceof Num) {
|
|
124
|
-
// RO Num has no backward path
|
|
125
|
-
// writable lenses both accept write-back.
|
|
114
|
+
// RO Num has no backward path; only writable Nums accept write-back.
|
|
126
115
|
if (!isReadonly(v)) {
|
|
127
116
|
return Bool.lens([this, v], (vals) => contains(vals[0], vals[1]), (target, vals) => {
|
|
128
117
|
const [r, n] = vals;
|
|
@@ -134,8 +123,7 @@ export class Range extends Cell {
|
|
|
134
123
|
}
|
|
135
124
|
return Bool.derive(() => contains(this.value, readNow(v)));
|
|
136
125
|
}
|
|
137
|
-
/**
|
|
138
|
-
* on a single Num, see `Num#clamp(lo, hi)`. */
|
|
126
|
+
/** Read-only clamp of `v` into `[lo, hi]`. */
|
|
139
127
|
clampedRead(v) {
|
|
140
128
|
return Num.derive(() => clamp(this.value, readNow(v)));
|
|
141
129
|
}
|
|
@@ -143,27 +131,22 @@ export class Range extends Cell {
|
|
|
143
131
|
paramOf(v) {
|
|
144
132
|
return Num.derive(() => paramOf(this.value, readNow(v)));
|
|
145
133
|
}
|
|
146
|
-
/** Tween-builder
|
|
134
|
+
/** Tween-builder. */
|
|
147
135
|
to(target, dur, ease) {
|
|
148
136
|
return tween(this, target, dur, ease);
|
|
149
137
|
}
|
|
150
138
|
}
|
|
151
|
-
/**
|
|
152
|
-
* here after lifting literals. */
|
|
139
|
+
/** Lens combining two writable `Num`s into a `Range`. */
|
|
153
140
|
function ends(lo, hi) {
|
|
154
141
|
return Range.lens([lo, hi], (vals) => ({ lo: vals[0], hi: vals[1] }), (target) => [target.lo, target.hi]);
|
|
155
142
|
}
|
|
156
|
-
/** Range over `[at, at + dur]`,
|
|
157
|
-
* timeline-clip shape: `.lo` slides the start, `.hi` the end, `.start`
|
|
158
|
-
* body-drags (preserving width). Backed by the live `at` / `dur` Nums. */
|
|
143
|
+
/** Range over `[at, at + dur]`, backed by the live `at` and `dur` Nums. */
|
|
159
144
|
export function span(at, dur) {
|
|
160
145
|
return Range.lens([at, dur], (vals) => ({ lo: vals[0], hi: vals[0] + vals[1] }), (target) => [target.lo, target.hi - target.lo]);
|
|
161
146
|
}
|
|
162
|
-
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
* `Range.derive(...)` for reactive RO tracking, or `cell.value` to
|
|
166
|
-
* snapshot. Lock an endpoint with `Num.pin(c)`. */
|
|
147
|
+
/** Writable `Range` over `[lo, hi]`. Each endpoint is a literal (new cell) or
|
|
148
|
+
* existing writable (passed through); for read-only sources use `Range.derive`.
|
|
149
|
+
* Lock an endpoint with `Num.pin`. */
|
|
167
150
|
export function range(lo = 0, hi = 1) {
|
|
168
151
|
if (typeof lo === "number" && typeof hi === "number") {
|
|
169
152
|
return new Range({ lo, hi });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Re } from "./engine.js";
|
|
2
|
+
/** A witness in `L(a) ∩ L(b)` (shortest), or `null` if the languages are
|
|
3
|
+
* disjoint. Product BFS over derivative pairs. */
|
|
4
|
+
export declare function intersects(a: Re, b: Re): string | null;
|
|
5
|
+
/** A witness string that `a · b` factors two distinct ways, or `null` if the
|
|
6
|
+
* concatenation is unambiguous. Ambiguous iff some nonempty bridge `t` can
|
|
7
|
+
* both extend a word of `L(a)` and be absorbed into `L(b)`. */
|
|
8
|
+
export declare function concatAmbiguity(a: Re, b: Re): string | null;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { alphabetOf, der, nullable, reKey } from "./engine.js";
|
|
2
|
+
const cp = (c) => String.fromCharCode(c);
|
|
3
|
+
/** Representative code units that distinguish every transition in `a` and `b`
|
|
4
|
+
* (the range endpoints of every char class). Sufficient to realize every
|
|
5
|
+
* reachable derivative pair. */
|
|
6
|
+
function alphaUnion(a, b) {
|
|
7
|
+
const set = alphabetOf(a);
|
|
8
|
+
alphabetOf(b, set);
|
|
9
|
+
return [...set];
|
|
10
|
+
}
|
|
11
|
+
/** Fold the derivative across a string. */
|
|
12
|
+
function applyStr(r, s) {
|
|
13
|
+
let cur = r;
|
|
14
|
+
for (let i = 0; i < s.length && cur.k !== "emp"; i++)
|
|
15
|
+
cur = der(cur, s.charCodeAt(i));
|
|
16
|
+
return cur;
|
|
17
|
+
}
|
|
18
|
+
/** A witness in `L(a) ∩ L(b)` (shortest), or `null` if the languages are
|
|
19
|
+
* disjoint. Product BFS over derivative pairs. */
|
|
20
|
+
export function intersects(a, b) {
|
|
21
|
+
const alpha = alphaUnion(a, b);
|
|
22
|
+
const seen = new Set([`${reKey(a)}|${reKey(b)}`]);
|
|
23
|
+
const queue = [{ a, b, w: "" }];
|
|
24
|
+
for (let head = 0; head < queue.length; head++) {
|
|
25
|
+
const { a: da, b: db, w } = queue[head];
|
|
26
|
+
if (nullable(da) && nullable(db))
|
|
27
|
+
return w;
|
|
28
|
+
for (const c of alpha) {
|
|
29
|
+
const na = der(da, c);
|
|
30
|
+
if (na.k === "emp")
|
|
31
|
+
continue;
|
|
32
|
+
const nb = der(db, c);
|
|
33
|
+
if (nb.k === "emp")
|
|
34
|
+
continue;
|
|
35
|
+
const key = `${reKey(na)}|${reKey(nb)}`;
|
|
36
|
+
if (seen.has(key))
|
|
37
|
+
continue;
|
|
38
|
+
seen.add(key);
|
|
39
|
+
queue.push({ a: na, b: nb, w: w + cp(c) });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/** All reachable derivative states of `r`, each with a shortest word reaching
|
|
45
|
+
* it (BFS over the derivative automaton). */
|
|
46
|
+
function reachableStates(r) {
|
|
47
|
+
const alpha = [...alphabetOf(r)];
|
|
48
|
+
const out = new Map([[reKey(r), { re: r, word: "" }]]);
|
|
49
|
+
const queue = [{ re: r, word: "" }];
|
|
50
|
+
for (let head = 0; head < queue.length; head++) {
|
|
51
|
+
const { re, word } = queue[head];
|
|
52
|
+
for (const c of alpha) {
|
|
53
|
+
const d = der(re, c);
|
|
54
|
+
if (d.k === "emp")
|
|
55
|
+
continue;
|
|
56
|
+
const k = reKey(d);
|
|
57
|
+
if (out.has(k))
|
|
58
|
+
continue;
|
|
59
|
+
const w = word + cp(c);
|
|
60
|
+
out.set(k, { re: d, word: w });
|
|
61
|
+
queue.push({ re: d, word: w });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
const SET_KEY = (states) => states.map(reKey).sort().join(",");
|
|
67
|
+
const dedup = (states) => {
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const s of states) {
|
|
71
|
+
const k = reKey(s);
|
|
72
|
+
if (!seen.has(k)) {
|
|
73
|
+
seen.add(k);
|
|
74
|
+
out.push(s);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
};
|
|
79
|
+
// A generous bound: if the product search explodes past this, refuse to certify
|
|
80
|
+
// (sound — we reject rather than risk admitting an ambiguous grammar).
|
|
81
|
+
const MAX_STATES = 200000;
|
|
82
|
+
/** A witness string that `a · b` factors two distinct ways, or `null` if the
|
|
83
|
+
* concatenation is unambiguous. Ambiguous iff some nonempty bridge `t` can
|
|
84
|
+
* both extend a word of `L(a)` and be absorbed into `L(b)`. */
|
|
85
|
+
export function concatAmbiguity(a, b) {
|
|
86
|
+
const statesA = reachableStates(a);
|
|
87
|
+
const accepting = [];
|
|
88
|
+
for (const st of statesA.values())
|
|
89
|
+
if (nullable(st.re))
|
|
90
|
+
accepting.push(st);
|
|
91
|
+
if (accepting.length === 0)
|
|
92
|
+
return null; // L(a) = ∅: nothing to split
|
|
93
|
+
const alpha = alphaUnion(a, b);
|
|
94
|
+
// Search for a nonempty bridge `t`: state = (set of A-derivatives reached
|
|
95
|
+
// from A's accepting states by `t`, der(b, t)).
|
|
96
|
+
const start = dedup(accepting.map(s => s.re));
|
|
97
|
+
const seen = new Set([`${SET_KEY(start)}|${reKey(b)}`]);
|
|
98
|
+
const queue = [{ sa: start, db: b, t: "" }];
|
|
99
|
+
for (let head = 0; head < queue.length; head++) {
|
|
100
|
+
if (seen.size > MAX_STATES)
|
|
101
|
+
throw new Error("reg: grammar too complex to verify");
|
|
102
|
+
const { sa, db, t } = queue[head];
|
|
103
|
+
if (t.length > 0 && sa.some(nullable)) {
|
|
104
|
+
const v = intersects(db, b); // v ∈ L(b) with t·v ∈ L(b)
|
|
105
|
+
if (v !== null)
|
|
106
|
+
return witnessFor(accepting, t, v);
|
|
107
|
+
}
|
|
108
|
+
for (const c of alpha) {
|
|
109
|
+
const db2 = der(db, c);
|
|
110
|
+
if (db2.k === "emp")
|
|
111
|
+
continue; // t·… can no longer be a prefix of L(b)
|
|
112
|
+
const sa2 = dedup(sa.map(s => der(s, c)).filter(s => s.k !== "emp"));
|
|
113
|
+
if (sa2.length === 0)
|
|
114
|
+
continue; // no accepting continuation on the left
|
|
115
|
+
const key = `${SET_KEY(sa2)}|${reKey(db2)}`;
|
|
116
|
+
if (seen.has(key))
|
|
117
|
+
continue;
|
|
118
|
+
seen.add(key);
|
|
119
|
+
queue.push({ sa: sa2, db: db2, t: t + cp(c) });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
/** Assemble the full doubly-parsing string `u·t·v`: pick an accepting `u ∈ L(a)`
|
|
125
|
+
* (shortest known) with `u·t ∈ L(a)`; falls back to `t·v` if none is found. */
|
|
126
|
+
function witnessFor(accepting, t, v) {
|
|
127
|
+
for (const s of accepting)
|
|
128
|
+
if (nullable(applyStr(s.re, t)))
|
|
129
|
+
return s.word + t + v;
|
|
130
|
+
return t + v;
|
|
131
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** A set of UTF-16 code units as sorted, merged, inclusive ranges. Negation is
|
|
2
|
+
* resolved at construction, so a set is always a positive union of ranges. */
|
|
3
|
+
export declare class CharSet {
|
|
4
|
+
readonly ranges: ReadonlyArray<readonly [number, number]>;
|
|
5
|
+
private constructor();
|
|
6
|
+
/** Build from arbitrary (possibly overlapping/unsorted) ranges. */
|
|
7
|
+
static of(ranges: ReadonlyArray<readonly [number, number]>): CharSet;
|
|
8
|
+
static char(cp: number): CharSet;
|
|
9
|
+
static range(lo: number, hi: number): CharSet;
|
|
10
|
+
/** The full code-unit alphabet. */
|
|
11
|
+
static full(): CharSet;
|
|
12
|
+
static empty(): CharSet;
|
|
13
|
+
has(cp: number): boolean;
|
|
14
|
+
isEmpty(): boolean;
|
|
15
|
+
union(other: CharSet): CharSet;
|
|
16
|
+
/** Do the two sets share any code unit? (Both are sorted/normalized.) */
|
|
17
|
+
overlaps(other: CharSet): boolean;
|
|
18
|
+
/** Complement against the full code-unit alphabet. */
|
|
19
|
+
complement(): CharSet;
|
|
20
|
+
/** Case-fold (ASCII + via `toUpperCase`/`toLowerCase`) for the `i` flag.
|
|
21
|
+
* Conservative: adds the upper/lower variant of every unit in range. */
|
|
22
|
+
ignoreCase(): CharSet;
|
|
23
|
+
}
|
|
24
|
+
/** A regular expression over code units. `emp` = ∅ (matches nothing), `eps` =
|
|
25
|
+
* ε (matches the empty string). Built only through the smart constructors
|
|
26
|
+
* below so that derivatives stay simplified. */
|
|
27
|
+
export type Re = {
|
|
28
|
+
readonly k: "emp";
|
|
29
|
+
} | {
|
|
30
|
+
readonly k: "eps";
|
|
31
|
+
} | {
|
|
32
|
+
readonly k: "chr";
|
|
33
|
+
readonly set: CharSet;
|
|
34
|
+
} | {
|
|
35
|
+
readonly k: "seq";
|
|
36
|
+
readonly a: Re;
|
|
37
|
+
readonly b: Re;
|
|
38
|
+
} | {
|
|
39
|
+
readonly k: "alt";
|
|
40
|
+
readonly a: Re;
|
|
41
|
+
readonly b: Re;
|
|
42
|
+
} | {
|
|
43
|
+
readonly k: "star";
|
|
44
|
+
readonly r: Re;
|
|
45
|
+
};
|
|
46
|
+
export declare const EMP: Re;
|
|
47
|
+
export declare const EPS: Re;
|
|
48
|
+
export declare function chr(set: CharSet): Re;
|
|
49
|
+
/** Concatenation, simplified: `∅·_ = _·∅ = ∅`, `ε·b = b`, `a·ε = a`. */
|
|
50
|
+
export declare function seq(a: Re, b: Re): Re;
|
|
51
|
+
/** Union, normalized modulo ACI: flatten nested alts and drop duplicate
|
|
52
|
+
* branches, preserving first-occurrence order. The derivative-state set is
|
|
53
|
+
* finite only modulo ACI, so this keeps `der` bounded. Order is preserved (not
|
|
54
|
+
* sorted) to keep greedy/backtracking semantics. */
|
|
55
|
+
export declare function alt(a: Re, b: Re): Re;
|
|
56
|
+
/** Kleene star, simplified: `∅* = ε* = ε`, `(r*)* = r*`. */
|
|
57
|
+
export declare function star(r: Re): Re;
|
|
58
|
+
/** N-ary concatenation (right-nested). */
|
|
59
|
+
export declare function seqAll(parts: readonly Re[]): Re;
|
|
60
|
+
/** N-ary union. */
|
|
61
|
+
export declare function altAll(branches: readonly Re[]): Re;
|
|
62
|
+
/** Bounded repetition `r{lo,hi}` (hi `undefined` = unbounded). */
|
|
63
|
+
export declare function repeat(r: Re, lo: number, hi: number | undefined): Re;
|
|
64
|
+
/** A canonical structural key, used both for ACI dedup in `alt` and for
|
|
65
|
+
* derivative-state dedup during language enumeration. */
|
|
66
|
+
export declare function reKey(r: Re): string;
|
|
67
|
+
/** Does `r` match the empty string? */
|
|
68
|
+
export declare function nullable(r: Re): boolean;
|
|
69
|
+
/** Brzozowski derivative of `r` with respect to code unit `cp`. */
|
|
70
|
+
export declare function der(r: Re, cp: number): Re;
|
|
71
|
+
/** Does `r` match exactly `s[from..to)`? */
|
|
72
|
+
export declare function accepts(r: Re, s: string, from?: number, to?: number): boolean;
|
|
73
|
+
/** Every prefix length `k ≥ 0` such that `r` matches `s[pos..pos+k)`, ascending.
|
|
74
|
+
* This is the backtracking lexer primitive: a leaf can accept several lengths
|
|
75
|
+
* (`\d+` over "123" accepts 1, 2, 3) and the value parser tries them
|
|
76
|
+
* greedily (longest first) with proper fallback. */
|
|
77
|
+
export declare function matchLengths(r: Re, s: string, pos: number): number[];
|
|
78
|
+
/** Characters that can begin a word in `L(r)`. */
|
|
79
|
+
export declare function firstSet(r: Re): CharSet;
|
|
80
|
+
/** Characters that can extend an already-complete match of `r` (the union of
|
|
81
|
+
* the first-sets of every reachable accepting derivative state). Finite and
|
|
82
|
+
* terminating because the derivative-state set is finite modulo ACI. */
|
|
83
|
+
export declare function followLast(r: Re): CharSet;
|
|
84
|
+
/** Representative code units that exercise every char-set boundary in `r`
|
|
85
|
+
* (each range's low/high endpoint). Enough to drive structural exploration
|
|
86
|
+
* without iterating the whole alphabet. */
|
|
87
|
+
export declare function alphabetOf(r: Re, out?: Set<number>): Set<number>;
|
|
88
|
+
/** Enumerate strings in `L(r)` over `alphabet`, shortest-first, up to `maxLen`
|
|
89
|
+
* and `cap` results. Used by the ambiguity oracle to find minimal
|
|
90
|
+
* counterexamples; bounded so it always terminates. */
|
|
91
|
+
export declare function language(r: Re, alphabet: readonly number[], maxLen: number, cap: number): Generator<string>;
|