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

davidhoo / jsonpath / 25490054326

07 May 2026 10:21AM UTC coverage: 80.984% (-0.1%) from 81.127%
25490054326

push

github

davidhoo
fix: use math.MaxInt/MinInt for 32-bit platform compatibility

- Replace hardcoded 9007199254740991 with math.MaxInt/MinInt
- Fixes goreleaser build failure on linux_386
- Update test cases for overflow detection

4 of 4 new or added lines in 1 file covered. (100.0%)

7 existing lines in 1 file now uncovered.

3965 of 4896 relevant lines covered (80.98%)

1182.8 hits per line

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

85.6
/parser.go
1
package jsonpath
2

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

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

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

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

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

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

45
        // 移除前导点
46
        dotStripped := false
6,806✔
47
        if strings.HasPrefix(path, ".") {
7,460✔
48
                path = path[1:]
654✔
49
                dotStripped = true
654✔
50
        }
654✔
51

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

58
        // 处理递归下降
59
        if strings.HasPrefix(path, ".") {
7,036✔
60
                return parseRecursive(path[1:])
262✔
61
        }
262✔
62

63
        // 处理常规路径
64
        return parseRegular(path)
6,512✔
65
}
66

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

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

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

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

104
        var args []interface{}
1,858✔
105
        var currentArg strings.Builder
1,858✔
106
        var inQuote bool
1,858✔
107
        var quoteChar rune
1,858✔
108
        parenDepth := 0
1,858✔
109
        bracketDepth := 0
1,858✔
110

1,858✔
111
        for i := 0; i < len(argsStr); i++ {
13,160✔
112
                ch := rune(argsStr[i])
11,302✔
113

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

163
        // 处理最后一个参数
164
        arg := strings.TrimSpace(currentArg.String())
1,858✔
165
        if arg != "" {
3,716✔
166
                parsedArg, err := parseSingleFunctionArg(arg)
1,858✔
167
                if err != nil {
1,858✔
168
                        return nil, err
×
169
                }
×
170
                args = append(args, parsedArg)
1,858✔
171
        }
172

173
        return args, nil
1,858✔
174
}
175

176
// parseSingleFunctionArg 解析单个函数参数
177
func parseSingleFunctionArg(arg string) (interface{}, error) {
2,342✔
178
        arg = strings.TrimSpace(arg)
2,342✔
179

2,342✔
180
        // 尝试解析为数字
2,342✔
181
        if num, err := strconv.ParseFloat(arg, 64); err == nil {
2,402✔
182
                return num, nil
60✔
183
        }
60✔
184

185
        // 处理布尔值
186
        if arg == "true" {
2,306✔
187
                return true, nil
24✔
188
        }
24✔
189
        if arg == "false" {
2,282✔
190
                return false, nil
24✔
191
        }
24✔
192

193
        // 处理 null
194
        if arg == "null" {
2,258✔
195
                return nil, nil
24✔
196
        }
24✔
197

198
        // 如果以 $ 开头,它是一个路径引用
199
        if strings.HasPrefix(arg, "$") {
2,498✔
200
                return arg, nil
288✔
201
        }
288✔
202

203
        // 处理 @ 引用(在过滤器上下文中)
204
        if strings.HasPrefix(arg, "@") {
3,388✔
205
                return arg, nil
1,466✔
206
        }
1,466✔
207

208
        // 其他情况都作为字符串处理(包括正则表达式模式)
209
        return arg, nil
456✔
210
}
211

212
// 解析递归下降路径
213
func parseRecursive(path string) ([]segment, error) {
276✔
214
        // Reject bare recursive descent: $..
276✔
215
        if path == "" {
286✔
216
                return nil, NewError(ErrSyntax, "bare recursive descent is not allowed", "..")
10✔
217
        }
10✔
218

219
        // Reject whitespace after recursive descent: $.. a
220
        if path[0] == ' ' || path[0] == '\t' || path[0] == '\n' || path[0] == '\r' {
298✔
221
                return nil, NewError(ErrSyntax, "whitespace is not allowed between recursive descent and member name", "..")
32✔
222
        }
32✔
223

224
        var segments []segment
234✔
225
        segments = append(segments, &recursiveSegment{})
234✔
226

234✔
227
        // 移除前导点
234✔
228
        path = strings.TrimPrefix(path, ".")
234✔
229

234✔
230
        // 如果还有路径,继续解析
234✔
231
        if path != "" {
468✔
232
                remainingSegments, err := parseRegular(path)
234✔
233
                if err != nil {
236✔
234
                        return nil, err
2✔
235
                }
2✔
236
                segments = append(segments, remainingSegments...)
232✔
237
        }
238

239
        return segments, nil
232✔
240
}
241

242
// 解析常规路径
243
func parseRegular(path string) ([]segment, error) {
6,746✔
244
        var segments []segment
6,746✔
245
        var current string
6,746✔
246
        afterDot := false
6,746✔
247
        parenDepth := 0
6,746✔
248

6,746✔
249
        // Use rune iteration to properly handle multi-byte UTF-8 characters
6,746✔
250
        runes := []rune(path)
6,746✔
251
        i := 0
6,746✔
252
        for i < len(runes) {
17,490✔
253
                r := runes[i]
10,744✔
254
                switch {
10,744✔
255
                case r == '[':
6,640✔
256
                        if current != "" {
6,758✔
257
                                seg, err := createDotSegment(current)
118✔
258
                                if err != nil {
118✔
259
                                        return nil, err
×
260
                                }
×
261
                                segments = append(segments, seg)
118✔
262
                                current = ""
118✔
263
                        }
264
                        afterDot = false
6,640✔
265

6,640✔
266
                        // Find the matching closing bracket by counting bracket depth
6,640✔
267
                        // This correctly handles nested brackets in filter expressions
6,640✔
268
                        depth := 0
6,640✔
269
                        j := i + 1
6,640✔
270
                        inQuotes := false
6,640✔
271
                        inSingleQuotes := false
6,640✔
272
                        for j < len(runes) {
73,372✔
273
                                ch := runes[j]
66,732✔
274
                                // Handle escape sequences inside strings
66,732✔
275
                                if (inQuotes || inSingleQuotes) && ch == '\\' && j+1 < len(runes) {
67,372✔
276
                                        j += 2 // skip the backslash and the next character
640✔
277
                                        continue
640✔
278
                                }
279
                                if ch == '"' && !inSingleQuotes {
67,740✔
280
                                        inQuotes = !inQuotes
1,648✔
281
                                } else if ch == '\'' && !inQuotes {
69,344✔
282
                                        inSingleQuotes = !inSingleQuotes
3,252✔
283
                                } else if !inQuotes && !inSingleQuotes {
120,188✔
284
                                        if ch == '[' {
56,080✔
285
                                                depth++
336✔
286
                                        } else if ch == ']' {
62,688✔
287
                                                if depth == 0 {
13,552✔
288
                                                        break
6,608✔
289
                                                }
290
                                                depth--
336✔
291
                                        }
292
                                }
293
                                j++
59,484✔
294
                        }
295
                        if j >= len(runes) {
6,672✔
296
                                return nil, NewError(ErrSyntax, "unclosed bracket", path)
32✔
297
                        }
32✔
298
                        bracketContent := string(runes[i+1 : j])
6,608✔
299
                        seg, err := parseBracketSegment(bracketContent)
6,608✔
300
                        if err != nil {
8,392✔
301
                                return nil, err
1,784✔
302
                        }
1,784✔
303
                        segments = append(segments, seg)
4,824✔
304
                        i = j // advance past the closing ']'
4,824✔
305

306
                case r == '(' && i == 0:
×
307
                        // Handle leading parenthesis in dot notation
×
308
                        parenDepth++
×
309
                        current += string(r)
×
310
                        afterDot = false
×
311

312
                case r == '(':
84✔
313
                        parenDepth++
84✔
314
                        current += string(r)
84✔
315
                        afterDot = false
84✔
316

317
                case r == ')':
84✔
318
                        parenDepth--
84✔
319
                        current += string(r)
84✔
320
                        afterDot = false
84✔
321

322
                case r == '.' && parenDepth == 0:
1,092✔
323
                        if afterDot {
1,116✔
324
                                // Second dot in ".." → recursive descent
24✔
325
                                segments = append(segments, &recursiveSegment{})
24✔
326
                                afterDot = false
24✔
327
                        } else {
1,092✔
328
                                if current != "" {
1,270✔
329
                                        seg, err := createDotSegment(current)
202✔
330
                                        if err != nil {
202✔
331
                                                return nil, err
×
332
                                        }
×
333
                                        segments = append(segments, seg)
202✔
334
                                        current = ""
202✔
335
                                }
336
                                afterDot = true
1,068✔
337
                        }
338

339
                case (r == ' ' || r == '\t' || r == '\n' || r == '\r') && parenDepth == 0:
120✔
340
                        // RFC 9535: whitespace is allowed between root and dot (e.g. "$ .a")
120✔
341
                        // but NOT between dot and name (e.g. "$. a" is invalid).
120✔
342
                        if afterDot {
120✔
343
                                // Whitespace immediately after dot: invalid
×
344
                                return nil, NewError(ErrSyntax, "whitespace is not allowed between dot and member name", path)
×
345
                        }
×
346
                        if current == "" {
240✔
347
                                // Leading whitespace (e.g. "$ .a"), skip it
120✔
348
                        } else {
120✔
349
                                // Whitespace after a name: flush the name as a segment
×
350
                                seg, err := createDotSegment(current)
×
351
                                if err != nil {
×
352
                                        return nil, err
×
353
                                }
×
354
                                segments = append(segments, seg)
×
355
                                current = ""
×
356
                        }
357

358
                default:
2,724✔
359
                        current += string(r)
2,724✔
360
                        afterDot = false
2,724✔
361
                }
362
                i++
8,928✔
363
        }
364

365
        // 处理最后一个段
366
        if current != "" {
5,970✔
367
                seg, err := createDotSegment(current)
1,040✔
368
                if err != nil {
1,056✔
369
                        return nil, err
16✔
370
                }
16✔
371
                segments = append(segments, seg)
1,024✔
372
        }
373

374
        return segments, nil
4,914✔
375
}
376

377
// 创建点表示法段
378
func createDotSegment(name string) (segment, error) {
1,360✔
379
        if name == "*" {
1,780✔
380
                return &wildcardSegment{}, nil
420✔
381
        }
420✔
382
        // Only validate non-function names (functions are handled by nameSegmentV3.evaluateFunction)
383
        if !strings.Contains(name, "(") && !isValidMemberName(name) {
956✔
384
                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid member name: %s", name), name)
16✔
385
        }
16✔
386
        return &nameSegment{name: name}, nil
924✔
387
}
388

389
// isValidMemberName checks if a name is valid for dot notation per RFC 9535.
390
// member-name-shorthand = name-first *name-char
391
// name-first = %x41-5A / "_" / %x61-7A / %x80-10FFFF  (letter / "_" / non-ASCII)
392
// name-char = name-first / %x30-39  (name-first / digit)
393
func isValidMemberName(name string) bool {
856✔
394
        if name == "" {
856✔
395
                return false
×
396
        }
×
397
        for i, r := range name {
2,726✔
398
                if i == 0 {
2,726✔
399
                        if !((r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' || r >= 0x80) {
872✔
400
                                return false
16✔
401
                        }
16✔
402
                } else {
1,014✔
403
                        if !((r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || r >= 0x80) {
1,014✔
404
                                return false
×
405
                        }
×
406
                }
407
        }
408
        return true
840✔
409
}
410

411
// 解析方括号段
412
func parseBracketSegment(content string) (segment, error) {
6,608✔
413
        // RFC 9535: whitespace is allowed around selectors in brackets
6,608✔
414
        content = strings.TrimSpace(content)
6,608✔
415

6,608✔
416
        // Reject empty brackets: $[]
6,608✔
417
        if content == "" {
6,616✔
418
                return nil, NewError(ErrSyntax, "empty bracket segment", "")
8✔
419
        }
8✔
420

421
        // Reject @ outside of filter expression (must be preceded by ?)
422
        if strings.HasPrefix(content, "@") {
6,608✔
423
                return nil, NewError(ErrSyntax, "@ is only allowed inside filter expressions", content)
8✔
424
        }
8✔
425

426
        // Reject $ outside of filter expression (must be preceded by ?)
427
        if strings.HasPrefix(content, "$") {
6,600✔
428
                return nil, NewError(ErrSyntax, "$ is only allowed inside filter expressions", content)
8✔
429
        }
8✔
430

431
        // Reject space-separated indices: $[0 2]
432
        // After trimming, check if content looks like "0 2" (numbers separated by space)
433
        // But allow whitespace in slice expressions (e.g., "1 :5:2", "1: 5:2")
434
        if strings.Contains(content, " ") && !strings.Contains(content, ",") && !strings.HasPrefix(content, "?") && !strings.HasPrefix(content, "'") && !strings.HasPrefix(content, "\"") && !strings.Contains(content, ":") {
6,600✔
435
                // Check if it looks like space-separated tokens (not just whitespace in a string)
16✔
436
                parts := strings.Fields(content)
16✔
437
                if len(parts) > 1 {
32✔
438
                        return nil, NewError(ErrSyntax, "space is not a valid separator in bracket selector, use comma", content)
16✔
439
                }
16✔
440
        }
441

442
        // 处理通配符
443
        if content == "*" {
6,608✔
444
                return &wildcardSegment{}, nil
40✔
445
        }
40✔
446

447
        // 处理过滤器表达式
448
        if strings.HasPrefix(content, "?") {
9,708✔
449
                // Check if there are multiple selectors (commas at top level)
3,180✔
450
                if hasTopLevelComma(content) {
3,228✔
451
                        return parseMultiIndexSegment(content)
48✔
452
                }
48✔
453
                return parseFilterSegment(content[1:])
3,132✔
454
        }
455

456
        // 处理多索引选择或多字段选择
457
        if strings.Contains(content, ",") ||
3,348✔
458
                ((strings.HasPrefix(content, "'") && strings.HasSuffix(content, "'")) && strings.Contains(content[1:len(content)-1], "','")) ||
3,348✔
459
                ((strings.HasPrefix(content, "\"") && strings.HasSuffix(content, "\"")) && strings.Contains(content[1:len(content)-1], "\",\"")) {
3,624✔
460
                return parseMultiIndexSegment(content)
276✔
461
        }
276✔
462

463
        // 处理切片表达式
464
        if strings.Contains(content, ":") {
3,840✔
465
                return parseSliceSegment(content)
768✔
466
        }
768✔
467

468
        // 处理索引或名称
469
        return parseIndexOrName(content)
2,304✔
470
}
471

472
// hasTopLevelComma checks if content has commas at the top level (not inside parentheses, brackets, or quotes)
473
func hasTopLevelComma(content string) bool {
3,180✔
474
        inQuotes := false
3,180✔
475
        inSingleQuotes := false
3,180✔
476
        parenDepth := 0
3,180✔
477
        bracketDepth := 0
3,180✔
478
        for i := 0; i < len(content); i++ {
43,376✔
479
                ch := content[i]
40,196✔
480
                if (inQuotes || inSingleQuotes) && ch == '\\' && i+1 < len(content) {
40,324✔
481
                        i++ // skip escaped character
128✔
482
                        continue
128✔
483
                }
484
                if ch == '"' && !inSingleQuotes {
40,388✔
485
                        inQuotes = !inQuotes
320✔
486
                } else if ch == '\'' && !inQuotes {
41,588✔
487
                        inSingleQuotes = !inSingleQuotes
1,520✔
488
                } else if !inQuotes && !inSingleQuotes {
75,138✔
489
                        if ch == '(' {
36,742✔
490
                                parenDepth++
1,352✔
491
                        } else if ch == ')' {
36,742✔
492
                                parenDepth--
1,352✔
493
                        } else if ch == '[' {
34,374✔
494
                                bracketDepth++
336✔
495
                        } else if ch == ']' {
33,022✔
496
                                bracketDepth--
336✔
497
                        } else if ch == ',' && parenDepth == 0 && bracketDepth == 0 {
32,398✔
498
                                return true
48✔
499
                        }
48✔
500
                }
501
        }
502
        return false
3,132✔
503
}
504

505
// splitTopLevel splits content by the given delimiter at the top level (not inside parentheses, brackets, or quotes)
506
func splitTopLevel(content string, delimiter byte) []string {
332✔
507
        var parts []string
332✔
508
        inQuotes := false
332✔
509
        inSingleQuotes := false
332✔
510
        parenDepth := 0
332✔
511
        bracketDepth := 0
332✔
512
        start := 0
332✔
513
        for i := 0; i < len(content); i++ {
2,780✔
514
                ch := content[i]
2,448✔
515
                if (inQuotes || inSingleQuotes) && ch == '\\' && i+1 < len(content) {
2,448✔
516
                        i++ // skip escaped character
×
517
                        continue
×
518
                }
519
                if ch == '"' && !inSingleQuotes {
2,448✔
520
                        inQuotes = !inQuotes
×
521
                } else if ch == '\'' && !inQuotes {
3,052✔
522
                        inSingleQuotes = !inSingleQuotes
604✔
523
                } else if !inQuotes && !inSingleQuotes {
3,888✔
524
                        if ch == '(' {
1,440✔
525
                                parenDepth++
×
526
                        } else if ch == ')' {
1,440✔
527
                                parenDepth--
×
528
                        } else if ch == '[' {
1,440✔
529
                                bracketDepth++
×
530
                        } else if ch == ']' {
1,440✔
531
                                bracketDepth--
×
532
                        } else if ch == delimiter && parenDepth == 0 && bracketDepth == 0 {
1,850✔
533
                                parts = append(parts, content[start:i])
410✔
534
                                start = i + 1
410✔
535
                        }
410✔
536
                }
537
        }
538
        parts = append(parts, content[start:])
332✔
539
        return parts
332✔
540
}
541

542
// 标准化过滤器表达式
543
func normalizeFilterExpression(expr string) string {
2,628✔
544
        expr = strings.TrimSpace(expr)
2,628✔
545
        return expr
2,628✔
546
}
2,628✔
547

548
// expressionParser is a recursive descent parser for filter expressions
549
type expressionParser struct {
550
        input string
551
        pos   int
552
}
553

554
// parseFilterExpression parses a filter expression string into an expression tree
555
func parseFilterExpression(input string) (exprNode, error) {
2,672✔
556
        p := &expressionParser{input: input, pos: 0}
2,672✔
557
        node, err := p.parseOr()
2,672✔
558
        if err != nil {
3,254✔
559
                return nil, err
582✔
560
        }
582✔
561
        p.skipSpaces()
2,090✔
562
        if p.pos < len(p.input) {
2,090✔
563
                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("unexpected character at position %d: %c", p.pos, p.input[p.pos]), input)
×
564
        }
×
565
        return node, nil
2,090✔
566
}
567

568
func (p *expressionParser) skipSpaces() {
13,116✔
569
        for p.pos < len(p.input) && p.input[p.pos] == ' ' {
13,388✔
570
                p.pos++
272✔
571
        }
272✔
572
}
573

574
func (p *expressionParser) parseOr() (exprNode, error) {
2,692✔
575
        left, err := p.parseAnd()
2,692✔
576
        if err != nil {
3,274✔
577
                return nil, err
582✔
578
        }
582✔
579

580
        children := []exprNode{left}
2,110✔
581
        for {
4,416✔
582
                p.skipSpaces()
2,306✔
583
                if p.pos+1 < len(p.input) && p.input[p.pos:p.pos+2] == "||" {
2,502✔
584
                        p.pos += 2
196✔
585
                        right, err := p.parseAnd()
196✔
586
                        if err != nil {
196✔
587
                                return nil, err
×
588
                        }
×
589
                        children = append(children, right)
196✔
590
                } else {
2,110✔
591
                        break
2,110✔
592
                }
593
        }
594

595
        if len(children) == 1 {
4,066✔
596
                return children[0], nil
1,956✔
597
        }
1,956✔
598
        return &orNode{children: children}, nil
154✔
599
}
600

601
func (p *expressionParser) parseAnd() (exprNode, error) {
2,888✔
602
        left, err := p.parseUnary()
2,888✔
603
        if err != nil {
3,470✔
604
                return nil, err
582✔
605
        }
582✔
606

607
        children := []exprNode{left}
2,306✔
608
        for {
4,818✔
609
                p.skipSpaces()
2,512✔
610
                if p.pos+1 < len(p.input) && p.input[p.pos:p.pos+2] == "&&" {
2,718✔
611
                        p.pos += 2
206✔
612
                        right, err := p.parseUnary()
206✔
613
                        if err != nil {
206✔
614
                                return nil, err
×
615
                        }
×
616
                        children = append(children, right)
206✔
617
                } else {
2,306✔
618
                        break
2,306✔
619
                }
620
        }
621

622
        if len(children) == 1 {
4,448✔
623
                return children[0], nil
2,142✔
624
        }
2,142✔
625
        return &andNode{children: children}, nil
164✔
626
}
627

628
func (p *expressionParser) parseUnary() (exprNode, error) {
3,094✔
629
        p.skipSpaces()
3,094✔
630
        if p.pos < len(p.input) && p.input[p.pos] == '!' {
3,190✔
631
                p.pos++
96✔
632
                inner, err := p.parsePrimary()
96✔
633
                if err != nil {
96✔
634
                        return nil, err
×
635
                }
×
636
                return negateNode(inner)
96✔
637
        }
638
        return p.parsePrimary()
2,998✔
639
}
640

641
func (p *expressionParser) parsePrimary() (exprNode, error) {
3,094✔
642
        p.skipSpaces()
3,094✔
643

3,094✔
644
        if p.pos >= len(p.input) {
3,094✔
645
                return nil, NewError(ErrInvalidFilter, "unexpected end of expression", p.input)
×
646
        }
×
647

648
        // Handle parenthesized expression
649
        if p.input[p.pos] == '(' {
3,114✔
650
                p.pos++ // skip '('
20✔
651
                node, err := p.parseOr()
20✔
652
                if err != nil {
20✔
653
                        return nil, err
×
654
                }
×
655
                p.skipSpaces()
20✔
656
                if p.pos >= len(p.input) || p.input[p.pos] != ')' {
20✔
657
                        return nil, NewError(ErrInvalidFilter, "missing closing parenthesis", p.input)
×
658
                }
×
659
                p.pos++ // skip ')'
20✔
660
                return node, nil
20✔
661
        }
662

663
        // Parse atomic condition (everything until next &&, ||, or unmatched )) or ])
664
        start := p.pos
3,074✔
665
        depth := 0
3,074✔
666
        bracketDepth := 0
3,074✔
667
        inQuotes := false
3,074✔
668
        inSingleQuotes := false
3,074✔
669

3,074✔
670
        for p.pos < len(p.input) {
29,580✔
671
                ch := p.input[p.pos]
26,506✔
672

26,506✔
673
                // Handle escape sequences inside strings
26,506✔
674
                if (inQuotes || inSingleQuotes) && ch == '\\' && p.pos+1 < len(p.input) {
26,522✔
675
                        p.pos += 2 // skip backslash and the escaped character
16✔
676
                        continue
16✔
677
                }
678

679
                if ch == '"' && !inSingleQuotes {
26,774✔
680
                        inQuotes = !inQuotes
284✔
681
                        p.pos++
284✔
682
                        continue
284✔
683
                }
684
                if ch == '\'' && !inQuotes {
27,102✔
685
                        inSingleQuotes = !inSingleQuotes
896✔
686
                        p.pos++
896✔
687
                        continue
896✔
688
                }
689

690
                if inQuotes || inSingleQuotes {
26,754✔
691
                        p.pos++
1,444✔
692
                        continue
1,444✔
693
                }
694

695
                if ch == '(' {
24,370✔
696
                        depth++
504✔
697
                        p.pos++
504✔
698
                        continue
504✔
699
                }
700
                if ch == ')' {
23,886✔
701
                        if depth == 0 {
544✔
702
                                break
20✔
703
                        }
704
                        depth--
504✔
705
                        p.pos++
504✔
706
                        continue
504✔
707
                }
708
                if ch == '[' {
23,158✔
709
                        bracketDepth++
320✔
710
                        p.pos++
320✔
711
                        continue
320✔
712
                }
713
                if ch == ']' {
22,838✔
714
                        if bracketDepth == 0 {
320✔
715
                                break
×
716
                        }
717
                        bracketDepth--
320✔
718
                        p.pos++
320✔
719
                        continue
320✔
720
                }
721

722
                // Check for top-level && or ||
723
                if depth == 0 && bracketDepth == 0 && p.pos+1 < len(p.input) {
39,462✔
724
                        op := p.input[p.pos : p.pos+2]
17,264✔
725
                        if op == "&&" || op == "||" {
17,656✔
726
                                break
392✔
727
                        }
728
                }
729

730
                p.pos++
21,806✔
731
        }
732

733
        condStr := strings.TrimSpace(p.input[start:p.pos])
3,074✔
734
        if condStr == "" {
3,074✔
735
                return nil, NewError(ErrInvalidFilter, "empty condition", p.input)
×
736
        }
×
737

738
        cond, err := parseFilterCondition(condStr)
3,074✔
739
        if err != nil {
3,656✔
740
                return nil, err
582✔
741
        }
582✔
742
        return &conditionNode{cond: cond}, nil
2,492✔
743
}
744

745
// negateNode applies negation to an expression node
746
func negateNode(node exprNode) (exprNode, error) {
144✔
747
        switch n := node.(type) {
144✔
748
        case *conditionNode:
142✔
749
                newCond := n.cond
142✔
750
                switch newCond.operator {
142✔
751
                case "==":
46✔
752
                        newCond.operator = "!="
46✔
753
                case "!=":
×
754
                        newCond.operator = "=="
×
755
                case "<":
×
756
                        newCond.operator = ">="
×
757
                case "<=":
×
758
                        newCond.operator = ">"
×
759
                case ">":
2✔
760
                        newCond.operator = "<="
2✔
761
                case ">=":
×
762
                        newCond.operator = "<"
×
763
                case "exists":
62✔
764
                        newCond.operator = "not_exists"
62✔
765
                case "not_exists":
×
766
                        newCond.operator = "exists"
×
767
                case "match":
16✔
768
                        newCond.operator = "not_match"
16✔
769
                case "not_match":
×
770
                        newCond.operator = "match"
×
771
                case "search":
16✔
772
                        newCond.operator = "not_search"
16✔
773
                case "not_search":
×
774
                        newCond.operator = "search"
×
775
                default:
×
776
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("cannot negate operator: %s", newCond.operator), "")
×
777
                }
778
                return &conditionNode{cond: newCond}, nil
142✔
779
        case *andNode:
×
780
                children := make([]exprNode, len(n.children))
×
781
                for i, child := range n.children {
×
782
                        negated, err := negateNode(child)
×
783
                        if err != nil {
×
784
                                return nil, err
×
785
                        }
×
786
                        children[i] = negated
×
787
                }
788
                return &orNode{children: children}, nil
×
789
        case *orNode:
2✔
790
                children := make([]exprNode, len(n.children))
2✔
791
                for i, child := range n.children {
6✔
792
                        negated, err := negateNode(child)
4✔
793
                        if err != nil {
4✔
794
                                return nil, err
×
795
                        }
×
796
                        children[i] = negated
4✔
797
                }
798
                return &andNode{children: children}, nil
2✔
799
        default:
×
800
                return nil, NewError(ErrInvalidFilter, "cannot negate expression", "")
×
801
        }
802
}
803

804
// normalizeFilterWhitespace handles whitespace in filter expressions
805
// Removes whitespace between ! and ( to support expressions like "!\n(@.a=='b')"
806
func normalizeFilterWhitespace(content string) string {
2,672✔
807
        if len(content) < 2 {
2,688✔
808
                return content
16✔
809
        }
16✔
810
        // Check if content starts with ! followed by whitespace and then (
811
        if content[0] == '!' {
2,796✔
812
                // Find the first non-whitespace character after !
140✔
813
                i := 1
140✔
814
                for i < len(content) && (content[i] == ' ' || content[i] == '\t' || content[i] == '\n' || content[i] == '\r') {
204✔
815
                        i++
64✔
816
                }
64✔
817
                if i < len(content) && content[i] == '(' {
184✔
818
                        // Remove whitespace between ! and (
44✔
819
                        return "!" + content[i:]
44✔
820
                }
44✔
821
        }
822
        return content
2,612✔
823
}
824

825
// 解析过滤器表达式
826
func parseFilterSegment(content string) (segment, error) {
3,212✔
827
        // RFC 9535: allow whitespace in filter expressions
3,212✔
828
        content = strings.TrimSpace(content)
3,212✔
829

3,212✔
830
        // 检查是否是完整的函数调用格式: functionName(arg1, arg2)
3,212✔
831
        // 使用 tryParseFunctionCall 进行正确的括号匹配
3,212✔
832
        if funcName, argsStr, ok := tryParseFunctionCall(content); ok {
3,622✔
833
                cond, err := parseFilterFunctionCall(funcName, argsStr)
410✔
834
                if err != nil {
450✔
835
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("invalid filter syntax: %s", content), content)
40✔
836
                }
40✔
837
                return &filterSegment{expr: &conditionNode{cond: cond}}, nil
370✔
838
        }
839

840
        // 检查语法 - 支持 @, $, 函数调用, 和 ! 作为过滤器表达式的开头
841
        trimmed := strings.TrimSpace(content)
2,802✔
842
        isFunctionCallExpr := false
2,802✔
843
        if !strings.HasPrefix(trimmed, "@") && !strings.HasPrefix(trimmed, "$") &&
2,802✔
844
                !strings.HasPrefix(trimmed, "(@") && !strings.HasPrefix(trimmed, "($") &&
2,802✔
845
                !strings.HasPrefix(trimmed, "!") && !strings.HasPrefix(trimmed, "(!") &&
2,802✔
846
                !strings.HasPrefix(trimmed, "(") {
3,332✔
847
                // Check if it starts with a function name (e.g., count(@..*)>2, length(@.a)>=2)
530✔
848
                if idx := strings.Index(trimmed, "("); idx > 0 {
962✔
849
                        funcName := trimmed[:idx]
432✔
850
                        if !isValidFunctionName(funcName) {
464✔
851
                                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("invalid filter syntax: %s", content), content)
32✔
852
                        }
32✔
853
                        // Check if there's a top-level comparison operator (not inside function parens)
854
                        // If so, it's a comparison expression, not a standalone function call
855
                        if hasTopLevelOperator(trimmed) {
800✔
856
                                // It's a comparison expression with function calls - pass to expression parser
400✔
857
                                isFunctionCallExpr = true
400✔
858
                        } else {
400✔
859
                                // Standalone function call - pass to expression parser
×
860
                                isFunctionCallExpr = true
×
861
                        }
×
862
                } else {
98✔
863
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("invalid filter syntax: %s", content), content)
98✔
864
                }
98✔
865
        }
866

867
        // Normalize whitespace: remove whitespace between ! and (
868
        // e.g., "!\n(@.a=='b')" becomes "(!@.a=='b')"
869
        content = normalizeFilterWhitespace(content)
2,672✔
870

2,672✔
871
        // 取过滤器内容
2,672✔
872
        var filterContent string
2,672✔
873

2,672✔
874
        switch {
2,672✔
875
        case isFunctionCallExpr:
400✔
876
                // Function call expression (e.g., count(@..*)>2) - pass to expression parser
400✔
877
                filterContent = content
400✔
878
        case strings.HasPrefix(content, "(!"):
×
879
                if !strings.HasSuffix(content, ")") {
×
880
                        return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
881
                }
×
882
                filterContent = content[2 : len(content)-1]
×
883
                // Apply De Morgan's laws: !(A && B) => !A || !B, !(A || B) => !A && !B
×
884
                expr, err := parseFilterExpression(filterContent)
×
885
                if err != nil {
×
886
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error parsing filter expression: %v", err), content)
×
887
                }
×
888
                negated, err := negateNode(expr)
×
889
                if err != nil {
×
890
                        return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error negating expression: %v", err), content)
×
891
                }
×
892
                return &filterSegment{expr: negated}, nil
×
893
        case strings.HasPrefix(content, "!@"):
32✔
894
                // Keep the ! in the content for the parser to handle as unary operator
32✔
895
                filterContent = content
32✔
896
        case strings.HasPrefix(content, "(@"):
212✔
897
                // Check if outer parens match properly
212✔
898
                depth := 0
212✔
899
                matchIdx := -1
212✔
900
                for i := 0; i < len(content); i++ {
2,504✔
901
                        if content[i] == '(' {
2,504✔
902
                                depth++
212✔
903
                        } else if content[i] == ')' {
2,504✔
904
                                depth--
212✔
905
                                if depth == 0 {
424✔
906
                                        matchIdx = i
212✔
907
                                        break
212✔
908
                                }
909
                        }
910
                }
911
                if matchIdx == len(content)-1 {
416✔
912
                        // The first '(' matches the last ')' - strip outer (@...)
204✔
913
                        filterContent = content[2 : len(content)-1]
204✔
914
                } else {
212✔
915
                        // The first '(' doesn't match the last ')' - pass whole content to parser
8✔
916
                        // e.g., "(@.a || @.b) && @.c"
8✔
917
                        filterContent = content
8✔
918
                }
8✔
919
        case strings.HasPrefix(content, "($"):
120✔
920
                // Check if outer parens match properly
120✔
921
                depth := 0
120✔
922
                matchIdx := -1
120✔
923
                for i := 0; i < len(content); i++ {
1,704✔
924
                        if content[i] == '(' {
1,704✔
925
                                depth++
120✔
926
                        } else if content[i] == ')' {
1,704✔
927
                                depth--
120✔
928
                                if depth == 0 {
240✔
929
                                        matchIdx = i
120✔
930
                                        break
120✔
931
                                }
932
                        }
933
                }
934
                if matchIdx == len(content)-1 {
240✔
935
                        // The first '(' matches the last ')' - strip outer ($...)
120✔
936
                        filterContent = content[2 : len(content)-1]
120✔
937
                } else {
120✔
938
                        // The first '(' doesn't match the last ')' - pass whole content to parser
×
939
                        filterContent = content
×
940
                }
×
941
        case strings.HasPrefix(content, "@"):
1,774✔
942
                filterContent = content
1,774✔
943
        case strings.HasPrefix(content, "$"):
24✔
944
                filterContent = content
24✔
945
        case strings.HasPrefix(content, "!"):
108✔
946
                // Keep the ! in the content for the parser to handle as unary operator
108✔
947
                filterContent = content
108✔
948
                // Check for !(expr) pattern
108✔
949
                if len(content) > 1 && content[1] == '(' {
152✔
950
                        if !strings.HasSuffix(content, ")") {
44✔
951
                                return nil, NewError(ErrInvalidFilter, "invalid filter syntax: missing closing parenthesis", content)
×
952
                        }
×
953
                        // Apply De Morgan's laws for !(expr)
954
                        innerContent := content[2 : len(content)-1]
44✔
955
                        expr, err := parseFilterExpression(innerContent)
44✔
956
                        if err != nil {
44✔
957
                                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error parsing filter expression: %v", err), content)
×
958
                        }
×
959
                        negated, err := negateNode(expr)
44✔
960
                        if err != nil {
44✔
961
                                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error negating expression: %v", err), content)
×
962
                        }
×
963
                        return &filterSegment{expr: negated}, nil
44✔
964
                }
965
        case strings.HasPrefix(content, "("):
2✔
966
                // Check if the outer parens actually match (not just first and last char)
2✔
967
                depth := 0
2✔
968
                matchingIdx := -1
2✔
969
                for i := 0; i < len(content); i++ {
54✔
970
                        if content[i] == '(' {
56✔
971
                                depth++
4✔
972
                        } else if content[i] == ')' {
56✔
973
                                depth--
4✔
974
                                if depth == 0 {
6✔
975
                                        matchingIdx = i
2✔
976
                                        break
2✔
977
                                }
978
                        }
979
                }
980
                if matchingIdx == len(content)-1 {
4✔
981
                        // The first '(' matches the last ')' - strip outer parens
2✔
982
                        filterContent = content[1 : len(content)-1]
2✔
983
                } else {
2✔
984
                        // The first '(' doesn't match the last ')' - pass whole content to parser
×
985
                        // e.g., "(@.a || @.b) && @.c"
×
986
                        filterContent = content
×
987
                }
×
988
        default:
×
989
                filterContent = content
×
990
        }
991

992
        // 标准化表达式
993
        filterContent = normalizeFilterExpression(filterContent)
2,628✔
994

2,628✔
995
        // 解析表达式为树结构
2,628✔
996
        expr, err := parseFilterExpression(filterContent)
2,628✔
997
        if err != nil {
3,210✔
998
                return nil, NewError(ErrInvalidFilter, fmt.Sprintf("error parsing filter expression: %v", err), content)
582✔
999
        }
582✔
1000

1001
        return &filterSegment{expr: expr}, nil
2,046✔
1002
}
1003

1004
// 解析过滤器条件
1005
// isNonSingularQuery checks if a path contains non-singular selectors
1006
// (wildcard, slice, multi-index, descendant) which are not allowed in comparisons
1007
func isNonSingularQuery(field string) bool {
1,726✔
1008
        // Check for wildcard
1,726✔
1009
        if strings.Contains(field, "*") {
1,798✔
1010
                return true
72✔
1011
        }
72✔
1012
        // Check for descendant (..)
1013
        if strings.Contains(field, "..") {
1,662✔
1014
                return true
8✔
1015
        }
8✔
1016
        // Check for bracket expressions
1017
        if strings.Contains(field, "[") {
1,718✔
1018
                // Check for slice (:)
72✔
1019
                if strings.Contains(field, ":") {
80✔
1020
                        return true
8✔
1021
                }
8✔
1022
                // Check for multi-index (,) - but need to be careful about commas in strings
1023
                // Simple check: if there's a comma outside of quotes
1024
                inQuotes := false
64✔
1025
                for i := 0; i < len(field); i++ {
464✔
1026
                        if field[i] == '\'' || field[i] == '"' {
416✔
1027
                                inQuotes = !inQuotes
16✔
1028
                        } else if field[i] == ',' && !inQuotes {
416✔
1029
                                return true
16✔
1030
                        }
16✔
1031
                }
1032
        }
1033
        return false
1,622✔
1034
}
1035

1036
func parseFilterCondition(content string) (filterCondition, error) {
3,074✔
1037
        // 检查是否是完整的函数调用(无比较操作符,如 match(@.a, 'pattern'))
3,074✔
1038
        // 先用括号深度跟踪来确认整个内容是一个函数调用
3,074✔
1039
        if funcName, argsStr, ok := tryParseFunctionCall(content); ok {
3,106✔
1040
                return parseFilterFunctionCall(funcName, argsStr)
32✔
1041
        }
32✔
1042

1043
        // 查找比较操作符 - 使用括号深度和方括号深度跟踪避免匹配函数参数和嵌套括号内的操作符
1044
        var operator string
3,042✔
1045
        var operatorIndex int
3,042✔
1046
        var operatorFound bool
3,042✔
1047

3,042✔
1048
        // 按长度排序的操作符列表,确保先匹配较长的操作符
3,042✔
1049
        operators := []string{"<=", ">=", "==", "!=", "<", ">"}
3,042✔
1050
        for _, op := range operators {
15,646✔
1051
                // 从左到右查找第一个在顶层(括号深度和方括号深度均为0)的操作符
12,604✔
1052
                inQuotes := false
12,604✔
1053
                inSingleQuotes := false
12,604✔
1054
                parenDepth := 0
12,604✔
1055
                bracketDepth := 0
12,604✔
1056
                for i := 0; i <= len(content)-len(op); i++ {
86,020✔
1057
                        ch := content[i]
73,416✔
1058
                        if ch == '"' && !inSingleQuotes {
73,774✔
1059
                                inQuotes = !inQuotes
358✔
1060
                                continue
358✔
1061
                        }
1062
                        if ch == '\'' && !inQuotes {
74,978✔
1063
                                inSingleQuotes = !inSingleQuotes
1,920✔
1064
                                continue
1,920✔
1065
                        }
1066
                        if inQuotes || inSingleQuotes {
74,198✔
1067
                                continue
3,060✔
1068
                        }
1069
                        if ch == '(' {
69,662✔
1070
                                parenDepth++
1,584✔
1071
                                continue
1,584✔
1072
                        }
1073
                        if ch == ')' {
67,966✔
1074
                                parenDepth--
1,472✔
1075
                                continue
1,472✔
1076
                        }
1077
                        if ch == '[' {
66,006✔
1078
                                bracketDepth++
984✔
1079
                                continue
984✔
1080
                        }
1081
                        if ch == ']' {
64,910✔
1082
                                bracketDepth--
872✔
1083
                                continue
872✔
1084
                        }
1085
                        if parenDepth == 0 && bracketDepth == 0 && content[i:i+len(op)] == op {
65,308✔
1086
                                // Check it's not a false match (e.g., "!=", not part of "!==")
2,142✔
1087
                                isValid := true
2,142✔
1088
                                // Ensure we're not inside quotes (already handled above)
2,142✔
1089
                                if inQuotes || inSingleQuotes {
2,142✔
1090
                                        isValid = false
×
1091
                                }
×
1092
                                if isValid {
4,284✔
1093
                                        operator = op
2,142✔
1094
                                        operatorIndex = i
2,142✔
1095
                                        operatorFound = true
2,142✔
1096
                                        break
2,142✔
1097
                                }
1098
                        }
1099
                }
1100
                if operatorFound {
14,746✔
1101
                        break
2,142✔
1102
                }
1103
        }
1104

1105
        if !operatorFound {
3,942✔
1106
                // No operator found - this could be an existence test
900✔
1107
                field := strings.TrimSpace(content)
900✔
1108
                // Check for invalid operator-like characters at top level (not inside brackets)
900✔
1109
                if hasTopLevelOperatorChar(field) {
902✔
1110
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("no valid operator found in condition: %s", content), content)
2✔
1111
                }
2✔
1112

1113
                // Check if this is a function call without comparison (invalid per RFC 9535 for some functions)
1114
                if _, _, isFunc := tryParseFunctionCall(field); isFunc {
898✔
1115
                        // Standalone function calls like count(@..*), length(@.a), value(@.a)
×
1116
                        // are invalid - they must be used in comparisons or as filter tests
×
1117
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid filter syntax: %s", content), content)
×
1118
                }
×
1119

1120
                // Determine if field is root ($) or current (@)
1121
                isRoot := false
898✔
1122
                if strings.HasPrefix(field, "$") {
914✔
1123
                        isRoot = true
16✔
1124
                } else if strings.HasPrefix(field, "@") {
1,712✔
1125
                        // ok
814✔
1126
                } else if strings.HasPrefix(field, ".") {
950✔
1127
                        field = "@" + field
68✔
1128
                } else {
68✔
1129
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid condition: %s", content), content)
×
1130
                }
×
1131

1132
                // Strip field prefix (@ or $)
1133
                // Handle @.. (descendant) before @. to avoid incorrect stripping
1134
                if strings.HasPrefix(field, "@..") {
898✔
1135
                        field = field[1:] // strip just @, leaving ..
×
1136
                } else if strings.HasPrefix(field, "$..") {
898✔
1137
                        field = field[1:] // strip just $, leaving ..
×
1138
                } else {
898✔
1139
                        field = strings.TrimPrefix(field, "@.")
898✔
1140
                        field = strings.TrimPrefix(field, "$.")
898✔
1141
                        field = strings.TrimPrefix(field, "@")
898✔
1142
                        field = strings.TrimPrefix(field, "$")
898✔
1143
                }
898✔
1144
                return filterCondition{
898✔
1145
                        field:    field,
898✔
1146
                        operator: "exists",
898✔
1147
                        value:    nil,
898✔
1148
                        isRoot:   isRoot,
898✔
1149
                }, nil
898✔
1150
        }
1151

1152
        // 分割路径和值
1153
        left := strings.TrimSpace(content[:operatorIndex])
2,142✔
1154
        right := strings.TrimSpace(content[operatorIndex+len(operator):])
2,142✔
1155

2,142✔
1156
        // Check if the left side is a function call (e.g., length(@.a) == value($..c), count(@..*)>2)
2,142✔
1157
        if leftFuncName, leftArgsStr, isLeftFunc := tryParseFunctionCall(left); isLeftFunc {
2,542✔
1158
                // Reject match/search results being compared with booleans
400✔
1159
                if leftFuncName == "match" || leftFuncName == "search" {
432✔
1160
                        if operator == "==" || operator == "!=" {
64✔
1161
                                if right == "true" || right == "false" {
48✔
1162
                                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("%s() result cannot be compared with boolean", leftFuncName), content)
16✔
1163
                                }
16✔
1164
                        }
1165
                }
1166

1167
                // Validate function arguments
1168
                if leftFuncName == "match" || leftFuncName == "search" {
400✔
1169
                        // For match/search, only validate param count (not types)
16✔
1170
                        if err := validateFunctionParamCount(leftFuncName, leftArgsStr); err != nil {
32✔
1171
                                return filterCondition{}, NewError(ErrInvalidFilter, err.Error(), content)
16✔
1172
                        }
16✔
1173
                } else {
368✔
1174
                        if err := validateFunctionArgs(leftFuncName, leftArgsStr); err != nil {
480✔
1175
                                return filterCondition{}, NewError(ErrInvalidFilter, err.Error(), content)
112✔
1176
                        }
112✔
1177
                }
1178

1179
                // Parse the right side value
1180
                parsedValue, err := parseFilterValue(right)
256✔
1181
                if err != nil {
304✔
1182
                        // Right side might also be a function call
48✔
1183
                        if rightFuncName, rightArgsStr, isRightFunc := tryParseFunctionCall(right); isRightFunc {
96✔
1184
                                // Validate right side function arguments
48✔
1185
                                if rightFuncName == "match" || rightFuncName == "search" {
48✔
1186
                                        if err := validateFunctionParamCount(rightFuncName, rightArgsStr); err != nil {
×
1187
                                                return filterCondition{}, NewError(ErrInvalidFilter, err.Error(), content)
×
1188
                                        }
×
1189
                                } else {
48✔
1190
                                        if err := validateFunctionArgs(rightFuncName, rightArgsStr); err != nil {
48✔
1191
                                                return filterCondition{}, NewError(ErrInvalidFilter, err.Error(), content)
×
1192
                                        }
×
1193
                                }
1194
                                // Both sides are function calls - store the whole expression for runtime evaluation
1195
                                return filterCondition{
48✔
1196
                                        field:    left,
48✔
1197
                                        operator: operator,
48✔
1198
                                        value:    right,
48✔
1199
                                        isRoot:   false,
48✔
1200
                                }, nil
48✔
1201
                        }
1202
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid value: %s", right), content)
×
1203
                }
1204

1205
                return filterCondition{
208✔
1206
                        field:    left,
208✔
1207
                        operator: operator,
208✔
1208
                        value:    parsedValue,
208✔
1209
                        isRoot:   false,
208✔
1210
                }, nil
208✔
1211
        }
1212

1213
        // Determine isRoot from the left side
1214
        isRoot := false
1,742✔
1215
        if strings.HasPrefix(left, "$") {
1,750✔
1216
                isRoot = true
8✔
1217
        } else if !strings.HasPrefix(left, "@") {
1,998✔
1218
                if strings.HasPrefix(left, ".") {
320✔
1219
                        left = "@" + left
64✔
1220
                } else {
256✔
1221
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid condition: %s", content), content)
192✔
1222
                }
192✔
1223
        }
1224

1225
        // RFC 9535: non-singular paths are not allowed in comparisons
1226
        if isNonSingularQuery(left) {
1,630✔
1227
                return filterCondition{}, NewError(ErrInvalidFilter, "non-singular query is not allowed in comparison", content)
80✔
1228
        }
80✔
1229

1230
        // 解析值
1231
        parsedValue, err := parseFilterValue(right)
1,470✔
1232
        if err != nil {
1,642✔
1233
                // Right side might be a function call
172✔
1234
                if _, _, isRightFunc := tryParseFunctionCall(right); isRightFunc {
180✔
1235
                        parsedValue = right // Store as string for runtime evaluation
8✔
1236
                } else {
172✔
1237
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid value: %s", right), content)
164✔
1238
                }
164✔
1239
        }
1240

1241
        // Strip field prefix (@ or $)
1242
        field := strings.TrimPrefix(left, "@.")
1,306✔
1243
        field = strings.TrimPrefix(field, "$.")
1,306✔
1244
        field = strings.TrimPrefix(field, "@")
1,306✔
1245
        field = strings.TrimPrefix(field, "$")
1,306✔
1246

1,306✔
1247
        return filterCondition{
1,306✔
1248
                field:    field,
1,306✔
1249
                operator: operator,
1,306✔
1250
                value:    parsedValue,
1,306✔
1251
                isRoot:   isRoot,
1,306✔
1252
        }, nil
1,306✔
1253
}
1254

1255
// tryParseFunctionCall attempts to parse content as a function call.
1256
// Returns (funcName, argsStr, true) if successful, ("", "", false) otherwise.
1257
func tryParseFunctionCall(content string) (string, string, bool) {
9,738✔
1258
        content = strings.TrimSpace(content)
9,738✔
1259
        if !strings.HasSuffix(content, ")") {
18,038✔
1260
                return "", "", false
8,300✔
1261
        }
8,300✔
1262
        // Find the opening paren
1263
        idx := strings.Index(content, "(")
1,438✔
1264
        if idx <= 0 {
1,764✔
1265
                return "", "", false
326✔
1266
        }
326✔
1267
        funcName := content[:idx]
1,112✔
1268
        if !isValidFunctionName(funcName) {
1,214✔
1269
                return "", "", false
102✔
1270
        }
102✔
1271
        // Find the matching ')' for the '(' at idx
1272
        depth := 0
1,010✔
1273
        matchingIdx := -1
1,010✔
1274
        for i := idx; i < len(content); i++ {
9,856✔
1275
                if content[i] == '(' {
9,888✔
1276
                        depth++
1,042✔
1277
                } else if content[i] == ')' {
9,888✔
1278
                        depth--
1,042✔
1279
                        if depth == 0 {
2,052✔
1280
                                matchingIdx = i
1,010✔
1281
                                break
1,010✔
1282
                        }
1283
                }
1284
        }
1285
        // The matching ')' must be at the end of the string
1286
        if matchingIdx != len(content)-1 {
1,106✔
1287
                return "", "", false
96✔
1288
        }
96✔
1289
        argsStr := content[idx+1 : len(content)-1]
914✔
1290
        return funcName, argsStr, true
914✔
1291
}
1292

1293
// hasTopLevelOperator checks if content has a comparison operator at the top level
1294
// (not inside parentheses, brackets, or quotes)
1295
func hasTopLevelOperator(content string) bool {
400✔
1296
        operators := []string{"<=", ">=", "==", "!=", "<", ">"}
400✔
1297
        for _, op := range operators {
1,808✔
1298
                inQuotes := false
1,408✔
1299
                inSingleQuotes := false
1,408✔
1300
                parenDepth := 0
1,408✔
1301
                bracketDepth := 0
1,408✔
1302
                for i := 0; i <= len(content)-len(op); i++ {
22,344✔
1303
                        ch := content[i]
20,936✔
1304
                        if (inQuotes || inSingleQuotes) && ch == '\\' && i+1 < len(content) {
20,936✔
1305
                                i++
×
1306
                                continue
×
1307
                        }
1308
                        if ch == '"' && !inSingleQuotes {
20,936✔
1309
                                inQuotes = !inQuotes
×
1310
                                continue
×
1311
                        }
1312
                        if ch == '\'' && !inQuotes {
21,480✔
1313
                                inSingleQuotes = !inSingleQuotes
544✔
1314
                                continue
544✔
1315
                        }
1316
                        if inQuotes || inSingleQuotes {
21,000✔
1317
                                continue
608✔
1318
                        }
1319
                        if ch == '(' {
21,352✔
1320
                                parenDepth++
1,568✔
1321
                                continue
1,568✔
1322
                        }
1323
                        if ch == ')' {
19,688✔
1324
                                parenDepth--
1,472✔
1325
                                continue
1,472✔
1326
                        }
1327
                        if ch == '[' {
16,936✔
1328
                                bracketDepth++
192✔
1329
                                continue
192✔
1330
                        }
1331
                        if ch == ']' {
16,744✔
1332
                                bracketDepth--
192✔
1333
                                continue
192✔
1334
                        }
1335
                        if parenDepth == 0 && bracketDepth == 0 && content[i:i+len(op)] == op {
16,760✔
1336
                                return true
400✔
1337
                        }
400✔
1338
                }
1339
        }
1340
        return false
×
1341
}
1342

1343
// hasTopLevelOperatorChar checks if content has operator-like characters (=, <, >, !)
1344
// at the top level (not inside brackets or quotes)
1345
func hasTopLevelOperatorChar(content string) bool {
900✔
1346
        inQuotes := false
900✔
1347
        inSingleQuotes := false
900✔
1348
        bracketDepth := 0
900✔
1349
        for i := 0; i < len(content); i++ {
3,734✔
1350
                ch := content[i]
2,834✔
1351
                if (inQuotes || inSingleQuotes) && ch == '\\' && i+1 < len(content) {
2,834✔
1352
                        i++
×
1353
                        continue
×
1354
                }
1355
                if ch == '"' && !inSingleQuotes {
2,834✔
1356
                        inQuotes = !inQuotes
×
1357
                        continue
×
1358
                }
1359
                if ch == '\'' && !inQuotes {
2,850✔
1360
                        inSingleQuotes = !inSingleQuotes
16✔
1361
                        continue
16✔
1362
                }
1363
                if inQuotes || inSingleQuotes {
2,826✔
1364
                        continue
8✔
1365
                }
1366
                if ch == '[' {
2,834✔
1367
                        bracketDepth++
24✔
1368
                        continue
24✔
1369
                }
1370
                if ch == ']' {
2,810✔
1371
                        bracketDepth--
24✔
1372
                        continue
24✔
1373
                }
1374
                if bracketDepth == 0 && (ch == '=' || ch == '<' || ch == '>' || ch == '!') {
2,764✔
1375
                        return true
2✔
1376
                }
2✔
1377
        }
1378
        return false
898✔
1379
}
1380

1381
// validateFunctionArgs validates function arguments per RFC 9535 rules
1382
func validateFunctionArgs(funcName, argsStr string) error {
440✔
1383
        args, err := parseFunctionArgsList(argsStr)
440✔
1384
        if err != nil {
440✔
1385
                return fmt.Errorf("invalid function arguments: %v", err)
×
1386
        }
×
1387

1388
        switch funcName {
440✔
1389
        case "length":
240✔
1390
                if len(args) != 1 {
256✔
1391
                        return fmt.Errorf("length() requires exactly 1 argument")
16✔
1392
                }
16✔
1393
                // length() argument must be a singular query (not non-singular)
1394
                // But allow function calls like value() as arguments
1395
                if argStr, ok := args[0].(string); ok {
416✔
1396
                        // Allow function calls (e.g., value($..c))
192✔
1397
                        if _, _, isFunc := tryParseFunctionCall(argStr); isFunc {
208✔
1398
                                // Function calls are allowed as arguments
16✔
1399
                        } else if isNonSingularQuery(argStr) {
216✔
1400
                                return fmt.Errorf("length() argument must be a singular query")
24✔
1401
                        }
24✔
1402
                }
1403
        case "count":
152✔
1404
                if len(args) != 1 {
168✔
1405
                        return fmt.Errorf("count() requires exactly 1 argument")
16✔
1406
                }
16✔
1407
                // count() argument must be a nodelist (path starting with @ or $)
1408
                argStr, ok := args[0].(string)
136✔
1409
                if !ok {
168✔
1410
                        return fmt.Errorf("count() argument must be a nodelist")
32✔
1411
                }
32✔
1412
                if !strings.HasPrefix(argStr, "@") && !strings.HasPrefix(argStr, "$") {
112✔
1413
                        return fmt.Errorf("count() argument must be a nodelist")
8✔
1414
                }
8✔
1415
        case "value":
48✔
1416
                if len(args) != 1 {
64✔
1417
                        return fmt.Errorf("value() requires exactly 1 argument")
16✔
1418
                }
16✔
1419
                // value() argument must be a nodelist (path starting with @ or $)
1420
                argStr, ok := args[0].(string)
32✔
1421
                if !ok {
32✔
1422
                        return fmt.Errorf("value() argument must be a nodelist")
×
1423
                }
×
1424
                if !strings.HasPrefix(argStr, "@") && !strings.HasPrefix(argStr, "$") {
32✔
1425
                        return fmt.Errorf("value() argument must be a nodelist")
×
1426
                }
×
1427
        case "match", "search":
×
1428
                if len(args) != 2 {
×
1429
                        return fmt.Errorf("%s() requires exactly 2 arguments", funcName)
×
1430
                }
×
1431
        }
1432
        return nil
328✔
1433
}
1434

1435
// validateFunctionParamCount validates only the parameter count for functions
1436
func validateFunctionParamCount(funcName, argsStr string) error {
16✔
1437
        args, err := parseFunctionArgsList(argsStr)
16✔
1438
        if err != nil {
16✔
1439
                return fmt.Errorf("invalid function arguments: %v", err)
×
1440
        }
×
1441

1442
        switch funcName {
16✔
1443
        case "length", "count", "value":
×
1444
                if len(args) != 1 {
×
1445
                        return fmt.Errorf("%s() requires exactly 1 argument", funcName)
×
1446
                }
×
1447
        case "match", "search":
16✔
1448
                if len(args) != 2 {
32✔
1449
                        return fmt.Errorf("%s() requires exactly 2 arguments", funcName)
16✔
1450
                }
16✔
1451
        }
1452
        return nil
×
1453
}
1454

1455
// parseFilterFunctionCall 解析过滤器中的函数调用
1456
func parseFilterFunctionCall(funcName, argsStr string) (filterCondition, error) {
442✔
1457
        // 解析参数
442✔
1458
        args, err := parseFunctionArgsList(argsStr)
442✔
1459
        if err != nil {
442✔
1460
                return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("invalid function arguments: %v", err), funcName+"("+argsStr+")")
×
1461
        }
×
1462

1463
        // Validate function arguments (but not for match/search - they accept any types)
1464
        if funcName != "match" && funcName != "search" {
466✔
1465
                if err := validateFunctionArgs(funcName, argsStr); err != nil {
24✔
1466
                        return filterCondition{}, NewError(ErrInvalidFilter, err.Error(), funcName+"("+argsStr+")")
×
1467
                }
×
1468
        }
1469

1470
        // 对于 match 和 search 函数,需要两个参数
1471
        if funcName == "match" || funcName == "search" {
860✔
1472
                if len(args) != 2 {
434✔
1473
                        return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("%s() requires exactly 2 arguments", funcName), funcName+"("+argsStr+")")
16✔
1474
                }
16✔
1475

1476
                // 第一个参数是字段路径或值
1477
                field := fmt.Sprintf("%v", args[0])
402✔
1478

402✔
1479
                // 第二个参数是模式
402✔
1480
                pattern := fmt.Sprintf("%v", args[1])
402✔
1481

402✔
1482
                return filterCondition{
402✔
1483
                        field:    strings.TrimPrefix(field, "@."),
402✔
1484
                        operator: funcName,
402✔
1485
                        value:    pattern,
402✔
1486
                }, nil
402✔
1487
        }
1488

1489
        // 对于其他函数,创建一个通用的函数调用条件
1490
        // count, length, value must be used in comparisons (not standalone)
1491
        if funcName == "count" || funcName == "length" || funcName == "value" {
48✔
1492
                return filterCondition{}, NewError(ErrInvalidFilter, fmt.Sprintf("%s() result must be compared", funcName), funcName+"("+argsStr+")")
24✔
1493
        }
24✔
1494
        return filterCondition{
×
1495
                field:    "",
×
1496
                operator: "function:" + funcName,
×
1497
                value:    args,
×
1498
        }, nil
×
1499
}
1500

1501
// compareValues compares two values based on the operator
1502
func compareValues(value1 interface{}, operator string, value2 interface{}) (bool, error) {
4,000✔
1503
        // 检查操作符是否有效
4,000✔
1504
        validOperators := map[string]bool{
4,000✔
1505
                "==":    true,
4,000✔
1506
                "!=":    true,
4,000✔
1507
                ">":     true,
4,000✔
1508
                "<":     true,
4,000✔
1509
                ">=":    true,
4,000✔
1510
                "<=":    true,
4,000✔
1511
                "match": true,
4,000✔
1512
        }
4,000✔
1513
        if !validOperators[operator] {
4,010✔
1514
                return false, fmt.Errorf("invalid operator: %s", operator)
10✔
1515
        }
10✔
1516

1517
        // Handle Nothing values
1518
        _, isNothing1 := value1.(Nothing)
3,990✔
1519
        _, isNothing2 := value2.(Nothing)
3,990✔
1520
        if isNothing1 || isNothing2 {
4,238✔
1521
                switch operator {
248✔
1522
                case "==":
168✔
1523
                        return isNothing1 && isNothing2, nil // Nothing == Nothing → true
168✔
1524
                case "!=":
8✔
1525
                        return !(isNothing1 && isNothing2), nil // Nothing != Nothing → false
8✔
1526
                default:
72✔
1527
                        return false, nil
72✔
1528
                }
1529
        }
1530

1531
        // 处理 nil 值
1532
        if value1 == nil || value2 == nil {
3,900✔
1533
                switch operator {
158✔
1534
                case "==":
62✔
1535
                        return value1 == value2, nil
62✔
1536
                case "!=":
32✔
1537
                        return value1 != value2, nil
32✔
1538
                case "<=", ">=":
32✔
1539
                        // null <= null and null >= null are true (same type comparison)
32✔
1540
                        return value1 == value2, nil
32✔
1541
                default:
32✔
1542
                        return false, nil
32✔
1543
                }
1544
        }
1545

1546
        // 处理数字类型
1547
        num1, num2, isNum := normalizeNumbers(value1, value2)
3,584✔
1548
        if isNum {
5,758✔
1549
                switch operator {
2,174✔
1550
                case "==":
926✔
1551
                        return num1 == num2, nil
926✔
1552
                case "!=":
152✔
1553
                        return num1 != num2, nil
152✔
1554
                case ">":
378✔
1555
                        return num1 > num2, nil
378✔
1556
                case "<":
238✔
1557
                        return num1 < num2, nil
238✔
1558
                case ">=":
258✔
1559
                        return num1 >= num2, nil
258✔
1560
                case "<=":
222✔
1561
                        return num1 <= num2, nil
222✔
1562
                default:
×
1563
                        return false, fmt.Errorf("invalid operator for numbers: %s", operator)
×
1564
                }
1565
        }
1566

1567
        // 处理字符串类型
1568
        if str1, ok := value1.(string); ok {
2,494✔
1569
                if str2, ok := value2.(string); ok {
1,872✔
1570
                        switch operator {
788✔
1571
                        case "==":
410✔
1572
                                return str1 == str2, nil
410✔
1573
                        case "!=":
212✔
1574
                                return str1 != str2, nil
212✔
1575
                        case ">":
54✔
1576
                                return str1 > str2, nil
54✔
1577
                        case "<":
32✔
1578
                                return str1 < str2, nil
32✔
1579
                        case ">=":
48✔
1580
                                return str1 >= str2, nil
48✔
1581
                        case "<=":
32✔
1582
                                return str1 <= str2, nil
32✔
1583
                        case "match":
×
1584
                                re, err := regexp.Compile(str2)
×
1585
                                if err != nil {
×
1586
                                        return false, fmt.Errorf("invalid regex pattern: %s", str2)
×
1587
                                }
×
1588
                                return re.MatchString(str1), nil
×
1589
                        default:
×
1590
                                return false, fmt.Errorf("invalid operator for strings: %s", operator)
×
1591
                        }
1592
                }
1593
                if operator == "match" {
296✔
1594
                        return false, fmt.Errorf("pattern must be a string")
×
1595
                }
×
1596
        }
1597
        if operator == "match" {
622✔
1598
                return false, fmt.Errorf("value must be a string")
×
1599
        }
×
1600

1601
        // 处理布尔类型
1602
        if bool1, ok := value1.(bool); ok {
788✔
1603
                if bool2, ok := value2.(bool); ok {
284✔
1604
                        switch operator {
118✔
1605
                        case "==":
38✔
1606
                                return bool1 == bool2, nil
38✔
1607
                        case "!=":
16✔
1608
                                return bool1 != bool2, nil
16✔
1609
                        case "<=", ">=":
32✔
1610
                                // true <= true, false <= false are true (same type comparison)
32✔
1611
                                return bool1 == bool2, nil
32✔
1612
                        default:
32✔
1613
                                return false, nil
32✔
1614
                        }
1615
                }
1616
        }
1617

1618
        // 处理数组类型 - RFC 9535 支持深度比较
1619
        if arr1, ok := value1.([]interface{}); ok {
576✔
1620
                if arr2, ok := value2.([]interface{}); ok {
144✔
1621
                        switch operator {
72✔
1622
                        case "==":
72✔
1623
                                return deepCompareValues(arr1, arr2), nil
72✔
1624
                        case "!=":
×
1625
                                return !deepCompareValues(arr1, arr2), nil
×
1626
                        default:
×
1627
                                return false, nil
×
1628
                        }
1629
                }
1630
        }
1631

1632
        // 处理对象类型 - RFC 9535 支持深度比较
1633
        if obj1, ok := value1.(map[string]interface{}); ok {
472✔
1634
                if obj2, ok := value2.(map[string]interface{}); ok {
80✔
1635
                        switch operator {
40✔
1636
                        case "==":
40✔
1637
                                return deepCompareValues(obj1, obj2), nil
40✔
1638
                        case "!=":
×
1639
                                return !deepCompareValues(obj1, obj2), nil
×
1640
                        default:
×
1641
                                return false, nil
×
1642
                        }
1643
                }
1644
        }
1645

1646
        // RFC 9535: comparison between incompatible types
1647
        // == returns false, != returns true
1648
        if operator == "!=" {
456✔
1649
                return true, nil
64✔
1650
        }
64✔
1651
        return false, nil
328✔
1652
}
1653

1654
// deepCompareValues performs deep comparison of two values
1655
func deepCompareValues(a, b interface{}) bool {
551✔
1656
        switch v1 := a.(type) {
551✔
1657
        case []interface{}:
152✔
1658
                v2, ok := b.([]interface{})
152✔
1659
                if !ok || len(v1) != len(v2) {
160✔
1660
                        return false
8✔
1661
                }
8✔
1662
                for i := range v1 {
464✔
1663
                        if !deepCompareValues(v1[i], v2[i]) {
352✔
1664
                                return false
32✔
1665
                        }
32✔
1666
                }
1667
                return true
112✔
1668
        case map[string]interface{}:
104✔
1669
                v2, ok := b.(map[string]interface{})
104✔
1670
                if !ok || len(v1) != len(v2) {
112✔
1671
                        return false
8✔
1672
                }
8✔
1673
                for k, val1 := range v1 {
215✔
1674
                        val2, exists := v2[k]
119✔
1675
                        if !exists || !deepCompareValues(val1, val2) {
135✔
1676
                                return false
16✔
1677
                        }
16✔
1678
                }
1679
                return true
80✔
1680
        default:
295✔
1681
                return a == b
295✔
1682
        }
1683
}
1684

1685
// normalizeNumbers 将两个值转换为 float64 类型
1686
func normalizeNumbers(value1, value2 interface{}) (float64, float64, bool) {
3,584✔
1687
        var num1, num2 float64
3,584✔
1688
        var ok1, ok2 bool
3,584✔
1689

3,584✔
1690
        // 尝试将 value1 转换为 float64
3,584✔
1691
        switch v := value1.(type) {
3,584✔
1692
        case float64:
2,182✔
1693
                num1, ok1 = v, true
2,182✔
1694
        case int64:
×
1695
                num1, ok1 = float64(v), true
×
1696
        case int:
40✔
1697
                num1, ok1 = float64(v), true
40✔
1698
        }
1699

1700
        // 尝试将 value2 转换为 float64
1701
        switch v := value2.(type) {
3,584✔
1702
        case float64:
2,374✔
1703
                num2, ok2 = v, true
2,374✔
1704
        case int64:
×
1705
                num2, ok2 = float64(v), true
×
1706
        case int:
×
1707
                num2, ok2 = float64(v), true
×
1708
        }
1709

1710
        return num1, num2, ok1 && ok2
3,584✔
1711
}
1712

1713
// compareStrings compares two strings using the specified operator
1714
func compareStrings(a string, operator string, b string) bool {
40✔
1715
        return standardCompareStrings(a, operator, b)
40✔
1716
}
40✔
1717

1718
// getFieldValue 获取对象中指定字段的值
1719
func getFieldValue(obj interface{}, field string) (interface{}, error) {
8,016✔
1720
        // 移除 @ 和前导点
8,016✔
1721
        field = strings.TrimPrefix(field, "@")
8,016✔
1722
        field = strings.TrimPrefix(field, ".")
8,016✔
1723

8,016✔
1724
        // 如果字段为空,返回对象本身
8,016✔
1725
        if field == "" {
9,546✔
1726
                return obj, nil
1,530✔
1727
        }
1,530✔
1728

1729
        // 分割字段路径
1730
        parts := strings.Split(field, ".")
6,486✔
1731
        current := obj
6,486✔
1732

6,486✔
1733
        for _, part := range parts {
12,984✔
1734
                // 确保当前是对象
6,498✔
1735
                m, ok := current.(map[string]interface{})
6,498✔
1736
                if !ok {
6,512✔
1737
                        return nil, fmt.Errorf("value is not an object")
14✔
1738
                }
14✔
1739

1740
                // 获取下一级字段值
1741
                var exists bool
6,484✔
1742
                current, exists = m[part]
6,484✔
1743
                if !exists {
7,582✔
1744
                        return nil, fmt.Errorf("field %s not found", part)
1,098✔
1745
                }
1,098✔
1746
        }
1747

1748
        return current, nil
5,374✔
1749
}
1750

1751
// 解析多索引选择 - RFC 9535 支持混合选择器类型
1752
func parseMultiIndexSegment(content string) (segment, error) {
352✔
1753
        // 检查前导和尾随逗号
352✔
1754
        if strings.HasPrefix(content, ",") {
362✔
1755
                return nil, NewError(ErrInvalidPath, "leading comma in multi-index segment", content)
10✔
1756
        }
10✔
1757
        if strings.HasSuffix(content, ",") {
352✔
1758
                return nil, NewError(ErrInvalidPath, "trailing comma in multi-index segment", content)
10✔
1759
        }
10✔
1760

1761
        parts := splitTopLevel(content, ',')
332✔
1762

332✔
1763
        // 检查空索引
332✔
1764
        for _, part := range parts {
1,072✔
1765
                if strings.TrimSpace(part) == "" {
742✔
1766
                        return nil, NewError(ErrInvalidPath, "empty index in multi-index segment", content)
2✔
1767
                }
2✔
1768
        }
1769

1770
        // RFC 9535: 每个部分独立解析,支持混合选择器类型
1771
        // 先尝试解析每个部分
1772
        selectors := make([]segment, 0, len(parts))
330✔
1773
        allIndices := true
330✔
1774
        allNames := true
330✔
1775

330✔
1776
        for _, part := range parts {
1,066✔
1777
                trimmed := strings.TrimSpace(part)
736✔
1778

736✔
1779
                // 检查是否是过滤器表达式
736✔
1780
                if strings.HasPrefix(trimmed, "?") {
816✔
1781
                        filter, err := parseFilterSegment(trimmed[1:])
80✔
1782
                        if err != nil {
80✔
1783
                                return nil, err
×
1784
                        }
×
1785
                        selectors = append(selectors, filter)
80✔
1786
                        allIndices = false
80✔
1787
                        allNames = false
80✔
1788
                        continue
80✔
1789
                }
1790

1791
                // 检查是否是通配符
1792
                if trimmed == "*" {
704✔
1793
                        selectors = append(selectors, &wildcardSegment{})
48✔
1794
                        allIndices = false
48✔
1795
                        allNames = false
48✔
1796
                        continue
48✔
1797
                }
1798

1799
                // 检查是否是切片
1800
                if strings.Contains(trimmed, ":") {
648✔
1801
                        slice, err := parseSliceSegment(trimmed)
40✔
1802
                        if err != nil {
40✔
1803
                                return nil, err
×
1804
                        }
×
1805
                        selectors = append(selectors, slice)
40✔
1806
                        allIndices = false
40✔
1807
                        allNames = false
40✔
1808
                        continue
40✔
1809
                }
1810

1811
                // 检查是否是带引号的字符串
1812
                if (strings.HasPrefix(trimmed, "'") && strings.HasSuffix(trimmed, "'")) ||
568✔
1813
                        (strings.HasPrefix(trimmed, "\"") && strings.HasSuffix(trimmed, "\"")) {
846✔
1814
                        quoteChar := trimmed[0]
278✔
1815
                        name := trimmed[1 : len(trimmed)-1]
278✔
1816
                        if !validateQuotedString(name, quoteChar) {
278✔
1817
                                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", trimmed), trimmed)
×
1818
                        }
×
1819
                        unescaped, err := unescapeString(name, quoteChar)
278✔
1820
                        if err != nil {
278✔
1821
                                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", trimmed), trimmed)
×
1822
                        }
×
1823
                        selectors = append(selectors, &nameSegment{name: unescaped})
278✔
1824
                        allIndices = false
278✔
1825
                        continue
278✔
1826
                }
1827

1828
                // 尝试解析为数字索引
1829
                if idx, err := strconv.Atoi(trimmed); err == nil {
526✔
1830
                        selectors = append(selectors, &indexSegment{index: idx})
236✔
1831
                        allNames = false
236✔
1832
                        continue
236✔
1833
                }
1834

1835
                // 不是数字也不是带引号的字符串,可能是不带引号的字段名
1836
                selectors = append(selectors, &nameSegment{name: trimmed})
54✔
1837
                allIndices = false
54✔
1838
        }
1839

1840
        // 如果所有部分都是索引,返回多索引段
1841
        if allIndices {
356✔
1842
                indices := make([]int, len(selectors))
26✔
1843
                for i, sel := range selectors {
88✔
1844
                        indices[i] = sel.(*indexSegment).index
62✔
1845
                }
62✔
1846
                return &multiIndexSegment{indices: indices}, nil
26✔
1847
        }
1848

1849
        // 如果所有部分都是名称,返回多名称段
1850
        if allNames {
428✔
1851
                names := make([]string, len(selectors))
124✔
1852
                for i, sel := range selectors {
370✔
1853
                        names[i] = sel.(*nameSegment).name
246✔
1854
                }
246✔
1855
                return &multiNameSegment{names: names}, nil
124✔
1856
        }
1857

1858
        // 混合类型,返回联合段
1859
        return &unionSegment{selectors: selectors}, nil
180✔
1860
}
1861

1862
// removeWhitespaceAroundColons removes whitespace around colons in slice expressions
1863
func removeWhitespaceAroundColons(content string) string {
836✔
1864
        var result []byte
836✔
1865
        inQuotes := false
836✔
1866
        inSingleQuotes := false
836✔
1867
        for i := 0; i < len(content); i++ {
10,218✔
1868
                ch := content[i]
9,382✔
1869
                if (inQuotes || inSingleQuotes) && ch == '\\' && i+1 < len(content) {
9,382✔
1870
                        result = append(result, ch)
×
1871
                        i++
×
1872
                        result = append(result, content[i])
×
1873
                        continue
×
1874
                }
1875
                if ch == '"' && !inSingleQuotes {
9,382✔
1876
                        inQuotes = !inQuotes
×
1877
                        result = append(result, ch)
×
1878
                        continue
×
1879
                }
1880
                if ch == '\'' && !inQuotes {
9,382✔
1881
                        inSingleQuotes = !inSingleQuotes
×
1882
                        result = append(result, ch)
×
1883
                        continue
×
1884
                }
1885
                if inQuotes || inSingleQuotes {
9,382✔
1886
                        result = append(result, ch)
×
1887
                        continue
×
1888
                }
1889
                if ch == ':' {
10,818✔
1890
                        // Remove trailing whitespace before colon
1,436✔
1891
                        for len(result) > 0 {
2,690✔
1892
                                lastByte := result[len(result)-1]
1,254✔
1893
                                if lastByte == ' ' || lastByte == '\t' || lastByte == '\n' || lastByte == '\r' {
1,318✔
1894
                                        result = result[:len(result)-1]
64✔
1895
                                } else {
1,254✔
1896
                                        break
1,190✔
1897
                                }
1898
                        }
1899
                        result = append(result, ch)
1,436✔
1900
                        // Skip whitespace after colon
1,436✔
1901
                        for i+1 < len(content) {
2,716✔
1902
                                nextCh := content[i+1]
1,280✔
1903
                                if nextCh == ' ' || nextCh == '\t' || nextCh == '\n' || nextCh == '\r' {
1,344✔
1904
                                        i++
64✔
1905
                                } else {
1,280✔
1906
                                        break
1,216✔
1907
                                }
1908
                        }
1909
                } else {
7,946✔
1910
                        result = append(result, ch)
7,946✔
1911
                }
7,946✔
1912
        }
1913
        return string(result)
836✔
1914
}
1915

1916
// 解析切片表达式
1917
func parseSliceSegment(content string) (segment, error) {
836✔
1918
        // Trim whitespace around colons (RFC 9535 allows whitespace in slice expressions)
836✔
1919
        content = strings.TrimSpace(content)
836✔
1920
        // Remove whitespace around colons
836✔
1921
        content = removeWhitespaceAroundColons(content)
836✔
1922

836✔
1923
        parts := strings.Split(content, ":")
836✔
1924
        if len(parts) > 3 {
846✔
1925
                return nil, NewError(ErrSyntax, "slice has too many colons", content)
10✔
1926
        }
10✔
1927

1928
        slice := &sliceSegment{start: 0, end: 0, step: 1}
826✔
1929
        hasStart := false
826✔
1930
        hasEnd := false
826✔
1931

826✔
1932
        // 解析起始索引
826✔
1933
        if parts[0] != "" {
1,406✔
1934
                if !validateIntegerLiteral(parts[0]) {
622✔
1935
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice start index: %s", parts[0]), parts[0])
42✔
1936
                }
42✔
1937
                // Reject -0 (negative zero) per RFC 9535
1938
                if parts[0] == "-0" {
546✔
1939
                        return nil, NewError(ErrSyntax, "negative zero is not a valid slice index", parts[0])
8✔
1940
                }
8✔
1941
                start, err := strconv.Atoi(parts[0])
530✔
1942
                if err != nil {
546✔
1943
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice start index: %s", parts[0]), parts[0])
16✔
1944
                }
16✔
1945
                // Range check: use math.MaxInt/MinInt which works on both 32-bit and 64-bit
1946
                if start < math.MinInt || start > math.MaxInt {
514✔
UNCOV
1947
                        return nil, NewError(ErrSyntax, fmt.Sprintf("slice start index out of range: %d", start), parts[0])
×
UNCOV
1948
                }
×
1949
                slice.start = start
514✔
1950
                hasStart = true
514✔
1951
        }
1952

1953
        // 解析结束索引
1954
        if len(parts) > 1 && parts[1] != "" {
1,306✔
1955
                if !validateIntegerLiteral(parts[1]) {
588✔
1956
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice end index: %s", parts[1]), parts[1])
42✔
1957
                }
42✔
1958
                // Reject -0 (negative zero) per RFC 9535
1959
                if parts[1] == "-0" {
512✔
1960
                        return nil, NewError(ErrSyntax, "negative zero is not a valid slice index", parts[1])
8✔
1961
                }
8✔
1962
                end, err := strconv.Atoi(parts[1])
496✔
1963
                if err != nil {
512✔
1964
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice end index: %s", parts[1]), parts[1])
16✔
1965
                }
16✔
1966
                // Range check: use math.MaxInt/MinInt which works on both 32-bit and 64-bit
1967
                if end < math.MinInt || end > math.MaxInt {
480✔
UNCOV
1968
                        return nil, NewError(ErrSyntax, fmt.Sprintf("slice end index out of range: %d", end), parts[1])
×
UNCOV
1969
                }
×
1970
                slice.end = end
480✔
1971
                hasEnd = true
480✔
1972
        }
1973

1974
        // 解析步长
1975
        if len(parts) > 2 && parts[2] != "" {
1,082✔
1976
                if !validateIntegerLiteral(parts[2]) {
438✔
1977
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice step: %s", parts[2]), parts[2])
50✔
1978
                }
50✔
1979
                // Reject -0 (negative zero) per RFC 9535
1980
                if parts[2] == "-0" {
346✔
1981
                        return nil, NewError(ErrSyntax, "negative zero is not a valid slice step", parts[2])
8✔
1982
                }
8✔
1983
                step, err := strconv.Atoi(parts[2])
330✔
1984
                if err != nil {
346✔
1985
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid slice step: %s", parts[2]), parts[2])
16✔
1986
                }
16✔
1987
                if step == 0 {
324✔
1988
                        // RFC 9535: zero step - the slice selects no elements (returns empty)
10✔
1989
                        // Store step as 0 to signal this behavior
10✔
1990
                        slice.step = 0
10✔
1991
                } else if step < math.MinInt || step > math.MaxInt {
314✔
UNCOV
1992
                        return nil, NewError(ErrSyntax, fmt.Sprintf("slice step out of range: %d", step), parts[2])
×
1993
                } else {
304✔
1994
                        slice.step = step
304✔
1995
                }
304✔
1996
        }
1997

1998
        slice.hasStart = hasStart
620✔
1999
        slice.hasEnd = hasEnd
620✔
2000

620✔
2001
        return slice, nil
620✔
2002
}
2003

2004
// validateIntegerLiteral validates an integer literal per RFC 9535.
2005
// Returns true if the string is a valid integer- or decimal-number.
2006
func validateIntegerLiteral(s string) bool {
3,874✔
2007
        if s == "" {
3,876✔
2008
                return false
2✔
2009
        }
2✔
2010
        i := 0
3,872✔
2011
        // Optional minus (no plus sign allowed)
3,872✔
2012
        if i < len(s) && s[i] == '-' {
4,364✔
2013
                i++
492✔
2014
        }
492✔
2015
        if i >= len(s) {
3,872✔
2016
                return false
×
2017
        }
×
2018
        // Must start with a digit
2019
        if s[i] < '0' || s[i] > '9' {
5,264✔
2020
                return false
1,392✔
2021
        }
1,392✔
2022
        // Leading zero check: if first digit is '0', no more digits allowed
2023
        if s[i] == '0' && i+1 < len(s) && s[i+1] >= '0' && s[i+1] <= '9' {
2,544✔
2024
                return false
64✔
2025
        }
64✔
2026
        // Consume digits
2027
        for i < len(s) && s[i] >= '0' && s[i] <= '9' {
12,266✔
2028
                i++
9,850✔
2029
        }
9,850✔
2030
        // Must have consumed all characters
2031
        return i == len(s)
2,416✔
2032
}
2033

2034
// looksLikeNumber checks if content looks like it could be a number (starts with digit, -digit, or +digit)
2035
func looksLikeNumber(s string) bool {
1,364✔
2036
        if s == "" {
1,366✔
2037
                return false
2✔
2038
        }
2✔
2039
        if s[0] >= '0' && s[0] <= '9' {
1,386✔
2040
                return true
24✔
2041
        }
24✔
2042
        if (s[0] == '-' || s[0] == '+') && len(s) > 1 && s[1] >= '0' && s[1] <= '9' {
1,354✔
2043
                return true
16✔
2044
        }
16✔
2045
        return false
1,322✔
2046
}
2047

2048
// validateQuotedString validates a quoted string content per RFC 9535.
2049
// Checks for invalid escape sequences and embedded control characters.
2050
// The input should be the string content WITHOUT the surrounding quotes.
2051
// quoteChar is the character used for quoting (' or ").
2052
func validateQuotedString(s string, quoteChar byte) bool {
1,862✔
2053
        i := 0
1,862✔
2054
        for i < len(s) {
4,862✔
2055
                ch := s[i]
3,000✔
2056
                if ch == '\\' {
3,512✔
2057
                        // Escape sequence
512✔
2058
                        i++
512✔
2059
                        if i >= len(s) {
512✔
2060
                                return false // incomplete escape
×
2061
                        }
×
2062
                        esc := s[i]
512✔
2063
                        switch esc {
512✔
2064
                        case '\\', '/', 'b', 'f', 'n', 'r', 't':
112✔
2065
                                // Valid simple escapes (same for both quote types)
112✔
2066
                                i++
112✔
2067
                        case '"':
24✔
2068
                                // Double quote escape only valid in double-quoted strings
24✔
2069
                                if quoteChar != '"' {
32✔
2070
                                        return false
8✔
2071
                                }
8✔
2072
                                i++
16✔
2073
                        case '\'':
24✔
2074
                                // Single quote escape only valid in single-quoted strings
24✔
2075
                                if quoteChar != '\'' {
32✔
2076
                                        return false
8✔
2077
                                }
8✔
2078
                                i++
16✔
2079
                        case 'u':
280✔
2080
                                // Unicode escape: \uXXXX
280✔
2081
                                i++
280✔
2082
                                if i+4 > len(s) {
304✔
2083
                                        return false // not enough hex digits
24✔
2084
                                }
24✔
2085
                                for j := 0; j < 4; j++ {
1,208✔
2086
                                        if i+j >= len(s) || !isHexDigit(s[i+j]) {
976✔
2087
                                                return false
24✔
2088
                                        }
24✔
2089
                                }
2090
                                i += 4
232✔
2091
                        default:
72✔
2092
                                return false // invalid escape sequence
72✔
2093
                        }
2094
                } else if ch == quoteChar {
2,488✔
2095
                        // Unescaped quote character - invalid
×
2096
                        return false
×
2097
                } else if ch < 0x20 {
3,000✔
2098
                        // Control characters (U+0000-U+001F) are not allowed unescaped
512✔
2099
                        return false
512✔
2100
                } else {
2,488✔
2101
                        i++
1,976✔
2102
                }
1,976✔
2103
        }
2104
        return true
1,214✔
2105
}
2106

2107
// unescapeString processes escape sequences in a quoted string per RFC 9535.
2108
// The input should be the string content WITHOUT the surrounding quotes.
2109
// quoteChar is the character used for quoting (' or ").
2110
func unescapeString(s string, quoteChar byte) (string, error) {
1,214✔
2111
        var result strings.Builder
1,214✔
2112
        result.Grow(len(s))
1,214✔
2113
        i := 0
1,214✔
2114
        for i < len(s) {
3,470✔
2115
                ch := s[i]
2,256✔
2116
                if ch == '\\' {
2,568✔
2117
                        i++
312✔
2118
                        if i >= len(s) {
312✔
2119
                                return "", fmt.Errorf("incomplete escape")
×
2120
                        }
×
2121
                        esc := s[i]
312✔
2122
                        switch esc {
312✔
2123
                        case '"':
16✔
2124
                                if quoteChar != '"' {
16✔
2125
                                        return "", fmt.Errorf("invalid escape: \\\" in single-quoted string")
×
2126
                                }
×
2127
                                result.WriteByte('"')
16✔
2128
                        case '\'':
16✔
2129
                                if quoteChar != '\'' {
16✔
2130
                                        return "", fmt.Errorf("invalid escape: \\' in double-quoted string")
×
2131
                                }
×
2132
                                result.WriteByte('\'')
16✔
2133
                        case '\\':
16✔
2134
                                result.WriteByte('\\')
16✔
2135
                        case '/':
16✔
2136
                                result.WriteByte('/')
16✔
2137
                        case 'b':
16✔
2138
                                result.WriteByte('\b')
16✔
2139
                        case 'f':
16✔
2140
                                result.WriteByte('\f')
16✔
2141
                        case 'n':
16✔
2142
                                result.WriteByte('\n')
16✔
2143
                        case 'r':
16✔
2144
                                result.WriteByte('\r')
16✔
2145
                        case 't':
16✔
2146
                                result.WriteByte('\t')
16✔
2147
                        case 'u':
168✔
2148
                                // Unicode escape: \uXXXX
168✔
2149
                                i++
168✔
2150
                                if i+4 > len(s) {
168✔
2151
                                        return "", fmt.Errorf("incomplete unicode escape")
×
2152
                                }
×
2153
                                hexStr := s[i : i+4]
168✔
2154
                                codePoint, err := strconv.ParseUint(hexStr, 16, 32)
168✔
2155
                                if err != nil {
168✔
2156
                                        return "", fmt.Errorf("invalid unicode escape: %s", hexStr)
×
2157
                                }
×
2158
                                // Validate Unicode range
2159
                                if codePoint > 0x10FFFF {
168✔
2160
                                        return "", fmt.Errorf("unicode code point out of range: %d", codePoint)
×
2161
                                }
×
2162
                                // Check for surrogate pairs
2163
                                if codePoint >= 0xD800 && codePoint <= 0xDFFF {
264✔
2164
                                        // High surrogate: must be followed by low surrogate
96✔
2165
                                        if codePoint <= 0xDBFF && i+10 <= len(s) && s[i+4] == '\\' && s[i+5] == 'u' {
144✔
2166
                                                lowHex := s[i+6 : i+10]
48✔
2167
                                                lowPoint, err := strconv.ParseUint(lowHex, 16, 32)
48✔
2168
                                                if err == nil && lowPoint >= 0xDC00 && lowPoint <= 0xDFFF {
80✔
2169
                                                        // Valid surrogate pair
32✔
2170
                                                        combined := 0x10000 + (codePoint-0xD800)*0x400 + (lowPoint - 0xDC00)
32✔
2171
                                                        result.WriteRune(rune(combined))
32✔
2172
                                                        i += 10
32✔
2173
                                                        continue
32✔
2174
                                                }
2175
                                        }
2176
                                        return "", fmt.Errorf("invalid surrogate pair")
64✔
2177
                                }
2178
                                // Valid Unicode range, safe to convert
2179
                                result.WriteRune(rune(codePoint)) // #nosec G115 - range validated above
72✔
2180
                                i += 4
72✔
2181
                                continue
72✔
2182
                        default:
×
2183
                                return "", fmt.Errorf("invalid escape: \\%c", esc)
×
2184
                        }
2185
                        i++
144✔
2186
                } else if ch == quoteChar {
1,944✔
2187
                        return "", fmt.Errorf("unescaped quote character")
×
2188
                } else if ch < 0x20 {
1,944✔
2189
                        return "", fmt.Errorf("control character")
×
2190
                } else {
1,944✔
2191
                        result.WriteByte(ch)
1,944✔
2192
                        i++
1,944✔
2193
                }
1,944✔
2194
        }
2195
        return result.String(), nil
1,150✔
2196
}
2197

2198
// isHexDigit checks if a byte is a hexadecimal digit
2199
func isHexDigit(ch byte) bool {
952✔
2200
        return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
952✔
2201
}
952✔
2202

2203
// parseIndexOrName parses a bracket content as an index or name selector
2204
func parseIndexOrName(content string) (segment, error) {
2,368✔
2205
        // 处理函数调用
2,368✔
2206
        if strings.HasSuffix(content, ")") {
2,376✔
2207
                return parseFunctionCall(content)
8✔
2208
        }
8✔
2209

2210
        // Try to parse as an integer index with RFC 9535 validation
2211
        if validateIntegerLiteral(content) {
3,356✔
2212
                idx, err := strconv.Atoi(content)
996✔
2213
                if err != nil {
1,008✔
2214
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid index: %s", content), content)
12✔
2215
                }
12✔
2216
                // Reject -0 (negative zero)
2217
                if content == "-0" {
992✔
2218
                        return nil, NewError(ErrSyntax, "negative zero is not a valid index", content)
8✔
2219
                }
8✔
2220
                // RFC 9535: index must be within IEEE 754 double precision range
2221
                // Use math.MaxInt/MinInt which works on both 32-bit and 64-bit
2222
                if idx < math.MinInt || idx > math.MaxInt {
976✔
UNCOV
2223
                        return nil, NewError(ErrSyntax, fmt.Sprintf("index out of range: %d", idx), content)
×
UNCOV
2224
                }
×
2225
                return &indexSegment{index: idx}, nil
976✔
2226
        }
2227

2228
        // If content looks like a number but fails validation, it's an invalid index
2229
        if looksLikeNumber(content) {
1,404✔
2230
                return nil, NewError(ErrSyntax, fmt.Sprintf("invalid index: %s", content), content)
40✔
2231
        }
40✔
2232

2233
        // 处理字符串字面量
2234
        if strings.HasPrefix(content, "'") && strings.HasSuffix(content, "'") && len(content) > 1 {
1,906✔
2235
                inner := content[1 : len(content)-1]
582✔
2236
                if !validateQuotedString(inner, '\'') {
846✔
2237
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
264✔
2238
                }
264✔
2239
                unescaped, err := unescapeString(inner, '\'')
318✔
2240
                if err != nil {
318✔
2241
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
×
2242
                }
×
2243
                return &nameSegment{name: unescaped}, nil
318✔
2244
        }
2245

2246
        // 处理双引号字符串字面量
2247
        if strings.HasPrefix(content, "\"") && strings.HasSuffix(content, "\"") && len(content) > 1 {
1,402✔
2248
                inner := content[1 : len(content)-1]
660✔
2249
                if !validateQuotedString(inner, '"') {
1,044✔
2250
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
384✔
2251
                }
384✔
2252
                unescaped, err := unescapeString(inner, '"')
276✔
2253
                if err != nil {
340✔
2254
                        return nil, NewError(ErrSyntax, fmt.Sprintf("invalid string literal: %s", content), content)
64✔
2255
                }
64✔
2256
                return &nameSegment{name: unescaped}, nil
212✔
2257
        }
2258

2259
        return &nameSegment{name: content}, nil
82✔
2260
}
2261

2262
// 解析函数调用
2263
func parseFunctionCall(content string) (segment, error) {
52✔
2264
        // 找到函数名和参数列表
52✔
2265
        openParen := strings.Index(content, "(")
52✔
2266
        if openParen == -1 {
54✔
2267
                return nil, fmt.Errorf("invalid function call syntax: missing opening parenthesis")
2✔
2268
        }
2✔
2269

2270
        // 检查闭合括号
2271
        if !strings.HasSuffix(content, ")") {
52✔
2272
                return nil, fmt.Errorf("invalid function call syntax: missing closing parenthesis")
2✔
2273
        }
2✔
2274

2275
        name := content[:openParen]
48✔
2276
        argsStr := content[openParen+1 : len(content)-1]
48✔
2277

48✔
2278
        // 解析参数
48✔
2279
        args := make([]interface{}, 0)
48✔
2280
        if argsStr != "" {
88✔
2281
                // 简单参数解析,暂时只支持数字和字符串
40✔
2282
                argParts := strings.Split(argsStr, ",")
40✔
2283
                for _, arg := range argParts {
108✔
2284
                        arg = strings.TrimSpace(arg)
68✔
2285
                        if arg == "" {
70✔
2286
                                continue // 跳过空参数
2✔
2287
                        }
2288
                        // 尝试解析为数字
2289
                        if num, err := strconv.ParseFloat(arg, 64); err == nil {
100✔
2290
                                args = append(args, num)
34✔
2291
                                continue
34✔
2292
                        }
2293
                        // 处理字符串参数
2294
                        if strings.HasPrefix(arg, "'") && strings.HasSuffix(arg, "'") {
58✔
2295
                                // 处理转义引号
26✔
2296
                                str := arg[1 : len(arg)-1]
26✔
2297
                                str = strings.ReplaceAll(str, "''", "'")
26✔
2298
                                args = append(args, str)
26✔
2299
                                continue
26✔
2300
                        }
2301
                        return nil, fmt.Errorf("unsupported argument type: %s", arg)
6✔
2302
                }
2303
        }
2304

2305
        return &functionSegment{name: name, args: args}, nil
42✔
2306
}
2307

2308
// validateNumberLiteral validates a number literal per RFC 9535 grammar.
2309
// number = ["-"] (int / (int "." 1*DIGIT))
2310
// int = "0" / (DIGIT1 *DIGIT)
2311
func validateNumberLiteral(s string) bool {
1,208✔
2312
        if s == "" {
1,210✔
2313
                return false
2✔
2314
        }
2✔
2315
        i := 0
1,206✔
2316
        // Optional minus
1,206✔
2317
        if s[i] == '-' {
1,238✔
2318
                i++
32✔
2319
        }
32✔
2320
        if i >= len(s) {
1,206✔
2321
                return false
×
2322
        }
×
2323
        // Must have integer part
2324
        if s[i] < '0' || s[i] > '9' {
1,760✔
2325
                return false
554✔
2326
        }
554✔
2327
        // Leading zero check
2328
        if s[i] == '0' {
708✔
2329
                i++
56✔
2330
                if i < len(s) && s[i] >= '0' && s[i] <= '9' {
72✔
2331
                        return false // leading zero
16✔
2332
                }
16✔
2333
        } else {
596✔
2334
                // Non-zero digit, consume all digits
596✔
2335
                for i < len(s) && s[i] >= '0' && s[i] <= '9' {
1,254✔
2336
                        i++
658✔
2337
                }
658✔
2338
        }
2339
        // Optional fractional part
2340
        if i < len(s) && s[i] == '.' {
708✔
2341
                i++
72✔
2342
                if i >= len(s) || s[i] < '0' || s[i] > '9' {
96✔
2343
                        return false // must have digit after decimal
24✔
2344
                }
24✔
2345
                for i < len(s) && s[i] >= '0' && s[i] <= '9' {
104✔
2346
                        i++
56✔
2347
                }
56✔
2348
        }
2349
        // Optional exponent
2350
        if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
764✔
2351
                i++
152✔
2352
                if i < len(s) && (s[i] == '+' || s[i] == '-') {
240✔
2353
                        i++
88✔
2354
                }
88✔
2355
                if i >= len(s) || s[i] < '0' || s[i] > '9' {
192✔
2356
                        return false // must have digit after e/E
40✔
2357
                }
40✔
2358
                for i < len(s) && s[i] >= '0' && s[i] <= '9' {
240✔
2359
                        i++
128✔
2360
                }
128✔
2361
        }
2362
        // Must have consumed all characters
2363
        return i == len(s)
572✔
2364
}
2365

2366
// parseFilterValue parses a filter value string into an appropriate type
2367
func parseFilterValue(valueStr string) (interface{}, error) {
1,726✔
2368
        valueStr = strings.TrimSpace(valueStr)
1,726✔
2369

1,726✔
2370
        // 处理 null
1,726✔
2371
        if valueStr == "null" {
1,800✔
2372
                return nil, nil
74✔
2373
        }
74✔
2374

2375
        // 处理布尔值
2376
        if valueStr == "true" {
1,706✔
2377
                return true, nil
54✔
2378
        }
54✔
2379
        if valueStr == "false" {
1,646✔
2380
                return false, nil
48✔
2381
        }
48✔
2382

2383
        // 处理字符串(带引号)
2384
        if strings.HasPrefix(valueStr, "'") && strings.HasSuffix(valueStr, "'") && len(valueStr) > 1 {
1,750✔
2385
                inner := valueStr[1 : len(valueStr)-1]
200✔
2386
                if !validateQuotedString(inner, '\'') {
200✔
2387
                        return nil, fmt.Errorf("invalid string literal: %s", valueStr)
×
2388
                }
×
2389
                unescaped, err := unescapeString(inner, '\'')
200✔
2390
                if err != nil {
200✔
2391
                        return nil, fmt.Errorf("invalid string literal: %s", valueStr)
×
2392
                }
×
2393
                return unescaped, nil
200✔
2394
        }
2395
        if strings.HasPrefix(valueStr, "\"") && strings.HasSuffix(valueStr, "\"") && len(valueStr) > 1 {
1,492✔
2396
                inner := valueStr[1 : len(valueStr)-1]
142✔
2397
                if !validateQuotedString(inner, '"') {
142✔
2398
                        return nil, fmt.Errorf("invalid string literal: %s", valueStr)
×
2399
                }
×
2400
                unescaped, err := unescapeString(inner, '"')
142✔
2401
                if err != nil {
142✔
2402
                        return nil, fmt.Errorf("invalid string literal: %s", valueStr)
×
2403
                }
×
2404
                return unescaped, nil
142✔
2405
        }
2406

2407
        // Try to parse as number with RFC 9535 validation
2408
        if validateNumberLiteral(valueStr) {
1,764✔
2409
                num, err := strconv.ParseFloat(valueStr, 64)
556✔
2410
                if err != nil {
556✔
2411
                        return nil, fmt.Errorf("invalid number: %s", valueStr)
×
2412
                }
×
2413
                return num, nil
556✔
2414
        }
2415

2416
        // 处理 $ 引用(根节点)
2417
        if valueStr == "$" {
660✔
2418
                return "$", nil
8✔
2419
        }
8✔
2420

2421
        // 处理 @ 引用(当前元素)
2422
        if valueStr == "@" {
652✔
2423
                return "@", nil
8✔
2424
        }
8✔
2425

2426
        // 处理 $.path 引用(根节点路径)
2427
        if strings.HasPrefix(valueStr, "$.") || strings.HasPrefix(valueStr, "$[") {
636✔
2428
                return valueStr, nil
×
2429
        }
×
2430

2431
        // 处理 @.path 引用(当前元素路径)
2432
        if strings.HasPrefix(valueStr, "@.") || strings.HasPrefix(valueStr, "@[") {
1,052✔
2433
                return valueStr, nil
416✔
2434
        }
416✔
2435

2436
        // 如果不是其他类型,返回错误
2437
        return nil, fmt.Errorf("invalid value: %s", valueStr)
220✔
2438
}
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