@@ -10,35 +10,60 @@ import "net/url"
1010// Otherwise, it will pop the last pushed rule of that chain (second argument) and append it to the new *And chain.
1111//
1212// Example: `foo=bar|bar~foo&col!~val`
13- // The second argument `rule ` is supposed to be a filter.Any * Chain contains the first two conditions.
13+ // The second argument `left ` is supposed to be a filter.Any Chain contains the first two conditions.
1414// We then call this function when the parser is processing the logical `&` op and the Unlike condition,
1515// and what this function will do is logically re-group the conditions into `foo=bar|(bar~foo&col!~val)`.
16- func reduceFilter (op string, rule Filter, rules ... Filter) Filter {
17- chain, ok := rule .(*Chain);
16+ func reduceFilter (op string, left Filter, right Filter) Filter {
17+ chain, ok := left .(*Chain)
1818 if ok && chain.op == Any && LogicalOp (op) == All {
19- // Retrieve the last pushed condition and append it to the new "And" chain instead
20- andChain, _ := NewChain (All, chain.pop ())
21- andChain.add (rules...)
19+ // Retrieve the last pushed filter Condition and append it to the new "And" chain instead
20+ back := chain.pop ()
21+ // Chain#pop can return a filter Chain, and since we are only allowed to regroup two filter conditions,
22+ // we must traverse the last element of every single popped Chain till we reach a filter condition.
23+ for back != nil {
24+ if backChain, ok := back.(*Chain); !ok || backChain.grouped {
25+ // If the popped element is not of type filter Chain or the filter chain is parenthesized,
26+ // we don't need to continue here, so break out of the loop.
27+ break
28+ }
29+
30+ // Re-add the just popped item before stepping into it and popping its last item.
31+ chain.add (back)
32+
33+ chain = back.(*Chain)
34+ back = chain.pop ()
35+ }
36+
37+ andChain, _ := NewChain (All, back)
38+ // We don't need to regroup an already grouped filter chain, since braces gain
39+ // a higher precedence than any logical operators.
40+ if anyChain, ok := right.(*Chain); ok && anyChain.op == Any && !chain.grouped && !anyChain.grouped {
41+ andChain.add (anyChain.top ())
42+ // Prepend the newly created All chain
43+ anyChain.rules = append ([]Filter{andChain}, anyChain.rules ...)
2244
23- chain.add (andChain)
45+ chain.add (anyChain)
46+ } else {
47+ andChain.add (right)
48+ chain.add (andChain)
49+ }
2450
25- return chain
51+ return left
2652 }
2753
28- // If the given operator is the same as the already existsing chains operator (*chain),
54+ // If the given operator is the same as the already existing chains operator (*chain),
2955 // we don't need to create another chain of the same operator type. Avoids something
3056 // like &Chain{op: All, &Chain{op: All, ...}}
3157 if chain == nil || chain.op != LogicalOp (op) {
32- newChain, err := NewChain (LogicalOp (op), rule)
33- if err != nil {
34- // Just panic, filter.Parse will try to recover from this.
35- panic (err)
36- }
37-
38- chain = newChain
58+ var err error
59+ chain, err = NewChain (LogicalOp (op), left)
60+ if err != nil {
61+ // Just panic, filter.Parse will try to recover from this.
62+ panic (err)
63+ }
3964 }
4065
41- chain.add (rules... )
66+ chain.add (right )
4267
4368 return chain
4469}
@@ -93,10 +118,15 @@ filter_rule: filter_chain logical_op filter_chain
93118 $$ = reduceFilter($2 , $1 , $3 )
94119 yylex.(*Lexer).rule = $$
95120 }
96- | filter_chain
121+ | filter_chain % prec PREFER_SHIFTING_LOGICAL_OP
97122 {
98123 yylex.(*Lexer).rule = $$
99124 }
125+ | filter_rule logical_op filter_chain
126+ {
127+ $$ = reduceFilter($2 , $1 , $3 )
128+ yylex.(*Lexer).rule = $$
129+ }
100130 ;
101131
102132filter_chain : conditions_expr logical_op maybe_negated_condition_expr
@@ -128,6 +158,9 @@ maybe_negated_condition_expr: optional_negation condition_expr
128158condition_expr : " (" filter_rule " )"
129159 {
130160 $$ = $2
161+ if chain, ok := $$ .(*Chain); ok {
162+ chain.grouped = true
163+ }
131164 }
132165 | identifier comparison_op identifier
133166 {
0 commit comments