summaryrefslogtreecommitdiff
path: root/test/racc/assets/nokogiri-css.y
diff options
context:
space:
mode:
Diffstat (limited to 'test/racc/assets/nokogiri-css.y')
-rw-r--r--test/racc/assets/nokogiri-css.y255
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'