main.mx raw
1 package main
2
3 import (
4 "os"
5
6 "git.smesh.lol/moxie/pkg/mxregexp"
7 )
8
9 // Reads a case file of "pattern<TAB>text" lines and prints one "i ok" line
10 // per case, so the same file fed to a Go regexp reference can be diffed
11 // against this output. The text column is escape-decoded (\n, \t, \r, \\)
12 // because a real newline cannot be carried in a line-oriented file; the
13 // pattern column is passed to the matcher verbatim.
14 func main() {
15 if len(os.Args) < 2 {
16 println("usage: regexp_case <cases-file>")
17 return
18 }
19 data, rerr := os.ReadFile(os.Args[1])
20 if rerr != nil {
21 println("cannot read " | os.Args[1])
22 return
23 }
24 line := int32(0)
25 start := int32(0)
26 i := int32(0)
27 for i <= int32(len(data)) {
28 if i < int32(len(data)) && data[i] != '\n' {
29 i++
30 continue
31 }
32 if i > start {
33 tab := start
34 for tab < i && data[tab] != '\t' {
35 tab++
36 }
37 if tab >= i {
38 println("case " | itoa(line) | ": missing tab")
39 return
40 }
41 // The error text is not compared: both sides must agree only on
42 // whether the pattern is rejected at all.
43 m, merr := mxregexp.Match(data[start:tab], unescape(data[tab+1:i]))
44 if merr != "" {
45 println(itoa(line) | " error")
46 } else if m {
47 println(itoa(line) | " true")
48 } else {
49 println(itoa(line) | " false")
50 }
51 }
52 line++
53 i++
54 start = i
55 }
56 }
57
58 // grow is mxutil.Ensure inlined: the test program must not import mxutil,
59 // whose file helpers are //export externs supplied by the compiler binary.
60 func grow(b []byte, n int32) (r0 []byte) {
61 if cap(b)-len(b) >= n {
62 return b
63 }
64 nc := cap(b) * 2
65 if nc < 16 {
66 nc = 16
67 }
68 for nc-len(b) < n {
69 nc = nc * 2
70 }
71 nb := []byte{:0:nc}
72 copy(nb[:len(b)], b)
73 return nb[:len(b)]
74 }
75
76 func unescape(s string) (r string) {
77 needs := false
78 for i := int32(0); i < int32(len(s)); i++ {
79 if s[i] == '\\' {
80 needs = true
81 break
82 }
83 }
84 if !needs {
85 return s
86 }
87 buf := []byte{:0:int32(len(s))}
88 j := int32(0)
89 for j < int32(len(s)) {
90 if s[j] == '\\' && j+1 < int32(len(s)) {
91 c := s[j+1]
92 if c == 'n' {
93 buf = grow(buf, 1)
94 push(buf, '\n')
95 j += 2
96 continue
97 }
98 if c == 't' {
99 buf = grow(buf, 1)
100 push(buf, '\t')
101 j += 2
102 continue
103 }
104 if c == 'r' {
105 buf = grow(buf, 1)
106 push(buf, '\r')
107 j += 2
108 continue
109 }
110 if c == '\\' {
111 buf = grow(buf, 1)
112 push(buf, '\\')
113 j += 2
114 continue
115 }
116 }
117 buf = grow(buf, 1)
118 push(buf, s[j])
119 j++
120 }
121 return string(buf)
122 }
123
124 func itoa(n int32) (s string) {
125 if n == 0 {
126 return "0"
127 }
128 buf := []byte{:0:12}
129 for n > 0 {
130 buf = grow(buf, 1)
131 push(buf, byte('0'+n%10))
132 n /= 10
133 }
134 for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
135 buf[i], buf[j] = buf[j], buf[i]
136 }
137 return string(buf)
138 }
139