goscript 0.0.25 → 0.0.28
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/README.md +4 -4
- package/cmd/goscript/cmd_compile.go +0 -3
- package/cmd/goscript/deps.go +11 -0
- package/compiler/analysis.go +259 -55
- package/compiler/assignment.go +2 -2
- package/compiler/builtin_test.go +1 -1
- package/compiler/compiler.go +201 -49
- package/compiler/compiler_test.go +53 -0
- package/compiler/composite-lit.go +32 -8
- package/compiler/decl.go +6 -6
- package/compiler/expr-call.go +83 -0
- package/compiler/expr.go +1 -1
- package/compiler/protobuf.go +557 -0
- package/compiler/spec-struct.go +4 -0
- package/compiler/spec-value.go +11 -3
- package/compiler/spec.go +18 -1
- package/compiler/stmt-assign.go +35 -0
- package/compiler/type-assert.go +87 -0
- package/compiler/type.go +5 -2
- package/dist/gs/builtin/builtin.d.ts +19 -1
- package/dist/gs/builtin/builtin.js +85 -5
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +59 -26
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/cmp/index.js.map +1 -1
- package/dist/gs/context/context.d.ts +1 -1
- package/dist/gs/context/context.js +20 -11
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/errors/errors.d.ts +7 -0
- package/dist/gs/errors/errors.js +190 -0
- package/dist/gs/errors/errors.js.map +1 -0
- package/dist/gs/errors/index.d.ts +1 -0
- package/dist/gs/errors/index.js +2 -0
- package/dist/gs/errors/index.js.map +1 -0
- package/dist/gs/internal/goarch/index.js +1 -1
- package/dist/gs/internal/goarch/index.js.map +1 -1
- package/dist/gs/io/index.d.ts +1 -0
- package/dist/gs/io/index.js +2 -0
- package/dist/gs/io/index.js.map +1 -0
- package/dist/gs/io/io.d.ts +107 -0
- package/dist/gs/io/io.js +385 -0
- package/dist/gs/io/io.js.map +1 -0
- package/dist/gs/iter/iter.js.map +1 -1
- package/dist/gs/math/bits/index.js +34 -32
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -0
- package/dist/gs/runtime/runtime.js +15 -18
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +1 -1
- package/dist/gs/slices/slices.js +1 -1
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/strings/builder.d.ts +18 -0
- package/dist/gs/strings/builder.js +205 -0
- package/dist/gs/strings/builder.js.map +1 -0
- package/dist/gs/strings/clone.d.ts +1 -0
- package/dist/gs/strings/clone.js +16 -0
- package/dist/gs/strings/clone.js.map +1 -0
- package/dist/gs/strings/compare.d.ts +1 -0
- package/dist/gs/strings/compare.js +14 -0
- package/dist/gs/strings/compare.js.map +1 -0
- package/dist/gs/strings/index.d.ts +2 -0
- package/dist/gs/strings/index.js +3 -0
- package/dist/gs/strings/index.js.map +1 -0
- package/dist/gs/strings/iter.d.ts +8 -0
- package/dist/gs/strings/iter.js +160 -0
- package/dist/gs/strings/iter.js.map +1 -0
- package/dist/gs/strings/reader.d.ts +34 -0
- package/dist/gs/strings/reader.js +418 -0
- package/dist/gs/strings/reader.js.map +1 -0
- package/dist/gs/strings/replace.d.ts +106 -0
- package/dist/gs/strings/replace.js +1136 -0
- package/dist/gs/strings/replace.js.map +1 -0
- package/dist/gs/strings/search.d.ts +24 -0
- package/dist/gs/strings/search.js +169 -0
- package/dist/gs/strings/search.js.map +1 -0
- package/dist/gs/strings/strings.d.ts +47 -0
- package/dist/gs/strings/strings.js +418 -0
- package/dist/gs/strings/strings.js.map +1 -0
- package/dist/gs/stringslite/index.d.ts +1 -0
- package/dist/gs/stringslite/index.js +2 -0
- package/dist/gs/stringslite/index.js.map +1 -0
- package/dist/gs/stringslite/strings.d.ts +11 -0
- package/dist/gs/stringslite/strings.js +67 -0
- package/dist/gs/stringslite/strings.js.map +1 -0
- package/dist/gs/sync/index.d.ts +1 -0
- package/dist/gs/sync/index.js +2 -0
- package/dist/gs/sync/index.js.map +1 -0
- package/dist/gs/sync/sync.d.ts +79 -0
- package/dist/gs/sync/sync.js +392 -0
- package/dist/gs/sync/sync.js.map +1 -0
- package/dist/gs/time/time.js +7 -7
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/index.d.ts +1 -0
- package/dist/gs/unicode/index.js +2 -0
- package/dist/gs/unicode/index.js.map +1 -0
- package/dist/gs/unicode/unicode.d.ts +105 -0
- package/dist/gs/unicode/unicode.js +332 -0
- package/dist/gs/unicode/unicode.js.map +1 -0
- package/dist/gs/unicode/utf8/index.d.ts +1 -0
- package/dist/gs/unicode/utf8/index.js +3 -0
- package/dist/gs/unicode/utf8/index.js.map +1 -0
- package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
- package/dist/gs/unicode/utf8/utf8.js +196 -0
- package/dist/gs/unicode/utf8/utf8.js.map +1 -0
- package/dist/gs/unsafe/index.d.ts +1 -0
- package/dist/gs/unsafe/index.js +2 -0
- package/dist/gs/unsafe/index.js.map +1 -0
- package/dist/gs/unsafe/unsafe.d.ts +11 -0
- package/dist/gs/unsafe/unsafe.js +44 -0
- package/dist/gs/unsafe/unsafe.js.map +1 -0
- package/go.mod +2 -1
- package/go.sum +6 -2
- package/gs/README.md +6 -0
- package/gs/builtin/builtin.ts +158 -0
- package/gs/builtin/channel.ts +683 -0
- package/gs/builtin/defer.ts +58 -0
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/io.ts +22 -0
- package/gs/builtin/map.ts +50 -0
- package/gs/builtin/slice.ts +1030 -0
- package/gs/builtin/type.ts +1106 -0
- package/gs/builtin/varRef.ts +25 -0
- package/gs/cmp/godoc.txt +8 -0
- package/gs/cmp/index.ts +29 -0
- package/gs/context/context.ts +401 -0
- package/gs/context/godoc.txt +69 -0
- package/gs/context/index.ts +1 -0
- package/gs/errors/errors.ts +223 -0
- package/gs/errors/godoc.txt +63 -0
- package/gs/errors/index.ts +1 -0
- package/gs/internal/goarch/godoc.txt +39 -0
- package/gs/internal/goarch/index.ts +18 -0
- package/gs/io/godoc.txt +61 -0
- package/gs/io/index.ts +1 -0
- package/gs/io/io.go +75 -0
- package/gs/io/io.ts +546 -0
- package/gs/iter/godoc.txt +203 -0
- package/gs/iter/index.ts +1 -0
- package/gs/iter/iter.ts +117 -0
- package/gs/math/bits/index.ts +356 -0
- package/gs/math/godoc.txt +76 -0
- package/gs/runtime/godoc.txt +331 -0
- package/gs/runtime/index.ts +1 -0
- package/gs/runtime/runtime.ts +178 -0
- package/gs/slices/godoc.txt +44 -0
- package/gs/slices/index.ts +1 -0
- package/gs/slices/slices.ts +22 -0
- package/gs/strings/builder.test.ts +121 -0
- package/gs/strings/builder.ts +223 -0
- package/gs/strings/clone.test.ts +43 -0
- package/gs/strings/clone.ts +17 -0
- package/gs/strings/compare.test.ts +84 -0
- package/gs/strings/compare.ts +13 -0
- package/gs/strings/godoc.txt +66 -0
- package/gs/strings/index.ts +2 -0
- package/gs/strings/iter.test.ts +343 -0
- package/gs/strings/iter.ts +171 -0
- package/gs/strings/reader.test.ts +243 -0
- package/gs/strings/reader.ts +451 -0
- package/gs/strings/replace.test.ts +181 -0
- package/gs/strings/replace.ts +1310 -0
- package/gs/strings/search.test.ts +214 -0
- package/gs/strings/search.ts +213 -0
- package/gs/strings/strings.test.ts +477 -0
- package/gs/strings/strings.ts +510 -0
- package/gs/stringslite/godoc.txt +17 -0
- package/gs/stringslite/index.ts +1 -0
- package/gs/stringslite/strings.ts +82 -0
- package/gs/sync/godoc.txt +21 -0
- package/gs/sync/index.ts +1 -0
- package/gs/sync/sync.go +64 -0
- package/gs/sync/sync.ts +449 -0
- package/gs/time/godoc.md +116 -0
- package/gs/time/godoc.txt +116 -0
- package/gs/time/index.ts +1 -0
- package/gs/time/time.ts +272 -0
- package/gs/unicode/godoc.txt +52 -0
- package/gs/unicode/index.ts +1 -0
- package/gs/unicode/unicode.go +38 -0
- package/gs/unicode/unicode.ts +418 -0
- package/gs/unicode/utf8/godoc.txt +22 -0
- package/gs/unicode/utf8/index.ts +2 -0
- package/gs/unicode/utf8/utf8.ts +227 -0
- package/gs/unsafe/godoc.txt +19 -0
- package/gs/unsafe/index.ts +1 -0
- package/gs/unsafe/unsafe.test.ts +68 -0
- package/gs/unsafe/unsafe.ts +77 -0
- package/package.json +6 -4
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import * as $ from '@goscript/builtin/builtin.js'
|
|
3
|
+
import {
|
|
4
|
+
Lines,
|
|
5
|
+
SplitSeq,
|
|
6
|
+
SplitAfterSeq,
|
|
7
|
+
FieldsSeq,
|
|
8
|
+
FieldsFuncSeq,
|
|
9
|
+
} from './iter.js'
|
|
10
|
+
|
|
11
|
+
describe('strings/iter', () => {
|
|
12
|
+
describe('Lines', () => {
|
|
13
|
+
it('should yield lines with newlines', () => {
|
|
14
|
+
const lines: string[] = []
|
|
15
|
+
const seq = Lines('line1\nline2\nline3')
|
|
16
|
+
seq((line: string) => {
|
|
17
|
+
lines.push(line)
|
|
18
|
+
return true
|
|
19
|
+
})
|
|
20
|
+
expect(lines).toEqual(['line1\n', 'line2\n', 'line3'])
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should handle empty string', () => {
|
|
24
|
+
const lines: string[] = []
|
|
25
|
+
const seq = Lines('')
|
|
26
|
+
seq((line: string) => {
|
|
27
|
+
lines.push(line)
|
|
28
|
+
return true
|
|
29
|
+
})
|
|
30
|
+
expect(lines).toEqual([])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should handle string without final newline', () => {
|
|
34
|
+
const lines: string[] = []
|
|
35
|
+
const seq = Lines('line1\nline2')
|
|
36
|
+
seq((line: string) => {
|
|
37
|
+
lines.push(line)
|
|
38
|
+
return true
|
|
39
|
+
})
|
|
40
|
+
expect(lines).toEqual(['line1\n', 'line2'])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should handle single line', () => {
|
|
44
|
+
const lines: string[] = []
|
|
45
|
+
const seq = Lines('single line')
|
|
46
|
+
seq((line: string) => {
|
|
47
|
+
lines.push(line)
|
|
48
|
+
return true
|
|
49
|
+
})
|
|
50
|
+
expect(lines).toEqual(['single line'])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should handle early termination', () => {
|
|
54
|
+
const lines: string[] = []
|
|
55
|
+
const seq = Lines('line1\nline2\nline3\n')
|
|
56
|
+
seq((line: string) => {
|
|
57
|
+
lines.push(line)
|
|
58
|
+
return lines.length < 2 // Stop after 2 lines
|
|
59
|
+
})
|
|
60
|
+
expect(lines).toEqual(['line1\n', 'line2\n'])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should handle multiple consecutive newlines', () => {
|
|
64
|
+
const lines: string[] = []
|
|
65
|
+
const seq = Lines('line1\n\nline3\n')
|
|
66
|
+
seq((line: string) => {
|
|
67
|
+
lines.push(line)
|
|
68
|
+
return true
|
|
69
|
+
})
|
|
70
|
+
expect(lines).toEqual(['line1\n', '\n', 'line3\n'])
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
describe('SplitSeq', () => {
|
|
75
|
+
it('should split by separator', () => {
|
|
76
|
+
const parts: string[] = []
|
|
77
|
+
const seq = SplitSeq('a,b,c', ',')
|
|
78
|
+
seq((part: string) => {
|
|
79
|
+
parts.push(part)
|
|
80
|
+
return true
|
|
81
|
+
})
|
|
82
|
+
expect(parts).toEqual(['a', 'b', 'c'])
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('should handle empty separator (explode)', () => {
|
|
86
|
+
const parts: string[] = []
|
|
87
|
+
const seq = SplitSeq('abc', '')
|
|
88
|
+
seq((part: string) => {
|
|
89
|
+
parts.push(part)
|
|
90
|
+
return true
|
|
91
|
+
})
|
|
92
|
+
expect(parts).toEqual(['a', 'b', 'c'])
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should handle no separator found', () => {
|
|
96
|
+
const parts: string[] = []
|
|
97
|
+
const seq = SplitSeq('hello', ',')
|
|
98
|
+
seq((part: string) => {
|
|
99
|
+
parts.push(part)
|
|
100
|
+
return true
|
|
101
|
+
})
|
|
102
|
+
expect(parts).toEqual(['hello'])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should handle empty string', () => {
|
|
106
|
+
const parts: string[] = []
|
|
107
|
+
const seq = SplitSeq('', ',')
|
|
108
|
+
seq((part: string) => {
|
|
109
|
+
parts.push(part)
|
|
110
|
+
return true
|
|
111
|
+
})
|
|
112
|
+
expect(parts).toEqual([''])
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should handle early termination', () => {
|
|
116
|
+
const parts: string[] = []
|
|
117
|
+
const seq = SplitSeq('a,b,c,d', ',')
|
|
118
|
+
seq((part: string) => {
|
|
119
|
+
parts.push(part)
|
|
120
|
+
return parts.length < 2
|
|
121
|
+
})
|
|
122
|
+
expect(parts).toEqual(['a', 'b'])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should handle unicode', () => {
|
|
126
|
+
const parts: string[] = []
|
|
127
|
+
const seq = SplitSeq('世界,你好', ',')
|
|
128
|
+
// This test expects an error due to UTF-8 slicing limitations in JavaScript
|
|
129
|
+
expect(() => {
|
|
130
|
+
seq((part: string) => {
|
|
131
|
+
parts.push(part)
|
|
132
|
+
return true
|
|
133
|
+
})
|
|
134
|
+
}).toThrow('Cannot slice string at byte indices')
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
describe('SplitAfterSeq', () => {
|
|
139
|
+
it('should split after separator', () => {
|
|
140
|
+
const parts: string[] = []
|
|
141
|
+
const seq = SplitAfterSeq('a,b,c', ',')
|
|
142
|
+
seq((part: string) => {
|
|
143
|
+
parts.push(part)
|
|
144
|
+
return true
|
|
145
|
+
})
|
|
146
|
+
expect(parts).toEqual(['a,', 'b,', 'c'])
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should handle empty separator (explode)', () => {
|
|
150
|
+
const parts: string[] = []
|
|
151
|
+
const seq = SplitAfterSeq('abc', '')
|
|
152
|
+
seq((part: string) => {
|
|
153
|
+
parts.push(part)
|
|
154
|
+
return true
|
|
155
|
+
})
|
|
156
|
+
expect(parts).toEqual(['a', 'b', 'c'])
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should handle no separator found', () => {
|
|
160
|
+
const parts: string[] = []
|
|
161
|
+
const seq = SplitAfterSeq('hello', ',')
|
|
162
|
+
seq((part: string) => {
|
|
163
|
+
parts.push(part)
|
|
164
|
+
return true
|
|
165
|
+
})
|
|
166
|
+
expect(parts).toEqual(['hello'])
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should handle trailing separator', () => {
|
|
170
|
+
const parts: string[] = []
|
|
171
|
+
const seq = SplitAfterSeq('a,b,', ',')
|
|
172
|
+
seq((part: string) => {
|
|
173
|
+
parts.push(part)
|
|
174
|
+
return true
|
|
175
|
+
})
|
|
176
|
+
expect(parts).toEqual(['a,', 'b,', ''])
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should handle early termination', () => {
|
|
180
|
+
const parts: string[] = []
|
|
181
|
+
const seq = SplitAfterSeq('a,b,c,d', ',')
|
|
182
|
+
seq((part: string) => {
|
|
183
|
+
parts.push(part)
|
|
184
|
+
return parts.length < 2
|
|
185
|
+
})
|
|
186
|
+
expect(parts).toEqual(['a,', 'b,'])
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
describe('FieldsSeq', () => {
|
|
191
|
+
it('should split on whitespace', () => {
|
|
192
|
+
const fields: string[] = []
|
|
193
|
+
const seq = FieldsSeq(' hello world ')
|
|
194
|
+
seq((field: string) => {
|
|
195
|
+
fields.push(field)
|
|
196
|
+
return true
|
|
197
|
+
})
|
|
198
|
+
expect(fields).toEqual(['hello', 'world'])
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('should handle empty string', () => {
|
|
202
|
+
const fields: string[] = []
|
|
203
|
+
const seq = FieldsSeq('')
|
|
204
|
+
seq((field: string) => {
|
|
205
|
+
fields.push(field)
|
|
206
|
+
return true
|
|
207
|
+
})
|
|
208
|
+
expect(fields).toEqual([])
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('should handle only whitespace', () => {
|
|
212
|
+
const fields: string[] = []
|
|
213
|
+
const seq = FieldsSeq(' \t\n ')
|
|
214
|
+
seq((field: string) => {
|
|
215
|
+
fields.push(field)
|
|
216
|
+
return true
|
|
217
|
+
})
|
|
218
|
+
expect(fields).toEqual([])
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
it('should handle single field', () => {
|
|
222
|
+
const fields: string[] = []
|
|
223
|
+
const seq = FieldsSeq('hello')
|
|
224
|
+
seq((field: string) => {
|
|
225
|
+
fields.push(field)
|
|
226
|
+
return true
|
|
227
|
+
})
|
|
228
|
+
expect(fields).toEqual(['hello'])
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('should handle mixed whitespace', () => {
|
|
232
|
+
const fields: string[] = []
|
|
233
|
+
const seq = FieldsSeq('hello\tworld\ntest')
|
|
234
|
+
seq((field: string) => {
|
|
235
|
+
fields.push(field)
|
|
236
|
+
return true
|
|
237
|
+
})
|
|
238
|
+
expect(fields).toEqual(['hello', 'world', 'test'])
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
it('should handle early termination', () => {
|
|
242
|
+
const fields: string[] = []
|
|
243
|
+
const seq = FieldsSeq('one two three four')
|
|
244
|
+
seq((field: string) => {
|
|
245
|
+
fields.push(field)
|
|
246
|
+
return fields.length < 2
|
|
247
|
+
})
|
|
248
|
+
expect(fields).toEqual(['one', 'two'])
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('should handle unicode whitespace', () => {
|
|
252
|
+
const fields: string[] = []
|
|
253
|
+
const seq = FieldsSeq('hello world') // contains unicode space
|
|
254
|
+
seq((field: string) => {
|
|
255
|
+
fields.push(field)
|
|
256
|
+
return true
|
|
257
|
+
})
|
|
258
|
+
expect(fields).toEqual(['hello', 'world'])
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe('FieldsFuncSeq', () => {
|
|
263
|
+
it('should split using predicate function', () => {
|
|
264
|
+
const fields: string[] = []
|
|
265
|
+
const seq = FieldsFuncSeq('a,b,c', (r: number) => r === 44) // comma
|
|
266
|
+
seq((field: string) => {
|
|
267
|
+
fields.push(field)
|
|
268
|
+
return true
|
|
269
|
+
})
|
|
270
|
+
expect(fields).toEqual(['a', 'b', 'c'])
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('should handle null predicate', () => {
|
|
274
|
+
const fields: string[] = []
|
|
275
|
+
const seq = FieldsFuncSeq('hello world', null)
|
|
276
|
+
seq((field: string) => {
|
|
277
|
+
fields.push(field)
|
|
278
|
+
return true
|
|
279
|
+
})
|
|
280
|
+
expect(fields).toEqual([])
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it('should handle empty string', () => {
|
|
284
|
+
const fields: string[] = []
|
|
285
|
+
const seq = FieldsFuncSeq('', (r: number) => r === 32) // space
|
|
286
|
+
seq((field: string) => {
|
|
287
|
+
fields.push(field)
|
|
288
|
+
return true
|
|
289
|
+
})
|
|
290
|
+
expect(fields).toEqual([])
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('should handle no separator matches', () => {
|
|
294
|
+
const fields: string[] = []
|
|
295
|
+
const seq = FieldsFuncSeq('hello', (r: number) => r === 44) // comma
|
|
296
|
+
seq((field: string) => {
|
|
297
|
+
fields.push(field)
|
|
298
|
+
return true
|
|
299
|
+
})
|
|
300
|
+
expect(fields).toEqual(['hello'])
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('should handle all characters match predicate', () => {
|
|
304
|
+
const fields: string[] = []
|
|
305
|
+
const seq = FieldsFuncSeq('aaa', (r: number) => r === 97) // 'a'
|
|
306
|
+
seq((field: string) => {
|
|
307
|
+
fields.push(field)
|
|
308
|
+
return true
|
|
309
|
+
})
|
|
310
|
+
expect(fields).toEqual([])
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
it('should handle mixed ascii and unicode', () => {
|
|
314
|
+
const fields: string[] = []
|
|
315
|
+
const seq = FieldsFuncSeq('hello世world', (r: number) => r === 0x4e16) // '世'
|
|
316
|
+
seq((field: string) => {
|
|
317
|
+
fields.push(field)
|
|
318
|
+
return true
|
|
319
|
+
})
|
|
320
|
+
expect(fields).toEqual(['hello', 'world'])
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('should handle early termination', () => {
|
|
324
|
+
const fields: string[] = []
|
|
325
|
+
const seq = FieldsFuncSeq('a;b;c;d', (r: number) => r === 59) // semicolon
|
|
326
|
+
seq((field: string) => {
|
|
327
|
+
fields.push(field)
|
|
328
|
+
return fields.length < 2
|
|
329
|
+
})
|
|
330
|
+
expect(fields).toEqual(['a', 'b'])
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it('should handle consecutive separators', () => {
|
|
334
|
+
const fields: string[] = []
|
|
335
|
+
const seq = FieldsFuncSeq('a,,b', (r: number) => r === 44) // comma
|
|
336
|
+
seq((field: string) => {
|
|
337
|
+
fields.push(field)
|
|
338
|
+
return true
|
|
339
|
+
})
|
|
340
|
+
expect(fields).toEqual(['a', 'b'])
|
|
341
|
+
})
|
|
342
|
+
})
|
|
343
|
+
})
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import * as $ from '@goscript/builtin/builtin.js'
|
|
2
|
+
import { Index, IndexByte } from './strings.js'
|
|
3
|
+
|
|
4
|
+
import * as iter from '@goscript/iter/index.js'
|
|
5
|
+
|
|
6
|
+
import * as unicode from '@goscript/unicode/index.js'
|
|
7
|
+
|
|
8
|
+
import * as utf8 from '@goscript/unicode/utf8/index.js'
|
|
9
|
+
|
|
10
|
+
// ASCII space characters lookup map
|
|
11
|
+
const asciiSpace: { [key: number]: boolean } = {
|
|
12
|
+
9: true, // \t
|
|
13
|
+
10: true, // \n
|
|
14
|
+
11: true, // \v
|
|
15
|
+
12: true, // \f
|
|
16
|
+
13: true, // \r
|
|
17
|
+
32: true, // space
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Lines returns an iterator over the newline-terminated lines in the string s.
|
|
21
|
+
// The lines yielded by the iterator include their terminating newlines.
|
|
22
|
+
// If s is empty, the iterator yields no lines at all.
|
|
23
|
+
// If s does not end in a newline, the final yielded line will not end in a newline.
|
|
24
|
+
// It returns a single-use iterator.
|
|
25
|
+
export function Lines(s: string): iter.Seq<string> {
|
|
26
|
+
return (_yield: ((p0: string) => boolean) | null): void => {
|
|
27
|
+
for (; $.len(s) > 0; ) {
|
|
28
|
+
let line: string = ''
|
|
29
|
+
{
|
|
30
|
+
let i = IndexByte(s, 10)
|
|
31
|
+
if (i >= 0) {
|
|
32
|
+
;[line, s] = [
|
|
33
|
+
$.sliceString(s, undefined, i + 1),
|
|
34
|
+
$.sliceString(s, i + 1, undefined),
|
|
35
|
+
]
|
|
36
|
+
} else {
|
|
37
|
+
;[line, s] = [s, '']
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!_yield!(line)) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// explodeSeq returns an iterator over the runes in s.
|
|
49
|
+
export function explodeSeq(s: string): iter.Seq<string> {
|
|
50
|
+
return (_yield: ((p0: string) => boolean) | null): void => {
|
|
51
|
+
for (; $.len(s) > 0; ) {
|
|
52
|
+
let [, size] = utf8.DecodeRuneInString(s)
|
|
53
|
+
if (!_yield!($.sliceString(s, undefined, size))) {
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
s = $.sliceString(s, size, undefined)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// splitSeq is SplitSeq or SplitAfterSeq, configured by how many
|
|
62
|
+
// bytes of sep to include in the results (none or all).
|
|
63
|
+
export function splitSeq(
|
|
64
|
+
s: string,
|
|
65
|
+
sep: string,
|
|
66
|
+
sepSave: number,
|
|
67
|
+
): iter.Seq<string> {
|
|
68
|
+
if ($.len(sep) == 0) {
|
|
69
|
+
return explodeSeq(s)
|
|
70
|
+
}
|
|
71
|
+
return (_yield: ((p0: string) => boolean) | null): void => {
|
|
72
|
+
for (;;) {
|
|
73
|
+
let i = Index(s, sep)
|
|
74
|
+
if (i < 0) {
|
|
75
|
+
break
|
|
76
|
+
}
|
|
77
|
+
let frag = $.sliceString(s, undefined, i + sepSave)
|
|
78
|
+
if (!_yield!(frag)) {
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
s = $.sliceString(s, i + $.len(sep), undefined)
|
|
82
|
+
}
|
|
83
|
+
_yield!(s)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// SplitSeq returns an iterator over all substrings of s separated by sep.
|
|
88
|
+
// The iterator yields the same strings that would be returned by [Split](s, sep),
|
|
89
|
+
// but without constructing the slice.
|
|
90
|
+
// It returns a single-use iterator.
|
|
91
|
+
export function SplitSeq(s: string, sep: string): iter.Seq<string> {
|
|
92
|
+
return splitSeq(s, sep, 0)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// SplitAfterSeq returns an iterator over substrings of s split after each instance of sep.
|
|
96
|
+
// The iterator yields the same strings that would be returned by [SplitAfter](s, sep),
|
|
97
|
+
// but without constructing the slice.
|
|
98
|
+
// It returns a single-use iterator.
|
|
99
|
+
export function SplitAfterSeq(s: string, sep: string): iter.Seq<string> {
|
|
100
|
+
return splitSeq(s, sep, $.len(sep))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// FieldsSeq returns an iterator over substrings of s split around runs of
|
|
104
|
+
// whitespace characters, as defined by [unicode.IsSpace].
|
|
105
|
+
// The iterator yields the same strings that would be returned by [Fields](s),
|
|
106
|
+
// but without constructing the slice.
|
|
107
|
+
export function FieldsSeq(s: string): iter.Seq<string> {
|
|
108
|
+
return (_yield: ((p0: string) => boolean) | null): void => {
|
|
109
|
+
let start = -1
|
|
110
|
+
for (let i = 0; i < $.len(s); ) {
|
|
111
|
+
let size = 1
|
|
112
|
+
let r = $.indexString(s, i) as number
|
|
113
|
+
let isSpace = asciiSpace[$.indexString(s, i)] === true
|
|
114
|
+
if (r >= utf8.RuneSelf) {
|
|
115
|
+
;[r, size] = utf8.DecodeRuneInString($.sliceString(s, i, undefined))
|
|
116
|
+
isSpace = unicode.IsSpace(r)
|
|
117
|
+
}
|
|
118
|
+
if (isSpace) {
|
|
119
|
+
if (start >= 0) {
|
|
120
|
+
if (!_yield!($.sliceString(s, start, i))) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
start = -1
|
|
124
|
+
}
|
|
125
|
+
} else if (start < 0) {
|
|
126
|
+
start = i
|
|
127
|
+
}
|
|
128
|
+
i += size
|
|
129
|
+
}
|
|
130
|
+
if (start >= 0) {
|
|
131
|
+
_yield!($.sliceString(s, start, undefined))
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// FieldsFuncSeq returns an iterator over substrings of s split around runs of
|
|
137
|
+
// Unicode code points satisfying f(c).
|
|
138
|
+
// The iterator yields the same strings that would be returned by [FieldsFunc](s),
|
|
139
|
+
// but without constructing the slice.
|
|
140
|
+
export function FieldsFuncSeq(
|
|
141
|
+
s: string,
|
|
142
|
+
f: ((p0: number) => boolean) | null,
|
|
143
|
+
): iter.Seq<string> {
|
|
144
|
+
return (_yield: ((p0: string) => boolean) | null): void => {
|
|
145
|
+
if (f === null) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
let start = -1
|
|
149
|
+
for (let i = 0; i < $.len(s); ) {
|
|
150
|
+
let size = 1
|
|
151
|
+
let r = $.indexString(s, i) as number
|
|
152
|
+
if (r >= utf8.RuneSelf) {
|
|
153
|
+
;[r, size] = utf8.DecodeRuneInString($.sliceString(s, i, undefined))
|
|
154
|
+
}
|
|
155
|
+
if (f(r)) {
|
|
156
|
+
if (start >= 0) {
|
|
157
|
+
if (!_yield!($.sliceString(s, start, i))) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
start = -1
|
|
161
|
+
}
|
|
162
|
+
} else if (start < 0) {
|
|
163
|
+
start = i
|
|
164
|
+
}
|
|
165
|
+
i += size
|
|
166
|
+
}
|
|
167
|
+
if (start >= 0) {
|
|
168
|
+
_yield!($.sliceString(s, start, undefined))
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|