Basic setup for lexing CSS pseudo selectors.

This includes support for the crazy 2n+1 syntax you can use with selectors such
as :nth-child().

CSS selectors: doing what XPath already does using an even crazier syntax,
because screw you.
This commit is contained in:
Yorick Peterse 2014-09-23 00:28:46 +02:00
parent ea4a429430
commit 73c5dbe636
4 changed files with 125 additions and 2 deletions

View File

@ -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;
*|;

View File

@ -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

View File

@ -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

View File

@ -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