diff options
Diffstat (limited to 'test/racc/assets/nokogiri-css.y')
-rw-r--r-- | test/racc/assets/nokogiri-css.y | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/test/racc/assets/nokogiri-css.y b/test/racc/assets/nokogiri-css.y new file mode 100644 index 0000000000..24dfbf3b1b --- /dev/null +++ b/test/racc/assets/nokogiri-css.y @@ -0,0 +1,255 @@ +class Nokogiri::CSS::Parser + +token FUNCTION INCLUDES DASHMATCH LBRACE HASH PLUS GREATER S STRING IDENT +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, [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, but hpricot supports it. + result = Node.new(:PSEUDO_CLASS, + [Node.new(:FUNCTION, ['nth-child(', val[1]])] + ) + } + ; + attrib_name + : namespace '|' IDENT { + result = Node.new(:ELEMENT_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(:ELEMENT_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 + # This is not CSS standard. It allows us to support this: + # 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, val) } + ; + attrib_val_0or1 + : eql_incl_dash IDENT { result = [val.first, val[1]] } + | eql_incl_dash STRING { 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 + | + ; +end + +---- header + +require 'nokogiri/css/parser_extras' |