From 0faceffacb7dd0df097aa628c26060de518d91ff Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Sun, 2 Nov 2014 19:29:09 +0100 Subject: [PATCH] Parsing support for nth-child(n+X) --- lib/oga/css/parser.y | 53 ++++++++++++------- .../parser/pseudo_classes/nth_child_spec.rb | 7 +++ 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/lib/oga/css/parser.y b/lib/oga/css/parser.y index 7b03892..a841752 100644 --- a/lib/oga/css/parser.y +++ b/lib/oga/css/parser.y @@ -274,11 +274,14 @@ rule # n : T_NTH { s(:nth, s(:int, 1)) } + # n+2 + | T_NTH integer { s(:nth, s(:int, 1), val[1]) } + # -n | T_MINUS T_NTH { s(:nth, s(:int, 1)) } # -n+2, -n-2 - | T_MINUS T_NTH integer { s(:nth, nil, val[2]) } + | T_MINUS T_NTH integer { s(:nth, s(:int, -1), val[2]) } # 2n | integer T_NTH { s(:nth, val[0]) } @@ -406,28 +409,12 @@ end else step, offset = *arg before_count = s(:add, count_call, s(:int, 1)) - - if step and step.children[0] >= 0 - compare = :gte - else - compare = :lte - end + compare = step_comparison(step) # 2n+2, 2n-4, etc if offset - # -2n - if step and non_positive_number?(step) - mod_val = s(:int, -step.children[0]) - - # 2n - elsif step - mod_val = step - - else - mod_val = s(:int, 1) - end - - node = s( + mod_val = step_modulo_value(step) + node = s( :and, s(compare, before_count, offset), s(:eq, s(:mod, s(:sub, before_count, offset), mod_val), s(:int, 0)) @@ -460,4 +447,30 @@ end return node.children[0] <= 0 end + ## + # @param [AST::Node] node + # @return [Symbol] + # + def step_comparison(node) + return node.children[0] >= 0 ? :gte : :lte + end + + ## + # @param [AST::Node] node + # @return [AST::Node] + # + def step_modulo_value(step) + # -2n + if step and non_positive_number?(step) + mod_val = s(:int, -step.children[0]) + + # 2n + elsif step + mod_val = step + + else + mod_val = s(:int, 1) + end + end + # vim: set ft=racc: diff --git a/spec/oga/css/parser/pseudo_classes/nth_child_spec.rb b/spec/oga/css/parser/pseudo_classes/nth_child_spec.rb index c62345b..3fe9217 100644 --- a/spec/oga/css/parser/pseudo_classes/nth_child_spec.rb +++ b/spec/oga/css/parser/pseudo_classes/nth_child_spec.rb @@ -52,6 +52,13 @@ describe Oga::CSS::Parser do ) end + example 'parse the x:nth-child(n+5) pseudo class' do + parse_css('x:nth-child(n+5)').should == parse_xpath( + 'descendant-or-self::x[(count(preceding-sibling::*) + 1) >= 5 ' \ + 'and (((count(preceding-sibling::*) + 1) - 5) mod 1) = 0]' + ) + end + example 'parse the x:nth-child(2n) pseudo class' do parse_css('x:nth-child(2n)').should == parse_css('x:nth-child(even)') end