also looking at this
Port to latest zig-master
#2
53 files
+1149
-1017
| @@ -10,13 +10,13 @@ const RegexError = @import("regex").RegexError; | ||
| 10 | 10 | |
| 11 | 11 | test "parser: quantifier ? at start is rejected" { |
| 12 | 12 | const allocator = std.testing.allocator; |
| 13 | const result = Regex.compile(allocator, "?abc"); | |
| 13 | const result = Regex.compile(allocator, std.testing.io, "?abc"); | |
| 14 | 14 | try std.testing.expectError(RegexError.UnexpectedCharacter, result); |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | 17 | test "parser: nested empty groups" { |
| 18 | 18 | const allocator = std.testing.allocator; |
| 19 | var regex = try Regex.compile(allocator, "(())"); | |
| 19 | var regex = try Regex.compile(allocator, std.testing.io, "(())"); | |
| 20 | 20 | defer regex.deinit(); |
| 21 | 21 | // Should compile without error |
| 22 | 22 | try std.testing.expect(try regex.isMatch("")); |
| @@ -25,7 +25,7 @@ test "parser: nested empty groups" { | ||
| 25 | 25 | test "parser: deeply nested groups" { |
| 26 | 26 | const allocator = std.testing.allocator; |
| 27 | 27 | // 20 levels of nesting - should be within limits |
| 28 | var regex = try Regex.compile(allocator, "((((((((((((((((((((a))))))))))))))))))))"); | |
| 28 | var regex = try Regex.compile(allocator, std.testing.io, "((((((((((((((((((((a))))))))))))))))))))"); | |
| 29 | 29 | defer regex.deinit(); |
| 30 | 30 | try std.testing.expect(try regex.isMatch("a")); |
| 31 | 31 | } |
| @@ -33,20 +33,20 @@ test "parser: deeply nested groups" { | ||
| 33 | 33 | test "parser: consecutive quantifiers rejected by analyzer" { |
| 34 | 34 | const allocator = std.testing.allocator; |
| 35 | 35 | // a** parses as (a*)* which is rejected as nested quantifiers |
| 36 | const result = Regex.compile(allocator, "a**"); | |
| 36 | const result = Regex.compile(allocator, std.testing.io, "a**"); | |
| 37 | 37 | try std.testing.expectError(RegexError.PatternTooComplex, result); |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | 40 | test "parser: quantifier on quantifier rejected by analyzer" { |
| 41 | 41 | const allocator = std.testing.allocator; |
| 42 | 42 | // a+* parses as (a+)* which is rejected as nested quantifiers |
| 43 | const result = Regex.compile(allocator, "a+*"); | |
| 43 | const result = Regex.compile(allocator, std.testing.io, "a+*"); | |
| 44 | 44 | try std.testing.expectError(RegexError.PatternTooComplex, result); |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | 47 | test "parser: empty alternation branch is valid" { |
| 48 | 48 | const allocator = std.testing.allocator; |
| 49 | var regex = try Regex.compile(allocator, "a|"); | |
| 49 | var regex = try Regex.compile(allocator, std.testing.io, "a|"); | |
| 50 | 50 | defer regex.deinit(); |
| 51 | 51 | // "a|" means "a or empty string" - should match empty |
| 52 | 52 | try std.testing.expect(try regex.isMatch("a")); |
| @@ -55,7 +55,7 @@ test "parser: empty alternation branch is valid" { | ||
| 55 | 55 | |
| 56 | 56 | test "parser: leading pipe alternation" { |
| 57 | 57 | const allocator = std.testing.allocator; |
| 58 | var regex = try Regex.compile(allocator, "|a"); | |
| 58 | var regex = try Regex.compile(allocator, std.testing.io, "|a"); | |
| 59 | 59 | defer regex.deinit(); |
| 60 | 60 | // "|a" means "empty string or a" |
| 61 | 61 | try std.testing.expect(try regex.isMatch("")); |
| @@ -66,7 +66,7 @@ test "parser: leading pipe alternation" { | ||
| 66 | 66 | |
| 67 | 67 | test "parser: {0} means zero occurrences" { |
| 68 | 68 | const allocator = std.testing.allocator; |
| 69 | var regex = try Regex.compile(allocator, "^a{0}b$"); | |
| 69 | var regex = try Regex.compile(allocator, std.testing.io, "^a{0}b$"); | |
| 70 | 70 | defer regex.deinit(); |
| 71 | 71 | |
| 72 | 72 | try std.testing.expect(try regex.isMatch("b")); |
| @@ -75,7 +75,7 @@ test "parser: {0} means zero occurrences" { | ||
| 75 | 75 | |
| 76 | 76 | test "parser: {1} means exactly once" { |
| 77 | 77 | const allocator = std.testing.allocator; |
| 78 | var regex = try Regex.compile(allocator, "^a{1}$"); | |
| 78 | var regex = try Regex.compile(allocator, std.testing.io, "^a{1}$"); | |
| 79 | 79 | defer regex.deinit(); |
| 80 | 80 | |
| 81 | 81 | try std.testing.expect(try regex.isMatch("a")); |
| @@ -86,7 +86,7 @@ test "parser: {1} means exactly once" { | ||
| 86 | 86 | test "parser: large but valid quantifier" { |
| 87 | 87 | const allocator = std.testing.allocator; |
| 88 | 88 | // {0,100} should be valid |
| 89 | var regex = try Regex.compile(allocator, "a{0,100}"); | |
| 89 | var regex = try Regex.compile(allocator, std.testing.io, "a{0,100}"); | |
| 90 | 90 | defer regex.deinit(); |
| 91 | 91 | try std.testing.expect(try regex.isMatch("")); |
| 92 | 92 | } |
| @@ -95,7 +95,7 @@ test "parser: large but valid quantifier" { | ||
| 95 | 95 | |
| 96 | 96 | test "parser: character class with escaped ]" { |
| 97 | 97 | const allocator = std.testing.allocator; |
| 98 | var regex = try Regex.compile(allocator, "[\\]]"); | |
| 98 | var regex = try Regex.compile(allocator, std.testing.io, "[\\]]"); | |
| 99 | 99 | defer regex.deinit(); |
| 100 | 100 | |
| 101 | 101 | try std.testing.expect(try regex.isMatch("]")); |
| @@ -105,7 +105,7 @@ test "parser: character class with escaped ]" { | ||
| 105 | 105 | test "parser: character class with hyphen between ranges" { |
| 106 | 106 | const allocator = std.testing.allocator; |
| 107 | 107 | // Hyphen at start is literal, not range |
| 108 | var regex = try Regex.compile(allocator, "[-az]"); | |
| 108 | var regex = try Regex.compile(allocator, std.testing.io, "[-az]"); | |
| 109 | 109 | defer regex.deinit(); |
| 110 | 110 | |
| 111 | 111 | try std.testing.expect(try regex.isMatch("-")); |
| @@ -116,7 +116,7 @@ test "parser: character class with hyphen between ranges" { | ||
| 116 | 116 | |
| 117 | 117 | test "parser: negated class with range" { |
| 118 | 118 | const allocator = std.testing.allocator; |
| 119 | var regex = try Regex.compile(allocator, "^[^0-9]+$"); | |
| 119 | var regex = try Regex.compile(allocator, std.testing.io, "^[^0-9]+$"); | |
| 120 | 120 | defer regex.deinit(); |
| 121 | 121 | |
| 122 | 122 | try std.testing.expect(try regex.isMatch("abc")); |
| @@ -128,7 +128,7 @@ test "parser: negated class with range" { | ||
| 128 | 128 | |
| 129 | 129 | test "parser: tab and newline escapes" { |
| 130 | 130 | const allocator = std.testing.allocator; |
| 131 | var regex = try Regex.compile(allocator, "\\t"); | |
| 131 | var regex = try Regex.compile(allocator, std.testing.io, "\\t"); | |
| 132 | 132 | defer regex.deinit(); |
| 133 | 133 | |
| 134 | 134 | try std.testing.expect(try regex.isMatch("\t")); |
| @@ -137,7 +137,7 @@ test "parser: tab and newline escapes" { | ||
| 137 | 137 | |
| 138 | 138 | test "parser: carriage return escape" { |
| 139 | 139 | const allocator = std.testing.allocator; |
| 140 | var regex = try Regex.compile(allocator, "\\r\\n"); | |
| 140 | var regex = try Regex.compile(allocator, std.testing.io, "\\r\\n"); | |
| 141 | 141 | defer regex.deinit(); |
| 142 | 142 | |
| 143 | 143 | try std.testing.expect(try regex.isMatch("\r\n")); |
| @@ -145,7 +145,7 @@ test "parser: carriage return escape" { | ||
| 145 | 145 | |
| 146 | 146 | test "parser: escaped caret and dollar" { |
| 147 | 147 | const allocator = std.testing.allocator; |
| 148 | var regex = try Regex.compile(allocator, "\\^\\$"); | |
| 148 | var regex = try Regex.compile(allocator, std.testing.io, "\\^\\$"); | |
| 149 | 149 | defer regex.deinit(); |
| 150 | 150 | |
| 151 | 151 | try std.testing.expect(try regex.isMatch("^$")); |
| @@ -156,7 +156,7 @@ test "parser: escaped caret and dollar" { | ||
| 156 | 156 | |
| 157 | 157 | test "compiler: alternation with quantifiers" { |
| 158 | 158 | const allocator = std.testing.allocator; |
| 159 | var regex = try Regex.compile(allocator, "^(ab+|cd*)e$"); | |
| 159 | var regex = try Regex.compile(allocator, std.testing.io, "^(ab+|cd*)e$"); | |
| 160 | 160 | defer regex.deinit(); |
| 161 | 161 | |
| 162 | 162 | try std.testing.expect(try regex.isMatch("abe")); |
| @@ -169,7 +169,7 @@ test "compiler: alternation with quantifiers" { | ||
| 169 | 169 | |
| 170 | 170 | test "compiler: character class in alternation" { |
| 171 | 171 | const allocator = std.testing.allocator; |
| 172 | var regex = try Regex.compile(allocator, "^([0-9]+|[a-z]+)$"); | |
| 172 | var regex = try Regex.compile(allocator, std.testing.io, "^([0-9]+|[a-z]+)$"); | |
| 173 | 173 | defer regex.deinit(); |
| 174 | 174 | |
| 175 | 175 | try std.testing.expect(try regex.isMatch("123")); |
| @@ -180,7 +180,7 @@ test "compiler: character class in alternation" { | ||
| 180 | 180 | |
| 181 | 181 | test "compiler: optional group" { |
| 182 | 182 | const allocator = std.testing.allocator; |
| 183 | var regex = try Regex.compile(allocator, "^colou?r$"); | |
| 183 | var regex = try Regex.compile(allocator, std.testing.io, "^colou?r$"); | |
| 184 | 184 | defer regex.deinit(); |
| 185 | 185 | |
| 186 | 186 | try std.testing.expect(try regex.isMatch("color")); |
| @@ -190,7 +190,7 @@ test "compiler: optional group" { | ||
| 190 | 190 | |
| 191 | 191 | test "compiler: optional group with parentheses" { |
| 192 | 192 | const allocator = std.testing.allocator; |
| 193 | var regex = try Regex.compile(allocator, "^(un)?happy$"); | |
| 193 | var regex = try Regex.compile(allocator, std.testing.io, "^(un)?happy$"); | |
| 194 | 194 | defer regex.deinit(); |
| 195 | 195 | |
| 196 | 196 | try std.testing.expect(try regex.isMatch("happy")); |
| @@ -200,7 +200,7 @@ test "compiler: optional group with parentheses" { | ||
| 200 | 200 | |
| 201 | 201 | test "compiler: multiple capture groups" { |
| 202 | 202 | const allocator = std.testing.allocator; |
| 203 | var regex = try Regex.compile(allocator, "(\\d{4})-(\\d{2})-(\\d{2})"); | |
| 203 | var regex = try Regex.compile(allocator, std.testing.io, "(\\d{4})-(\\d{2})-(\\d{2})"); | |
| 204 | 204 | defer regex.deinit(); |
| 205 | 205 | |
| 206 | 206 | if (try regex.find("Today is 2024-01-15!")) |match| { |
| @@ -220,7 +220,7 @@ test "compiler: multiple capture groups" { | ||
| 220 | 220 | |
| 221 | 221 | test "compiler: anchored pattern only tries position 0" { |
| 222 | 222 | const allocator = std.testing.allocator; |
| 223 | var regex = try Regex.compile(allocator, "^abc"); | |
| 223 | var regex = try Regex.compile(allocator, std.testing.io, "^abc"); | |
| 224 | 224 | defer regex.deinit(); |
| 225 | 225 | |
| 226 | 226 | try std.testing.expect(try regex.isMatch("abcdef")); |
| @@ -230,7 +230,7 @@ test "compiler: anchored pattern only tries position 0" { | ||
| 230 | 230 | |
| 231 | 231 | test "compiler: end-anchored pattern" { |
| 232 | 232 | const allocator = std.testing.allocator; |
| 233 | var regex = try Regex.compile(allocator, "abc$"); | |
| 233 | var regex = try Regex.compile(allocator, std.testing.io, "abc$"); | |
| 234 | 234 | defer regex.deinit(); |
| 235 | 235 | |
| 236 | 236 | if (try regex.find("xyzabc")) |match| { |
| @@ -244,7 +244,7 @@ test "compiler: end-anchored pattern" { | ||
| 244 | 244 | |
| 245 | 245 | test "compiler: both anchors" { |
| 246 | 246 | const allocator = std.testing.allocator; |
| 247 | var regex = try Regex.compile(allocator, "^exact$"); | |
| 247 | var regex = try Regex.compile(allocator, std.testing.io, "^exact$"); | |
| 248 | 248 | defer regex.deinit(); |
| 249 | 249 | |
| 250 | 250 | try std.testing.expect(try regex.isMatch("exact")); |
| @@ -257,13 +257,13 @@ test "compiler: both anchors" { | ||
| 257 | 257 | |
| 258 | 258 | test "compiler: password strength check" { |
| 259 | 259 | const allocator = std.testing.allocator; |
| 260 | var has_digit = try Regex.compile(allocator, "\\d"); | |
| 260 | var has_digit = try Regex.compile(allocator, std.testing.io, "\\d"); | |
| 261 | 261 | defer has_digit.deinit(); |
| 262 | var has_lower = try Regex.compile(allocator, "[a-z]"); | |
| 262 | var has_lower = try Regex.compile(allocator, std.testing.io, "[a-z]"); | |
| 263 | 263 | defer has_lower.deinit(); |
| 264 | var has_upper = try Regex.compile(allocator, "[A-Z]"); | |
| 264 | var has_upper = try Regex.compile(allocator, std.testing.io, "[A-Z]"); | |
| 265 | 265 | defer has_upper.deinit(); |
| 266 | var min_length = try Regex.compile(allocator, ".{8,}"); | |
| 266 | var min_length = try Regex.compile(allocator, std.testing.io, ".{8,}"); | |
| 267 | 267 | defer min_length.deinit(); |
| 268 | 268 | |
| 269 | 269 | const password = "MyP4ssword"; |
| @@ -280,7 +280,7 @@ test "compiler: password strength check" { | ||
| 280 | 280 | |
| 281 | 281 | test "compiler: CSV field extraction" { |
| 282 | 282 | const allocator = std.testing.allocator; |
| 283 | var regex = try Regex.compile(allocator, "[^,]+"); | |
| 283 | var regex = try Regex.compile(allocator, std.testing.io, "[^,]+"); | |
| 284 | 284 | defer regex.deinit(); |
| 285 | 285 | |
| 286 | 286 | const matches = try regex.findAll(allocator, "foo,bar,baz"); |
| @@ -300,7 +300,7 @@ test "compiler: CSV field extraction" { | ||
| 300 | 300 | |
| 301 | 301 | test "compiler: whitespace normalization via replaceAll" { |
| 302 | 302 | const allocator = std.testing.allocator; |
| 303 | var regex = try Regex.compile(allocator, "\\s+"); | |
| 303 | var regex = try Regex.compile(allocator, std.testing.io, "\\s+"); | |
| 304 | 304 | defer regex.deinit(); |
| 305 | 305 | |
| 306 | 306 | const result = try regex.replaceAll(allocator, " hello world ", " "); |
| @@ -312,7 +312,7 @@ test "compiler: whitespace normalization via replaceAll" { | ||
| 312 | 312 | |
| 313 | 313 | test "compiler: pattern with only ^ anchor" { |
| 314 | 314 | const allocator = std.testing.allocator; |
| 315 | var regex = try Regex.compile(allocator, "^"); | |
| 315 | var regex = try Regex.compile(allocator, std.testing.io, "^"); | |
| 316 | 316 | defer regex.deinit(); |
| 317 | 317 | |
| 318 | 318 | // ^ matches at position 0 of any string (matches zero-width) |
| @@ -322,7 +322,7 @@ test "compiler: pattern with only ^ anchor" { | ||
| 322 | 322 | |
| 323 | 323 | test "compiler: pattern with only $ anchor" { |
| 324 | 324 | const allocator = std.testing.allocator; |
| 325 | var regex = try Regex.compile(allocator, "$"); | |
| 325 | var regex = try Regex.compile(allocator, std.testing.io, "$"); | |
| 326 | 326 | defer regex.deinit(); |
| 327 | 327 | |
| 328 | 328 | // $ matches at end of any string (zero-width) |
| @@ -334,7 +334,7 @@ test "compiler: pattern with only $ anchor" { | ||
| 334 | 334 | |
| 335 | 335 | test "compiler: dot plus" { |
| 336 | 336 | const allocator = std.testing.allocator; |
| 337 | var regex = try Regex.compile(allocator, "^.+$"); | |
| 337 | var regex = try Regex.compile(allocator, std.testing.io, "^.+$"); | |
| 338 | 338 | defer regex.deinit(); |
| 339 | 339 | |
| 340 | 340 | try std.testing.expect(try regex.isMatch("a")); |
| @@ -344,7 +344,7 @@ test "compiler: dot plus" { | ||
| 344 | 344 | |
| 345 | 345 | test "compiler: dot question" { |
| 346 | 346 | const allocator = std.testing.allocator; |
| 347 | var regex = try Regex.compile(allocator, "^.?$"); | |
| 347 | var regex = try Regex.compile(allocator, std.testing.io, "^.?$"); | |
| 348 | 348 | defer regex.deinit(); |
| 349 | 349 | |
| 350 | 350 | try std.testing.expect(try regex.isMatch("")); |
| @@ -354,7 +354,7 @@ test "compiler: dot question" { | ||
| 354 | 354 | |
| 355 | 355 | test "compiler: dot repeat" { |
| 356 | 356 | const allocator = std.testing.allocator; |
| 357 | var regex = try Regex.compile(allocator, "^.{3}$"); | |
| 357 | var regex = try Regex.compile(allocator, std.testing.io, "^.{3}$"); | |
| 358 | 358 | defer regex.deinit(); |
| 359 | 359 | |
| 360 | 360 | try std.testing.expect(try regex.isMatch("abc")); |