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
= selector selectors_follow*
{
node = val[0]
query = val[0]
val[1].each do |chunk|
node = s(:pipe, node, chunk)
query = s(:pipe, query, chunk)
end
node
query
}
;
@ -43,88 +43,95 @@ selectors_follow
;
selector
= axis_or_descendant_or_self predicates? selector_follow?
# foo, foo.bar, foo[bar], etc
= node_test predicates? selector_follow?
{
node = val[0]
pred = val[1]
more = val[2]
node = s(:axis, 'descendant', val[0])
preds = val[1]
more = val[2]
if pred
node = s(:predicate, node, pred)
if preds
node = s(:predicate, node, preds)
end
if more
node = node.updated(nil, node.children + [more])
end
node = add_child(node, more) if more
node
}
# > foo, + foo, etc
| axis
# .foo, :nth-child(2n), etc
| predicates selector_follow?
{
node = s(:axis, 'descendant', on_test(nil, '*'))
preds = val[0]
more = val[1]
if more
s(:predicate, node, preds, more)
if preds
node = s(:predicate, node, preds)
else
s(:predicate, node, preds)
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)
node = s(:predicate, node)
end
s(
:predicate,
s(:axis, 'following-sibling', on_test(nil, '*')),
s(:int, 1),
child
)
node = add_child(node, more) if more
node
}
;
selector_follow
= T_SPACE selector { val[1] }
;
descendant_or_self
= node_test { s(:axis, 'descendant', val[0]) }
| axis
;
axis
# > 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
| 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
= descendant_or_self
| axis
axis_selector
# foo.bar
= node_test predicates?
# .foo
| predicates { [nil, val[0]] }
;
axis_selector
= node_test
| axis
id_class_pseudo_class
= id
| class
| pseudo_class
;
node_test
@ -153,9 +160,7 @@ predicates
;
predicate
= class
| id
| pseudo_class
= id_class_pseudo_class
| attribute_predicate
;
@ -678,4 +683,32 @@ even
mod_val
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
}