include @link("std/third-party/tre/tre_full.c") import "./core.zc" import "./string.zc" import "./vec.zc" import "./option.zc" import "./mem.zc" struct Match { text: char*; start: int; len: int; } struct ZRegMatch { rm_so: int; rm_eo: int; } impl Match { /// Compiles a regular expression pattern. fn new(text: char*, start: int, len: int) -> Match { return Match { text: text, start: start, len: len }; } fn as_string(self) -> char* { let s: char* = alloc_n(self.len + 1); memcpy(s, self.text + self.start, self.len); s[self.len] = 0; return s; } fn end(self) -> int { return self.start + self.len; } } struct Regex { preg: void*; pattern: char*; flags: int; } impl Regex { fn compile(pattern: char*) -> Regex { return Regex::compile_with_flags(pattern, 1 | 2); } fn compile_with_flags(pattern: char*, flags: int) -> Regex { let preg = alloc_bytes(1024); let status: int = 0; raw { status = tre_regcomp((regex_t*)preg, pattern, flags); } if (status != 0) { dealloc(preg); return Regex { preg: NULL, pattern: NULL, flags: flags }; } return Regex { preg: preg, pattern: pattern, flags: flags }; } fn is_valid(self) -> bool { return self.preg != NULL; } /// Returns whether the pattern matches the text. fn match(self, text: char*) -> bool { if (self.preg == NULL) { return false; } let res: bool = false; raw { res = (tre_regexec((regex_t*)self->preg, text, 0, 0, 0) == 0); } return res; } fn match_full(self, text: char*) -> bool { return self.match(text); } fn match_at(self, text: char*, offset: int) -> bool { if (self.preg == NULL) { return false; } let len = strlen(text); if (offset < 0 || offset > len) { return false; } let res: bool = false; raw { res = (tre_regexec((regex_t*)self->preg, text + offset, 0, 0, 0) == 0); } return res; } /// Returns whether the pattern matches. fn is_match(self, text: char*) -> bool { return self.match(text); } /// Returns the first match, or `None`. fn find(self, text: char*) -> Option { if (self.preg == NULL) { return Option::None(); } let pmatch: ZRegMatch[1]; let res: int = -1; raw { res = tre_regexec((regex_t*)self->preg, text, 1, (regmatch_t*)pmatch, 0); } if (res == 0) { let start = pmatch[0].rm_so; let end = pmatch[0].rm_eo; if (start != -1) { return Option::Some(Match::new(text, start, end - start)); } } return Option::None(); } fn find_at(self, text: char*, start: int) -> Option { let len = strlen(text); if (start < 0 || start >= len) { return Option::None(); } return self.find(text + start); } /// Returns the number of matches. fn count(self, text: char*) -> int { if (self.preg == NULL) { return 0; } let count = 0; let pos = 0; let t_len = strlen(text); while (pos < t_len) { let res: bool = false; raw { res = (tre_regexec((regex_t*)self->preg, text + pos, 0, 0, 0) == 0); } if (res) { count = count + 1; pos = pos + 1; } else { break; } } return count; } /// Splits the text on matches. fn split(self, text: char*) -> Vec { let parts = Vec::new(); if (self.preg == NULL) { parts.push(String::from(text)); return parts; } let t_len = strlen(text); let last_pos = 0; let pos = 0; while (pos < t_len) { let res: bool = false; raw { res = (tre_regexec((regex_t*)self->preg, text + pos, 0, 0, 0) == 0); } if (res) { if (pos > last_pos) { let before = text + last_pos; let part_len = pos - last_pos; let v = Vec::new(); for (let i = 0; i < part_len; i = i + 1) { v.push(before[i]); } v.push(0); parts.push(String { vec: v }); } last_pos = pos + 1; pos = pos + 1; } else { pos = pos + 1; } } if (last_pos < t_len) { parts.push(String::from(text + last_pos)); } return parts; } fn pattern(self) -> char* { return self.pattern; } fn flags(self) -> int { return self.flags; } fn is_valid_pattern(pattern: char*) -> bool { let test_regex = Regex::compile(pattern); let valid = test_regex.is_valid(); test_regex.destroy(); return valid; } fn destroy(self) { if (self.preg != NULL) { raw { tre_regfree((regex_t*)self->preg); } dealloc(self.preg); } } } fn regex_match(pattern: char*, text: char*) -> bool { let re = Regex::compile(pattern); let result = re.match(text); re.destroy(); return result; } fn regex_find(pattern: char*, text: char*) -> Option { let re = Regex::compile(pattern); let result = re.find(text); re.destroy(); return result; } fn regex_count(pattern: char*, text: char*) -> int { let re = Regex::compile(pattern); let count = re.count(text); re.destroy(); return count; } fn regex_split(pattern: char*, text: char*) -> Vec { let re = Regex::compile(pattern); let parts = re.split(text); re.destroy(); return parts; }