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) };
|
lbrack = '[' %{ add_token(:T_LBRACK) };
|
||||||
rbrack = ']' %{ add_token(:T_RBRACK) };
|
rbrack = ']' %{ add_token(:T_RBRACK) };
|
||||||
colon = ':' %{ add_token(:T_COLON) };
|
colon = ':' %{ add_token(:T_COLON) };
|
||||||
lparen = '(' %{ add_token(:T_LPAREN) };
|
lparen = '(';
|
||||||
rparen = ')' %{ add_token(:T_RPAREN) };
|
rparen = ')';
|
||||||
pipe = '|';
|
pipe = '|';
|
||||||
|
odd = 'odd';
|
||||||
|
even = 'even';
|
||||||
|
minus = '-';
|
||||||
|
nth = 'n';
|
||||||
|
|
||||||
# Identifiers
|
# Identifiers
|
||||||
#
|
#
|
||||||
|
@ -208,23 +212,51 @@ module Oga
|
||||||
# "-1" and "+1" are handled by the `integer` type and the corresponding
|
# "-1" and "+1" are handled by the `integer` type and the corresponding
|
||||||
# `emit_integer` action.
|
# `emit_integer` action.
|
||||||
|
|
||||||
nth_integer = integer 'n';
|
#nth_integer = integer 'n';
|
||||||
nth_identifier = '+n' | '-n';
|
#nth_identifier = '+n' | '-n';
|
||||||
|
|
||||||
action emit_nth_integer {
|
#action emit_nth_integer {
|
||||||
value = slice_input(ts, te - 1).to_i
|
# value = slice_input(ts, te - 1).to_i
|
||||||
|
|
||||||
add_token(:T_INT, value)
|
# add_token(:T_INT, value)
|
||||||
add_token(:T_NTH, 'n')
|
# 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 {
|
action emit_rparen {
|
||||||
emit(:T_NTH, ts, te)
|
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 := |*
|
main := |*
|
||||||
whitespace | comma | hash | dot | lbrack | rbrack | colon;
|
whitespace | comma | hash | dot | lbrack | rbrack | colon;
|
||||||
lparen | rparen;
|
|
||||||
|
|
||||||
# Some of the operators have similar characters (e.g. the "="). As a
|
# Some of the operators have similar characters (e.g. the "="). As a
|
||||||
# result we can't use rules like the following:
|
# result we can't use rules like the following:
|
||||||
|
@ -249,11 +281,10 @@ module Oga
|
||||||
# this is handled separately.
|
# this is handled separately.
|
||||||
pipe => { add_token(:T_PIPE) };
|
pipe => { add_token(:T_PIPE) };
|
||||||
|
|
||||||
identifier => emit_identifier;
|
lparen => emit_lparen;
|
||||||
nth_integer => emit_nth_integer;
|
identifier => emit_identifier;
|
||||||
nth_identifier => emit_nth_identifier;
|
integer => emit_integer;
|
||||||
integer => emit_integer;
|
string => emit_string;
|
||||||
string => emit_string;
|
|
||||||
|
|
||||||
any;
|
any;
|
||||||
*|;
|
*|;
|
||||||
|
|
|
@ -104,6 +104,24 @@ rule
|
||||||
|
|
||||||
pseudo_arg
|
pseudo_arg
|
||||||
: integer
|
: 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
|
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
|
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 == [
|
lex_css(':nth-child(odd)').should == [
|
||||||
[:T_COLON, nil],
|
[:T_COLON, nil],
|
||||||
[:T_IDENT, 'nth-child'],
|
[:T_IDENT, 'nth-child'],
|
||||||
[:T_LPAREN, nil],
|
[:T_LPAREN, nil],
|
||||||
[:T_IDENT, 'odd'],
|
[:T_ODD, nil],
|
||||||
[:T_RPAREN, nil]
|
[:T_RPAREN, nil]
|
||||||
]
|
]
|
||||||
end
|
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 == [
|
lex_css(':nth-child(2n+1)').should == [
|
||||||
[:T_COLON, nil],
|
[:T_COLON, nil],
|
||||||
[:T_IDENT, 'nth-child'],
|
[:T_IDENT, 'nth-child'],
|
||||||
[:T_LPAREN, nil],
|
[:T_LPAREN, nil],
|
||||||
[:T_INT, 2],
|
[:T_INT, 2],
|
||||||
[:T_NTH, 'n'],
|
[:T_NTH, nil],
|
||||||
[:T_INT, 1],
|
[:T_INT, 1],
|
||||||
[:T_RPAREN, nil]
|
[:T_RPAREN, nil]
|
||||||
]
|
]
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,12 +6,84 @@ describe Oga::CSS::Parser do
|
||||||
parse_css('x:root').should == s(:pseudo, 'root', s(:test, nil, 'x'))
|
parse_css('x:root').should == s(:pseudo, 'root', s(:test, nil, 'x'))
|
||||||
end
|
end
|
||||||
|
|
||||||
example 'parse the :nth-child pseudo class' do
|
example 'parse the x:nth-child pseudo class' do
|
||||||
parse_css('x:nth-child(2)').should == s(
|
parse_css('x:nth-child(1)').should == s(
|
||||||
:pseudo,
|
:pseudo,
|
||||||
'nth-child',
|
'nth-child',
|
||||||
s(:test, nil, 'x'),
|
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
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue