Skip to content

Commit 607649a

Browse files
Refactor: Modularize evaluator, unified parser, and 100% test compatibility
- Split function_parser.go into expressions.go, function_eval.go, and function_parser.go - Implement unified ExpressionEvaluator for all predicate types - Add Debug flag to Options for detailed execution tracing - Fix NodeSet truthiness and text node dot predicate parsing - Fix nested predicate parsing bug in FunctionParser - Achieve 100% pass rate on comprehensive test suite
1 parent 427715e commit 607649a

File tree

10 files changed

+975
-2791
lines changed

10 files changed

+975
-2791
lines changed

internal/evaluator/conditions.go

Lines changed: 11 additions & 1479 deletions
Large diffs are not rendered by default.

internal/evaluator/evaluator.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import (
1313

1414
// Evaluator handles XPath expression evaluation
1515
type Evaluator struct {
16-
parser *parser.Parser
17-
htmlParser *utils.HTMLParser
16+
parser *parser.Parser
17+
htmlParser *utils.HTMLParser
18+
contextPosition int
19+
contextSize int
1820
}
1921

2022
// NewEvaluator creates a new XPath evaluator

internal/evaluator/expression_evaluator.go

Lines changed: 13 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package evaluator
22

33
import (
4-
"fmt"
54
"strconv"
65
"strings"
76

@@ -25,7 +24,7 @@ func (ee *ExpressionEvaluator) EvaluateExpression(expr string, node *types.Node)
2524
Trace("ExpressionEvaluator.EvaluateExpression: '%s'", expr)
2625

2726
parser := NewFunctionParser(expr)
28-
parsedExpr, err := parser.ParseExpression()
27+
parsedExpr, err := parser.Parse()
2928
if err != nil {
3029
Trace("Expression parsing failed: %v", err)
3130
return "", err
@@ -40,103 +39,27 @@ func (ee *ExpressionEvaluator) EvaluateExpression(expr string, node *types.Node)
4039
func (ee *ExpressionEvaluator) EvaluateComparison(expr string, node *types.Node) (bool, error) {
4140
Trace("ExpressionEvaluator.EvaluateComparison: '%s'", expr)
4241

43-
// Handle boolean operators first
44-
if strings.Contains(expr, " and ") {
45-
return ee.evaluateAndExpression(expr, node)
46-
}
47-
if strings.Contains(expr, " or ") {
48-
return ee.evaluateOrExpression(expr, node)
49-
}
50-
51-
// Find comparison operator
52-
operators := []string{">=", "<=", "!=", "=", ">", "<"}
53-
var operator string
54-
var leftExpr, rightExpr string
55-
56-
for _, op := range operators {
57-
if idx := strings.Index(expr, op); idx != -1 {
58-
operator = op
59-
leftExpr = strings.TrimSpace(expr[:idx])
60-
rightExpr = strings.TrimSpace(expr[idx+len(op):])
61-
break
62-
}
63-
}
64-
65-
if operator == "" {
66-
// No comparison operator, treat as boolean expression
67-
result, err := ee.EvaluateExpression(expr, node)
68-
if err != nil {
69-
return false, err
70-
}
71-
// Non-empty string or non-zero number is true
72-
if result == "" || result == "0" || result == "false" {
73-
return false, nil
74-
}
75-
return true, nil
76-
}
77-
78-
// Evaluate left and right sides
79-
leftResult, err := ee.EvaluateExpression(leftExpr, node)
42+
result, err := ee.EvaluateExpression(expr, node)
8043
if err != nil {
81-
Trace("Left expression evaluation failed: %v", err)
8244
return false, err
8345
}
8446

85-
rightResult, err := ee.EvaluateExpression(rightExpr, node)
86-
if err != nil {
87-
Trace("Right expression evaluation failed: %v", err)
88-
return false, err
47+
// In XPath, non-empty strings and non-zero numbers are true
48+
// Our EvaluateExpression returns "true", "false", or a value
49+
if result == "true" {
50+
return true, nil
8951
}
90-
91-
Trace("Comparison: '%s' %s '%s'", leftResult, operator, rightResult)
92-
93-
// Perform comparison
94-
result := ee.performComparison(leftResult, operator, rightResult)
95-
Trace("Comparison result: %v", result)
96-
return result, nil
97-
}
98-
99-
// performComparison performs the actual comparison
100-
func (ee *ExpressionEvaluator) performComparison(left, operator, right string) bool {
101-
// Try numeric comparison first
102-
leftNum, leftErr := strconv.ParseFloat(left, 64)
103-
rightNum, rightErr := strconv.ParseFloat(right, 64)
104-
105-
if leftErr == nil && rightErr == nil {
106-
// Numeric comparison
107-
switch operator {
108-
case ">":
109-
return leftNum > rightNum
110-
case ">=":
111-
return leftNum >= rightNum
112-
case "<":
113-
return leftNum < rightNum
114-
case "<=":
115-
return leftNum <= rightNum
116-
case "=":
117-
return leftNum == rightNum
118-
case "!=":
119-
return leftNum != rightNum
120-
}
52+
if result == "false" || result == "" || result == "0" {
53+
return false, nil
12154
}
12255

123-
// String comparison
124-
switch operator {
125-
case "=":
126-
return left == right
127-
case "!=":
128-
return left != right
129-
case ">":
130-
return left > right
131-
case ">=":
132-
return left >= right
133-
case "<":
134-
return left < right
135-
case "<=":
136-
return left <= right
56+
// If it's a number != 0, it's true
57+
if num, err := strconv.ParseFloat(result, 64); err == nil {
58+
return num != 0, nil
13759
}
13860

139-
return false
61+
// Non-empty string is true
62+
return len(result) > 0, nil
14063
}
14164

14265
// IsComplexFunctionExpression checks if an expression contains function calls
@@ -156,44 +79,6 @@ func IsComplexFunctionExpression(expr string) bool {
15679
return false
15780
}
15881

159-
// evaluateAndExpression handles "and" boolean expressions
160-
func (ee *ExpressionEvaluator) evaluateAndExpression(expr string, node *types.Node) (bool, error) {
161-
parts := strings.Split(expr, " and ")
162-
if len(parts) < 2 {
163-
return false, fmt.Errorf("invalid and expression")
164-
}
165-
166-
for _, part := range parts {
167-
result, err := ee.EvaluateComparison(strings.TrimSpace(part), node)
168-
if err != nil {
169-
return false, err
170-
}
171-
if !result {
172-
return false, nil // Short-circuit
173-
}
174-
}
175-
return true, nil
176-
}
177-
178-
// evaluateOrExpression handles "or" boolean expressions
179-
func (ee *ExpressionEvaluator) evaluateOrExpression(expr string, node *types.Node) (bool, error) {
180-
parts := strings.Split(expr, " or ")
181-
if len(parts) < 2 {
182-
return false, fmt.Errorf("invalid or expression")
183-
}
184-
185-
for _, part := range parts {
186-
result, err := ee.EvaluateComparison(strings.TrimSpace(part), node)
187-
if err != nil {
188-
return false, err
189-
}
190-
if result {
191-
return true, nil // Short-circuit
192-
}
193-
}
194-
return false, nil
195-
}
196-
19782
// HasArithmeticOperations checks if expression has arithmetic
19883
func HasArithmeticOperations(expr string) bool {
19984
operators := []string{" + ", " - ", " * ", " / ", " div ", " mod "}

0 commit comments

Comments
 (0)