Better parsing for the nth-child pseudo class.
This uses stricter (and more correct) rules in both the lexer and the parser. The resulting AST has also received a small rework to make it more compact and less confusing.
This commit is contained in:
parent
60da2bdd3a
commit
16d66a7eb6
|
@ -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;
|
||||
*|;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue