hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 54d9ecbb0a564d7060a6df5ec3f271217970a838
parent a268bf7cdfa5e474b31e6018ba7843ff753e113b
Author: Sebastian <sebastian@sebsite.pw>
Date:   Tue, 17 Jan 2023 01:03:48 -0500

regex: make find and findall return [][]capture

Previously, find and findall would return void if no matches were found.
This commit changes this behavior, so an empty slice is returned
instead.

Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Mcmd/haredoc/color.ha | 7+------
Mregex/+test.ha | 106+++++++++++++++++++++++++++++++++++++------------------------------------------
Mregex/README | 30++++++++++++------------------
Mregex/regex.ha | 14++++++++------
4 files changed, 70 insertions(+), 87 deletions(-)

diff --git a/cmd/haredoc/color.ha b/cmd/haredoc/color.ha @@ -45,12 +45,7 @@ fn init_colors() void = { const expr = regex::compile(`([a-z][a-z]*)=(_|[0-9;]*)`)!; defer regex::finish(&expr); - const matches = match (regex::findall(&expr, env_colors)) { - case void => - return; - case let matches: [][]regex::capture => - yield matches; - }; + const matches = regex::findall(&expr, env_colors); defer regex::free_matches(matches); for (let i = 0z; i < len(matches); i += 1) :colors { diff --git a/regex/+test.ha b/regex/+test.ha @@ -36,31 +36,30 @@ fn run_find_case( }; defer finish(&re); - match (find(&re, string)) { - case void => + const captures = find(&re, string); + defer free_captures(captures); + if (len(captures) == 0) { if (expected == matchres::MATCH) { fmt::errorfln("Expected expression /{}/ to match string \"{}\", but it did not", expr, string)!; abort(); }; + return; + } else if (expected == matchres::NOMATCH) { + fmt::errorfln("Expected expression /{}/ to not match string \"{}\", but it did", + expr, string)!; + abort(); + }; - case let captures: []capture => - defer free_captures(captures); - if (expected == matchres::NOMATCH) { - fmt::errorfln("Expected expression /{}/ to not match string \"{}\", but it did", - expr, string)!; - abort(); - }; - if (start: size != captures[0].start) { - fmt::errorfln("Expected start of main capture to be {} but it was {}", - start, captures[0].start)!; - abort(); - }; - if (end: size != captures[0].end) { - fmt::errorfln("Expected end of main capture to be {} but it was {}", - end, captures[0].end)!; - abort(); - }; + if (start: size != captures[0].start) { + fmt::errorfln("Expected start of main capture to be {} but it was {}", + start, captures[0].start)!; + abort(); + }; + if (end: size != captures[0].end) { + fmt::errorfln("Expected end of main capture to be {} but it was {}", + end, captures[0].end)!; + abort(); }; }; @@ -68,15 +67,14 @@ fn run_submatch_case( expr: str, string: str, expected: matchres, - count: size, targets: []str ) void = { const re = compile(expr)!; defer finish(&re); - const captures = find(&re, string) as []capture; + const captures = find(&re, string); defer free_captures(captures); - assert(len(captures) == count, "Invalid number of captures"); + assert(len(captures) == len(targets), "Invalid number of captures"); for (let i = 0z; i < len(targets); i += 1) { assert(targets[i] == captures[i].content, "Invalid capture"); }; @@ -86,7 +84,6 @@ fn run_findall_case( expr: str, string: str, expected: matchres, - count: size, targets: []str ) void = { const re = match (compile(expr)) { @@ -108,33 +105,30 @@ fn run_findall_case( abort(); }; - match (findall(&re, string)) { - case void => - if (expected == matchres::MATCH) { - fmt::errorfln("Expected expression /{}/ to match string \"{}\", but it did not", - expr, string)!; - abort(); - }; + const matches = findall(&re, string); + if (len(matches) == 0 && expected == matchres::MATCH) { + fmt::errorfln("Expected expression /{}/ to match string \"{}\", but it did not", + expr, string)!; + abort(); + }; + defer free_matches(matches); - case let matches: [][]capture => - defer free_matches(matches); - if (expected == matchres::NOMATCH) { - fmt::errorfln("Expected expression /{}/ to not match string \"{}\", but it did", - expr, string)!; - abort(); - }; - if (count != len(matches)) { - fmt::errorfln("Expected to find {} matches but found {}", - count, len(matches))!; + if (expected == matchres::NOMATCH) { + fmt::errorfln("Expected expression /{}/ to not match string \"{}\", but it did", + expr, string)!; + abort(); + }; + if (len(targets) != len(matches)) { + fmt::errorfln("Expected expression /{}/ to find {} matches but found {}", + expr, len(targets), len(matches))!; + abort(); + }; + for (let i = 0z; i < len(matches); i += 1) { + if (matches[i][0].content != targets[i]) { + fmt::errorfln("Expected submatch of expression /{}/ to be {} but it was {}", + expr, targets[i], matches[i][0].content)!; abort(); }; - for (let i = 0z; i < len(matches); i += 1) { - if (matches[i][0].content != targets[i]) { - fmt::errorfln("Expected submatch to be {} but it was {}", - targets[i], matches[i][0].content)!; - abort(); - }; - }; }; }; @@ -553,32 +547,30 @@ fn run_findall_case( const submatch_cases = [ // literals - (`aaa ([^ ]*) (...)`, "aaa bbb ccc", matchres::MATCH, 3z, - ["aaa bbb ccc", "bbb", "ccc"]), + (`aaa ([^ ]*) (...)`, "aaa bbb ccc", matchres::MATCH, + ["aaa bbb ccc", "bbb", "ccc"]: []str), ]; for (let i = 0z; i < len(submatch_cases); i += 1) { const expr = submatch_cases[i].0; const string = submatch_cases[i].1; const should_match = submatch_cases[i].2; - const count = submatch_cases[i].3; - const targets = submatch_cases[i].4; - run_submatch_case(expr, string, should_match, count, targets); + const targets = submatch_cases[i].3; + run_submatch_case(expr, string, should_match, targets); }; }; @test fn findall() void = { const cases = [ - (`ab.`, "hello abc and abあ test abq thanks", matchres::MATCH, 3z, - ["abc", "abあ", "abq"]), + (`ab.`, "hello abc and abあ test abq thanks", matchres::MATCH, + ["abc", "abあ", "abq"]: []str), ]; for (let i = 0z; i < len(cases); i += 1) { const expr = cases[i].0; const string = cases[i].1; const should_match = cases[i].2; - const count = cases[i].3; - const targets = cases[i].4; - run_findall_case(expr, string, should_match, count, targets); + const targets = cases[i].3; + run_findall_case(expr, string, should_match, targets); }; }; diff --git a/regex/README b/regex/README @@ -23,10 +23,8 @@ the longest match among the leftmost matches. const does_match = regex::test(&re, "Hello Hare, hello Hare."); fmt::printfln("matched? {}", does_match)!; - const first_match = regex::find(&re, "Hello Hare, hello Hare."); - match (first_match) { - case void => void; - case let captures: []regex::capture => + const captures = regex::find(&re, "Hello Hare, hello Hare."); + if (len(captures) != 0) { defer regex::free_captures(captures); // captures[0]: The full matching string. // captures[1...]: A capture for every capture group. @@ -35,20 +33,16 @@ the longest match among the leftmost matches. captures[0].end)!; }; - const all_matches = regex::findall(&re, "Hello Hare, hello Hare."); - match (all_matches) { - case void => void; - case let matches: [][]regex::capture => - defer regex::free_matches(matches); - // matches[0]: All captures for the first match. - // matches[0][0]: The full matching string for the first match. - // matches[0][1...]: A capture for every capture group in the - // first match. - for (let i = 0z; i < len(matches); i += 1) { - fmt::printfln("{} ({}, {})", matches[i][0].content, - matches[i][0].start, - matches[i][0].end)!; - }; + const matches = regex::findall(&re, "Hello Hare, hello Hare."); + defer regex::free_matches(matches); + // matches[0]: All captures for the first match. + // matches[0][0]: The full matching string for the first match. + // matches[0][1...]: A capture for every capture group in the + // first match. + for (let i = 0z; i < len(matches); i += 1) { + fmt::printfln("{} ({}, {})", matches[i][0].content, + matches[i][0].start, + matches[i][0].end)!; }; [0]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04 diff --git a/regex/regex.ha b/regex/regex.ha @@ -773,16 +773,21 @@ export fn test(re: *regex, string: str) bool = { // Attempts to match a regular expression against a string and returns the // longest leftmost match, or void if there is no match. -export fn find(re: *regex, string: str) (void | []capture) = { +export fn find(re: *regex, string: str) []capture = { let str_idx = -1; let str_iter = strings::iter(string); let str_bytesize = 0z; - return search(re, string, &str_iter, &str_idx, &str_bytesize, true); + match (search(re, string, &str_iter, &str_idx, &str_bytesize, true)) { + case let m: []capture => + return m; + case void => + return []; + }; }; // Attempts to match a regular expression against a string and returns all // non-overlapping matches, or void if there are no matches. -export fn findall(re: *regex, string: str) (void | [][]capture) = { +export fn findall(re: *regex, string: str) [][]capture = { let res: [][]capture = alloc([]); let str_idx = -1; let str_iter = strings::iter(string); @@ -804,9 +809,6 @@ export fn findall(re: *regex, string: str) (void | [][]capture) = { case void => break; }; }; - if (len(res) == 0) { - return void; - }; return res; };