• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

davidhoo / jsonpath / 25476724141

07 May 2026 04:51AM UTC coverage: 81.149% (+1.0%) from 80.118%
25476724141

push

github

davidhoo
fix: improve CTS pass rate to 85.2% (599/703)

- Fix index validation: reject leading zeros, positive sign, negative zero, out-of-range
- Fix slice validation: same integer validation as indices
- Fix non-singular query detection in filter comparisons
- Fix filter number literal validation
- Fix name selector escape handling (53 tests)
- Fix filter absent/null comparisons

340 of 377 new or added lines in 2 files covered. (90.19%)

7 existing lines in 1 file now uncovered.

3319 of 4090 relevant lines covered (81.15%)

654.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.44
/parser.go
1
package jsonpath
2

3
import (
4
        "fmt"
5
        "regexp"
6
        "strconv"
7
        "strings"
8
)
9

10
// 解析 JSONPath 表达式
11
func parse(path string) ([]segment, error) {
6,134✔
12
        // 处理空路径
6,134✔
13
        if path == "" {
6,134✔
14
                return nil, nil
×
15
        }
×
16

17
        // 检查是否是函数调用格式: functionName(arg1, arg2, ...)
18
        // 这是 RFC 9535 的 match() 和 search() 函数语法
19
        if idx := strings.Index(path, "("); idx > 0 && strings.HasSuffix(path, ")") {
6,250✔
20
                funcName := path[:idx]
116✔
21
                // 验证函数名是有效的标识符
116✔
22
                if isValidFunctionName(funcName) {
148✔
23
                        argsStr := path[idx+1 : len(path)-1]
32✔
24
                        return parseTopLevelFunctionCall(funcName, argsStr)
32✔
25
                }
32✔
26
        }
27

28
        // 检查并移除 $ 前缀
29
        if !strings.HasPrefix(path, "$") {
6,110✔
30
                return nil, NewError(ErrSyntax, "path must start with $", path)
8✔
31
        }
8✔
32
        path = strings.TrimPrefix(path, "$")
6,094✔
33

6,094✔
34
        // 如果路径只有 $,返回空段列表
6,094✔
35
        if path == "" {
6,102✔
36
                return nil, nil
8✔
37
        }
8✔
38

39
        // Reject path that is only whitespace after $ (e.g. "$ ")
40
        if strings.TrimSpace(path) == "" {
6,094✔
41
                return nil, NewError(ErrSyntax, "invalid path: trailing whitespace after $", "$"+path)
8✔
42
        }
8✔
43

44
        // 移除前导点
45
        dotStripped := false
6,078✔
46
        if strings.HasPrefix(path, ".") {
6,828✔
47
                path = path[1:]
750✔
48
                dotStripped = true
750✔
49
        }
750✔
50

51
        // Reject whitespace between dot and name (e.g. "$. a" after stripping $)
52
        // Only applies when a dot was actually stripped from the path
53
        if dotStripped && len(path) > 0 && (path[0] == ' ' || path[0] == '\t' || path[0] == '\n' || path[0] == '\r') {
6,110✔
54
                return nil, NewError(ErrSyntax, "whitespace is not allowed between dot and member name", "$")
32✔
55
        }
32✔
56

57
        // 处理递归下降
58
        if strings.HasPrefix(path, ".") {
6,204✔
59
                return parseRecursive(path[1:])
158✔
60
        }
158✔
61

62
        // 处理常规路径
63
        return parseRegular(path)
5,888✔
64
}
65

66
// isValidFunctionName 检查是否是有效的函数名
67
func isValidFunctionName(name string) bool {
676✔
68
        if name == "" {
676✔
69
                return false
×
70
        }
×
71
        for i, r := range name {
3,612✔
72
                if i == 0 {
3,612✔
73
                        if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || r == '_') {
862✔
74
                                return false
186✔
75
                        }
186✔
76
                } else {
2,260✔
77
                        if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_') {
2,260✔
78
                                return false
×
79
                        }
×
80
                }
81
        }
82
        return true
490✔
83
}
84

85
// parseTopLevelFunctionCall 解析顶层函数调用
86
func parseTopLevelFunctionCall(funcName, argsStr string) ([]segment, error) {
32✔
87
        // 解析参数
32✔
88
        args, err := parseFunctionArgsList(argsStr)
32✔
89
        if err != nil {
32✔
90
                return nil, err
×
91
        }
×
92

93
        // 创建函数段
94
        return []segment{&functionSegment{name: funcName, args: args}}, nil
32✔
95
}
96

97
// parseFunctionArgsList 解析函数参数列表
98
func parseFunctionArgsList(argsStr string) ([]interface{}, error) {
490✔
99
        if strings.TrimSpace(argsStr) == "" {
490✔
100
                return nil, nil
×
101
        }
×
102

103
        var args []interface{}
490✔
104
        var currentArg strings.Builder
490✔
105
        var inQuote bool
490✔
106
        var quoteChar rune
490✔
107
        depth := 0
490✔
108

490✔
109
        for i := 0; i < len(argsStr); i++ {
5,848✔
110
                ch := rune(argsStr[i])
5,358✔
111

5,358✔
112
                switch {
5,358✔
113
                case (ch == '\'' || ch == '"') && !inQuote:
362✔
114
                        // 开始引号
362✔
115
                        inQuote = true
362✔
116
                        quoteChar = ch
362✔
117
                        // 不将引号写入 currentArg
118
                case ch == quoteChar && inQuote:
362✔
119
                        // 结束引号
362✔
120
                        inQuote = false
362✔
121
                        quoteChar = 0
362✔
122
                        // 不将引号写入 currentArg
123
                case ch == '\\' && inQuote && i+1 < len(argsStr):
86✔
124
                        // 处理转义字符
86✔
125
                        nextCh := rune(argsStr[i+1])
86✔
126
                        if nextCh == quoteChar || nextCh == '\\' {
172✔
127
                                // 转义的引号或反斜杠
86✔
128
                                currentArg.WriteRune(nextCh)
86✔
129
                                i++ // 跳过下一个字符
86✔
130
                        } else {
86✔
131
                                // 其他转义序列,保持原样
×
132
                                currentArg.WriteRune(ch)
×
133
                        }
×
134
                case ch == '(' && !inQuote:
72✔
135
                        depth++
72✔
136
                        currentArg.WriteRune(ch)
72✔
137
                case ch == ')' && !inQuote:
72✔
138
                        depth--
72✔
139
                        currentArg.WriteRune(ch)
72✔
140
                case ch == ',' && !inQuote && depth == 0:
412✔
141
                        arg := strings.TrimSpace(currentArg.String())
412✔
142
                        if arg != "" {
824✔
143
                                parsedArg, err := parseSingleFunctionArg(arg)
412✔
144
                                if err != nil {
412✔
145
                                        return nil, err
×
146
                                }
×
147
                                args = append(args, parsedArg)
412✔
148
                        }
149
                        currentArg.Reset()
412✔
150
                default:
3,992✔
151
                        currentArg.WriteRune(ch)
3,992✔
152
                }
153
        }
154

155
        // 处理最后一个参数
156
        arg := strings.TrimSpace(currentArg.String())
490✔
157
        if arg != "" {
980✔
158
                parsedArg, err := parseSingleFunctionArg(arg)
490✔
159
                if err != nil {
490✔
160
                        return nil, err
×
161
                }
×
162
                args = append(args, parsedArg)
490✔
163
        }
164

165
        return args, nil
490✔
166
}
167

168
// parseSingleFunctionArg 解析单个函数参数
169
func parseSingleFunctionArg(arg string) (interface{}, error) {
902✔
170
        arg = strings.TrimSpace(arg)
902✔
171

902✔
172
        // 尝试解析为数字
902✔
173
        if num, err := strconv.ParseFloat(arg, 64); err == nil {
938✔
174
                return num, nil
36✔
175
        }
36✔
176

177
        // 处理布尔值
178
        if arg == "true" {
866✔
179
                return true, nil
×
180
        }
×
181
        if arg == "false" {
866✔
182
                return false, nil
×
183
        }
×
184

185
        // 处理 null
186
        if arg == "null" {
866✔
187
                return nil, nil
×
188
        }
×
189

190
        // 如果以 $ 开头,它是一个路径引用
191
        if strings.HasPrefix(arg, "$") {
914✔
192
                return arg, nil
48✔
193
        }
48✔
194

195
        // 处理 @ 引用(在过滤器上下文中)
196
        if strings.HasPrefix(arg, "@") {
1,276✔
197
                return arg, nil
458✔
198
        }
458✔
199

200
        // 其他情况都作为字符串处理(包括正则表达式模式)
201
        return arg, nil
360✔
202
}
203

204
// 解析递归下降路径
205
func parseRecursive(path string) ([]segment, error) {
172✔
206
        // Reject bare recursive descent: $..
172✔
207
        if path == "" {
182✔
208
                return nil, NewError(ErrSyntax, "bare recursive descent is not allowed", "..")
10✔
209
        }
10✔
210

211
        var segments []segment
162✔
212
        segments = append(segments, &recursiveSegment{})
162✔
213

162✔
214
        // 移除前导点
162✔
215
        path = strings.TrimPrefix(path, ".")
162✔
216

162✔
217
        // 如果还有路径,继续解析
162✔
218
        if path != "" {
324✔
219
                remainingSegments, err := parseRegular(path)
162✔
220
                if err != nil {
164✔
221
                        return nil, err
2✔
222
                }
2✔
223
                segments = append(segments, remainingSegments...)
160✔
224
        }
225

226
        return segments, nil
160✔
227
}
228

229
// 解析常规路径
230
func parseRegular(path string) ([]segment, error) {
6,050✔
231
        var segments []segment
6,050✔
232
        var current string
6,050✔
233
        var inBracket bool
6,050✔
234
        var bracketContent string
6,050✔
235
        var bracketDepth int
6,050✔
236
        afterDot := false
6,050✔
237
        parenDepth := 0
6,050✔
238

6,050✔
239
        // Use rune iteration to properly handle multi-byte UTF-8 characters
6,050✔
240
        for _, r := range path {
79,558✔
241
                switch {
73,508✔
242
                case r == '[':
6,128✔
243
                        if inBracket {
6,576✔
244
                                // Inside bracket, nested brackets are allowed for filter expressions
448✔
245
                                // (e.g., $[?@.list[0]])
448✔
246
                                bracketDepth++
448✔
247
                                bracketContent += string(r)
448✔
248
                        } else {
6,128✔
249
                                if current != "" {
5,918✔
250
                                        seg, err := createDotSegment(current)
238✔
251
                                        if err != nil {
238✔
252
                                                return nil, err
×
253
                                        }
×
254
                                        segments = append(segments, seg)
238✔
255
                                        current = ""
238✔
256
                                }
257
                                inBracket = true
5,680✔
258
                                bracketDepth = 0
5,680✔
259
                                afterDot = false
5,680✔
260
                        }
261

262
                case r == ']':
6,112✔
263
                        if !inBracket {
6,112✔
264
                                return nil, NewError(ErrSyntax, "unexpected closing bracket", path)
×
265
                        }
×
266
                        if bracketDepth > 0 {
6,560✔
267
                                // Closing a nested bracket inside the filter
448✔
268
                                bracketDepth--
448✔
269
                                bracketContent += string(r)
448✔
270
                        } else {
6,112✔
271
                                // Closing the outer bracket
5,664✔
272
                                seg, err := parseBracketSegment(bracketContent)
5,664✔
273
                                if err != nil {
7,976✔
274
                                        return nil, err
2,312✔
275
                                }
2,312✔
276
                                segments = append(segments, seg)
3,352✔
277
                                bracketContent = ""
3,352✔
278
                                inBracket = false
3,352✔
279
                        }
280

281
                case r == '(' && !inBracket:
84✔
282
                        parenDepth++
84✔
283
                        current += string(r)
84✔
284
                        afterDot = false
84✔
285

286
                case r == ')' && !inBracket:
84✔
287
                        parenDepth--
84✔
288
                        current += string(r)
84✔
289
                        afterDot = false
84✔
290

291
                case r == '.' && !inBracket:
188✔
292
                        if current != "" {
326✔
293
                                seg, err := createDotSegment(current)
138✔
294
                                if err != nil {
138✔
295
                                        return nil, err
×
296
                                }
×
297
                                segments = append(segments, seg)
138✔
298
                                current = ""
138✔
299
                        }
300
                        afterDot = true
188✔
301

302
                case (r == ' ' || r == '\t' || r == '\n' || r == '\r') && !inBracket && parenDepth == 0:
152✔
303
                        // RFC 9535: whitespace is allowed between root and dot (e.g. "$ .a")
152✔
304
                        // but NOT between dot and name (e.g. "$. a" is invalid).
152✔
305
                        if afterDot {
152✔
306
                                // Whitespace immediately after dot: invalid
×
307
                                return nil, NewError(ErrSyntax, "whitespace is not allowed between dot and member name", path)
×
308
                        }
×
309
                        if current == "" {
304✔
310
                                // Leading whitespace (e.g. "$ .a"), skip it
152✔
311
                        } else {
152✔
312
                                // Whitespace after a name: flush the name as a segment
×
313
                                seg, err := createDotSegment(current)
×
314
                                if err != nil {
×
315
                                        return nil, err
×
316
                                }
×
317
                                segments = append(segments, seg)
×
318
                                current = ""
×
319
                        }
320

321
                default:
60,760✔
322
                        if inBracket {
119,188✔
323
                                bracketContent += string(r)
58,428✔
324
                        } else {
60,760✔
325
                                current += string(r)
2,332✔
326
                                afterDot = false
2,332✔
327
                        }
2,332✔
328
                }
329
        }
330

331
        // 处理最后一个段
332
        if inBracket {
3,754✔
333
                return nil, NewError(ErrSyntax, "unclosed bracket", path)
16✔
334
        }
16✔
335
        if current != "" {
4,186✔
336
                seg, err := createDotSegment(current)
464✔
337
                if err != nil {
576✔
338
                        return nil, err
112✔
339
                }
112✔
340
                segments = append(segments, seg)
352✔
341
        }
342

343
        return segments, nil
3,610✔
344
}
345

346
// 创建点表示法段
347
func createDotSegment(name string) (segment, error) {
840✔
348
        if name == "*" {
908✔
349
                return &wildcardSegment{}, nil
68✔
350
        }
68✔
351
        // Only validate non-function names (functions are handled by nameSegmentV3.evaluateFunction)
352
        if !strings.Contains(name, "(") && !isValidMemberName(name) {
884✔
353
                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid member name: %s", name), name)
112✔
354
        }
112✔
355
        return &nameSegment{name: name}, nil
660✔
356
}
357

358
// isValidMemberName checks if a name is valid for dot notation per RFC 9535.
359
// member-name-shorthand = name-first *name-char
360
// name-first = %x41-5A / "_" / %x61-7A / %x80-10FFFF  (letter / "_" / non-ASCII)
361
// name-char = name-first / %x30-39  (name-first / digit)
362
func isValidMemberName(name string) bool {
688✔
363
        if name == "" {
688✔
364
                return false
×
365
        }
×
366
        for i, r := range name {
2,502✔
367
                if i == 0 {
2,502✔
368
                        if !((r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' || r >= 0x80) {
704✔
369
                                return false
16✔
370
                        }
16✔
371
                } else {
1,126✔
372
                        if !((r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r >= 0x80) {
1,222✔
373
                                return false
96✔
374
                        }
96✔
375
                }
376
        }
377
        return true
576✔
378
}
379

380
// 解析方括号段
381
func parseBracketSegment(content string) (segment, error) {
5,664✔
382
        // RFC 9535: whitespace is allowed around selectors in brackets
5,664✔
383
        content = strings.TrimSpace(content)
5,664✔
384

5,664✔
385
        // Reject empty brackets: $[]
5,664✔
386
        if content == "" {
5,672✔
387
                return nil, NewError(ErrSyntax, "empty bracket segment", "")
8✔
388
        }
8✔
389

390
        // Reject @ outside of filter expression (must be preceded by ?)
391
        if strings.HasPrefix(content, "@") {
5,664✔
392
                return nil, NewError(ErrSyntax, "@ is only allowed inside filter expressions", content)
8✔
393
        }
8✔
394

395
        // Reject $ outside of filter expression (must be preceded by ?)
396
        if strings.HasPrefix(content, "$") {
5,656✔
397
                return nil, NewError(ErrSyntax, "$ is only allowed inside filter expressions", content)
8✔
398
        }
8✔
399

400
        // Reject space-separated indices: $[0 2]
401
        // After trimming, check if content looks like "0 2" (numbers separated by space)
402
        if strings.Contains(content, " ") && !strings.Contains(content, ",") && !strings.HasPrefix(content, "?") && !strings.HasPrefix(content, "'") && !strings.HasPrefix(content, "\"") {
5,712✔
403
                // Check if it looks like space-separated tokens (not just whitespace in a string)
72✔
404
                parts := strings.Fields(content)
72✔
405
                if len(parts) > 1 {
144✔
406
                        return nil, NewError(ErrSyntax, "space is not a valid separator in bracket selector, use comma", content)
72✔
407
                }
72✔
408
        }
409

410
        // 处理通配符
411
        if content == "*" {
5,608✔
412
                return &wildcardSegment{}, nil
40✔
413
        }
40✔
414

415
        // 处理过滤器表达式
416
        if strings.HasPrefix(content, "?") {
8,660✔
417
                return parseFilterSegment(content[1:])
3,132✔
418
        }
3,132✔
419

420
        // 处理多索引选择或多字段选择
421
        if strings.Contains(content, ",") ||
2,396✔
422
                ((strings.HasPrefix(content, "'") && strings.HasSuffix(content, "'")) && strings.Contains(content[1:len(content)-1], "','")) ||
2,396✔
423
                ((strings.HasPrefix(content, "\"") && strings.HasSuffix(content, "\"")) && strings.Contains(content[1:len(content)-1], "\",\"")) {
2,648✔
424
                return parseMultiIndexSegment(content)
252✔
425
        }
252✔
426

427
        // 处理切片表达式
428
        if strings.Contains(content, ":") {
2,856✔
429
                return parseSliceSegment(content)
712✔
430
        }
712✔
431

432
        // 处理索引或名称
433
        return parseIndexOrName(content)
1,432✔
434
}
435

436
// 标准化过滤器表达式
437
func normalizeFilterExpression(expr string) string {
2,188✔
438
        expr = strings.TrimSpace(expr)
2,188✔
439
        return expr
2,188✔
440
}
2,188✔
441

442
// expressionParser is a recursive descent parser for filter expressions
443
type expressionParser struct {
444
        input string
445
        pos   int
446
}
447

448
// parseFilterExpression parses a filter expression string into an expression tree
449
func parseFilterExpression(input string) (exprNode, error) {
2,200✔
450
        p := &expressionParser{input: input, pos: 0}
2,200✔
451
        node, err := p.parseOr()
2,200✔
452
        if err != nil {
2,710✔
453
                return nil, err
510✔
454
        }
510✔
455
        p.skipSpaces()
1,690✔
456
        if p.pos < len(p.input) {
1,690✔
457
                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("unexpected character at position %d: %c", p.pos, p.input[p.pos]), input)
×
458
        }
×
459
        return node, nil
1,690✔
460
}
461

462
func (p *expressionParser) skipSpaces() {
10,916✔
463
        for p.pos < len(p.input) && p.input[p.pos] == ' ' {
11,172✔
464
                p.pos++
256✔
465
        }
256✔
466
}
467

468
func (p *expressionParser) parseOr() (exprNode, error) {
2,220✔
469
        left, err := p.parseAnd()
2,220✔
470
        if err != nil {
2,730✔
471
                return nil, err
510✔
472
        }
510✔
473

474
        children := []exprNode{left}
1,710✔
475
        for {
3,608✔
476
                p.skipSpaces()
1,898✔
477
                if p.pos+1 < len(p.input) && p.input[p.pos:p.pos+2] == "||" {
2,086✔
478
                        p.pos += 2
188✔
479
                        right, err := p.parseAnd()
188✔
480
                        if err != nil {
188✔
481
                                return nil, err
×
482
                        }
×
483
                        children = append(children, right)
188✔
484
                } else {
1,710✔
485
                        break
1,710✔
486
                }
487
        }
488

489
        if len(children) == 1 {
3,274✔
490
                return children[0], nil
1,564✔
491
        }
1,564✔
492
        return &orNode{children: children}, nil
146✔
493
}
494

495
func (p *expressionParser) parseAnd() (exprNode, error) {
2,408✔
496
        left, err := p.parseUnary()
2,408✔
497
        if err != nil {
2,918✔
498
                return nil, err
510✔
499
        }
510✔
500

501
        children := []exprNode{left}
1,898✔
502
        for {
3,994✔
503
                p.skipSpaces()
2,096✔
504
                if p.pos+1 < len(p.input) && p.input[p.pos:p.pos+2] == "&&" {
2,294✔
505
                        p.pos += 2
198✔
506
                        right, err := p.parseUnary()
198✔
507
                        if err != nil {
198✔
508
                                return nil, err
×
509
                        }
×
510
                        children = append(children, right)
198✔
511
                } else {
1,898✔
512
                        break
1,898✔
513
                }
514
        }
515

516
        if len(children) == 1 {
3,640✔
517
                return children[0], nil
1,742✔
518
        }
1,742✔
519
        return &andNode{children: children}, nil
156✔
520
}
521

522
func (p *expressionParser) parseUnary() (exprNode, error) {
2,606✔
523
        p.skipSpaces()
2,606✔
524
        if p.pos < len(p.input) && p.input[p.pos] == '!' {
2,734✔
525
                p.pos++
128✔
526
                inner, err := p.parsePrimary()
128✔
527
                if err != nil {
152✔
528
                        return nil, err
24✔
529
                }
24✔
530
                return negateNode(inner)
104✔
531
        }
532
        return p.parsePrimary()
2,478✔
533
}
534

535
func (p *expressionParser) parsePrimary() (exprNode, error) {
2,606✔
536
        p.skipSpaces()
2,606✔
537

2,606✔
538
        if p.pos >= len(p.input) {
2,606✔
539
                return nil, NewError(ErrInvalidFilter, "unexpected end of expression", p.input)
×
540
        }
×
541

542
        // Handle parenthesized expression
543
        if p.input[p.pos] == '(' {
2,626✔
544
                p.pos++ // skip '('
20✔
545
                node, err := p.parseOr()
20✔
546
                if err != nil {
20✔
547
                        return nil, err
×
548
                }
×
549
                p.skipSpaces()
20✔
550
                if p.pos >= len(p.input) || p.input[p.pos] != ')' {
20✔
551
                        return nil, NewError(ErrInvalidFilter, "missing closing parenthesis", p.input)
×
552
                }
×
553
                p.pos++ // skip ')'
20✔
554
                return node, nil
20✔
555
        }
556

557
        // Parse atomic condition (everything until next &&, ||, or unmatched ))
558
        start := p.pos
2,586✔
559
        depth := 0
2,586✔
560
        inQuotes := false
2,586✔
561
        inSingleQuotes := false
2,586✔
562

2,586✔
563
        for p.pos < len(p.input) {
22,300✔
564
                ch := p.input[p.pos]
19,714✔
565

19,714✔
566
                if ch == '"' && !inSingleQuotes {
20,006✔
567
                        inQuotes = !inQuotes
292✔
568
                        p.pos++
292✔
569
                        continue
292✔
570
                }
571
                if ch == '\'' && !inQuotes {
20,198✔
572
                        inSingleQuotes = !inSingleQuotes
776✔
573
                        p.pos++
776✔
574
                        continue
776✔
575
                }
576

577
                if inQuotes || inSingleQuotes {
19,842✔
578
                        p.pos++
1,196✔
579
                        continue
1,196✔
580
                }
581

582
                if ch == '(' {
17,514✔
583
                        depth++
64✔
584
                        p.pos++
64✔
585
                        continue
64✔
586
                }
587
                if ch == ')' {
17,470✔
588
                        if depth == 0 {
104✔
589
                                break
20✔
590
                        }
591
                        depth--
64✔
592
                        p.pos++
64✔
593
                        continue
64✔
594
                }
595

596
                // Check for top-level && or ||
597
                if depth == 0 && p.pos+1 < len(p.input) {
32,454✔
598
                        op := p.input[p.pos : p.pos+2]
15,152✔
599
                        if op == "&&" || op == "||" {
15,536✔
600
                                break
384✔
601
                        }
602
                }
603

604
                p.pos++
16,918✔
605
        }
606

607
        condStr := strings.TrimSpace(p.input[start:p.pos])
2,586✔
608
        if condStr == "" {
2,586✔
609
                return nil, NewError(ErrInvalidFilter, "empty condition", p.input)
×
610
        }
×
611

612
        cond, err := parseFilterCondition(condStr)
2,586✔
613
        if err != nil {
3,064✔
614
                return nil, err
478✔
615
        }
478✔
616
        return &conditionNode{cond: cond}, nil
2,108✔
617
}
618

619
// negateNode applies negation to an expression node
620
func negateNode(node exprNode) (exprNode, error) {
120✔
621
        switch n := node.(type) {
120✔
622
        case *conditionNode:
118✔
623
                newCond := n.cond
118✔
624
                switch newCond.operator {
118✔
625
                case "==":
22✔
626
                        newCond.operator = "!="
22✔
627
                case "!=":
×
628
                        newCond.operator = "=="
×
629
                case "<":
×
630
                        newCond.operator = ">="
×
631
                case "<=":
×
632
                        newCond.operator = ">"
×
633
                case ">":
2✔
634
                        newCond.operator = "<="
2✔
635
                case ">=":
×
636
                        newCond.operator = "<"
×
637
                case "exists":
62✔
638
                        newCond.operator = "not_exists"
62✔
639
                case "not_exists":
×
640
                        newCond.operator = "exists"
×
641
                default:
32✔
642
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("cannot negate operator: %s", newCond.operator), "")
32✔
643
                }
644
                return &conditionNode{cond: newCond}, nil
86✔
645
        case *andNode:
×
646
                children := make([]exprNode, len(n.children))
×
647
                for i, child := range n.children {
×
648
                        negated, err := negateNode(child)
×
649
                        if err != nil {
×
650
                                return nil, err
×
651
                        }
×
652
                        children[i] = negated
×
653
                }
654
                return &orNode{children: children}, nil
×
655
        case *orNode:
2✔
656
                children := make([]exprNode, len(n.children))
2✔
657
                for i, child := range n.children {
6✔
658
                        negated, err := negateNode(child)
4✔
659
                        if err != nil {
4✔
660
                                return nil, err
×
661
                        }
×
662
                        children[i] = negated
4✔
663
                }
664
                return &andNode{children: children}, nil
2✔
665
        default:
×
666
                return nil, NewError(ErrInvalidFilter, "cannot negate expression", "")
×
667
        }
668
}
669

670
// 解析过滤器表达式
671
func parseFilterSegment(content string) (segment, error) {
3,132✔
672
        // RFC 9535: allow whitespace in filter expressions
3,132✔
673
        content = strings.TrimSpace(content)
3,132✔
674

3,132✔
675
        // 检查是否是函数调用格式: functionName(arg1, arg2)
3,132✔
676
        // 这是 RFC 9535 的 match() 和 search() 函数语法
3,132✔
677
        if idx := strings.Index(content, "("); idx > 0 && strings.HasSuffix(content, ")") {
3,652✔
678
                funcName := content[:idx]
520✔
679
                if isValidFunctionName(funcName) {
946✔
680
                        // 这是一个函数调用,解析为过滤器表达式
426✔
681
                        argsStr := content[idx+1 : len(content)-1]
426✔
682
                        cond, err := parseFilterFunctionCall(funcName, argsStr)
426✔
683
                        if err != nil {
474✔
684
                                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("invalid filter syntax: %s", content), content)
48✔
685
                        }
48✔
686
                        return &filterSegment{expr: &conditionNode{cond: cond}}, nil
378✔
687
                }
688
        }
689

690
        // 检查语法 - 支持 @, $, 和函数调用作为过滤器表达式的开头
691
        trimmed := strings.TrimSpace(content)
2,706✔
692
        if !strings.HasPrefix(trimmed, "@") && !strings.HasPrefix(trimmed, "$") &&
2,706✔
693
                !strings.HasPrefix(trimmed, "(@") && !strings.HasPrefix(trimmed, "($") &&
2,706✔
694
                !strings.HasPrefix(trimmed, "!") && !strings.HasPrefix(trimmed, "(!") &&
2,706✔
695
                !strings.HasPrefix(trimmed, "(") {
3,204✔
696
                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("invalid filter syntax: %s", content), content)
498✔
697
        }
498✔
698

699
        // 取过滤器内容
700
        var filterContent string
2,208✔
701

2,208✔
702
        switch {
2,208✔
703
        case strings.HasPrefix(content, "(!"):
×
704
                if !strings.HasSuffix(content, ")") {
×
705
                        return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
706
                }
×
707
                filterContent = content[2 : len(content)-1]
×
708
                // Apply De Morgan's laws: !(A && B) => !A || !B, !(A || B) => !A && !B
×
709
                expr, err := parseFilterExpression(filterContent)
×
710
                if err != nil {
×
711
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error parsing filter expression: %v", err), content)
×
712
                }
×
713
                negated, err := negateNode(expr)
×
714
                if err != nil {
×
715
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error negating expression: %v", err), content)
×
716
                }
×
717
                return &filterSegment{expr: negated}, nil
×
718
        case strings.HasPrefix(content, "!@"):
32✔
719
                // Keep the ! in the content for the parser to handle as unary operator
32✔
720
                filterContent = content
32✔
721
        case strings.HasPrefix(content, "(@"):
212✔
722
                if !strings.HasSuffix(content, ")") {
220✔
723
                        return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
8✔
724
                }
8✔
725
                filterContent = content[2 : len(content)-1]
204✔
726
        case strings.HasPrefix(content, "($"):
120✔
727
                if !strings.HasSuffix(content, ")") {
120✔
728
                        return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
729
                }
×
730
                filterContent = content[2 : len(content)-1]
120✔
731
        case strings.HasPrefix(content, "@"):
1,710✔
732
                filterContent = content
1,710✔
733
        case strings.HasPrefix(content, "$"):
24✔
734
                filterContent = content
24✔
735
        case strings.HasPrefix(content, "!"):
108✔
736
                // Keep the ! in the content for the parser to handle as unary operator
108✔
737
                filterContent = content
108✔
738
                // Check for !(expr) pattern
108✔
739
                if len(content) > 1 && content[1] == '(' {
120✔
740
                        if !strings.HasSuffix(content, ")") {
12✔
741
                                return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
742
                        }
×
743
                        // Apply De Morgan's laws for !(expr)
744
                        innerContent := content[2 : len(content)-1]
12✔
745
                        expr, err := parseFilterExpression(innerContent)
12✔
746
                        if err != nil {
12✔
747
                                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error parsing filter expression: %v", err), content)
×
748
                        }
×
749
                        negated, err := negateNode(expr)
12✔
750
                        if err != nil {
12✔
751
                                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error negating expression: %v", err), content)
×
752
                        }
×
753
                        return &filterSegment{expr: negated}, nil
12✔
754
                }
755
        case strings.HasPrefix(content, "("):
2✔
756
                if !strings.HasSuffix(content, ")") {
2✔
757
                        return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
758
                }
×
759
                filterContent = content[1 : len(content)-1]
2✔
760
        default:
×
761
                filterContent = content
×
762
        }
763

764
        // 标准化表达式
765
        filterContent = normalizeFilterExpression(filterContent)
2,188✔
766

2,188✔
767
        // 解析表达式为树结构
2,188✔
768
        expr, err := parseFilterExpression(filterContent)
2,188✔
769
        if err != nil {
2,698✔
770
                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error parsing filter expression: %v", err), content)
510✔
771
        }
510✔
772

773
        return &filterSegment{expr: expr}, nil
1,678✔
774
}
775

776
// 解析过滤器条件
777
// isNonSingularQuery checks if a path contains non-singular selectors
778
// (wildcard, slice, multi-index, descendant) which are not allowed in comparisons
779
func isNonSingularQuery(field string) bool {
1,486✔
780
        // Check for wildcard
1,486✔
781
        if strings.Contains(field, "*") {
1,550✔
782
                return true
64✔
783
        }
64✔
784
        // Check for descendant (..)
785
        if strings.Contains(field, "..") {
1,430✔
786
                return true
8✔
787
        }
8✔
788
        // Check for bracket expressions
789
        if strings.Contains(field, "[") {
1,446✔
790
                // Check for slice (:)
32✔
791
                if strings.Contains(field, ":") {
40✔
792
                        return true
8✔
793
                }
8✔
794
                // Check for multi-index (,) - but need to be careful about commas in strings
795
                // Simple check: if there's a comma outside of quotes
796
                inQuotes := false
24✔
797
                for i := 0; i < len(field); i++ {
136✔
798
                        if field[i] == '\'' || field[i] == '"' {
128✔
799
                                inQuotes = !inQuotes
16✔
800
                        } else if field[i] == ',' && !inQuotes {
112✔
NEW
801
                                return true
×
NEW
802
                        }
×
803
                }
804
        }
805
        return false
1,406✔
806
}
807

808
func parseFilterCondition(content string) (filterCondition, error) {
2,586✔
809
        // 检查是否是函数调用格式: functionName(arg1, arg2)
2,586✔
810
        // 这是 RFC 9535 的 match() 和 search() 函数语法
2,586✔
811
        if idx := strings.Index(content, "("); idx > 0 && strings.HasSuffix(content, ")") {
2,626✔
812
                funcName := content[:idx]
40✔
813
                if isValidFunctionName(funcName) {
72✔
814
                        argsStr := content[idx+1 : len(content)-1]
32✔
815
                        return parseFilterFunctionCall(funcName, argsStr)
32✔
816
                }
32✔
817
        }
818

819
        // 确保条件以 @ 或 $ 开头
820
        isRoot := false
2,554✔
821
        if strings.HasPrefix(content, "$") {
2,578✔
822
                isRoot = true
24✔
823
        } else if !strings.HasPrefix(content, "@") {
2,902✔
824
                if strings.HasPrefix(content, ".") {
480✔
825
                        content = "@" + content
132✔
826
                } else {
348✔
827
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid condition: %s", content), content)
216✔
828
                }
216✔
829
        }
830

831
        // 检查是否是旧式方法调用: @.field.match(pattern)
832
        if strings.Contains(content, ".match(") {
2,338✔
833
                parts := strings.Split(content, ".match(")
×
834
                if len(parts) != 2 || !strings.HasSuffix(parts[1], ")") {
×
835
                        return filterCondition{}, NewError(ErrInvalidFilter, "invalid match function syntax", content)
×
836
                }
×
837

838
                field := strings.TrimSpace(parts[0])
×
839
                pattern := strings.TrimSpace(parts[1][:len(parts[1])-1])
×
840

×
841
                // 验证字段格式
×
842
                if !strings.HasPrefix(field, "@") && !strings.HasPrefix(field, "$") {
×
843
                        return filterCondition{}, NewError(ErrInvalidFilter, "filter condition must start with @ or $", content)
×
844
                }
×
845

846
                // 移除引号
847
                pattern = strings.Trim(pattern, "\"'")
×
848

×
849
                fieldIsRoot := strings.HasPrefix(field, "$")
×
850
                return filterCondition{
×
851
                        field:    strings.TrimPrefix(strings.TrimPrefix(field, "@."), "$."),
×
852
                        operator: "match",
×
853
                        value:    pattern,
×
854
                        isRoot:   fieldIsRoot,
×
855
                }, nil
×
856
        }
857

858
        // 查找比较操作符
859
        var operator string
2,338✔
860
        var operatorIndex int
2,338✔
861
        var operatorFound bool
2,338✔
862

2,338✔
863
        // 按长度排序的操作符列表,确保先匹配较长的操作符
2,338✔
864
        operators := []string{"<=", ">=", "==", "!=", "<", ">"}
2,338✔
865
        for _, op := range operators {
12,470✔
866
                idx := strings.Index(content, op)
10,132✔
867
                if idx != -1 {
11,618✔
868
                        // 确保这是一个独立的操作符,不是符串值的一部分
1,486✔
869
                        inQuotes := false
1,486✔
870
                        inParens := 0
1,486✔
871
                        isValid := true
1,486✔
872
                        for i := 0; i < idx; i++ {
6,474✔
873
                                switch content[i] {
4,988✔
874
                                case '"':
×
875
                                        inQuotes = !inQuotes
×
876
                                case '(':
×
877
                                        inParens++
×
878
                                case ')':
×
879
                                        inParens--
×
880
                                }
881
                        }
882
                        if inQuotes || inParens > 0 {
1,486✔
883
                                isValid = false
×
884
                        }
×
885
                        if isValid {
2,972✔
886
                                operator = op
1,486✔
887
                                operatorIndex = idx
1,486✔
888
                                operatorFound = true
1,486✔
889
                                break
1,486✔
890
                        }
891
                }
892
        }
893

894
        if !operatorFound {
3,190✔
895
                // No operator found - validate that this is a valid field path
852✔
896
                field := strings.TrimSpace(content)
852✔
897
                // Check for invalid operator-like characters
852✔
898
                if strings.ContainsAny(field, "=<>!") {
854✔
899
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("no valid operator found in condition: %s", content), content)
2✔
900
                }
2✔
901
                // Strip field prefix (@ or $)
902
                field = strings.TrimPrefix(field, "@.")
850✔
903
                field = strings.TrimPrefix(field, "$.")
850✔
904
                field = strings.TrimPrefix(field, "@")
850✔
905
                field = strings.TrimPrefix(field, "$")
850✔
906
                return filterCondition{
850✔
907
                        field:    field,
850✔
908
                        operator: "exists",
850✔
909
                        value:    nil,
850✔
910
                        isRoot:   isRoot,
850✔
911
                }, nil
850✔
912
        }
913

914
        // 分割路径和值
915
        field := strings.TrimSpace(content[:operatorIndex])
1,486✔
916
        value := strings.TrimSpace(content[operatorIndex+len(operator):])
1,486✔
917

1,486✔
918
        // RFC 9535: non-singular paths are not allowed in comparisons
1,486✔
919
        // Check the left side (field) for non-singular queries
1,486✔
920
        if isNonSingularQuery(field) {
1,566✔
921
                return filterCondition{}, NewError(ErrInvalidFilter, "non-singular query is not allowed in comparison", content)
80✔
922
        }
80✔
923

924
        // 解析值
925
        parsedValue, err := parseFilterValue(value)
1,406✔
926
        if err != nil {
1,586✔
927
                return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid value: %s", value), content)
180✔
928
        }
180✔
929

930
        // Strip field prefix (@ or $)
931
        field = strings.TrimPrefix(field, "@.")
1,226✔
932
        field = strings.TrimPrefix(field, "$.")
1,226✔
933
        field = strings.TrimPrefix(field, "@")
1,226✔
934
        field = strings.TrimPrefix(field, "$")
1,226✔
935

1,226✔
936
        return filterCondition{
1,226✔
937
                field:    field,
1,226✔
938
                operator: operator,
1,226✔
939
                value:    parsedValue,
1,226✔
940
                isRoot:   isRoot,
1,226✔
941
        }, nil
1,226✔
942
}
943

944
// parseFilterFunctionCall 解析过滤器中的函数调用
945
func parseFilterFunctionCall(funcName, argsStr string) (filterCondition, error) {
458✔
946
        // 解析参数
458✔
947
        args, err := parseFunctionArgsList(argsStr)
458✔
948
        if err != nil {
458✔
949
                return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid function arguments: %v", err), funcName+"("+argsStr+")")
×
950
        }
×
951

952
        // 对于 match 和 search 函数,需要两个参数
953
        if funcName == "match" || funcName == "search" {
844✔
954
                if len(args) != 2 {
402✔
955
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("%s() requires exactly 2 arguments", funcName), funcName+"("+argsStr+")")
16✔
956
                }
16✔
957

958
                // 第一个参数是字段路径
959
                field, ok := args[0].(string)
370✔
960
                if !ok {
386✔
961
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("%s() first argument must be a string", funcName), funcName+"("+argsStr+")")
16✔
962
                }
16✔
963

964
                // 第二个参数是模式
965
                pattern, ok := args[1].(string)
354✔
966
                if !ok {
370✔
967
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("%s() second argument must be a string", funcName), funcName+"("+argsStr+")")
16✔
968
                }
16✔
969

970
                return filterCondition{
338✔
971
                        field:    strings.TrimPrefix(field, "@."),
338✔
972
                        operator: funcName,
338✔
973
                        value:    pattern,
338✔
974
                }, nil
338✔
975
        }
976

977
        // 对于其他函数,创建一个通用的函数调用条件
978
        return filterCondition{
72✔
979
                field:    "",
72✔
980
                operator: "function:" + funcName,
72✔
981
                value:    args,
72✔
982
        }, nil
72✔
983
}
984

985
// compareValues compares two values based on the operator
986
func compareValues(value1 interface{}, operator string, value2 interface{}) (bool, error) {
3,096✔
987
        // 检查操作符是否有效
3,096✔
988
        validOperators := map[string]bool{
3,096✔
989
                "==":    true,
3,096✔
990
                "!=":    true,
3,096✔
991
                ">":     true,
3,096✔
992
                "<":     true,
3,096✔
993
                ">=":    true,
3,096✔
994
                "<=":    true,
3,096✔
995
                "match": true,
3,096✔
996
        }
3,096✔
997
        if !validOperators[operator] {
3,146✔
998
                return false, fmt.Errorf("invalid operator: %s", operator)
50✔
999
        }
50✔
1000

1001
        // 处理 nil 值
1002
        if value1 == nil || value2 == nil {
3,204✔
1003
                switch operator {
158✔
1004
                case "==":
62✔
1005
                        return value1 == value2, nil
62✔
1006
                case "!=":
32✔
1007
                        return value1 != value2, nil
32✔
1008
                default:
64✔
1009
                        return false, nil
64✔
1010
                }
1011
        }
1012

1013
        // 处理数字类型
1014
        num1, num2, isNum := normalizeNumbers(value1, value2)
2,888✔
1015
        if isNum {
4,454✔
1016
                switch operator {
1,566✔
1017
                case "==":
486✔
1018
                        return num1 == num2, nil
486✔
1019
                case "!=":
152✔
1020
                        return num1 != num2, nil
152✔
1021
                case ">":
242✔
1022
                        return num1 > num2, nil
242✔
1023
                case "<":
238✔
1024
                        return num1 < num2, nil
238✔
1025
                case ">=":
226✔
1026
                        return num1 >= num2, nil
226✔
1027
                case "<=":
222✔
1028
                        return num1 <= num2, nil
222✔
1029
                default:
×
1030
                        return false, fmt.Errorf("invalid operator for numbers: %s", operator)
×
1031
                }
1032
        }
1033

1034
        // 处理字符串类型
1035
        if str1, ok := value1.(string); ok {
2,318✔
1036
                if str2, ok := value2.(string); ok {
1,696✔
1037
                        switch operator {
700✔
1038
                        case "==":
394✔
1039
                                return str1 == str2, nil
394✔
1040
                        case "!=":
140✔
1041
                                return str1 != str2, nil
140✔
1042
                        case ">":
54✔
1043
                                return str1 > str2, nil
54✔
1044
                        case "<":
32✔
1045
                                return str1 < str2, nil
32✔
1046
                        case ">=":
48✔
1047
                                return str1 >= str2, nil
48✔
1048
                        case "<=":
32✔
1049
                                return str1 <= str2, nil
32✔
1050
                        case "match":
×
1051
                                re, err := regexp.Compile(str2)
×
1052
                                if err != nil {
×
1053
                                        return false, fmt.Errorf("invalid regex pattern: %s", str2)
×
1054
                                }
×
1055
                                return re.MatchString(str1), nil
×
1056
                        default:
×
1057
                                return false, fmt.Errorf("invalid operator for strings: %s", operator)
×
1058
                        }
1059
                }
1060
                if operator == "match" {
296✔
1061
                        return false, fmt.Errorf("pattern must be a string")
×
1062
                }
×
1063
        }
1064
        if operator == "match" {
622✔
1065
                return false, fmt.Errorf("value must be a string")
×
1066
        }
×
1067

1068
        // 处理布尔类型
1069
        if bool1, ok := value1.(bool); ok {
788✔
1070
                if bool2, ok := value2.(bool); ok {
284✔
1071
                        switch operator {
118✔
1072
                        case "==":
38✔
1073
                                return bool1 == bool2, nil
38✔
1074
                        case "!=":
16✔
1075
                                return bool1 != bool2, nil
16✔
1076
                        default:
64✔
1077
                                return false, nil
64✔
1078
                        }
1079
                }
1080
        }
1081

1082
        // 处理数组类型 - RFC 9535 支持深度比较
1083
        if arr1, ok := value1.([]interface{}); ok {
576✔
1084
                if arr2, ok := value2.([]interface{}); ok {
144✔
1085
                        switch operator {
72✔
1086
                        case "==":
72✔
1087
                                return deepCompareValues(arr1, arr2), nil
72✔
1088
                        case "!=":
×
1089
                                return !deepCompareValues(arr1, arr2), nil
×
1090
                        default:
×
1091
                                return false, nil
×
1092
                        }
1093
                }
1094
        }
1095

1096
        // 处理对象类型 - RFC 9535 支持深度比较
1097
        if obj1, ok := value1.(map[string]interface{}); ok {
472✔
1098
                if obj2, ok := value2.(map[string]interface{}); ok {
80✔
1099
                        switch operator {
40✔
1100
                        case "==":
40✔
1101
                                return deepCompareValues(obj1, obj2), nil
40✔
1102
                        case "!=":
×
1103
                                return !deepCompareValues(obj1, obj2), nil
×
1104
                        default:
×
1105
                                return false, nil
×
1106
                        }
1107
                }
1108
        }
1109

1110
        // RFC 9535: comparison between incompatible types
1111
        // == returns false, != returns true
1112
        if operator == "!=" {
456✔
1113
                return true, nil
64✔
1114
        }
64✔
1115
        return false, nil
328✔
1116
}
1117

1118
// deepCompareValues performs deep comparison of two values
1119
func deepCompareValues(a, b interface{}) bool {
552✔
1120
        switch v1 := a.(type) {
552✔
1121
        case []interface{}:
152✔
1122
                v2, ok := b.([]interface{})
152✔
1123
                if !ok || len(v1) != len(v2) {
160✔
1124
                        return false
8✔
1125
                }
8✔
1126
                for i := range v1 {
464✔
1127
                        if !deepCompareValues(v1[i], v2[i]) {
352✔
1128
                                return false
32✔
1129
                        }
32✔
1130
                }
1131
                return true
112✔
1132
        case map[string]interface{}:
104✔
1133
                v2, ok := b.(map[string]interface{})
104✔
1134
                if !ok || len(v1) != len(v2) {
112✔
1135
                        return false
8✔
1136
                }
8✔
1137
                for k, val1 := range v1 {
216✔
1138
                        val2, exists := v2[k]
120✔
1139
                        if !exists || !deepCompareValues(val1, val2) {
136✔
1140
                                return false
16✔
1141
                        }
16✔
1142
                }
1143
                return true
80✔
1144
        default:
296✔
1145
                return a == b
296✔
1146
        }
1147
}
1148

1149
// normalizeNumbers 将两个值转换为 float64 类型
1150
func normalizeNumbers(value1, value2 interface{}) (float64, float64, bool) {
2,888✔
1151
        var num1, num2 float64
2,888✔
1152
        var ok1, ok2 bool
2,888✔
1153

2,888✔
1154
        // 尝试将 value1 转换为 float64
2,888✔
1155
        switch v := value1.(type) {
2,888✔
1156
        case float64:
1,574✔
1157
                num1, ok1 = v, true
1,574✔
1158
        case int64:
×
1159
                num1, ok1 = float64(v), true
×
1160
        case int:
40✔
1161
                num1, ok1 = float64(v), true
40✔
1162
        }
1163

1164
        // 尝试将 value2 转换为 float64
1165
        switch v := value2.(type) {
2,888✔
1166
        case float64:
1,766✔
1167
                num2, ok2 = v, true
1,766✔
1168
        case int64:
×
1169
                num2, ok2 = float64(v), true
×
1170
        case int:
×
1171
                num2, ok2 = float64(v), true
×
1172
        }
1173

1174
        return num1, num2, ok1 && ok2
2,888✔
1175
}
1176

1177
// compareStrings compares two strings using the specified operator
1178
func compareStrings(a string, operator string, b string) bool {
40✔
1179
        return standardCompareStrings(a, operator, b)
40✔
1180
}
40✔
1181

1182
// getFieldValue 获取对象中指定字段的值
1183
func getFieldValue(obj interface{}, field string) (interface{}, error) {
7,528✔
1184
        // 移除 @ 和前导点
7,528✔
1185
        field = strings.TrimPrefix(field, "@")
7,528✔
1186
        field = strings.TrimPrefix(field, ".")
7,528✔
1187

7,528✔
1188
        // 如果字段为空,返回对象本身
7,528✔
1189
        if field == "" {
8,938✔
1190
                return obj, nil
1,410✔
1191
        }
1,410✔
1192

1193
        // 分割字段路径
1194
        parts := strings.Split(field, ".")
6,118✔
1195
        current := obj
6,118✔
1196

6,118✔
1197
        for _, part := range parts {
12,248✔
1198
                // 确保当前是对象
6,130✔
1199
                m, ok := current.(map[string]interface{})
6,130✔
1200
                if !ok {
6,288✔
1201
                        return nil, fmt.Errorf("value is not an object")
158✔
1202
                }
158✔
1203

1204
                // 获取下一级字段值
1205
                var exists bool
5,972✔
1206
                current, exists = m[part]
5,972✔
1207
                if !exists {
6,982✔
1208
                        return nil, fmt.Errorf("field %s not found", part)
1,010✔
1209
                }
1,010✔
1210
        }
1211

1212
        return current, nil
4,950✔
1213
}
1214

1215
// 解析多索引选择 - RFC 9535 支持混合选择器类型
1216
func parseMultiIndexSegment(content string) (segment, error) {
280✔
1217
        // 检查前导和尾随逗号
280✔
1218
        if strings.HasPrefix(content, ",") {
290✔
1219
                return nil, NewError(ErrInvalidPath, "leading comma in multi-index segment", content)
10✔
1220
        }
10✔
1221
        if strings.HasSuffix(content, ",") {
280✔
1222
                return nil, NewError(ErrInvalidPath, "trailing comma in multi-index segment", content)
10✔
1223
        }
10✔
1224

1225
        parts := strings.Split(content, ",")
260✔
1226

260✔
1227
        // 检查空索引
260✔
1228
        for _, part := range parts {
856✔
1229
                if strings.TrimSpace(part) == "" {
598✔
1230
                        return nil, NewError(ErrInvalidPath, "empty index in multi-index segment", content)
2✔
1231
                }
2✔
1232
        }
1233

1234
        // RFC 9535: 每个部分独立解析,支持混合选择器类型
1235
        // 先尝试解析每个部分
1236
        selectors := make([]segment, 0, len(parts))
258✔
1237
        allIndices := true
258✔
1238
        allNames := true
258✔
1239

258✔
1240
        for _, part := range parts {
850✔
1241
                trimmed := strings.TrimSpace(part)
592✔
1242

592✔
1243
                // 检查是否是通配符
592✔
1244
                if trimmed == "*" {
632✔
1245
                        selectors = append(selectors, &wildcardSegment{})
40✔
1246
                        allIndices = false
40✔
1247
                        allNames = false
40✔
1248
                        continue
40✔
1249
                }
1250

1251
                // 检查是否是切片
1252
                if strings.Contains(trimmed, ":") {
584✔
1253
                        slice, err := parseSliceSegment(trimmed)
32✔
1254
                        if err != nil {
32✔
1255
                                return nil, err
×
1256
                        }
×
1257
                        selectors = append(selectors, slice)
32✔
1258
                        allIndices = false
32✔
1259
                        allNames = false
32✔
1260
                        continue
32✔
1261
                }
1262

1263
                // 检查是否是带引号的字符串
1264
                if (strings.HasPrefix(trimmed, "'") && strings.HasSuffix(trimmed, "'")) ||
520✔
1265
                        (strings.HasPrefix(trimmed, "\"") && strings.HasSuffix(trimmed, "\"")) {
798✔
1266
                        quoteChar := trimmed[0]
278✔
1267
                        name := trimmed[1 : len(trimmed)-1]
278✔
1268
                        if !validateQuotedString(name, quoteChar) {
278✔
NEW
1269
                                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", trimmed), trimmed)
×
NEW
1270
                        }
×
1271
                        unescaped, err := unescapeString(name, quoteChar)
278✔
1272
                        if err != nil {
278✔
NEW
1273
                                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", trimmed), trimmed)
×
NEW
1274
                        }
×
1275
                        selectors = append(selectors, &nameSegment{name: unescaped})
278✔
1276
                        allIndices = false
278✔
1277
                        continue
278✔
1278
                }
1279

1280
                // 尝试解析为数字索引
1281
                if idx, err := strconv.Atoi(trimmed); err == nil {
470✔
1282
                        selectors = append(selectors, &indexSegment{index: idx})
228✔
1283
                        allNames = false
228✔
1284
                        continue
228✔
1285
                }
1286

1287
                // 不是数字也不是带引号的字符串,可能是不带引号的字段名
1288
                selectors = append(selectors, &nameSegment{name: trimmed})
14✔
1289
                allIndices = false
14✔
1290
        }
1291

1292
        // 如果所有部分都是索引,返回多索引段
1293
        if allIndices {
284✔
1294
                indices := make([]int, len(selectors))
26✔
1295
                for i, sel := range selectors {
88✔
1296
                        indices[i] = sel.(*indexSegment).index
62✔
1297
                }
62✔
1298
                return &multiIndexSegment{indices: indices}, nil
26✔
1299
        }
1300

1301
        // 如果所有部分都是名称,返回多名称段
1302
        if allNames {
332✔
1303
                names := make([]string, len(selectors))
100✔
1304
                for i, sel := range selectors {
298✔
1305
                        names[i] = sel.(*nameSegment).name
198✔
1306
                }
198✔
1307
                return &multiNameSegment{names: names}, nil
100✔
1308
        }
1309

1310
        // 混合类型,返回联合段
1311
        return &unionSegment{selectors: selectors}, nil
132✔
1312
}
1313

1314
// 解析切片表达式
1315
func parseSliceSegment(content string) (segment, error) {
772✔
1316
        parts := strings.Split(content, ":")
772✔
1317
        if len(parts) > 3 {
782✔
1318
                return nil, NewError(ErrSyntax, "slice has too many colons", content)
10✔
1319
        }
10✔
1320

1321
        slice := &sliceSegment{start: 0, end: 0, step: 1}
762✔
1322

762✔
1323
        // 解析起始索引
762✔
1324
        if parts[0] != "" {
1,294✔
1325
                if !validateIntegerLiteral(parts[0]) {
590✔
1326
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice start index: %s", parts[0]), parts[0])
58✔
1327
                }
58✔
1328
                start, err := strconv.Atoi(parts[0])
474✔
1329
                if err != nil {
490✔
1330
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice start index: %s", parts[0]), parts[0])
16✔
1331
                }
16✔
1332
                if start < -9007199254740991 || start > 9007199254740991 {
474✔
1333
                        return nil, NewError(ErrSyntax, fmt.Sprintf("slice start index out of range: %d", start), parts[0])
16✔
1334
                }
16✔
1335
                slice.start = start
442✔
1336
        }
1337

1338
        // 解析结束索引
1339
        if len(parts) > 1 && parts[1] != "" {
1,154✔
1340
                if !validateIntegerLiteral(parts[1]) {
564✔
1341
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice end index: %s", parts[1]), parts[1])
82✔
1342
                }
82✔
1343
                end, err := strconv.Atoi(parts[1])
400✔
1344
                if err != nil {
416✔
1345
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice end index: %s", parts[1]), parts[1])
16✔
1346
                }
16✔
1347
                if end < -9007199254740991 || end > 9007199254740991 {
400✔
1348
                        return nil, NewError(ErrSyntax, fmt.Sprintf("slice end index out of range: %d", end), parts[1])
16✔
1349
                }
16✔
1350
                slice.end = end
368✔
1351
        }
1352

1353
        // 解析步长
1354
        if len(parts) > 2 && parts[2] != "" {
834✔
1355
                if !validateIntegerLiteral(parts[2]) {
342✔
1356
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice step: %s", parts[2]), parts[2])
66✔
1357
                }
66✔
1358
                step, err := strconv.Atoi(parts[2])
210✔
1359
                if err != nil {
226✔
1360
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice step: %s", parts[2]), parts[2])
16✔
1361
                }
16✔
1362
                if step == 0 {
212✔
1363
                        return nil, NewError(ErrSyntax, "slice step cannot be zero", parts[2])
18✔
1364
                }
18✔
1365
                if step < -9007199254740991 || step > 9007199254740991 {
192✔
1366
                        return nil, NewError(ErrSyntax, fmt.Sprintf("slice step out of range: %d", step), parts[2])
16✔
1367
                }
16✔
1368
                slice.step = step
160✔
1369
        }
1370

1371
        return slice, nil
442✔
1372
}
1373

1374
// validateIntegerLiteral validates an integer literal per RFC 9535.
1375
// Returns true if the string is a valid integer- or decimal-number.
1376
func validateIntegerLiteral(s string) bool {
2,778✔
1377
        if s == "" {
2,780✔
1378
                return false
2✔
1379
        }
2✔
1380
        i := 0
2,776✔
1381
        // Optional minus (no plus sign allowed)
2,776✔
1382
        if i < len(s) && s[i] == '-' {
3,244✔
1383
                i++
468✔
1384
        }
468✔
1385
        if i >= len(s) {
2,776✔
NEW
1386
                return false
×
NEW
1387
        }
×
1388
        // Must start with a digit
1389
        if s[i] < '0' || s[i] > '9' {
4,160✔
1390
                return false
1,384✔
1391
        }
1,384✔
1392
        // Leading zero check: if first digit is '0', no more digits allowed
1393
        if s[i] == '0' && i+1 < len(s) && s[i+1] >= '0' && s[i+1] <= '9' {
1,456✔
1394
                return false
64✔
1395
        }
64✔
1396
        // Consume digits
1397
        for i < len(s) && s[i] >= '0' && s[i] <= '9' {
10,086✔
1398
                i++
8,758✔
1399
        }
8,758✔
1400
        // Must have consumed all characters
1401
        return i == len(s)
1,328✔
1402
}
1403

1404
// looksLikeNumber checks if content looks like it could be a number (starts with digit, -digit, or +digit)
1405
func looksLikeNumber(s string) bool {
1,332✔
1406
        if s == "" {
1,334✔
1407
                return false
2✔
1408
        }
2✔
1409
        if s[0] >= '0' && s[0] <= '9' {
1,354✔
1410
                return true
24✔
1411
        }
24✔
1412
        if (s[0] == '-' || s[0] == '+') && len(s) > 1 && s[1] >= '0' && s[1] <= '9' {
1,322✔
1413
                return true
16✔
1414
        }
16✔
1415
        return false
1,290✔
1416
}
1417

1418
// validateQuotedString validates a quoted string content per RFC 9535.
1419
// Checks for invalid escape sequences and embedded control characters.
1420
// The input should be the string content WITHOUT the surrounding quotes.
1421
// quoteChar is the character used for quoting (' or ").
1422
func validateQuotedString(s string, quoteChar byte) bool {
1,552✔
1423
        i := 0
1,552✔
1424
        for i < len(s) {
3,508✔
1425
                ch := s[i]
1,956✔
1426
                if ch == '\\' {
2,468✔
1427
                        // Escape sequence
512✔
1428
                        i++
512✔
1429
                        if i >= len(s) {
528✔
1430
                                return false // incomplete escape
16✔
1431
                        }
16✔
1432
                        esc := s[i]
496✔
1433
                        switch esc {
496✔
1434
                        case '\\', '/', 'b', 'f', 'n', 'r', 't':
112✔
1435
                                // Valid simple escapes (same for both quote types)
112✔
1436
                                i++
112✔
1437
                        case '"':
16✔
1438
                                // Double quote escape only valid in double-quoted strings
16✔
1439
                                if quoteChar != '"' {
24✔
1440
                                        return false
8✔
1441
                                }
8✔
1442
                                i++
8✔
1443
                        case '\'':
16✔
1444
                                // Single quote escape only valid in single-quoted strings
16✔
1445
                                if quoteChar != '\'' {
24✔
1446
                                        return false
8✔
1447
                                }
8✔
1448
                                i++
8✔
1449
                        case 'u':
280✔
1450
                                // Unicode escape: \uXXXX
280✔
1451
                                i++
280✔
1452
                                if i+4 > len(s) {
304✔
1453
                                        return false // not enough hex digits
24✔
1454
                                }
24✔
1455
                                for j := 0; j < 4; j++ {
1,208✔
1456
                                        if i+j >= len(s) || !isHexDigit(s[i+j]) {
976✔
1457
                                                return false
24✔
1458
                                        }
24✔
1459
                                }
1460
                                i += 4
232✔
1461
                        default:
72✔
1462
                                return false // invalid escape sequence
72✔
1463
                        }
1464
                } else if ch == quoteChar {
1,460✔
1465
                        // Unescaped quote character - invalid
16✔
1466
                        return false
16✔
1467
                } else if ch < 0x20 {
1,956✔
1468
                        // Control characters (U+0000-U+001F) are not allowed unescaped
512✔
1469
                        return false
512✔
1470
                } else {
1,428✔
1471
                        i++
916✔
1472
                }
916✔
1473
        }
1474
        return true
872✔
1475
}
1476

1477
// unescapeString processes escape sequences in a quoted string per RFC 9535.
1478
// The input should be the string content WITHOUT the surrounding quotes.
1479
// quoteChar is the character used for quoting (' or ").
1480
func unescapeString(s string, quoteChar byte) (string, error) {
872✔
1481
        var result strings.Builder
872✔
1482
        result.Grow(len(s))
872✔
1483
        i := 0
872✔
1484
        for i < len(s) {
2,052✔
1485
                ch := s[i]
1,180✔
1486
                if ch == '\\' {
1,476✔
1487
                        i++
296✔
1488
                        if i >= len(s) {
296✔
NEW
1489
                                return "", fmt.Errorf("incomplete escape")
×
NEW
1490
                        }
×
1491
                        esc := s[i]
296✔
1492
                        switch esc {
296✔
1493
                        case '"':
8✔
1494
                                if quoteChar != '"' {
8✔
NEW
1495
                                        return "", fmt.Errorf("invalid escape: \\\" in single-quoted string")
×
NEW
1496
                                }
×
1497
                                result.WriteByte('"')
8✔
1498
                        case '\'':
8✔
1499
                                if quoteChar != '\'' {
8✔
NEW
1500
                                        return "", fmt.Errorf("invalid escape: \\' in double-quoted string")
×
NEW
1501
                                }
×
1502
                                result.WriteByte('\'')
8✔
1503
                        case '\\':
16✔
1504
                                result.WriteByte('\\')
16✔
1505
                        case '/':
16✔
1506
                                result.WriteByte('/')
16✔
1507
                        case 'b':
16✔
1508
                                result.WriteByte('\b')
16✔
1509
                        case 'f':
16✔
1510
                                result.WriteByte('\f')
16✔
1511
                        case 'n':
16✔
1512
                                result.WriteByte('\n')
16✔
1513
                        case 'r':
16✔
1514
                                result.WriteByte('\r')
16✔
1515
                        case 't':
16✔
1516
                                result.WriteByte('\t')
16✔
1517
                        case 'u':
168✔
1518
                                // Unicode escape: \uXXXX
168✔
1519
                                i++
168✔
1520
                                if i+4 > len(s) {
168✔
NEW
1521
                                        return "", fmt.Errorf("incomplete unicode escape")
×
NEW
1522
                                }
×
1523
                                hexStr := s[i : i+4]
168✔
1524
                                codePoint, err := strconv.ParseUint(hexStr, 16, 32)
168✔
1525
                                if err != nil {
168✔
NEW
1526
                                        return "", fmt.Errorf("invalid unicode escape: %s", hexStr)
×
NEW
1527
                                }
×
1528
                                // Check for surrogate pairs
1529
                                if codePoint >= 0xD800 && codePoint <= 0xDFFF {
264✔
1530
                                        // High surrogate: must be followed by low surrogate
96✔
1531
                                        if codePoint <= 0xDBFF && i+10 <= len(s) && s[i+4] == '\\' && s[i+5] == 'u' {
144✔
1532
                                                lowHex := s[i+6 : i+10]
48✔
1533
                                                lowPoint, err := strconv.ParseUint(lowHex, 16, 32)
48✔
1534
                                                if err == nil && lowPoint >= 0xDC00 && lowPoint <= 0xDFFF {
80✔
1535
                                                        // Valid surrogate pair
32✔
1536
                                                        combined := 0x10000 + (codePoint-0xD800)*0x400 + (lowPoint - 0xDC00)
32✔
1537
                                                        result.WriteRune(rune(combined))
32✔
1538
                                                        i += 10
32✔
1539
                                                        continue
32✔
1540
                                                }
1541
                                        }
1542
                                        return "", fmt.Errorf("invalid surrogate pair")
64✔
1543
                                }
1544
                                result.WriteRune(rune(codePoint))
72✔
1545
                                i += 4
72✔
1546
                                continue
72✔
NEW
1547
                        default:
×
NEW
1548
                                return "", fmt.Errorf("invalid escape: \\%c", esc)
×
1549
                        }
1550
                        i++
128✔
1551
                } else if ch == quoteChar {
884✔
NEW
1552
                        return "", fmt.Errorf("unescaped quote character")
×
1553
                } else if ch < 0x20 {
884✔
NEW
1554
                        return "", fmt.Errorf("control character")
×
1555
                } else {
884✔
1556
                        result.WriteByte(ch)
884✔
1557
                        i++
884✔
1558
                }
884✔
1559
        }
1560
        return result.String(), nil
808✔
1561
}
1562

1563
// isHexDigit checks if a byte is a hexadecimal digit
1564
func isHexDigit(ch byte) bool {
952✔
1565
        return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
952✔
1566
}
952✔
1567

1568
// parseIndexOrName parses a bracket content as an index or name selector
1569
func parseIndexOrName(content string) (segment, error) {
1,496✔
1570
        // 处理函数调用
1,496✔
1571
        if strings.HasSuffix(content, ")") {
1,504✔
1572
                return parseFunctionCall(content)
8✔
1573
        }
8✔
1574

1575
        // Try to parse as an integer index with RFC 9535 validation
1576
        if validateIntegerLiteral(content) {
1,644✔
1577
                idx, err := strconv.Atoi(content)
156✔
1578
                if err != nil {
164✔
1579
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid index: %s", content), content)
8✔
1580
                }
8✔
1581
                // Reject -0 (negative zero)
1582
                if content == "-0" {
156✔
1583
                        return nil, NewError(ErrSyntax, "negative zero is not a valid index", content)
8✔
1584
                }
8✔
1585
                // RFC 9535: index must be within IEEE 754 double precision range
1586
                if idx < -9007199254740991 || idx > 9007199254740991 {
160✔
1587
                        return nil, NewError(ErrSyntax, fmt.Sprintf("index out of range: %d", idx), content)
20✔
1588
                }
20✔
1589
                return &indexSegment{index: idx}, nil
120✔
1590
        }
1591

1592
        // If content looks like a number but fails validation, it's an invalid index
1593
        if looksLikeNumber(content) {
1,372✔
1594
                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid index: %s", content), content)
40✔
1595
        }
40✔
1596

1597
        // 处理字符串字面量
1598
        if strings.HasPrefix(content, "'") && strings.HasSuffix(content, "'") && len(content) > 1 {
1,890✔
1599
                inner := content[1 : len(content)-1]
598✔
1600
                if !validateQuotedString(inner, '\'') {
878✔
1601
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
280✔
1602
                }
280✔
1603
                unescaped, err := unescapeString(inner, '\'')
318✔
1604
                if err != nil {
318✔
NEW
1605
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
×
NEW
1606
                }
×
1607
                return &nameSegment{name: unescaped}, nil
318✔
1608
        }
1609

1610
        // 处理双引号字符串字面量
1611
        if strings.HasPrefix(content, "\"") && strings.HasSuffix(content, "\"") && len(content) > 1 {
1,370✔
1612
                inner := content[1 : len(content)-1]
676✔
1613
                if !validateQuotedString(inner, '"') {
1,076✔
1614
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
400✔
1615
                }
400✔
1616
                unescaped, err := unescapeString(inner, '"')
276✔
1617
                if err != nil {
340✔
1618
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
64✔
1619
                }
64✔
1620
                return &nameSegment{name: unescaped}, nil
212✔
1621
        }
1622

1623
        return &nameSegment{name: content}, nil
18✔
1624
}
1625

1626
// 解析函数调用
1627
func parseFunctionCall(content string) (segment, error) {
52✔
1628
        // 找到函数名和参数列表
52✔
1629
        openParen := strings.Index(content, "(")
52✔
1630
        if openParen == -1 {
54✔
1631
                return nil, fmt.Errorf("invalid function call syntax: missing opening parenthesis")
2✔
1632
        }
2✔
1633

1634
        // 检查闭合括号
1635
        if !strings.HasSuffix(content, ")") {
52✔
1636
                return nil, fmt.Errorf("invalid function call syntax: missing closing parenthesis")
2✔
1637
        }
2✔
1638

1639
        name := content[:openParen]
48✔
1640
        argsStr := content[openParen+1 : len(content)-1]
48✔
1641

48✔
1642
        // 解析参数
48✔
1643
        args := make([]interface{}, 0)
48✔
1644
        if argsStr != "" {
88✔
1645
                // 简单参数解析,暂时只支持数字和字符串
40✔
1646
                argParts := strings.Split(argsStr, ",")
40✔
1647
                for _, arg := range argParts {
108✔
1648
                        arg = strings.TrimSpace(arg)
68✔
1649
                        if arg == "" {
70✔
1650
                                continue // 跳过空参数
2✔
1651
                        }
1652
                        // 尝试解析为数字
1653
                        if num, err := strconv.ParseFloat(arg, 64); err == nil {
100✔
1654
                                args = append(args, num)
34✔
1655
                                continue
34✔
1656
                        }
1657
                        // 处理字符串参数
1658
                        if strings.HasPrefix(arg, "'") && strings.HasSuffix(arg, "'") {
58✔
1659
                                // 处理转义引号
26✔
1660
                                str := arg[1 : len(arg)-1]
26✔
1661
                                str = strings.ReplaceAll(str, "''", "'")
26✔
1662
                                args = append(args, str)
26✔
1663
                                continue
26✔
1664
                        }
1665
                        return nil, fmt.Errorf("unsupported argument type: %s", arg)
6✔
1666
                }
1667
        }
1668

1669
        return &functionSegment{name: name, args: args}, nil
42✔
1670
}
1671

1672
// validateNumberLiteral validates a number literal per RFC 9535 grammar.
1673
// number = ["-"] (int / (int "." 1*DIGIT))
1674
// int = "0" / (DIGIT1 *DIGIT)
1675
func validateNumberLiteral(s string) bool {
928✔
1676
        if s == "" {
930✔
1677
                return false
2✔
1678
        }
2✔
1679
        i := 0
926✔
1680
        // Optional minus
926✔
1681
        if s[i] == '-' {
958✔
1682
                i++
32✔
1683
        }
32✔
1684
        if i >= len(s) {
926✔
NEW
1685
                return false
×
NEW
1686
        }
×
1687
        // Must have integer part
1688
        if s[i] < '0' || s[i] > '9' {
1,432✔
1689
                return false
506✔
1690
        }
506✔
1691
        // Leading zero check
1692
        if s[i] == '0' {
468✔
1693
                i++
48✔
1694
                if i < len(s) && s[i] >= '0' && s[i] <= '9' {
64✔
1695
                        return false // leading zero
16✔
1696
                }
16✔
1697
        } else {
372✔
1698
                // Non-zero digit, consume all digits
372✔
1699
                for i < len(s) && s[i] >= '0' && s[i] <= '9' {
806✔
1700
                        i++
434✔
1701
                }
434✔
1702
        }
1703
        // Optional fractional part
1704
        if i < len(s) && s[i] == '.' {
476✔
1705
                i++
72✔
1706
                if i >= len(s) || s[i] < '0' || s[i] > '9' {
96✔
1707
                        return false // must have digit after decimal
24✔
1708
                }
24✔
1709
                for i < len(s) && s[i] >= '0' && s[i] <= '9' {
104✔
1710
                        i++
56✔
1711
                }
56✔
1712
        }
1713
        // Optional exponent
1714
        if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
532✔
1715
                i++
152✔
1716
                if i < len(s) && (s[i] == '+' || s[i] == '-') {
240✔
1717
                        i++
88✔
1718
                }
88✔
1719
                if i >= len(s) || s[i] < '0' || s[i] > '9' {
192✔
1720
                        return false // must have digit after e/E
40✔
1721
                }
40✔
1722
                for i < len(s) && s[i] >= '0' && s[i] <= '9' {
240✔
1723
                        i++
128✔
1724
                }
128✔
1725
        }
1726
        // Must have consumed all characters
1727
        return i == len(s)
340✔
1728
}
1729

1730
// parseFilterValue parses a filter value string into an appropriate type
1731
func parseFilterValue(valueStr string) (interface{}, error) {
1,406✔
1732
        valueStr = strings.TrimSpace(valueStr)
1,406✔
1733

1,406✔
1734
        // 处理 null
1,406✔
1735
        if valueStr == "null" {
1,480✔
1736
                return nil, nil
74✔
1737
        }
74✔
1738

1739
        // 处理布尔值
1740
        if valueStr == "true" {
1,386✔
1741
                return true, nil
54✔
1742
        }
54✔
1743
        if valueStr == "false" {
1,326✔
1744
                return false, nil
48✔
1745
        }
48✔
1746

1747
        // 处理字符串(带引号)
1748
        if (strings.HasPrefix(valueStr, "'") && strings.HasSuffix(valueStr, "'")) ||
1,230✔
1749
                (strings.HasPrefix(valueStr, "\"") && strings.HasSuffix(valueStr, "\"")) {
1,532✔
1750
                return valueStr[1 : len(valueStr)-1], nil
302✔
1751
        }
302✔
1752

1753
        // Try to parse as number with RFC 9535 validation
1754
        if validateNumberLiteral(valueStr) {
1,244✔
1755
                num, err := strconv.ParseFloat(valueStr, 64)
316✔
1756
                if err != nil {
316✔
NEW
1757
                        return nil, fmt.Errorf("invalid number: %s", valueStr)
×
NEW
1758
                }
×
1759
                return num, nil
316✔
1760
        }
1761

1762
        // 处理 $ 引用(根节点)
1763
        if valueStr == "$" {
620✔
1764
                return "$", nil
8✔
1765
        }
8✔
1766

1767
        // 处理 @ 引用(当前元素)
1768
        if valueStr == "@" {
612✔
1769
                return "@", nil
8✔
1770
        }
8✔
1771

1772
        // 处理 $.path 引用(根节点路径)
1773
        if strings.HasPrefix(valueStr, "$.") || strings.HasPrefix(valueStr, "$[") {
596✔
1774
                return valueStr, nil
×
1775
        }
×
1776

1777
        // 处理 @.path 引用(当前元素路径)
1778
        if strings.HasPrefix(valueStr, "@.") || strings.HasPrefix(valueStr, "@[") {
1,012✔
1779
                return valueStr, nil
416✔
1780
        }
416✔
1781

1782
        // 如果不是其他类型,返回错误
1783
        return nil, fmt.Errorf("invalid value: %s", valueStr)
180✔
1784
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc