Basic support for :nth-child()

This already includes support for formulas such as 2n, odd, even and 2n+1.
Negative formulas, just "n" and others are not yet supported.
This commit is contained in:
Yorick Peterse 2014-10-28 00:21:11 +01:00
parent 46646e2ace
commit 87f6b9c723
2 changed files with 69 additions and 30 deletions

View File

@ -259,8 +259,19 @@ rule
| selector | selector
; ;
string
: T_STRING { s(:string, val[0]) }
;
integer
: T_INT { s(:int, val[0].to_i) }
;
# These AST nodes are _not_ the final AST nodes. Instead they are used by
# on_pseudo_class_nth_child() to determine what the final AST should be.
nth nth
: T_NTH { s(:nth) } : T_NTH { s(:nth, s(:int, 1)) }
| T_MINUS T_NTH { s(:nth) } | T_MINUS T_NTH { s(:nth) }
| integer T_NTH { s(:nth, val[0]) } | integer T_NTH { s(:nth, val[0]) }
| integer T_NTH integer { s(:nth, val[0], val[2]) } | integer T_NTH integer { s(:nth, val[0], val[2]) }
@ -273,14 +284,6 @@ rule
even even
: T_EVEN { s(:nth, s(:int, 2)) } : T_EVEN { s(:nth, s(:int, 2)) }
; ;
string
: T_STRING { s(:string, val[0]) }
;
integer
: T_INT { s(:int, val[0].to_i) }
;
end end
---- inner ---- inner
@ -347,4 +350,45 @@ end
def on_pseudo_class_root def on_pseudo_class_root
return s(:call, 'not', s(:axis, 'parent', s(:test, nil, '*'))) return s(:call, 'not', s(:axis, 'parent', s(:test, nil, '*')))
end end
##
# Generates the AST for the `nth-child` pseudo class.
#
# @param [AST::Node] arg
# @return [AST::Node]
#
def on_pseudo_class_nth_child(arg)
if arg.type == :int
node = s(
:eq,
s(
:call,
'count',
s(:axis, 'preceding-sibling', s(:test, nil, '*'))
),
s(:int, arg.children[0] - 1)
)
else
step, offset = *arg
before_count = s(
:add,
s(:call, 'count', s(:axis, 'preceding-sibling', s(:test, nil, '*'))),
s(:int, 1)
)
if offset
node = s(
:and,
s(:gte, before_count, offset),
s(:eq, s(:mod, s(:sub, before_count, offset), s(:int, 2)), s(:int, 0))
)
else
node = s(:mod, before_count, step)
end
end
return node
end
# vim: set ft=racc: # vim: set ft=racc:

View File

@ -15,38 +15,33 @@ describe Oga::CSS::Parser do
end end
example 'parse the x:nth-child(1) pseudo class' do example 'parse the x:nth-child(1) pseudo class' do
parse_css('x:nth-child(1)').should == s( parse_css('x:nth-child(1)').should == parse_xpath(
:pseudo, 'descendant-or-self::x[count(preceding-sibling::*) = 0]'
s(:test, nil, 'x'),
'nth-child',
s(:int, 1)
) )
end end
example 'parse the :nth-child(1) pseudo class' do example 'parse the :nth-child(1) pseudo class' do
parse_css(':nth-child(1)').should == s( parse_css(':nth-child(1)').should == parse_xpath(
:pseudo, 'descendant-or-self::*[count(preceding-sibling::*) = 0]'
nil,
'nth-child',
s(:int, 1)
) )
end end
example 'parse the x:nth-child(odd) pseudo class' do example 'parse the :nth-child(2) pseudo class' do
parse_css('x:nth-child(odd)').should == s( parse_css(':nth-child(2)').should == parse_xpath(
:pseudo, 'descendant-or-self::*[count(preceding-sibling::*) = 1]'
s(:test, nil, 'x'),
'nth-child',
s(:odd)
) )
end end
example 'parse the x:nth-child(even) pseudo class' do example 'parse the x:nth-child(even) pseudo class' do
parse_css('x:nth-child(even)').should == s( parse_css('x:nth-child(even)').should == parse_xpath(
:pseudo, 'descendant-or-self::x[(count(preceding-sibling::*) + 1) mod 2]'
s(:test, nil, 'x'), )
'nth-child', end
s(:even)
example 'parse the x:nth-child(odd) pseudo class' do
parse_css('x:nth-child(odd)').should == parse_xpath(
'descendant-or-self::x[(count(preceding-sibling::*) + 1) >= 1 ' \
'and (((count(preceding-sibling::*) + 1) - 1) mod 2) = 0]'
) )
end end