Model card: restore full README from GitHub, add commercial license option
Browse files
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
|