diff --git a/lib/nokogiri/css/parser.rb b/lib/nokogiri/css/parser.rb index 5cdc80d44b..5d67e4f16a 100644 --- a/lib/nokogiri/css/parser.rb +++ b/lib/nokogiri/css/parser.rb @@ -1,4 +1,3 @@ -# frozen_string_literal: true # # DO NOT MODIFY!!!! # This file is automatically generated by Racc 1.6.0 @@ -341,7 +340,7 @@ def unescape_css_string(str) # reduce 0 omitted def _reduce_1(val, _values, result) - result = [val.first, val.last].flatten + result = [val[0], val[2]].flatten result end @@ -352,7 +351,7 @@ def _reduce_2(val, _values, result) end def _reduce_3(val, _values, result) - result = [val.last].flatten + result = [val[1]].flatten result end @@ -382,11 +381,11 @@ def _reduce_8(val, _values, result) end def _reduce_9(val, _values, result) - result = if val[1].nil? - val.first - else - Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]]) - end + result = if val[1].nil? + val[0] + else + Node.new(:CONDITIONAL_SELECTOR, [val[0], val[1]]) + end result end @@ -394,39 +393,39 @@ def _reduce_9(val, _values, result) # reduce 10 omitted def _reduce_11(val, _values, result) - result = Node.new(:CONDITIONAL_SELECTOR, val) + result = Node.new(:CONDITIONAL_SELECTOR, val) result end def _reduce_12(val, _values, result) - result = Node.new(:CONDITIONAL_SELECTOR, val) + result = Node.new(:CONDITIONAL_SELECTOR, val) result end def _reduce_13(val, _values, result) - result = Node.new(:CONDITIONAL_SELECTOR, - [Node.new(:ELEMENT_NAME, ['*']), val.first] - ) + result = Node.new(:CONDITIONAL_SELECTOR, + [Node.new(:ELEMENT_NAME, ['*']), val[0]] + ) result end def _reduce_14(val, _values, result) - result = Node.new(val.first, [nil, val.last]) + result = Node.new(val[0], [nil, val[1]]) result end def _reduce_15(val, _values, result) - result = Node.new(val[1], [val.first, val.last]) + result = Node.new(val[1], [val[0], val[2]]) result end def _reduce_16(val, _values, result) - result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last]) + result = Node.new(:DESCENDANT_SELECTOR, [val[0], val[2]]) result end @@ -446,16 +445,16 @@ def _reduce_20(val, _values, result) end def _reduce_21(val, _values, result) - result = Node.new(:ELEMENT_NAME, - [[val.first, val.last].compact.join(':')] - ) + result = Node.new(:ELEMENT_NAME, + [[val[0], val[2]].compact.join(':')] + ) result end def _reduce_22(val, _values, result) - name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first - result = Node.new(:ELEMENT_NAME, [name]) + name = @namespaces.key?('xmlns') ? "xmlns:#{val[0]}" : val[0] + result = Node.new(:ELEMENT_NAME, [name]) result end @@ -468,88 +467,88 @@ def _reduce_23(val, _values, result) # reduce 24 omitted def _reduce_25(val, _values, result) - result = Node.new(:ATTRIBUTE_CONDITION, - [val[1]] + (val[2] || []) - ) + result = Node.new(:ATTRIBUTE_CONDITION, + [val[1]] + (val[2] || []) + ) result end def _reduce_26(val, _values, result) - result = Node.new(:ATTRIBUTE_CONDITION, - [val[1]] + (val[2] || []) - ) + result = Node.new(:ATTRIBUTE_CONDITION, + [val[1]] + (val[2] || []) + ) result end def _reduce_27(val, _values, result) - # non-standard, from hpricot - result = Node.new(:PSEUDO_CLASS, - [Node.new(:FUNCTION, ['nth-child(', val[1]])] - ) + # non-standard, from hpricot + result = Node.new(:PSEUDO_CLASS, + [Node.new(:FUNCTION, ['nth-child(', val[1]])] + ) result end def _reduce_28(val, _values, result) - result = Node.new(:ATTRIB_NAME, - [[val.first, val.last].compact.join(':')] - ) + result = Node.new(:ATTRIB_NAME, + [[val[0], val[2]].compact.join(':')] + ) result end def _reduce_29(val, _values, result) - # Default namespace is not applied to attributes. - # So we don't add prefix "xmlns:" as in namespaced_ident. - result = Node.new(:ATTRIB_NAME, [val.first]) + # Default namespace is not applied to attributes. + # So we don't add prefix "xmlns:" as in namespaced_ident. + result = Node.new(:ATTRIB_NAME, [val[0]]) result end def _reduce_30(val, _values, result) - result = Node.new(:FUNCTION, [val.first.strip]) + result = Node.new(:FUNCTION, [val[0].strip]) result end def _reduce_31(val, _values, result) - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) result end def _reduce_32(val, _values, result) - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) result end def _reduce_33(val, _values, result) - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) result end def _reduce_34(val, _values, result) - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) result end def _reduce_35(val, _values, result) - result = [val.first, val.last] + result = [val[0], val[2]] result end def _reduce_36(val, _values, result) - result = [val.first, val.last] + result = [val[0], val[2]] result end def _reduce_37(val, _values, result) - result = [val.first, val.last] + result = [val[0], val[2]] result end @@ -558,72 +557,68 @@ def _reduce_37(val, _values, result) # reduce 39 omitted def _reduce_40(val, _values, result) - case val[0] - when 'even' - result = Node.new(:NTH, ['2','n','+','0']) - when 'odd' - result = Node.new(:NTH, ['2','n','+','1']) - when 'n' - result = Node.new(:NTH, ['1','n','+','0']) - else - # non-standard to support custom functions: - # assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)')) - # assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)')) - # assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)')) - result = val - end + case val[0] + when 'even' + result = Node.new(:NTH, ['2','n','+','0']) + when 'odd' + result = Node.new(:NTH, ['2','n','+','1']) + when 'n' + result = Node.new(:NTH, ['1','n','+','0']) + else + result = val + end result end def _reduce_41(val, _values, result) - if val[1] == 'n' - result = Node.new(:NTH, val) - else - raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" - end + if val[1] == 'n' + result = Node.new(:NTH, val) + else + raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" + end result end def _reduce_42(val, _values, result) # n+3, -n+3 - if val[0] == 'n' - val.unshift("1") - result = Node.new(:NTH, val) - elsif val[0] == '-n' - val[0] = 'n' - val.unshift("-1") - result = Node.new(:NTH, val) - else - raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" - end + if val[0] == 'n' + val.unshift("1") + result = Node.new(:NTH, val) + elsif val[0] == '-n' + val[0] = 'n' + val.unshift("-1") + result = Node.new(:NTH, val) + else + raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" + end result end def _reduce_43(val, _values, result) # 5n, -5n, 10n-1 - n = val[1] - if n[0, 2] == 'n-' - val[1] = 'n' - val << "-" - # b is contained in n as n is the string "n-b" - val << n[2, n.size] - result = Node.new(:NTH, val) - elsif n == 'n' - val << "+" - val << "0" - result = Node.new(:NTH, val) - else - raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" - end + n = val[1] + if n[0, 2] == 'n-' + val[1] = 'n' + val << "-" + # b is contained in n as n is the string "n-b" + val << n[2, n.size] + result = Node.new(:NTH, val) + elsif n == 'n' + val << "+" + val << "0" + result = Node.new(:NTH, val) + else + raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" + end result end def _reduce_44(val, _values, result) - result = Node.new(:PSEUDO_CLASS, [val[1]]) + result = Node.new(:PSEUDO_CLASS, [val[1]]) result end @@ -638,31 +633,31 @@ def _reduce_45(val, _values, result) # reduce 47 omitted def _reduce_48(val, _values, result) - result = Node.new(:COMBINATOR, val) + result = Node.new(:COMBINATOR, val) result end def _reduce_49(val, _values, result) - result = Node.new(:COMBINATOR, val) + result = Node.new(:COMBINATOR, val) result end def _reduce_50(val, _values, result) - result = Node.new(:COMBINATOR, val) + result = Node.new(:COMBINATOR, val) result end def _reduce_51(val, _values, result) - result = Node.new(:COMBINATOR, val) + result = Node.new(:COMBINATOR, val) result end def _reduce_52(val, _values, result) - result = Node.new(:COMBINATOR, val) + result = Node.new(:COMBINATOR, val) result end @@ -678,22 +673,22 @@ def _reduce_52(val, _values, result) # reduce 57 omitted def _reduce_58(val, _values, result) - result = Node.new(:ID, [unescape_css_identifier(val.first)]) + result = Node.new(:ID, [unescape_css_identifier(val[0])]) result end def _reduce_59(val, _values, result) - result = [val.first, unescape_css_identifier(val[1])] + result = [val[0], unescape_css_identifier(val[1])] result end def _reduce_60(val, _values, result) - result = [val.first, unescape_css_string(val[1])] + result = [val[0], unescape_css_string(val[1])] result end def _reduce_61(val, _values, result) - result = [val.first, val[1]] + result = [val[0], val[1]] result end @@ -735,7 +730,7 @@ def _reduce_69(val, _values, result) end def _reduce_70(val, _values, result) - result = Node.new(:NOT, [val[1]]) + result = Node.new(:NOT, [val[1]]) result end diff --git a/lib/nokogiri/css/parser.y b/lib/nokogiri/css/parser.y index d5c4af1f6c..6f1733ffe9 100644 --- a/lib/nokogiri/css/parser.y +++ b/lib/nokogiri/css/parser.y @@ -5,250 +5,268 @@ token COMMA NUMBER PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH TILDE NOT_EQUAL token SLASH DOUBLESLASH NOT EQUAL RPAREN LSQUARE RSQUARE HAS rule - selector - : selector COMMA simple_selector_1toN { - result = [val.first, val.last].flatten - } - | prefixless_combinator_selector { result = val.flatten } - | optional_S simple_selector_1toN { result = [val.last].flatten } - ; - combinator - : PLUS { result = :DIRECT_ADJACENT_SELECTOR } - | GREATER { result = :CHILD_SELECTOR } - | TILDE { result = :FOLLOWING_SELECTOR } - | DOUBLESLASH { result = :DESCENDANT_SELECTOR } - | SLASH { result = :CHILD_SELECTOR } - ; - simple_selector - : element_name hcap_0toN { - result = if val[1].nil? - val.first - else - Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]]) - end - } - | function - | function pseudo { - result = Node.new(:CONDITIONAL_SELECTOR, val) - } - | function attrib { - result = Node.new(:CONDITIONAL_SELECTOR, val) - } - | hcap_1toN { - result = Node.new(:CONDITIONAL_SELECTOR, - [Node.new(:ELEMENT_NAME, ['*']), val.first] - ) - } - ; - prefixless_combinator_selector - : combinator simple_selector_1toN { - result = Node.new(val.first, [nil, val.last]) - } - ; - simple_selector_1toN - : simple_selector combinator simple_selector_1toN { - result = Node.new(val[1], [val.first, val.last]) - } - | simple_selector S simple_selector_1toN { - result = Node.new(:DESCENDANT_SELECTOR, [val.first, val.last]) - } - | simple_selector - ; - class - : '.' IDENT { result = Node.new(:CLASS_CONDITION, [unescape_css_identifier(val[1])]) } - ; - element_name - : namespaced_ident - | '*' { result = Node.new(:ELEMENT_NAME, val) } - ; - namespaced_ident - : namespace '|' IDENT { - result = Node.new(:ELEMENT_NAME, - [[val.first, val.last].compact.join(':')] - ) - } - | IDENT { - name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first - result = Node.new(:ELEMENT_NAME, [name]) - } - ; - namespace - : IDENT { result = val[0] } - | - ; - attrib - : LSQUARE attrib_name attrib_val_0or1 RSQUARE { - result = Node.new(:ATTRIBUTE_CONDITION, - [val[1]] + (val[2] || []) - ) - } - | LSQUARE function attrib_val_0or1 RSQUARE { - result = Node.new(:ATTRIBUTE_CONDITION, - [val[1]] + (val[2] || []) - ) - } - | LSQUARE NUMBER RSQUARE { - # non-standard, from hpricot - result = Node.new(:PSEUDO_CLASS, - [Node.new(:FUNCTION, ['nth-child(', val[1]])] - ) - } - ; - attrib_name - : namespace '|' IDENT { - result = Node.new(:ATTRIB_NAME, - [[val.first, val.last].compact.join(':')] - ) - } - | IDENT { - # Default namespace is not applied to attributes. - # So we don't add prefix "xmlns:" as in namespaced_ident. - result = Node.new(:ATTRIB_NAME, [val.first]) - } - ; - function - : FUNCTION RPAREN { - result = Node.new(:FUNCTION, [val.first.strip]) - } - | FUNCTION expr RPAREN { - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) - } - | FUNCTION nth RPAREN { - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) - } - | NOT expr RPAREN { - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) - } - | HAS selector RPAREN { - result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten) - } - ; - expr - : NUMBER COMMA expr { result = [val.first, val.last] } - | STRING COMMA expr { result = [val.first, val.last] } - | IDENT COMMA expr { result = [val.first, val.last] } - | NUMBER - | STRING - | IDENT # even, odd - { - case val[0] - when 'even' - result = Node.new(:NTH, ['2','n','+','0']) - when 'odd' - result = Node.new(:NTH, ['2','n','+','1']) - when 'n' - result = Node.new(:NTH, ['1','n','+','0']) - else - # non-standard to support custom functions: - # assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)')) - # assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)')) - # assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)')) - result = val - end - } - ; - nth - : NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3 - { - if val[1] == 'n' - result = Node.new(:NTH, val) - else - raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" - end - } - | IDENT PLUS NUMBER { # n+3, -n+3 - if val[0] == 'n' - val.unshift("1") - result = Node.new(:NTH, val) - elsif val[0] == '-n' - val[0] = 'n' - val.unshift("-1") - result = Node.new(:NTH, val) - else - raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" - end - } - | NUMBER IDENT { # 5n, -5n, 10n-1 - n = val[1] - if n[0, 2] == 'n-' - val[1] = 'n' - val << "-" - # b is contained in n as n is the string "n-b" - val << n[2, n.size] - result = Node.new(:NTH, val) - elsif n == 'n' - val << "+" - val << "0" - result = Node.new(:NTH, val) - else - raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" - end - } - ; - pseudo - : ':' function { - result = Node.new(:PSEUDO_CLASS, [val[1]]) - } - | ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) } - ; - hcap_0toN - : hcap_1toN - | - ; - hcap_1toN - : attribute_id hcap_1toN { - result = Node.new(:COMBINATOR, val) - } - | class hcap_1toN { - result = Node.new(:COMBINATOR, val) - } - | attrib hcap_1toN { - result = Node.new(:COMBINATOR, val) - } - | pseudo hcap_1toN { - result = Node.new(:COMBINATOR, val) - } - | negation hcap_1toN { - result = Node.new(:COMBINATOR, val) - } - | attribute_id - | class - | attrib - | pseudo - | negation - ; - attribute_id - : HASH { result = Node.new(:ID, [unescape_css_identifier(val.first)]) } - ; - attrib_val_0or1 - : eql_incl_dash IDENT { result = [val.first, unescape_css_identifier(val[1])] } - | eql_incl_dash STRING { result = [val.first, unescape_css_string(val[1])] } - | eql_incl_dash NUMBER { result = [val.first, val[1]] } - | - ; - eql_incl_dash - : EQUAL { result = :equal } - | PREFIXMATCH { result = :prefix_match } - | SUFFIXMATCH { result = :suffix_match } - | SUBSTRINGMATCH { result = :substring_match } - | NOT_EQUAL { result = :not_equal } - | INCLUDES { result = :includes } - | DASHMATCH { result = :dash_match } - ; - negation - : NOT negation_arg RPAREN { - result = Node.new(:NOT, [val[1]]) - } - ; - negation_arg - : element_name - | element_name hcap_1toN - | hcap_1toN - ; - optional_S - : S - | - ; + selector: + selector COMMA simple_selector_1toN { + result = [val[0], val[2]].flatten + } + | prefixless_combinator_selector { result = val.flatten } + | optional_S simple_selector_1toN { result = [val[1]].flatten } + ; + + combinator: + PLUS { result = :DIRECT_ADJACENT_SELECTOR } + | GREATER { result = :CHILD_SELECTOR } + | TILDE { result = :FOLLOWING_SELECTOR } + | DOUBLESLASH { result = :DESCENDANT_SELECTOR } + | SLASH { result = :CHILD_SELECTOR } + ; + + simple_selector: + element_name hcap_0toN { + result = if val[1].nil? + val[0] + else + Node.new(:CONDITIONAL_SELECTOR, [val[0], val[1]]) + end + } + | function + | function pseudo { + result = Node.new(:CONDITIONAL_SELECTOR, val) + } + | function attrib { + result = Node.new(:CONDITIONAL_SELECTOR, val) + } + | hcap_1toN { + result = Node.new(:CONDITIONAL_SELECTOR, + [Node.new(:ELEMENT_NAME, ['*']), val[0]] + ) + } + ; + + prefixless_combinator_selector: + combinator simple_selector_1toN { + result = Node.new(val[0], [nil, val[1]]) + } + ; + + simple_selector_1toN: + simple_selector combinator simple_selector_1toN { + result = Node.new(val[1], [val[0], val[2]]) + } + | simple_selector S simple_selector_1toN { + result = Node.new(:DESCENDANT_SELECTOR, [val[0], val[2]]) + } + | simple_selector + ; + + class: + '.' IDENT { result = Node.new(:CLASS_CONDITION, [unescape_css_identifier(val[1])]) } + ; + + element_name: + namespaced_ident + | '*' { result = Node.new(:ELEMENT_NAME, val) } + ; + + namespaced_ident: + namespace '|' IDENT { + result = Node.new(:ELEMENT_NAME, + [[val[0], val[2]].compact.join(':')] + ) + } + | IDENT { + name = @namespaces.key?('xmlns') ? "xmlns:#{val[0]}" : val[0] + result = Node.new(:ELEMENT_NAME, [name]) + } + ; + + namespace: + IDENT { result = val[0] } + | + ; + + attrib: + LSQUARE attrib_name attrib_val_0or1 RSQUARE { + result = Node.new(:ATTRIBUTE_CONDITION, + [val[1]] + (val[2] || []) + ) + } + | LSQUARE function attrib_val_0or1 RSQUARE { + result = Node.new(:ATTRIBUTE_CONDITION, + [val[1]] + (val[2] || []) + ) + } + | LSQUARE NUMBER RSQUARE { + # non-standard, from hpricot + result = Node.new(:PSEUDO_CLASS, + [Node.new(:FUNCTION, ['nth-child(', val[1]])] + ) + } + ; + + attrib_name: + namespace '|' IDENT { + result = Node.new(:ATTRIB_NAME, + [[val[0], val[2]].compact.join(':')] + ) + } + | IDENT { + # Default namespace is not applied to attributes. + # So we don't add prefix "xmlns:" as in namespaced_ident. + result = Node.new(:ATTRIB_NAME, [val[0]]) + } + ; + + function: + FUNCTION RPAREN { + result = Node.new(:FUNCTION, [val[0].strip]) + } + | FUNCTION expr RPAREN { + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) + } + | FUNCTION nth RPAREN { + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) + } + | NOT expr RPAREN { + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) + } + | HAS selector RPAREN { + result = Node.new(:FUNCTION, [val[0].strip, val[1]].flatten) + } + ; + + expr: + NUMBER COMMA expr { result = [val[0], val[2]] } + | STRING COMMA expr { result = [val[0], val[2]] } + | IDENT COMMA expr { result = [val[0], val[2]] } + | NUMBER + | STRING + | IDENT { + case val[0] + when 'even' + result = Node.new(:NTH, ['2','n','+','0']) + when 'odd' + result = Node.new(:NTH, ['2','n','+','1']) + when 'n' + result = Node.new(:NTH, ['1','n','+','0']) + else + result = val + end + } + ; + + nth: + NUMBER IDENT PLUS NUMBER # 5n+3 -5n+3 + { + if val[1] == 'n' + result = Node.new(:NTH, val) + else + raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" + end + } + | IDENT PLUS NUMBER { # n+3, -n+3 + if val[0] == 'n' + val.unshift("1") + result = Node.new(:NTH, val) + elsif val[0] == '-n' + val[0] = 'n' + val.unshift("-1") + result = Node.new(:NTH, val) + else + raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" + end + } + | NUMBER IDENT { # 5n, -5n, 10n-1 + n = val[1] + if n[0, 2] == 'n-' + val[1] = 'n' + val << "-" + # b is contained in n as n is the string "n-b" + val << n[2, n.size] + result = Node.new(:NTH, val) + elsif n == 'n' + val << "+" + val << "0" + result = Node.new(:NTH, val) + else + raise Racc::ParseError, "parse error on IDENT '#{val[1]}'" + end + } + ; + + pseudo: + ':' function { + result = Node.new(:PSEUDO_CLASS, [val[1]]) + } + | ':' IDENT { result = Node.new(:PSEUDO_CLASS, [val[1]]) } + ; + + hcap_0toN: + hcap_1toN + | + ; + + hcap_1toN: + attribute_id hcap_1toN { + result = Node.new(:COMBINATOR, val) + } + | class hcap_1toN { + result = Node.new(:COMBINATOR, val) + } + | attrib hcap_1toN { + result = Node.new(:COMBINATOR, val) + } + | pseudo hcap_1toN { + result = Node.new(:COMBINATOR, val) + } + | negation hcap_1toN { + result = Node.new(:COMBINATOR, val) + } + | attribute_id + | class + | attrib + | pseudo + | negation + ; + + attribute_id: + HASH { result = Node.new(:ID, [unescape_css_identifier(val[0])]) } + ; + + attrib_val_0or1: + eql_incl_dash IDENT { result = [val[0], unescape_css_identifier(val[1])] } + | eql_incl_dash STRING { result = [val[0], unescape_css_string(val[1])] } + | eql_incl_dash NUMBER { result = [val[0], val[1]] } + | + ; + + eql_incl_dash: + EQUAL { result = :equal } + | PREFIXMATCH { result = :prefix_match } + | SUFFIXMATCH { result = :suffix_match } + | SUBSTRINGMATCH { result = :substring_match } + | NOT_EQUAL { result = :not_equal } + | INCLUDES { result = :includes } + | DASHMATCH { result = :dash_match } + ; + + negation: + NOT negation_arg RPAREN { + result = Node.new(:NOT, [val[1]]) + } + ; + + negation_arg: + element_name + | element_name hcap_1toN + | hcap_1toN + ; + + optional_S: + S + | + ; + end ---- header