diff --git a/lib/oga/css/lexer.rl b/lib/oga/css/lexer.rl index a8b709a..aab61bc 100644 --- a/lib/oga/css/lexer.rl +++ b/lib/oga/css/lexer.rl @@ -137,6 +137,9 @@ module Oga dot = '.' %{ add_token(:T_DOT) }; 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) }; pipe = '|'; # Identifiers @@ -168,8 +171,43 @@ module Oga op_fol_direct = '+'; op_fol = '~'; + # Numbers + # + # CSS selectors only understand integers, floating points are not + # supported. + + integer = ('-' | '+')* digit+; + + action emit_integer { + value = slice_input(ts, te).to_i + + add_token(:T_INT, value) + } + + # Nth numbers + # + # These numbers are in the form of 2n+1 and are used for + # pseudo-selectors such as nth-child(2n+1). The following parts such as + # "-1" and "+1" are handled by the `integer` type and the corresponding + # `emit_integer` action. + + nth_integer = integer 'n'; + nth_identifier = '+n' | '-n'; + + action emit_nth_integer { + value = slice_input(ts, te - 1).to_i + + add_token(:T_INT, value) + add_token(:T_NTH, 'n') + } + + action emit_nth_identifier { + emit(:T_NTH, ts, te) + } + main := |* - whitespace | comma | hash | dot | lbrack | rbrack; + 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: @@ -194,7 +232,10 @@ module Oga # this is handled separately. pipe => { add_token(:T_PIPE) }; - identifier => emit_identifier; + identifier => emit_identifier; + nth_integer => emit_nth_integer; + nth_identifier => emit_nth_identifier; + integer => emit_integer; any; *|; diff --git a/spec/oga/css/lexer/integers_spec.rb b/spec/oga/css/lexer/integers_spec.rb new file mode 100644 index 0000000..95e549d --- /dev/null +++ b/spec/oga/css/lexer/integers_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe Oga::CSS::Lexer do + context 'integers' do + example 'lex an integer' do + lex_css('10').should == [[:T_INT, 10]] + end + end +end diff --git a/spec/oga/css/lexer/nth_numbers_spec.rb b/spec/oga/css/lexer/nth_numbers_spec.rb new file mode 100644 index 0000000..a09fb0a --- /dev/null +++ b/spec/oga/css/lexer/nth_numbers_spec.rb @@ -0,0 +1,29 @@ +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 new file mode 100644 index 0000000..6b73c50 --- /dev/null +++ b/spec/oga/css/lexer/pseudo_classes_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe Oga::CSS::Lexer do + context 'pseudo classes' do + example 'lex the :root pseudo class' do + lex_css(':root').should == [ + [:T_COLON, nil], + [:T_IDENT, 'root'] + ] + end + + example 'lex the :nth-child pseudo class' do + lex_css(':nth-child(1)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_INT, 1], + [:T_RPAREN, nil] + ] + end + + example 'lex the :nth-child pseudo class using "odd" as an argument' do + lex_css(':nth-child(odd)').should == [ + [:T_COLON, nil], + [:T_IDENT, 'nth-child'], + [:T_LPAREN, nil], + [:T_IDENT, 'odd'], + [:T_RPAREN, nil] + ] + end + + example 'lex the :nth-child pseudo class using "2n+1" as an argument' 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_INT, 1], + [:T_RPAREN, nil] + ] + end + end +end