ruby/test/racc/assets/nokogiri-css.y

256 строки
7.0 KiB
Plaintext

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'