From 9cce93fc4ab92e64668235fed01dd24c9eb7f201 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Sat, 1 Nov 2014 20:58:28 +0100 Subject: [PATCH] Parsing support for :nth-last-child. --- lib/oga/css/parser.y | 40 ++++---- .../pseudo_classes/nth_last_child_spec.rb | 95 +++++++++++++++++++ 2 files changed, 118 insertions(+), 17 deletions(-) create mode 100644 spec/oga/css/parser/pseudo_classes/nth_last_child_spec.rb diff --git a/lib/oga/css/parser.y b/lib/oga/css/parser.y index 741d333..db901a9 100644 --- a/lib/oga/css/parser.y +++ b/lib/oga/css/parser.y @@ -379,26 +379,32 @@ end # @return [AST::Node] # def on_pseudo_class_nth_child(arg) - # literal 2, 4, etc + return generate_nth_child('preceding-sibling', arg) + end + + ## + # Generates the AST for the `nth-last-child` pseudo class. + # + # @param [AST::Node] arg + # @return [AST::Node] + # + def on_pseudo_class_nth_last_child(arg) + return generate_nth_child('following-sibling', arg) + end + + ## + # @param [String] count_axis + # @param [AST::Node] arg + # @return [AST::Node] + # + def generate_nth_child(count_axis, arg) + count_call = s(:call, 'count', s(:axis, count_axis, s(:test, nil, '*'))) + + # literal 2, 4, etc if arg.type == :int - node = s( - :eq, - s( - :call, - 'count', - s(:axis, 'preceding-sibling', s(:test, nil, '*')) - ), - s(:int, arg.children[0] - 1) - ) + node = s(:eq, count_call, s(:int, arg.children[0] - 1)) else step, offset = *arg - - count_call = s( - :call, - 'count', - s(:axis, 'preceding-sibling', s(:test, nil, '*')) - ) - before_count = s(:add, count_call, s(:int, 1)) if step and step.children[0] >= 0 diff --git a/spec/oga/css/parser/pseudo_classes/nth_last_child_spec.rb b/spec/oga/css/parser/pseudo_classes/nth_last_child_spec.rb new file mode 100644 index 0000000..359807c --- /dev/null +++ b/spec/oga/css/parser/pseudo_classes/nth_last_child_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper' + +describe Oga::CSS::Parser do + context ':nth-last-child pseudo class' do + example 'parse the x:nth-last-child(1) pseudo class' do + parse_css('x:nth-last-child(1)').should == parse_xpath( + 'descendant-or-self::x[count(following-sibling::*) = 0]' + ) + end + + example 'parse the :nth-last-child(1) pseudo class' do + parse_css(':nth-last-child(1)').should == parse_xpath( + 'descendant-or-self::*[count(following-sibling::*) = 0]' + ) + end + + example 'parse the :nth-last-child(2) pseudo class' do + parse_css(':nth-last-child(2)').should == parse_xpath( + 'descendant-or-self::*[count(following-sibling::*) = 1]' + ) + end + + example 'parse the x:nth-last-child(even) pseudo class' do + parse_css('x:nth-last-child(even)').should == parse_xpath( + 'descendant-or-self::x[((count(following-sibling::*) + 1) mod 2) = 0]' + ) + end + + example 'parse the x:nth-last-child(odd) pseudo class' do + parse_css('x:nth-last-child(odd)').should == parse_xpath( + 'descendant-or-self::x[(count(following-sibling::*) + 1) >= 1 ' \ + 'and (((count(following-sibling::*) + 1) - 1) mod 2) = 0]' + ) + end + + example 'parse the x:nth-last-child(n) pseudo class' do + parse_css('x:nth-last-child(n)').should == parse_xpath( + 'descendant-or-self::x[((count(following-sibling::*) + 1) mod 1) = 0]' + ) + end + + example 'parse the x:nth-last-child(-n) pseudo class' do + parse_css('x:nth-last-child(-n)').should == parse_xpath( + 'descendant-or-self::x[((count(following-sibling::*) + 1) mod 1) = 0]' + ) + end + + example 'parse the x:nth-last-child(-n+6) pseudo class' do + parse_css('x:nth-last-child(-n+6)').should == parse_xpath( + 'descendant-or-self::x[((count(following-sibling::*) + 1) <= 6) ' \ + 'and (((count(following-sibling::*) + 1) - 6) mod 1) = 0]' + ) + end + + example 'parse the x:nth-last-child(-n-6) pseudo class' do + parse_css('x:nth-last-child(-n-6)').should == parse_xpath( + 'descendant-or-self::x[count(following-sibling::*) = -1]' + ) + end + + example 'parse the x:nth-last-child(2n) pseudo class' do + parse_css('x:nth-last-child(2n)').should == parse_css( + 'x:nth-last-child(even)' + ) + end + + example 'parse the x:nth-last-child(2n+1) pseudo class' do + parse_css('x:nth-last-child(2n+1)').should == parse_xpath( + 'descendant-or-self::x[(count(following-sibling::*) + 1) >= 1 ' \ + 'and (((count(following-sibling::*) + 1) - 1) mod 2) = 0]' + ) + end + + example 'parse the x:nth-last-child(2n-6) pseudo class' do + parse_css('x:nth-last-child(2n-6)').should == parse_xpath( + 'descendant-or-self::x[(count(following-sibling::*) + 1) >= 2 ' \ + 'and (((count(following-sibling::*) + 1) - 2) mod 2) = 0]' + ) + end + + example 'parse the x:nth-last-child(-2n-6) pseudo class' do + parse_css('x:nth-last-child(-2n-6)').should == parse_xpath( + 'descendant-or-self::x[((count(following-sibling::*) + 1) <= -2) ' \ + 'and (((count(following-sibling::*) + 1) - -2) mod 2) = 0]' + ) + end + + example 'parse the x:nth-last-child(-2n+6) pseudo class' do + parse_css('x:nth-last-child(-2n+6)').should == parse_xpath( + 'descendant-or-self::x[((count(following-sibling::*) + 1) <= 6) ' \ + 'and (((count(following-sibling::*) + 1) - 6) mod 2) = 0]' + ) + end + end +end