ReviewOS

also looking at this

zig-utils/zig-regex

Port to latest zig-master

#2
Closed armagidon-exception wants to merge main into main
53 files +1149 -1017
docs/ARCHITECTURE.mdmodified+68-24
Changes to docs/ARCHITECTURE.md
@@ -2,7 +2,7 @@
22
33## Overview
44
5The 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.
5The 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.
66
77## Design Philosophy
88
@@ -55,9 +55,11 @@ The zig-regex library implements a regular expression engine using **Thompson's
5555## Module Breakdown
5656
5757### 1. common.zig (174 lines)
58
5859**Purpose:** Shared types and utilities
5960
6061**Key Components:**
62
6163- `CharRange` - Represents a character range (e.g., 'a'-'z')
6264- `CharClass` - Set of character ranges with negation support
6365- `CharClasses` - Predefined classes (\d, \w, \s, etc.)
@@ -65,13 +67,16 @@ The zig-regex library implements a regular expression engine using **Thompson's
6567- `Span` - Source position for error reporting
6668
6769**Design Notes:**
70
6871- Character classes use ranges for memory efficiency
6972- Negation handled at match time, not during construction
7073
7174### 2. ast.zig (267 lines)
75
7276**Purpose:** Abstract Syntax Tree representation
7377
7478**Node Types:**
79
7580```zig
7681pub const NodeType = enum {
7782 literal, // Single character 'a'
@@ -90,20 +95,24 @@ pub const NodeType = enum {
9095```
9196
9297**Key Features:**
98
9399- Tagged union for type-safe node data
94100- Recursive structure for nested expressions
95101- Memory management via allocator
96102- Span tracking for error messages
97103
98104**Design Notes:**
105
99106- Each node owns its children (tree ownership)
100107- `destroy()` recursively frees the entire tree
101108- Factory methods ensure proper initialization
102109
103110### 3. parser.zig (395 lines)
111
104112**Purpose:** Lexical analysis and parsing
105113
106114**Architecture:**
115
107116```
108117Lexer (Tokenizer)
109118 │
@@ -122,21 +131,25 @@ Parser (Recursive Descent)
122131```
123132
124133**Operator Precedence (high to low):**
134
1251351. Literals, groups, character classes
1262. Quantifiers (*, +, ?, {m,n})
1362. Quantifiers (\*, +, ?, {m,n})
1271373. Concatenation (implicit)
1281384. Alternation (|)
129139
130140**Design Notes:**
141
131142- Single-pass parsing
132143- Error recovery not implemented (fails fast)
133144- Lookahead limited to one token
134145- Character classes parsed separately
135146
136147### 4. compiler.zig (455 lines)
148
137149**Purpose:** NFA construction via Thompson's algorithm
138150
139151**NFA Components:**
152
140153```zig
141154pub const State = struct {
142155 id: StateId,
@@ -155,23 +168,26 @@ pub const Transition = struct {
155168
156169**Thompson Construction Rules:**
157170
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` |
162175| Alternation 'a\|b' | `s0 --ε--> (s1 --a--> s2)<br>&nbsp;&nbsp;&nbsp;&nbsp;└--ε--> (s3 --b--> s4)` |
163| Star 'a*' | `s0 --ε--> s1 --a--> s2 --ε--> s1<br>&nbsp;&nbsp;&nbsp;&nbsp;└--ε--> accept` |
176| Star 'a\*' | `s0 --ε--> s1 --a--> s2 --ε--> s1<br>&nbsp;&nbsp;&nbsp;&nbsp;└--ε--> accept` |
164177
165178**Design Notes:**
179
166180- Each AST node compiles to a Fragment (start, accept states)
167181- Epsilon transitions used for control flow
168182- Greedy matching achieved through simulation order
169183- Capture groups marked on states, not transitions
170184
171185### 5. vm.zig (340 lines)
186
172187**Purpose:** NFA simulation engine
173188
174189**Thread-Based Simulation:**
190
175191```
176192Thread = {
177193 state: StateId,
@@ -190,29 +206,35 @@ Algorithm:
190206```
191207
192208**Key Features:**
209
193210- **Greedy Matching:** Continues matching to find longest match
194211- **Epsilon Closure:** Precomputed before consuming input
195212- **Capture Tracking:** Each thread maintains capture positions
196213- **No Backtracking:** All paths explored simultaneously
197214
198215**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
201219
202220**Space Complexity:**
203- O(m * c) where m = states, c = capture groups
221
222- O(m \* c) where m = states, c = capture groups
204223- Thread list grows with state complexity
205224
206225**Design Notes:**
226
207227- Threads represent possible execution paths
208228- Visited states tracked to avoid infinite loops
209229- Anchors checked during epsilon closure
210230- Match preference: longer match > earlier match
211231
212232### 6. regex.zig (385 lines)
233
213234**Purpose:** Public API and convenience functions
214235
215236**API Surface:**
237
216238```zig
217239// Compilation
218240Regex.compile(allocator, pattern) -> Regex
@@ -230,15 +252,18 @@ regex.split(allocator, input) -> [][]const u8
230252```
231253
232254**Design Notes:**
255
233256- Immutable compiled patterns (can reuse)
234257- All allocations explicit and user-controlled
235258- Match results contain slices (no copies)
236259- Iterator pattern not yet implemented
237260
238261### 7. errors.zig (67 lines)
262
239263**Purpose:** Error types and reporting
240264
241265**Error Types:**
266
242267```zig
243268pub const RegexError = error{
244269 // Parse errors
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -260,6 +285,7 @@ pub const RegexError = error{ formatting only
260285```
261286
262287**Error Context:**
288
263289- Position tracking in pattern
264290- Human-readable messages
265291- Pointer to error location
Changes to docs/ARCHITECTURE.md
@@ -269,12 +295,14 @@ pub const RegexError = error{
269295### Thompson's NFA Construction
270296
271297**Why Thompson?**
298
2722991. Simple to implement
2733002. Guaranteed linear time matching
2743013. No catastrophic backtracking
2753024. Naturally supports alternation
276303
277304**How it works:**
305
2783061. Each regex operator has a standard NFA fragment pattern
2793072. Fragments compose recursively
2803083. Epsilon transitions connect fragments
@@ -283,16 +311,19 @@ pub const RegexError = error{
283311### Thread-Based Simulation
284312
285313**Why Threads?**
314
286315- Simulates NFA without building DFA (space efficient)
287316- Handles large state spaces
288317- Natural support for captures
289318
290319**Greedy Matching:**
320
291321- Continue simulation even after finding a match
292322- Keep longest match found
293323- Ensures `a*` matches "aaa" not ""
294324
295325**Epsilon Closure:**
326
296327- Precompute all states reachable via ε-transitions
297328- Check anchors during closure
298329- Avoids redundant state exploration
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -300,6 +331,7 @@ pub const RegexError = error{ formatting only
300331## Memory Management
301332
302333**Ownership Model:**
334
303335```
304336Regex
305337 └─ owns NFA
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -315,6 +347,7 @@ Matches formatting only
315347```
316348
317349**Allocation Points:**
350
3183511. Pattern string duplication
3193522. AST node creation (freed after compilation)
3203533. NFA states and transitions
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -322,6 +355,7 @@ Matches formatting only
3223555. Match results and captures
323356
324357**Guidelines:**
358
325359- User provides allocator for all operations
326360- No hidden global allocations
327361- Clear ownership semantics
Changes to docs/ARCHITECTURE.md
@@ -331,27 +365,28 @@ Matches
331365
332366### Time Complexity
333367
334| Operation | Complexity | Notes |
335|-----------|------------|-------|
336| Compile | O(p) | p = pattern length |
337| isMatch | O(n * m) | n = input, m = states |
338| find | O(n * m * k) | k = positions to try |
339| findAll | O(n * m) | Amortized |
340| replace | O(n + r) | r = replacement length |
341| split | O(n * m) | Plus allocation |
368| Operation | Complexity | Notes |
369| --------- | ------------ | ---------------------- |
370| Compile | O(p) | p = pattern length |
371| isMatch | O(n \* m) | n = input, m = states |
372| find | O(n _ m _ k) | k = positions to try |
373| findAll | O(n \* m) | Amortized |
374| replace | O(n + r) | r = replacement length |
375| split | O(n \* m) | Plus allocation |
342376
343377### Space Complexity
344378
345| Component | Complexity | Notes |
346|-----------|------------|-------|
347| AST | O(p) | Temporary |
348| NFA | O(p) | Persistent |
349| VM Threads | O(m * c) | c = captures |
350| Matches | O(r) | r = result count |
379| Component | Complexity | Notes |
380| ---------- | ---------- | ---------------- |
381| AST | O(p) | Temporary |
382| NFA | O(p) | Persistent |
383| VM Threads | O(m \* c) | c = captures |
384| Matches | O(r) | r = result count |
351385
352386### Optimization Opportunities
353387
354388**Not Yet Implemented:**
389
3553901. DFA construction for static patterns
3563912. State minimization
3573923. Literal prefix extraction
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -362,6 +397,7 @@ Matches formatting only
362397## Testing Strategy
363398
364399**Test Categories:**
400
3654011. **Unit Tests** - Each module tested independently
3664022. **Integration Tests** - End-to-end pattern matching
3674033. **Edge Cases** - Empty strings, boundary conditions
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -369,6 +405,7 @@ Matches formatting only
3694055. **Property Tests** - Invariants (future)
370406
371407**Current Coverage:**
408
372409- 35+ tests across all modules
373410- All basic features tested
374411- Edge cases covered
Changes to docs/ARCHITECTURE.md
@@ -377,18 +414,21 @@ Matches
377414## Future Enhancements
378415
379416### Phase 6: Quality & Testing
417
380418- Comprehensive fuzzing
381419- Property-based tests
382420- Performance regression tests
383421- Memory leak detection
384422
385423### Phase 7: Performance
424
386425- DFA construction option
387426- Literal prefix optimization
388427- State minimization
389428- Benchmark suite
390429
391430### Phase 8: Extended Features
431
392432- Named capture groups
393433- Backreferences
394434- Lookahead/lookbehind
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -396,6 +436,7 @@ Matches formatting only
396436- Unicode properties
397437
398438### Phase 9: API Improvements
439
399440- Iterator interface
400441- Streaming matches
401442- Partial matching
Changes to docs/ARCHITECTURE.md
@@ -404,10 +445,12 @@ Matches
404445## References
405446
406447**Algorithms:**
448
407449- Thompson, Ken (1968). "Regular Expression Search Algorithm"
408450- Cox, Russ (2007). "Regular Expression Matching Can Be Simple And Fast"
409451
410452**Implementations:**
453
411454- RE2 (Google) - Inspiration for architecture
412455- Rust regex crate - API design reference
413456- PCRE - Feature completeness reference
formatting only · 7 hidden lines
Changes to docs/ARCHITECTURE.md
@@ -415,6 +458,7 @@ Matches formatting only
415458## Contributing
416459
417460When modifying the architecture:
461
4184621. Maintain phase separation
4194632. Keep allocations explicit
4204643. Preserve linear time guarantees