Revamp CSS parser for better axis support

This makes it possible to parse expressions such as "foo>bar", "> .bar",
"> foo.bar", and similar expressions.

This fixes #126 and fixes #131.
This commit is contained in:
Yorick Peterse 2015-09-04 16:06:20 +02:00
parent c713f6250f
commit f753f08f18
1 changed files with 86 additions and 53 deletions

View File

@ -28,13 +28,13 @@ css
selectors selectors
= selector selectors_follow* = selector selectors_follow*
{ {
node = val[0] query = val[0]
val[1].each do |chunk| val[1].each do |chunk|
node = s(:pipe, node, chunk) query = s(:pipe, query, chunk)
end end
node query
} }
; ;
@ -43,88 +43,95 @@ selectors_follow
; ;
selector selector
= axis_or_descendant_or_self predicates? selector_follow? # foo, foo.bar, foo[bar], etc
= node_test predicates? selector_follow?
{ {
node = val[0] node = s(:axis, 'descendant', val[0])
pred = val[1] preds = val[1]
more = val[2] more = val[2]
if pred if preds
node = s(:predicate, node, pred) node = s(:predicate, node, preds)
end end
if more node = add_child(node, more) if more
node = node.updated(nil, node.children + [more])
end
node node
} }
# > foo, + foo, etc
| axis
# .foo, :nth-child(2n), etc
| predicates selector_follow? | predicates selector_follow?
{ {
node = s(:axis, 'descendant', on_test(nil, '*')) node = s(:axis, 'descendant', on_test(nil, '*'))
preds = val[0] preds = val[0]
more = val[1] more = val[1]
if more if preds
s(:predicate, node, preds, more) node = s(:predicate, node, preds)
else else
s(:predicate, node, preds) node = s(:predicate, node)
end
}
# + foo
#
# This branch is not part of the "axis" rule because we need more fine grained
# control over what to do with any selec
| T_PLUS axis_selector selector_follow?
{
selector = val[1]
more = val[2]
if more
child = s(:axis, 'self', selector, more)
else
child = s(:axis, 'self', selector)
end end
s( node = add_child(node, more) if more
:predicate,
s(:axis, 'following-sibling', on_test(nil, '*')), node
s(:int, 1),
child
)
} }
; ;
selector_follow selector_follow
= T_SPACE selector { val[1] } = T_SPACE selector { val[1] }
; | axis
descendant_or_self
= node_test { s(:axis, 'descendant', val[0]) }
; ;
axis axis
# > foo # > foo
= T_GREATER axis_selector = T_GREATER axis_selector selector_follow?
{ {
s(:axis, 'child', val[1]) test, preds = val[1]
more = val[2]
generate_axis('child', test, preds, more)
}
# + foo
| T_PLUS axis_selector selector_follow?
{
test, preds = val[1]
more = val[2]
s(
:predicate,
s(:axis, 'following-sibling', on_test(nil, '*')),
s(:int, 1),
generate_axis('self', test, preds, more)
)
} }
# ~ foo # ~ foo
| T_TILDE axis_selector | T_TILDE axis_selector selector_follow?
{ {
s(:axis, 'following-sibling', val[1]) test, preds = val[1]
more = val[2]
generate_axis('following-sibling', test, preds, more)
} }
; ;
axis_or_descendant_or_self axis_selector
= descendant_or_self # foo.bar
| axis = node_test predicates?
# .foo
| predicates { [nil, val[0]] }
; ;
axis_selector id_class_pseudo_class
= node_test = id
| axis | class
| pseudo_class
; ;
node_test node_test
@ -153,9 +160,7 @@ predicates
; ;
predicate predicate
= class = id_class_pseudo_class
| id
| pseudo_class
| attribute_predicate | attribute_predicate
; ;
@ -678,4 +683,32 @@ even
mod_val mod_val
end end
# @param [String] name
# @param [AST::Node] test
# @param [AST::Node] predicates
# @param [AST::Node] more
# @return [AST::Node]
def generate_axis(name, test = nil, predicates = nil, more = nil)
if test
node = s(:axis, name, test)
else
node = s(:axis, name, on_test(nil, '*'))
end
if predicates
node = s(:predicate, node, predicates)
end
node = add_child(node, more) if more
node
end
# @param [AST::Node] node
# @param [AST::Node] child
# @return [AST::Node]
def add_child(node, child)
node.updated(nil, node.children + [child])
end
} }