mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
255 lines
7 KiB
Text
255 lines
7 KiB
Text
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'
|