SNAPKITTYWEST commited on
Commit
d0ab4ec
·
verified ·
1 Parent(s): d6df5f0

Model card: restore full README from GitHub, add commercial license option

Browse files
Files changed (1) hide show
  1. README.md +118 -0
README.md CHANGED
@@ -13,3 +13,121 @@ tags:
13
 
14
  > Mirrored from [https://github.com/SNAPKITTYWEST/snapkitty-regex](https://github.com/SNAPKITTYWEST/snapkitty-regex) at commit `b14a2eb`. Part of the **SnapKitty October 2026 main drop**.
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  > Mirrored from [https://github.com/SNAPKITTYWEST/snapkitty-regex](https://github.com/SNAPKITTYWEST/snapkitty-regex) at commit `b14a2eb`. Part of the **SnapKitty October 2026 main drop**.
15
 
16
+ # snapkitty-regex
17
+
18
+ A Thompson NFA regular expression engine in C — under 700 lines, zero
19
+ dependencies, no exponential backtracking, ever.
20
+
21
+ Built as an exercise from the guides in
22
+ [AHMADALIPARR/build-your-own-x](https://github.com/AHMADALIPARR/build-your-own-x)
23
+ ("Build your own `Regex Engine`"), primarily Russ Cox's
24
+ *[Regular Expression Matching Can Be Simple And Fast](https://swtch.com/~rsc/regexp/regexp1.html)*.
25
+
26
+ ## The idea
27
+
28
+ Most popular regex engines (Perl, Python, Ruby, Java, PCRE) simulate an
29
+ NFA by *backtracking*: guess a path, and retry on failure. On patterns
30
+ like `a?a?a?aaa` that means trying all 2^n combinations — Perl needs
31
+ **60+ seconds** to match a 29-character string.
32
+
33
+ This engine instead simulates *all* NFA paths in parallel, tracking the
34
+ set of reachable states one input character at a time (Thompson's 1968
35
+ algorithm). Matching is **O(m·n)** worst case for pattern length m and
36
+ text length n. There are no pathological inputs.
37
+
38
+ ```
39
+ infix pattern --re2post--> postfix --post2nfa--> NFA --pump--> match?
40
+ (explicit '.' concat) (Thompson construction)
41
+ ```
42
+
43
+ ## Syntax
44
+
45
+ | construct | meaning |
46
+ |---|---|
47
+ | `abc` | literal characters match themselves |
48
+ | `e1 e2` | concatenation |
49
+ | `e1\|e2` | alternation |
50
+ | `e*` | zero or more |
51
+ | `e+` | one or more |
52
+ | `e?` | zero or one |
53
+ | `(e)` | grouping |
54
+ | `\` + metachar | literal metacharacter: `\* \+ \? \| \( \) \\` |
55
+ | `.` | literal dot (no wildcard — see roadmap) |
56
+
57
+ Precedence: `|` weakest, then concatenation, then `* + ?`. Matching is
58
+ byte-oriented.
59
+
60
+ ## Build & test
61
+
62
+ ```sh
63
+ make # builds ./regex, clean compile, zero warnings
64
+ make test # 82-check correctness harness (fullmatch, search, bad patterns)
65
+ make bench # pathological benchmark vs Python's re
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ ```sh
71
+ $ ./regex 'a(bb)+a' abbbba abba abbba
72
+ abbbba: MATCH
73
+ abba: MATCH
74
+ abbba: NO MATCH
75
+
76
+ $ ./regex -s 'colou?r' 'the colour blue'
77
+ the colour blue: MATCH
78
+ ```
79
+
80
+ Exit codes: `0` all matched, `1` some didn't, `2` usage/bad pattern.
81
+
82
+ As a library (`src/regex.h`):
83
+
84
+ ```c
85
+ Regex *re = re_compile("(ab|cd)+e");
86
+ int m = re_fullmatch(re, "cdabe"); /* whole string */
87
+ int p = re_match(re, "cdabex"); /* prefix of string */
88
+ int s = re_search(re, "xxcdabexx"); /* anywhere in string */
89
+ re_free(re);
90
+ ```
91
+
92
+ ## Benchmark
93
+
94
+ `a?`^n`a`^n against `a`^n — the article's pathological case:
95
+
96
+ | n | snapkitty-regex | python `re` |
97
+ |---|---|---|
98
+ | 10 | 0.0011s | 0.0000s |
99
+ | 20 | 0.0010s | 0.0442s |
100
+ | 24 | 0.0013s | 0.7139s |
101
+ | 26 | 0.0014s | 3.0580s |
102
+ | 500 | 0.0021s | (heat death of universe) |
103
+
104
+ (The ~1ms floor on our side is process-spawn overhead, not matching.)
105
+
106
+ ## What's hardened vs the article's reference code
107
+
108
+ - **No fixed static buffers** — postfix output and the paren stack are
109
+ sized from the input instead of `char buf[8000]` / `paren[100]`.
110
+ - **Backslash escapes** for metacharacters.
111
+ - **Literal dots** — a `.` in the pattern can't collide with the postfix
112
+ concatenation operator anymore (this crashes the original).
113
+ - **Real error paths** — malformed patterns and `malloc` failures return
114
+ `NULL` instead of crashing; partial allocations unwind cleanly.
115
+ - **Reusable, releasable** — compiled patterns are opaque `Regex*`
116
+ values with `re_free`; the accept state and dedup generation are
117
+ per-pattern, not globals.
118
+ - **Prefix match + search** on top of full match.
119
+
120
+ ## Roadmap
121
+
122
+ - `.` wildcard / character classes `[a-z]`
123
+ - Capture groups and submatch extraction
124
+ - DFA / hybrid execution for very large inputs
125
+ - Streaming matcher API
126
+
127
+ ---
128
+
129
+ ### 💼 Commercial License
130
+
131
+ SnapKitty code is free and open under **AGPL-3.0** for open-source use. Building a commercial product or service? A **proprietary commercial license** from Snapkitty Collective LLC lets you ship this code without the AGPL's source-sharing and network-use obligations.
132
+
133
+ **[→ Get a commercial license](mailto:A.parr@belespritdaccord.uk?subject=Commercial%20license:%20snapkitty-regex)** · A.parr@belespritdaccord.uk