diff --git a/lib/oga/css/lexer.rl b/lib/oga/css/lexer.rl index b98d860..90efda7 100644 --- a/lib/oga/css/lexer.rl +++ b/lib/oga/css/lexer.rl @@ -138,9 +138,13 @@ module Oga lbrack = '[' %{ add_token(:T_LBRACK) }; rbrack = ']' %{ add_token(:T_RBRACK) }; colon = ':' %{ add_token(:T_COLON) }; - lparen = '(' %{ add_token(:T_LPAREN) }; - rparen = ')' %{ add_token(:T_RPAREN) }; + lparen = '('; + rparen = ')'; pipe = '|'; + odd = 'odd'; + even = 'even'; + minus = '-'; + nth = 'n'; # Identifiers # @@ -208,23 +212,51 @@ module Oga # "-1" and "+1" are handled by the `integer` type and the corresponding # `emit_integer` action. - nth_integer = integer 'n'; - nth_identifier = '+n' | '-n'; + #nth_integer = integer 'n'; + #nth_identifier = '+n' | '-n'; - action emit_nth_integer { - value = slice_input(ts, te - 1).to_i + #action emit_nth_integer { + # value = slice_input(ts, te - 1).to_i - add_token(:T_INT, value) - add_token(:T_NTH, 'n') + # add_token(:T_INT, value) + # add_token(:T_NTH, nil) + #} + + #action emit_nth_identifier { + # emit(:T_NTH, ts, te) + #} + + # Pseudo Classes + # + # http://www.w3.org/TR/css3-selectors/#structural-pseudos + + action emit_lparen { + add_token(:T_LPAREN) + + fnext pseudo_args; } - action emit_nth_identifier { - emit(:T_NTH, ts, te) + action emit_rparen { + add_token(:T_RPAREN) + + fnext main; } + # Machine used for processing the arguments of a pseudo class. These are + # handled in a separate machine as these arguments can contain data not + # allowed elsewhere. For example, "2n" is not allowed to appear outside + # of the arguments list. + pseudo_args := |* + nth => { add_token(:T_NTH) }; + minus => { add_token(:T_MINUS) }; + odd => { add_token(:T_ODD) }; + even => { add_token(:T_EVEN) }; + integer => emit_integer; + rparen => emit_rparen; + *|; + main := |* whitespace | comma | hash | dot | lbrack | rbrack | colon; - lparen | rparen; # Some of the operators have similar characters (e.g. the "="). As a # result we can't use rules like the following: @@ -249,11 +281,10 @@ module Oga # this is handled separately. pipe => { add_token(:T_PIPE) }; - identifier => emit_identifier; - nth_integer => emit_nth_integer; - nth_identifier => emit_nth_identifier; - integer => emit_integer; - string => emit_string; + lparen => emit_lparen; + identifier => emit_identifier; + integer => emit_integer; + string => emit_string; any; *|; diff --git a/lib/oga/css/parser.y b/lib/oga/css/parser.y index 0e76563..71c4507 100644 --- a/lib/oga/css/parser.y +++ b/lib/oga/css/parser.y @@ -104,6 +104,24 @@ rule pseudo_arg : integer + | odd + | even + | nth + ; + + odd + : T_ODD { s(:odd) } + ; + + even + : T_EVEN { s(:even) } + ; + + nth + : T_NTH { s(:nth) } + | T_MINUS T_NTH { s(:nth) } + | integer T_NTH { s(:nth, val[0]) } + | integer T_NTH integer { s(:nth, val[0], val[2]) } ; string diff --git a/spec/oga/css/lexer/nth_numbers_spec.rb b/spec/oga/css/lexer/nth_numbers_spec.rb deleted file mode 100644 index a09fb0a..0000000 --- a/spec/oga/css/lexer/nth_numbers_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -require 'spec_helper' - -describe Oga::CSS::Lexer do - context 'nth numbers' do - example 'lex the 2n number' do - lex_css('2n').should == [[:T_INT, 2], [:T_NTH, 'n']] - end - - example 'lex the 2n+1 number' do - lex_css('2n+1').should == [[:T_INT, 2], [:T_NTH, 'n'], [:T_INT, 1]] - end - - example 'lex the 2n-1 number' do - lex_css('2n-1').should == [[:T_INT, 2], [:T_NTH, 'n'], [:T_INT, -1]] - end - - example 'lex the 2n -1 number' do - lex_css('2n -1').should == [[:T_INT, 2], [:T_NTH, 'n'], [:T_INT, -1]] - end - - example 'lex the -n number' do - lex_css('-n').should == [[:T_NTH, '-n']] - end - - example 'lex the +n number' do - lex_css('+n').should == [[:T_NTH, '+n']] - end - end -end diff --git a/spec/oga/css/lexer/pseudo_classes_spec.rb b/spec/oga/css/lexer/pseudo_classes_spec.rb index 6b73c50..d05a088 100644 --- a/spec/oga/css/lexer/pseudo_classes_spec.rb +++ b/spec/oga/css/lexer/pseudo_classes_spec.rb @@ -19,26 +19,92 @@ describe Oga::CSS::Lexer do ] end - example 'lex the :nth-child pseudo class using "odd" as an argument' do + example 'lex the :nth-child(odd) pseudo class' do lex_css(':nth-child(odd)').should == [ [:T_COLON, nil], [:T_IDENT, 'nth-child'], [:T_LPAREN, nil], - [:T_IDENT, 'odd'], + [:T_ODD, nil], [:T_RPAREN, nil] ] end - example 'lex the :nth-child pseudo class using "2n+1" as an argument' do + example 'lex the :nth-child(even) pseudo class' do + lex_css(':nth-child(even)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_EVEN, nil], + [:T_RPAREN, nil] + ] + end + + example 'lex the :nth-child(n) pseudo class' do + lex_css(':nth-child(n)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_NTH, nil], + [:T_RPAREN, nil] + ] + end + + example 'lex the :nth-child(-n) pseudo class' do + lex_css(':nth-child(-n)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_MINUS, nil], + [:T_NTH, nil], + [:T_RPAREN, nil] + ] + end + + example 'lex the :nth-child(2n) pseudo class' do + lex_css(':nth-child(2n)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_INT, 2], + [:T_NTH, nil], + [:T_RPAREN, nil] + ] + end + + example 'lex the :nth-child(2n+1) pseudo class' do lex_css(':nth-child(2n+1)').should == [ [:T_COLON, nil], [:T_IDENT, 'nth-child'], [:T_LPAREN, nil], [:T_INT, 2], - [:T_NTH, 'n'], + [:T_NTH, nil], [:T_INT, 1], [:T_RPAREN, nil] ] end + + example 'lex the :nth-child(2n-1) pseudo class' do + lex_css(':nth-child(2n-1)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_INT, 2], + [:T_NTH, nil], + [:T_INT, -1], + [:T_RPAREN, nil] + ] + end + + example 'lex the :nth-child(-2n-1) pseudo class' do + lex_css(':nth-child(-2n-1)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_INT, -2], + [:T_NTH, nil], + [:T_INT, -1], + [:T_RPAREN, nil] + ] + end end end diff --git a/spec/oga/css/parser/pseudo_classes_spec.rb b/spec/oga/css/parser/pseudo_classes_spec.rb index 386b8ae..702f332 100644 --- a/spec/oga/css/parser/pseudo_classes_spec.rb +++ b/spec/oga/css/parser/pseudo_classes_spec.rb @@ -6,12 +6,84 @@ describe Oga::CSS::Parser do parse_css('x:root').should == s(:pseudo, 'root', s(:test, nil, 'x')) end - example 'parse the :nth-child pseudo class' do - parse_css('x:nth-child(2)').should == s( + example 'parse the x:nth-child pseudo class' do + parse_css('x:nth-child(1)').should == s( :pseudo, 'nth-child', s(:test, nil, 'x'), - s(:int, 2) + s(:int, 1) + ) + end + + example 'parse the x:nth-child(odd) pseudo class' do + parse_css('x:nth-child(odd)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:odd) + ) + end + + example 'parse the x:nth-child(even) pseudo class' do + parse_css('x:nth-child(even)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:even) + ) + end + + example 'parse the x:nth-child(n) pseudo class' do + parse_css('x:nth-child(n)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:nth) + ) + end + + example 'parse the x:nth-child(-n) pseudo class' do + parse_css('x:nth-child(-n)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:nth) + ) + end + + example 'parse the x:nth-child(2n) pseudo class' do + parse_css('x:nth-child(2n)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:nth, s(:int, 2)) + ) + end + + example 'parse the x:nth-child(2n+1) pseudo class' do + parse_css('x:nth-child(2n+1)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:nth, s(:int, 2), s(:int, 1)) + ) + end + + example 'parse the x:nth-child(2n-1) pseudo class' do + parse_css('x:nth-child(2n-1)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:nth, s(:int, 2), s(:int, -1)) + ) + end + + example 'parse the x:nth-child(-2n-1) pseudo class' do + parse_css('x:nth-child(-2n-1)').should == s( + :pseudo, + 'nth-child', + s(:test, nil, 'x'), + s(:nth, s(:int, -2), s(:int, -1)) ) end end