1 package mw
2
3 import "git.smesh.lol/moxie/pkg/mxutil"
4
5 type MW struct {
6 S string
7 I int32
8 }
9
10 func New(s string) (m MW) {
11 w := MW{S: s}
12 for w.I < len(s) && s[w.I] != '[' {
13 w.I++
14 }
15 if w.I < len(s) {
16 w.I++
17 }
18 return w
19 }
20
21 func (w *MW) Sep() {
22 // Declared outside the loop: a declaration in the body of a self-mutating
23 // method allocates in the sovereign arena on every iteration.
24 var c byte
25 for w.I < len(w.S) {
26 c = w.S[w.I]
27 if c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != ',' {
28 break
29 }
30 w.I++
31 }
32 }
33
34 func (w *MW) Str() (s string) {
35 w.Sep()
36 if w.I >= len(w.S) || w.S[w.I] != '"' {
37 return ""
38 }
39 w.I++
40 start := w.I
41 for w.I < len(w.S) && w.S[w.I] != '"' {
42 if w.S[w.I] == '\\' {
43 w.I++
44 }
45 w.I++
46 }
47 if w.I >= len(w.S) {
48 return ""
49 }
50 result := w.S[start:w.I]
51 w.I++
52 return result
53 }
54
55 func (w *MW) Num() (n int64) {
56 w.Sep()
57 neg := false
58 if w.I < len(w.S) && w.S[w.I] == '-' {
59 neg = true
60 w.I++
61 }
62 var n int64
63 for w.I < len(w.S) && w.S[w.I] >= '0' && w.S[w.I] <= '9' {
64 n = n*10 + int64(w.S[w.I]-'0')
65 w.I++
66 }
67 if neg {
68 n = -n
69 }
70 return n
71 }
72
73 func (w *MW) Raw() (s string) {
74 w.Sep()
75 start := w.I
76 w.I = Skipval(w.S, w.I)
77 if w.I < 0 {
78 w.I = len(w.S)
79 return ""
80 }
81 return w.S[start:w.I]
82 }
83
84 func (w *MW) Strs() (ss []string) {
85 w.Sep()
86 if w.I >= len(w.S) || w.S[w.I] != '[' {
87 return nil
88 }
89 w.I++
90 // Declared outside the loop, as above.
91 var out []string
92 var s string
93 for {
94 w.Sep()
95 if w.I >= len(w.S) || w.S[w.I] == ']' {
96 if w.I < len(w.S) {
97 w.I++
98 }
99 return out
100 }
101 s = w.Str()
102 out = mxutil.Ensure(out, 1)
103 out = push(out, s)
104 }
105 }
106
107 func Unescape(s string) (sv string) {
108 if len(s) == 0 {
109 return s
110 }
111 i := 0
112 for i < len(s) {
113 if s[i] == '\\' {
114 break
115 }
116 i++
117 }
118 if i == len(s) {
119 return s
120 }
121 out := []byte{:0:len(s)}
122 out = out | s[:i]
123 for i < len(s) {
124 if s[i] == '\\' && i+1 < len(s) {
125 c := s[i+1]
126 if c == '"' || c == '\\' || c == '/' {
127 out = push(out, c)
128 } else if c == 'n' {
129 out = out | "\n"
130 } else if c == 'r' {
131 out = out | "\r"
132 } else if c == 't' {
133 out = out | "\t"
134 } else {
135 out = push(out, s[i])
136 out = push(out, c)
137 }
138 i += 2
139 } else {
140 out = push(out, s[i])
141 i++
142 }
143 }
144 return string(out)
145 }
146
147 func Skipval(s string, i int32) (n int32) {
148 if i >= len(s) {
149 return -1
150 }
151 c := s[i]
152 if c == '"' {
153 i++
154 for i < len(s) && s[i] != '"' {
155 if s[i] == '\\' {
156 i++
157 }
158 i++
159 }
160 if i >= len(s) {
161 return -1
162 }
163 return i + 1
164 }
165 if c == '{' || c == '[' {
166 open := c
167 closeB := byte('}')
168 if open == '[' {
169 closeB = ']'
170 }
171 depth := 1
172 i++
173 for i < len(s) && depth > 0 {
174 if s[i] == '"' {
175 i++
176 for i < len(s) && s[i] != '"' {
177 if s[i] == '\\' {
178 i++
179 }
180 i++
181 }
182 if i >= len(s) {
183 return -1
184 }
185 i++
186 continue
187 }
188 if s[i] == open {
189 depth++
190 } else if s[i] == closeB {
191 depth--
192 if depth == 0 {
193 return i + 1
194 }
195 }
196 i++
197 }
198 return -1
199 }
200 for i < len(s) {
201 c = s[i]
202 if c == ',' || c == ']' || c == '}' {
203 return i
204 }
205 i++
206 }
207 return i
208 }
209