1 | import os |
2 | |
3 | fn regex_match(src string, pat string) bool { |
4 | src_size := src.len + 1 |
5 | pat_size := pat.len + 1 |
6 | mut memo := [][]int{len: src_size, init: []int{len: pat_size, init: -1}} |
7 | return regex_match_core(src, pat, 0, 0, mut memo) |
8 | } |
9 | |
10 | fn regex_match_core(src string, pat string, src_pos int, pat_pos int, mut memo [][]int) bool { |
11 | if memo[src_pos][pat_pos] != -1 { |
12 | return memo[src_pos][pat_pos] == 1 |
13 | } |
14 | mut spos := src_pos |
15 | mut ppos := pat_pos |
16 | if spos >= src.len && ppos >= pat.len { |
17 | memo[src_pos][pat_pos] = 1 |
18 | return true |
19 | } else if spos < src.len && ppos >= pat.len { |
20 | memo[src_pos][pat_pos] = 0 |
21 | return false |
22 | } else if spos >= src.len && ppos < pat.len { |
23 | if pat[ppos] == `\\` { |
24 | ppos++ |
25 | } |
26 | res := ppos + 1 < pat.len && pat[ppos + 1] in [`*`, `?`] |
27 | && regex_match_core(src, pat, spos, ppos + 2, mut memo) |
28 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
29 | return res |
30 | } else { |
31 | first_is_bslash := pat[ppos] == `\\` |
32 | if first_is_bslash { |
33 | ppos++ |
34 | } |
35 | first_bslash_and_match := first_is_bslash && ppos < pat.len |
36 | && (((pat[ppos] == `d` && src[spos].is_digit()) |
37 | || (pat[ppos] == `D` && !src[spos].is_digit()) |
38 | || (pat[ppos] == `s` && src[spos].is_space()) |
39 | || (pat[ppos] == `S` && !src[spos].is_space()) |
40 | || (pat[ppos] == `w` && (src[spos].is_digit() || src[spos].is_letter() |
41 | || src[spos] == `_`)) || (pat[ppos] == `W` && !(src[spos].is_digit() |
42 | || src[spos].is_letter() || src[spos] == `_`))) |
43 | || (pat[ppos] in [`d`, `D`, `s`, `S`, `w`, `W`] && ppos + 1 < pat.len |
44 | && pat[ppos + 1] in [`*`, `?`, `+`]) |
45 | || (pat[ppos] !in [`d`, `D`, `s`, `S`, `w`, `W`] && src[spos] == pat[ppos])) |
46 | if ppos + 1 < pat.len { |
47 | match pat[ppos + 1] { |
48 | `*` { |
49 | if first_bslash_and_match { |
50 | res := regex_match_core(src, pat, spos + 1, ppos - 1, mut memo) |
51 | || regex_match_core(src, pat, spos, ppos + 2, mut memo) |
52 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
53 | return res |
54 | } else if src[spos] == pat[ppos] || pat[ppos] == `.` { |
55 | res := regex_match_core(src, pat, spos + 1, ppos, mut memo) |
56 | || regex_match_core(src, pat, spos, ppos + 2, mut memo) |
57 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
58 | return res |
59 | } else { |
60 | res := regex_match_core(src, pat, spos, ppos + 2, mut memo) |
61 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
62 | return res |
63 | } |
64 | } |
65 | `+` { |
66 | if first_bslash_and_match { |
67 | res := regex_match_core(src, pat, spos + 1, ppos - 1, mut memo) |
68 | || regex_match_core(src, pat, spos + 1, ppos + 2, mut memo) |
69 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
70 | return res |
71 | } else if src[spos] == pat[ppos] || pat[ppos] == `.` { |
72 | res := regex_match_core(src, pat, spos + 1, ppos, mut memo) |
73 | || regex_match_core(src, pat, spos + 1, ppos + 2, mut memo) |
74 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
75 | return res |
76 | } else { |
77 | memo[src_pos][pat_pos] = 0 |
78 | return false |
79 | } |
80 | } |
81 | `?` { |
82 | if first_bslash_and_match || src[spos] == pat[ppos] || pat[ppos] == `.` { |
83 | res := regex_match_core(src, pat, spos + 1, ppos + 2, mut memo) |
84 | || regex_match_core(src, pat, spos, ppos + 2, mut memo) |
85 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
86 | return res |
87 | } else { |
88 | res := regex_match_core(src, pat, spos, ppos + 2, mut memo) |
89 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
90 | return res |
91 | } |
92 | } |
93 | else {} |
94 | } |
95 | } |
96 | if first_is_bslash { |
97 | res := first_bslash_and_match |
98 | && regex_match_core(src, pat, spos + 1, ppos + 1, mut memo) |
99 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
100 | return res |
101 | } else { |
102 | res := (src[spos] == pat[ppos] || pat[ppos] == `.`) && pat[ppos] != `\\` |
103 | && regex_match_core(src, pat, spos + 1, ppos + 1, mut memo) |
104 | memo[src_pos][pat_pos] = if res { 1 } else { 0 } |
105 | return res |
106 | } |
107 | } |
108 | } |
109 | |
110 | fn main() { |
111 | mut cnt := 0 |
112 | println('currently supported patterns: . ? + * \\ \\d \\D \\s \\S \\w \\W') |
113 | println('example: source `[email protected]` matches pattern `\\w+@domain\\.net`') |
114 | println('enter `exit` to quit\n') |
115 | for { |
116 | cnt++ |
117 | src := os.input('[${cnt}] enter source string: ') |
118 | if src == 'exit' { |
119 | break |
120 | } |
121 | pat := os.input('[${cnt}] enter pattern string: ') |
122 | if pat == 'exit' { |
123 | break |
124 | } |
125 | println('[${cnt}] whether `${src}` matches `${pat}`: ${regex_match(src, pat)}') |
126 | } |
127 | } |