@px-petals/roku.motion-web 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bands/bands.js +3 -1
- package/bundle/bundle.js +56 -0
- package/list/list.js +12 -1
- package/lottie/lottie.layers.js +64 -11
- package/lottie/lottie.paint.js +30 -0
- package/lottie/lottie.paths.js +19 -4
- package/lottie/lottie.props.js +18 -8
- package/lottie/lottie.shapes.js +2 -1
- package/mixer/mixer.js +31 -8
- package/mixer/mixer.mix.js +17 -0
- package/motion/motion.convert.js +32 -1
- package/motion/motion.flow.js +20 -2
- package/motion/motion.machine.js +28 -13
- package/motion/motion.values.js +13 -9
- package/motion.js +196 -20
- package/package.json +10 -16
- package/player/player.js +25 -5
- package/readme.md +78 -3
- package/script/luau/lib/base/base.js +202 -0
- package/script/luau/lib/base/base.table.js +189 -0
- package/script/luau/lib/lib.args.js +152 -0
- package/script/luau/lib/lib.js +23 -0
- package/script/luau/lib/math/math.bit32.js +144 -0
- package/script/luau/lib/math/math.js +288 -0
- package/script/luau/lib/math/math.vector.js +125 -0
- package/script/luau/lib/string/string.format.js +143 -0
- package/script/luau/lib/string/string.js +160 -0
- package/script/luau/lib/string/string.pattern.js +505 -0
- package/script/luau/load/load.js +362 -0
- package/script/luau/load/load.ops.js +79 -0
- package/script/luau/luau.convert.js +259 -0
- package/script/luau/luau.js +132 -0
- package/script/luau/luau.md +111 -0
- package/script/luau/vm/vm.js +167 -0
- package/script/luau/vm/vm.meta.js +401 -0
- package/script/luau/vm/vm.num.js +236 -0
- package/script/luau/vm/vm.run.js +842 -0
- package/script/luau/vm/vm.value.js +406 -0
- package/script/play/play.js +199 -0
- package/script/play/play.objects.js +295 -0
- package/script/play/play.scope.js +91 -0
- package/script/rive/api/api.statics.js +31 -0
- package/script/rive/api/api.type.js +188 -0
- package/script/rive/api/artboard/artboard.js +89 -0
- package/script/rive/api/assets/assets.js +72 -0
- package/script/rive/api/data/data.value.js +99 -0
- package/script/rive/api/draw/draw.contours.js +163 -0
- package/script/rive/api/draw/draw.measure.js +196 -0
- package/script/rive/api/draw/draw.paint.js +235 -0
- package/script/rive/api/draw/draw.path.js +153 -0
- package/script/rive/api/draw/draw.renderer.js +117 -0
- package/script/rive/api/math/math.color.js +96 -0
- package/script/rive/api/math/math.mat2d.js +180 -0
- package/script/rive/api/math/math.vector.js +121 -0
- package/script/rive/api/media/media.audio.js +113 -0
- package/script/rive/api/media/media.draw.js +73 -0
- package/script/rive/api/media/media.image.js +61 -0
- package/script/rive/api/media/media.mesh.js +50 -0
- package/script/rive/api/pointer/pointer.event.js +64 -0
- package/script/rive/api/pointer/pointer.route.js +69 -0
- package/script/rive/api/text/text.font.js +129 -0
- package/script/rive/api/text/text.js +278 -0
- package/script/rive/api/text/text.layout.js +259 -0
- package/script/rive/context/context.js +76 -0
- package/script/rive/context/context.memory.js +196 -0
- package/script/rive/context/context.viewmodel.js +302 -0
- package/script/rive/context/lists/lists.js +160 -0
- package/script/rive/context/lists/lists.values.js +281 -0
- package/script/rive/host/host.globals.js +44 -0
- package/script/rive/host/host.js +533 -0
- package/script/rive/host/host.node.js +45 -0
- package/script/rive/rive.js +23 -0
- package/script/rive/rive.md +180 -0
- package/script/script.js +11 -0
- package/skin/skin.deform.js +514 -0
- package/skin/skin.ik.js +260 -0
- package/skin/skin.js +297 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lua patterns: a port of the matcher in Lua 5.1's `lstrlib.c`, which Luau's
|
|
3
|
+
* `string.find`, `match`, `gmatch` and `gsub` use. Positions are 0-based
|
|
4
|
+
* indices into the byte string; `-1` means no match.
|
|
5
|
+
*
|
|
6
|
+
* Supported: `.`, character classes `%a %c %d %l %p %s %u %w %x` and their
|
|
7
|
+
* upper-case negations, sets `[...]` with ranges and `^`, the quantifiers
|
|
8
|
+
* `* + - ?`, anchors `^` and `$`, captures `(...)` and position captures
|
|
9
|
+
* `()`, back-references `%1`-`%9`, balanced matches `%b()` and frontiers `%f[set]`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { fail } from '../../vm/vm.value.js'
|
|
13
|
+
|
|
14
|
+
const CAP_UNFINISHED = -1
|
|
15
|
+
const CAP_POSITION = -2
|
|
16
|
+
const MAX_CAPTURES = 32
|
|
17
|
+
const MAX_DEPTH = 200
|
|
18
|
+
|
|
19
|
+
// Character classes
|
|
20
|
+
// ■■■■■■■■■■■■■■■■■
|
|
21
|
+
|
|
22
|
+
/** @param {number} c */
|
|
23
|
+
const isAlpha = (c) => (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
|
|
24
|
+
/** @param {number} c */
|
|
25
|
+
const isDigit = (c) => c >= 48 && c <= 57
|
|
26
|
+
/** @param {number} c */
|
|
27
|
+
const isLower = (c) => c >= 97 && c <= 122
|
|
28
|
+
/** @param {number} c */
|
|
29
|
+
const isUpper = (c) => c >= 65 && c <= 90
|
|
30
|
+
/** @param {number} c */
|
|
31
|
+
const isSpace = (c) => c === 32 || (c >= 9 && c <= 13)
|
|
32
|
+
/** @param {number} c */
|
|
33
|
+
const isCntrl = (c) => c < 32 || c === 127
|
|
34
|
+
/** @param {number} c */
|
|
35
|
+
const isPunct = (c) => (c >= 33 && c <= 47) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126)
|
|
36
|
+
/** @param {number} c */
|
|
37
|
+
const isXDigit = (c) => isDigit(c) || (c >= 65 && c <= 70) || (c >= 97 && c <= 102)
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Whether char code `c` is in the class named by the letter `cl` (`a`, `D`, ...); any other letter matches itself.
|
|
41
|
+
*
|
|
42
|
+
* @param {number} c
|
|
43
|
+
* @param {number} cl
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
function matchClass(c, cl) {
|
|
47
|
+
let res
|
|
48
|
+
switch (cl | 32) {
|
|
49
|
+
case 97: res = isAlpha(c); break
|
|
50
|
+
case 99: res = isCntrl(c); break
|
|
51
|
+
case 100: res = isDigit(c); break
|
|
52
|
+
case 108: res = isLower(c); break
|
|
53
|
+
case 112: res = isPunct(c); break
|
|
54
|
+
case 115: res = isSpace(c); break
|
|
55
|
+
case 117: res = isUpper(c); break
|
|
56
|
+
case 119: res = isAlpha(c) || isDigit(c); break
|
|
57
|
+
case 120: res = isXDigit(c); break
|
|
58
|
+
default: return cl === c
|
|
59
|
+
}
|
|
60
|
+
return isUpper(cl) ? !res : res
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Matcher
|
|
64
|
+
// ■■■■■■■
|
|
65
|
+
|
|
66
|
+
/** The state of one match attempt */
|
|
67
|
+
export class MatchState {
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} src
|
|
70
|
+
* @param {string} pat
|
|
71
|
+
*/
|
|
72
|
+
constructor(src, pat) {
|
|
73
|
+
this.src = src
|
|
74
|
+
this.pat = pat
|
|
75
|
+
this.level = 0
|
|
76
|
+
this.depth = 0
|
|
77
|
+
/** @type {number[]} */
|
|
78
|
+
this.capInit = []
|
|
79
|
+
/** @type {number[]} */
|
|
80
|
+
this.capLen = []
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
reset() {
|
|
84
|
+
this.level = 0
|
|
85
|
+
this.depth = 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The index just past the single-character class that starts at `p`.
|
|
90
|
+
*
|
|
91
|
+
* @param {number} p
|
|
92
|
+
* @returns {number}
|
|
93
|
+
*/
|
|
94
|
+
classEnd(p) {
|
|
95
|
+
const pat = this.pat
|
|
96
|
+
const c = pat.charCodeAt(p++)
|
|
97
|
+
if (c === 37) {
|
|
98
|
+
if (p >= pat.length) throw fail("malformed pattern (ends with '%')")
|
|
99
|
+
return p + 1
|
|
100
|
+
}
|
|
101
|
+
if (c === 91) {
|
|
102
|
+
if (pat.charCodeAt(p) === 94) p++
|
|
103
|
+
do {
|
|
104
|
+
if (p >= pat.length) throw fail("malformed pattern (missing ']')")
|
|
105
|
+
const d = pat.charCodeAt(p++)
|
|
106
|
+
if (d === 37 && p < pat.length) p++
|
|
107
|
+
} while (pat.charCodeAt(p) !== 93)
|
|
108
|
+
return p + 1
|
|
109
|
+
}
|
|
110
|
+
return p
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {number} c char code
|
|
115
|
+
* @param {number} p index of the `[`
|
|
116
|
+
* @param {number} ec index of the closing `]`
|
|
117
|
+
* @returns {boolean}
|
|
118
|
+
*/
|
|
119
|
+
matchBracketClass(c, p, ec) {
|
|
120
|
+
const pat = this.pat
|
|
121
|
+
let sig = true
|
|
122
|
+
if (pat.charCodeAt(p + 1) === 94) {
|
|
123
|
+
sig = false
|
|
124
|
+
p++
|
|
125
|
+
}
|
|
126
|
+
while (++p < ec) {
|
|
127
|
+
const pc = pat.charCodeAt(p)
|
|
128
|
+
if (pc === 37) {
|
|
129
|
+
p++
|
|
130
|
+
if (matchClass(c, pat.charCodeAt(p))) return sig
|
|
131
|
+
} else if (pat.charCodeAt(p + 1) === 45 && p + 2 < ec) {
|
|
132
|
+
p += 2
|
|
133
|
+
if (pc <= c && c <= pat.charCodeAt(p)) return sig
|
|
134
|
+
} else if (pc === c) return sig
|
|
135
|
+
}
|
|
136
|
+
return !sig
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {number} s
|
|
141
|
+
* @param {number} p
|
|
142
|
+
* @param {number} ep the end of the class at p
|
|
143
|
+
* @returns {boolean}
|
|
144
|
+
*/
|
|
145
|
+
singleMatch(s, p, ep) {
|
|
146
|
+
if (s >= this.src.length) return false
|
|
147
|
+
const c = this.src.charCodeAt(s)
|
|
148
|
+
const pc = this.pat.charCodeAt(p)
|
|
149
|
+
switch (pc) {
|
|
150
|
+
case 46: return true
|
|
151
|
+
case 37: return matchClass(c, this.pat.charCodeAt(p + 1))
|
|
152
|
+
case 91: return this.matchBracketClass(c, p, ep - 1)
|
|
153
|
+
default: return pc === c
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {number} s
|
|
159
|
+
* @param {number} p index just past `%b`
|
|
160
|
+
* @returns {number}
|
|
161
|
+
*/
|
|
162
|
+
matchBalance(s, p) {
|
|
163
|
+
if (p + 1 >= this.pat.length) throw fail("missing arguments to '%b'")
|
|
164
|
+
const src = this.src
|
|
165
|
+
if (s >= src.length || src.charCodeAt(s) !== this.pat.charCodeAt(p)) return -1
|
|
166
|
+
const b = this.pat.charCodeAt(p)
|
|
167
|
+
const e = this.pat.charCodeAt(p + 1)
|
|
168
|
+
let cont = 1
|
|
169
|
+
while (++s < src.length) {
|
|
170
|
+
const c = src.charCodeAt(s)
|
|
171
|
+
if (c === e) {
|
|
172
|
+
if (--cont === 0) return s + 1
|
|
173
|
+
} else if (c === b) cont++
|
|
174
|
+
}
|
|
175
|
+
return -1
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {number} s
|
|
180
|
+
* @param {number} p
|
|
181
|
+
* @param {number} ep
|
|
182
|
+
* @returns {number}
|
|
183
|
+
*/
|
|
184
|
+
maxExpand(s, p, ep) {
|
|
185
|
+
let i = 0
|
|
186
|
+
while (this.singleMatch(s + i, p, ep)) i++
|
|
187
|
+
while (i >= 0) {
|
|
188
|
+
const res = this.match(s + i, ep + 1)
|
|
189
|
+
if (res !== -1) return res
|
|
190
|
+
i--
|
|
191
|
+
}
|
|
192
|
+
return -1
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @param {number} s
|
|
197
|
+
* @param {number} p
|
|
198
|
+
* @param {number} ep
|
|
199
|
+
* @returns {number}
|
|
200
|
+
*/
|
|
201
|
+
minExpand(s, p, ep) {
|
|
202
|
+
for (;;) {
|
|
203
|
+
const res = this.match(s, ep + 1)
|
|
204
|
+
if (res !== -1) return res
|
|
205
|
+
if (this.singleMatch(s, p, ep)) s++
|
|
206
|
+
else return -1
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @param {number} s
|
|
212
|
+
* @param {number} p
|
|
213
|
+
* @param {number} what CAP_UNFINISHED or CAP_POSITION
|
|
214
|
+
* @returns {number}
|
|
215
|
+
*/
|
|
216
|
+
startCapture(s, p, what) {
|
|
217
|
+
if (this.level >= MAX_CAPTURES) throw fail('too many captures')
|
|
218
|
+
this.capInit[this.level] = s
|
|
219
|
+
this.capLen[this.level] = what
|
|
220
|
+
this.level++
|
|
221
|
+
const res = this.match(s, p)
|
|
222
|
+
if (res === -1) this.level--
|
|
223
|
+
return res
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @param {number} s
|
|
228
|
+
* @param {number} p
|
|
229
|
+
* @returns {number}
|
|
230
|
+
*/
|
|
231
|
+
endCapture(s, p) {
|
|
232
|
+
let l = this.level - 1
|
|
233
|
+
while (l >= 0 && this.capLen[l] !== CAP_UNFINISHED) l--
|
|
234
|
+
if (l < 0) throw fail('invalid pattern capture')
|
|
235
|
+
this.capLen[l] = s - this.capInit[l]
|
|
236
|
+
const res = this.match(s, p)
|
|
237
|
+
if (res === -1) this.capLen[l] = CAP_UNFINISHED
|
|
238
|
+
return res
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @param {number} s
|
|
243
|
+
* @param {number} digit the char code of the digit after `%`
|
|
244
|
+
* @returns {number}
|
|
245
|
+
*/
|
|
246
|
+
matchCapture(s, digit) {
|
|
247
|
+
const l = digit - 49
|
|
248
|
+
if (l < 0 || l >= this.level || this.capLen[l] === CAP_UNFINISHED) throw fail('invalid capture index %' + (l + 1))
|
|
249
|
+
const len = this.capLen[l]
|
|
250
|
+
const src = this.src
|
|
251
|
+
if (src.length - s >= len && src.substr(this.capInit[l], len) === src.substr(s, len)) return s + len
|
|
252
|
+
return -1
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Tries to match the pattern from `p` at `s`.
|
|
257
|
+
*
|
|
258
|
+
* @param {number} s
|
|
259
|
+
* @param {number} p
|
|
260
|
+
* @returns {number} the index after the match, or -1
|
|
261
|
+
*/
|
|
262
|
+
match(s, p) {
|
|
263
|
+
if (++this.depth > MAX_DEPTH) throw fail('pattern too complex')
|
|
264
|
+
try {
|
|
265
|
+
const pat = this.pat
|
|
266
|
+
for (;;) {
|
|
267
|
+
if (p >= pat.length) return s
|
|
268
|
+
const pc = pat.charCodeAt(p)
|
|
269
|
+
let plain = false
|
|
270
|
+
switch (pc) {
|
|
271
|
+
case 40: // (
|
|
272
|
+
if (pat.charCodeAt(p + 1) === 41) return this.startCapture(s, p + 2, CAP_POSITION)
|
|
273
|
+
return this.startCapture(s, p + 1, CAP_UNFINISHED)
|
|
274
|
+
case 41: // )
|
|
275
|
+
return this.endCapture(s, p + 1)
|
|
276
|
+
case 36: // $
|
|
277
|
+
if (p + 1 === pat.length) return s === this.src.length ? s : -1
|
|
278
|
+
plain = true
|
|
279
|
+
break
|
|
280
|
+
case 37: { // %
|
|
281
|
+
const next = pat.charCodeAt(p + 1)
|
|
282
|
+
if (next === 98) { // b
|
|
283
|
+
s = this.matchBalance(s, p + 2)
|
|
284
|
+
if (s !== -1) {
|
|
285
|
+
p += 4
|
|
286
|
+
continue
|
|
287
|
+
}
|
|
288
|
+
return -1
|
|
289
|
+
}
|
|
290
|
+
if (next === 102) { // f
|
|
291
|
+
p += 2
|
|
292
|
+
if (pat.charCodeAt(p) !== 91) throw fail("missing '[' after '%f' in pattern")
|
|
293
|
+
const ep = this.classEnd(p)
|
|
294
|
+
const prev = s === 0 ? 0 : this.src.charCodeAt(s - 1)
|
|
295
|
+
const cur = s < this.src.length ? this.src.charCodeAt(s) : 0
|
|
296
|
+
if (!this.matchBracketClass(prev, p, ep - 1) && this.matchBracketClass(cur, p, ep - 1)) {
|
|
297
|
+
p = ep
|
|
298
|
+
continue
|
|
299
|
+
}
|
|
300
|
+
return -1
|
|
301
|
+
}
|
|
302
|
+
if (next >= 48 && next <= 57) {
|
|
303
|
+
s = this.matchCapture(s, next)
|
|
304
|
+
if (s !== -1) {
|
|
305
|
+
p += 2
|
|
306
|
+
continue
|
|
307
|
+
}
|
|
308
|
+
return -1
|
|
309
|
+
}
|
|
310
|
+
plain = true
|
|
311
|
+
break
|
|
312
|
+
}
|
|
313
|
+
default:
|
|
314
|
+
plain = true
|
|
315
|
+
}
|
|
316
|
+
if (!plain) return -1
|
|
317
|
+
const ep = this.classEnd(p)
|
|
318
|
+
const epc = pat.charCodeAt(ep)
|
|
319
|
+
if (!this.singleMatch(s, p, ep)) {
|
|
320
|
+
if (epc === 42 || epc === 63 || epc === 45) { // * ? -
|
|
321
|
+
p = ep + 1
|
|
322
|
+
continue
|
|
323
|
+
}
|
|
324
|
+
return -1
|
|
325
|
+
}
|
|
326
|
+
if (epc === 63) { // ?
|
|
327
|
+
const res = this.match(s + 1, ep + 1)
|
|
328
|
+
if (res !== -1) return res
|
|
329
|
+
p = ep + 1
|
|
330
|
+
continue
|
|
331
|
+
}
|
|
332
|
+
if (epc === 43) return this.maxExpand(s + 1, p, ep) // +
|
|
333
|
+
if (epc === 42) return this.maxExpand(s, p, ep) // *
|
|
334
|
+
if (epc === 45) return this.minExpand(s, p, ep) // -
|
|
335
|
+
s++
|
|
336
|
+
p = ep
|
|
337
|
+
}
|
|
338
|
+
} finally {
|
|
339
|
+
this.depth--
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Capture `i` of the last match, which spanned `s` to `e`.
|
|
345
|
+
*
|
|
346
|
+
* @param {number} i
|
|
347
|
+
* @param {number} s
|
|
348
|
+
* @param {number} e
|
|
349
|
+
* @returns {string | number}
|
|
350
|
+
*/
|
|
351
|
+
capture(i, s, e) {
|
|
352
|
+
if (i >= this.level) {
|
|
353
|
+
if (i === 0) return this.src.substring(s, e)
|
|
354
|
+
throw fail('invalid capture index')
|
|
355
|
+
}
|
|
356
|
+
const l = this.capLen[i]
|
|
357
|
+
if (l === CAP_UNFINISHED) throw fail('unfinished capture')
|
|
358
|
+
if (l === CAP_POSITION) return this.capInit[i] + 1
|
|
359
|
+
return this.src.substr(this.capInit[i], l)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* All the captures of the last match, or the whole match when the pattern has none.
|
|
364
|
+
*
|
|
365
|
+
* @param {number} s
|
|
366
|
+
* @param {number} e
|
|
367
|
+
* @param {boolean} wholeIfNone
|
|
368
|
+
* @returns {(string | number)[]}
|
|
369
|
+
*/
|
|
370
|
+
captures(s, e, wholeIfNone) {
|
|
371
|
+
const n = this.level === 0 && wholeIfNone ? 1 : this.level
|
|
372
|
+
const out = []
|
|
373
|
+
for (let i = 0; i < n; i++) out.push(this.capture(i, s, e))
|
|
374
|
+
return out
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Search
|
|
379
|
+
// ■■■■■■
|
|
380
|
+
|
|
381
|
+
/** Pattern characters that make a pattern more than a literal */
|
|
382
|
+
const SPECIALS = /[\^$*+.?()[\]%-]/
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* `string.find` and `string.match`.
|
|
386
|
+
*
|
|
387
|
+
* @param {string} s
|
|
388
|
+
* @param {string} pat
|
|
389
|
+
* @param {number} init 1-based start, negative counts from the end
|
|
390
|
+
* @param {boolean} plain search for pat as text
|
|
391
|
+
* @param {boolean} isFind
|
|
392
|
+
* @returns {any[] | undefined} the results, or undefined for nil
|
|
393
|
+
*/
|
|
394
|
+
export function find(s, pat, init, plain, isFind) {
|
|
395
|
+
const len = s.length
|
|
396
|
+
let start = init > 0 ? init - 1 : init === 0 ? 0 : Math.max(len + init, 0)
|
|
397
|
+
if (start > len) return undefined
|
|
398
|
+
if (isFind && (plain || !SPECIALS.test(pat))) {
|
|
399
|
+
const at = s.indexOf(pat, start)
|
|
400
|
+
return at < 0 ? undefined : [ at + 1, at + pat.length ]
|
|
401
|
+
}
|
|
402
|
+
const anchor = pat.charCodeAt(0) === 94
|
|
403
|
+
const ms = new MatchState(s, pat)
|
|
404
|
+
const p = anchor ? 1 : 0
|
|
405
|
+
do {
|
|
406
|
+
ms.reset()
|
|
407
|
+
const e = ms.match(start, p)
|
|
408
|
+
if (e !== -1) {
|
|
409
|
+
if (isFind) return /** @type {any[]} */ ([ start + 1, e ]).concat(ms.captures(start, e, false))
|
|
410
|
+
return ms.captures(start, e, true)
|
|
411
|
+
}
|
|
412
|
+
start++
|
|
413
|
+
} while (start <= len && !anchor)
|
|
414
|
+
return undefined
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* The iterator behind `string.gmatch`.
|
|
419
|
+
*
|
|
420
|
+
* @param {string} s
|
|
421
|
+
* @param {string} pat
|
|
422
|
+
* @returns {() => (string | number)[] | undefined} each call gives the next match's captures, then undefined
|
|
423
|
+
*/
|
|
424
|
+
export function gmatch(s, pat) {
|
|
425
|
+
const ms = new MatchState(s, pat)
|
|
426
|
+
let pos = 0
|
|
427
|
+
return function () {
|
|
428
|
+
for (let start = pos; start <= s.length; start++) {
|
|
429
|
+
ms.reset()
|
|
430
|
+
const e = ms.match(start, 0)
|
|
431
|
+
if (e !== -1) {
|
|
432
|
+
pos = e === start ? e + 1 : e
|
|
433
|
+
return ms.captures(start, e, true)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
pos = s.length + 1
|
|
437
|
+
return undefined
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* `string.gsub`.
|
|
443
|
+
*
|
|
444
|
+
* @param {string} s
|
|
445
|
+
* @param {string} pat
|
|
446
|
+
* @param {(captures: (string | number)[]) => string | number | boolean | undefined} replace the replacement for a match, given its captures; false or undefined keeps the match
|
|
447
|
+
* @param {string | null} template a replacement string with `%1` escapes, or null to use `replace`
|
|
448
|
+
* @param {number} max
|
|
449
|
+
* @returns {[string, number]}
|
|
450
|
+
*/
|
|
451
|
+
export function gsub(s, pat, replace, template, max) {
|
|
452
|
+
const anchor = pat.charCodeAt(0) === 94
|
|
453
|
+
const p = anchor ? 1 : 0
|
|
454
|
+
const ms = new MatchState(s, pat)
|
|
455
|
+
let out = ''
|
|
456
|
+
let pos = 0
|
|
457
|
+
let n = 0
|
|
458
|
+
while (n < max) {
|
|
459
|
+
ms.reset()
|
|
460
|
+
const e = ms.match(pos, p)
|
|
461
|
+
if (e !== -1) {
|
|
462
|
+
n++
|
|
463
|
+
const whole = s.substring(pos, e)
|
|
464
|
+
if (template !== null) out += expand(ms, template, pos, e)
|
|
465
|
+
else {
|
|
466
|
+
const r = replace(ms.captures(pos, e, true))
|
|
467
|
+
if (r === undefined || r === false) out += whole
|
|
468
|
+
else out += r
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (e !== -1 && e > pos) pos = e
|
|
472
|
+
else if (pos < s.length) out += s.charAt(pos++)
|
|
473
|
+
else break
|
|
474
|
+
if (anchor) break
|
|
475
|
+
}
|
|
476
|
+
return [ out + s.substring(pos), n ]
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* A replacement string with its `%` escapes filled in.
|
|
481
|
+
*
|
|
482
|
+
* @param {MatchState} ms
|
|
483
|
+
* @param {string} template
|
|
484
|
+
* @param {number} s
|
|
485
|
+
* @param {number} e
|
|
486
|
+
* @returns {string}
|
|
487
|
+
*/
|
|
488
|
+
function expand(ms, template, s, e) {
|
|
489
|
+
let out = ''
|
|
490
|
+
for (let i = 0; i < template.length; i++) {
|
|
491
|
+
const c = template.charAt(i)
|
|
492
|
+
if (c !== '%') {
|
|
493
|
+
out += c
|
|
494
|
+
continue
|
|
495
|
+
}
|
|
496
|
+
i++
|
|
497
|
+
const d = template.charAt(i)
|
|
498
|
+
if (d === '%') out += '%'
|
|
499
|
+
else if (d >= '0' && d <= '9') {
|
|
500
|
+
const cap = d === '0' ? ms.src.substring(s, e) : ms.capture(d.charCodeAt(0) - 49, s, e)
|
|
501
|
+
out += cap
|
|
502
|
+
} else throw fail("invalid use of '%' in replacement string")
|
|
503
|
+
}
|
|
504
|
+
return out
|
|
505
|
+
}
|