| @@ -2,7 +2,7 @@ |
| 2 | 2 | |
| 3 | 3 | ## Overview |
| 4 | 4 | |
| 5 | | -The zig-regex library implements a regular expression engine using **Thompson's NFA construction** algorithm combined with **thread-based simulation** for pattern matching. This architecture provides linear time complexity O(n*m) for matching, where n is the input length and m is the pattern size. |
| 5 | +The zig-regex library implements a regular expression engine using **Thompson's NFA construction** algorithm combined with **thread-based simulation** for pattern matching. This architecture provides linear time complexity O(n\*m) for matching, where n is the input length and m is the pattern size. |
| 6 | 6 | |
| 7 | 7 | ## Design Philosophy |
| 8 | 8 | |
| @@ -55,9 +55,11 @@ The zig-regex library implements a regular expression engine using **Thompson's |
| 55 | 55 | ## Module Breakdown |
| 56 | 56 | |
| 57 | 57 | ### 1. common.zig (174 lines) |
| 58 | + |
| 58 | 59 | **Purpose:** Shared types and utilities |
| 59 | 60 | |
| 60 | 61 | **Key Components:** |
| 62 | + |
| 61 | 63 | - `CharRange` - Represents a character range (e.g., 'a'-'z') |
| 62 | 64 | - `CharClass` - Set of character ranges with negation support |
| 63 | 65 | - `CharClasses` - Predefined classes (\d, \w, \s, etc.) |
| @@ -65,13 +67,16 @@ The zig-regex library implements a regular expression engine using **Thompson's |
| 65 | 67 | - `Span` - Source position for error reporting |
| 66 | 68 | |
| 67 | 69 | **Design Notes:** |
| 70 | + |
| 68 | 71 | - Character classes use ranges for memory efficiency |
| 69 | 72 | - Negation handled at match time, not during construction |
| 70 | 73 | |
| 71 | 74 | ### 2. ast.zig (267 lines) |
| 75 | + |
| 72 | 76 | **Purpose:** Abstract Syntax Tree representation |
| 73 | 77 | |
| 74 | 78 | **Node Types:** |
| 79 | + |
| 75 | 80 | ```zig |
| 76 | 81 | pub const NodeType = enum { |
| 77 | 82 | literal, // Single character 'a' |
| @@ -90,20 +95,24 @@ pub const NodeType = enum { |
| 90 | 95 | ``` |
| 91 | 96 | |
| 92 | 97 | **Key Features:** |
| 98 | + |
| 93 | 99 | - Tagged union for type-safe node data |
| 94 | 100 | - Recursive structure for nested expressions |
| 95 | 101 | - Memory management via allocator |
| 96 | 102 | - Span tracking for error messages |
| 97 | 103 | |
| 98 | 104 | **Design Notes:** |
| 105 | + |
| 99 | 106 | - Each node owns its children (tree ownership) |
| 100 | 107 | - `destroy()` recursively frees the entire tree |
| 101 | 108 | - Factory methods ensure proper initialization |
| 102 | 109 | |
| 103 | 110 | ### 3. parser.zig (395 lines) |
| 111 | + |
| 104 | 112 | **Purpose:** Lexical analysis and parsing |
| 105 | 113 | |
| 106 | 114 | **Architecture:** |
| 115 | + |
| 107 | 116 | ``` |
| 108 | 117 | Lexer (Tokenizer) |
| 109 | 118 | │ |
| @@ -122,21 +131,25 @@ Parser (Recursive Descent) |
| 122 | 131 | ``` |
| 123 | 132 | |
| 124 | 133 | **Operator Precedence (high to low):** |
| 134 | + |
| 125 | 135 | 1. Literals, groups, character classes |
| 126 | | -2. Quantifiers (*, +, ?, {m,n}) |
| 136 | +2. Quantifiers (\*, +, ?, {m,n}) |
| 127 | 137 | 3. Concatenation (implicit) |
| 128 | 138 | 4. Alternation (|) |
| 129 | 139 | |
| 130 | 140 | **Design Notes:** |
| 141 | + |
| 131 | 142 | - Single-pass parsing |
| 132 | 143 | - Error recovery not implemented (fails fast) |
| 133 | 144 | - Lookahead limited to one token |
| 134 | 145 | - Character classes parsed separately |
| 135 | 146 | |
| 136 | 147 | ### 4. compiler.zig (455 lines) |
| 148 | + |
| 137 | 149 | **Purpose:** NFA construction via Thompson's algorithm |
| 138 | 150 | |
| 139 | 151 | **NFA Components:** |
| 152 | + |
| 140 | 153 | ```zig |
| 141 | 154 | pub const State = struct { |
| 142 | 155 | id: StateId, |
| @@ -155,23 +168,26 @@ pub const Transition = struct { |
| 155 | 168 | |
| 156 | 169 | **Thompson Construction Rules:** |
| 157 | 170 | |
| 158 | | -| Pattern | NFA Fragment | |
| 159 | | -|---------|--------------| |
| 160 | | -| Literal 'a' | `s0 --a--> s1` | |
| 161 | | -| Concatenation 'ab' | `s0 --a--> s1 --ε--> s2 --b--> s3` | |
| 171 | +| Pattern | NFA Fragment | |
| 172 | +| ------------------ | ---------------------------------------------------------------------------- | |
| 173 | +| Literal 'a' | `s0 --a--> s1` | |
| 174 | +| Concatenation 'ab' | `s0 --a--> s1 --ε--> s2 --b--> s3` | |
| 162 | 175 | | Alternation 'a\|b' | `s0 --ε--> (s1 --a--> s2)<br> └--ε--> (s3 --b--> s4)` | |
| 163 | | -| Star 'a*' | `s0 --ε--> s1 --a--> s2 --ε--> s1<br> └--ε--> accept` | |
| 176 | +| Star 'a\*' | `s0 --ε--> s1 --a--> s2 --ε--> s1<br> └--ε--> accept` | |
| 164 | 177 | |
| 165 | 178 | **Design Notes:** |
| 179 | + |
| 166 | 180 | - Each AST node compiles to a Fragment (start, accept states) |
| 167 | 181 | - Epsilon transitions used for control flow |
| 168 | 182 | - Greedy matching achieved through simulation order |
| 169 | 183 | - Capture groups marked on states, not transitions |
| 170 | 184 | |
| 171 | 185 | ### 5. vm.zig (340 lines) |
| 186 | + |
| 172 | 187 | **Purpose:** NFA simulation engine |
| 173 | 188 | |
| 174 | 189 | **Thread-Based Simulation:** |
| 190 | + |
| 175 | 191 | ``` |
| 176 | 192 | Thread = { |
| 177 | 193 | state: StateId, |
| @@ -190,29 +206,35 @@ Algorithm: |
| 190 | 206 | ``` |
| 191 | 207 | |
| 192 | 208 | **Key Features:** |
| 209 | + |
| 193 | 210 | - **Greedy Matching:** Continues matching to find longest match |
| 194 | 211 | - **Epsilon Closure:** Precomputed before consuming input |
| 195 | 212 | - **Capture Tracking:** Each thread maintains capture positions |
| 196 | 213 | - **No Backtracking:** All paths explored simultaneously |
| 197 | 214 | |
| 198 | 215 | **Time Complexity:** |
| 199 | | -- O(n * m) where n = input length, m = number of states |
| 200 | | -- Worst case: O(n * 2^p) where p = number of alternations |
| 216 | + |
| 217 | +- O(n \* m) where n = input length, m = number of states |
| 218 | +- Worst case: O(n \* 2^p) where p = number of alternations |
| 201 | 219 | |
| 202 | 220 | **Space Complexity:** |
| 203 | | -- O(m * c) where m = states, c = capture groups |
| 221 | + |
| 222 | +- O(m \* c) where m = states, c = capture groups |
| 204 | 223 | - Thread list grows with state complexity |
| 205 | 224 | |
| 206 | 225 | **Design Notes:** |
| 226 | + |
| 207 | 227 | - Threads represent possible execution paths |
| 208 | 228 | - Visited states tracked to avoid infinite loops |
| 209 | 229 | - Anchors checked during epsilon closure |
| 210 | 230 | - Match preference: longer match > earlier match |
| 211 | 231 | |
| 212 | 232 | ### 6. regex.zig (385 lines) |
| 233 | + |
| 213 | 234 | **Purpose:** Public API and convenience functions |
| 214 | 235 | |
| 215 | 236 | **API Surface:** |
| 237 | + |
| 216 | 238 | ```zig |
| 217 | 239 | // Compilation |
| 218 | 240 | Regex.compile(allocator, pattern) -> Regex |
| @@ -230,15 +252,18 @@ regex.split(allocator, input) -> [][]const u8 |
| 230 | 252 | ``` |
| 231 | 253 | |
| 232 | 254 | **Design Notes:** |
| 255 | + |
| 233 | 256 | - Immutable compiled patterns (can reuse) |
| 234 | 257 | - All allocations explicit and user-controlled |
| 235 | 258 | - Match results contain slices (no copies) |
| 236 | 259 | - Iterator pattern not yet implemented |
| 237 | 260 | |
| 238 | 261 | ### 7. errors.zig (67 lines) |
| 262 | + |
| 239 | 263 | **Purpose:** Error types and reporting |
| 240 | 264 | |
| 241 | 265 | **Error Types:** |
| 266 | + |
| 242 | 267 | ```zig |
| 243 | 268 | pub const RegexError = error{ |
| 244 | 269 | // Parse errors |