@descent-vtt/spec-graph 0.4.0 → 0.5.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.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A regular-expression matcher that cannot backtrack.
3
+ *
4
+ * `~=` is the one predicate in the selector language that used to hand a
5
+ * user-supplied string to `RegExp` and wait. That is tolerable at a prompt,
6
+ * where a human can press Ctrl-C. It is not tolerable in `.spec-graph.json`,
7
+ * where the same string runs unattended in CI against every node in the corpus.
8
+ *
9
+ * The cost is neither theoretical nor exotic. `^([A-Za-z0-9_]+[ ]?)+$` is what
10
+ * somebody writes to say "the title is words separated by single spaces", and
11
+ * against a title that does not match, V8 tries one branch per way of cutting
12
+ * each word into pieces:
13
+ *
14
+ * ```text
15
+ * RegExp here
16
+ * the quick brown fox jumps over the lazy dog! 0.9s 11us
17
+ * the quick brown fox jumps over the lazy dog and! 5.4s 12us
18
+ * the quick brown fox jumps over the lazy dog and keeps! 103s 13us
19
+ * ```
20
+ *
21
+ * Each word of k letters can be cut into pieces 2^(k-1) ways, and the engine
22
+ * tries the product of those, which is why adding one five-letter word costs
23
+ * another factor of sixteen. A sixty-character title does not finish this year.
24
+ *
25
+ * So a pattern here is compiled to a **non-deterministic finite automaton** and
26
+ * simulated over the subject one character at a time (Thompson, 1968), keeping
27
+ * a set of live states instead of a stack of choices. Each state is visited at
28
+ * most once per position, which makes the work exactly O(pattern x subject):
29
+ * the right-hand column above, growing with the subject and not with the
30
+ * pattern's ambiguity.
31
+ *
32
+ * What it costs is a dialect. Backreferences and lookaround are not regular and
33
+ * have no place in a linear automaton, so they are **rejected while compiling**,
34
+ * with a message and an offset, rather than accepted and then slow. That is the
35
+ * whole trade: a predicate that finishes, against a short list of constructs
36
+ * that a documentation selector never needed.
37
+ *
38
+ * Matching is case-insensitive and unanchored because that is what `~=` has
39
+ * always been - `new RegExp(source, 'i')` and `.test()`. There are no capture
40
+ * groups at all: nothing asks where the match was, only whether there was one,
41
+ * which is why this is a plain NFA simulation rather than a Pike VM.
42
+ *
43
+ * See ADR-0017.
44
+ */
45
+ /** A pattern that will not compile, and where in it the problem starts. */
46
+ export declare class PatternError extends Error {
47
+ /** Offset into the pattern source. */
48
+ readonly offset: number;
49
+ constructor(message: string, offset: number);
50
+ }
51
+ /** A compiled pattern. Immutable, reusable, and safe to cache. */
52
+ export interface Matcher {
53
+ /** True when the pattern matches anywhere in the subject. */
54
+ test(subject: string): boolean;
55
+ /** State count. Published for the size test, and for nothing else. */
56
+ readonly size: number;
57
+ }
58
+ /**
59
+ * Compiles a pattern into a matcher.
60
+ *
61
+ * Throws {@link PatternError} on anything it cannot run in linear time, which
62
+ * is the point: the alternative to a message here is a build that stops.
63
+ */
64
+ export declare function compilePattern(source: string): Matcher;