Reworked CSS parser rules.

This includes better rules for parsing separate path members, pseudo class
arguments and some changes to remove all remaining parsing conflicts.
This commit is contained in:
Yorick Peterse 2014-10-07 22:47:47 +02:00
parent b40c0243ce
commit 6792127600
3 changed files with 86 additions and 24 deletions

View File

@ -3,30 +3,44 @@
#
class Oga::CSS::Parser
token T_IDENT T_PIPE T_LBRACK T_RBRACK T_COLON T_SPACE T_LPAREN T_RPAREN T_MINUS
token T_EQ T_SPACE_IN T_STARTS_WITH T_ENDS_WITH T_IN T_HYPHEN_IN
token T_CHILD T_FOLLOWING T_FOLLOWING_DIRECT
token T_NTH T_INT T_STRING T_ODD T_EVEN
options no_result_var
prechigh
left T_COLON
left T_CHILD T_FOLLOWING T_FOLLOWING_DIRECT
preclow
rule
css
: path { val[0] }
: expression { val[0] }
| /* none */ { nil }
;
expression
: path
| path_member
;
path
: path_members { s(:path, *val[0]) }
;
path_members
: path_member T_SPACE path_member { [val[0], val[2]] }
| path_member T_SPACE path_members { [val[0], *val[2]] }
;
path_member
: node_test
| axis
| pseudo_class
;
path_members
: path_member path_member { [val[0], val[1]] }
| path_member path_members { [val[0], *val[1]] }
;
path
: path_members { s(:path, *val[0]) }
| path_member
;
node_test
: node_name { s(:test, *val[0]) }
| node_name predicate { s(:test, *val[0], val[1]) }
@ -68,19 +82,19 @@ rule
axis
# x > y
: node_test T_CHILD node_test
: path_member T_CHILD path_member
{
s(:axis, 'child', val[0], val[2])
}
# x + y
| node_test T_FOLLOWING node_test
| path_member T_FOLLOWING path_member
{
s(:axis, 'following', val[0], val[2])
}
# x ~ y
| node_test T_FOLLOWING_DIRECT node_test
| path_member T_FOLLOWING_DIRECT path_member
{
s(:axis, 'following-direct', val[0], val[2])
}
@ -88,20 +102,15 @@ rule
pseudo_class
# x:root
: node_test T_COLON T_IDENT { s(:pseudo, val[2], val[0]) }
: path_member T_COLON T_IDENT { s(:pseudo, val[2], val[0]) }
# x:nth-child(2)
| node_test T_COLON T_IDENT T_LPAREN pseudo_args T_RPAREN
| path_member T_COLON T_IDENT T_LPAREN pseudo_arg T_RPAREN
{
s(:pseudo, val[2], val[0], *val[4])
s(:pseudo, val[2], val[0], val[4])
}
;
pseudo_args
: pseudo_args pseudo_arg { val[0] << val[1] }
| pseudo_arg { val }
;
pseudo_arg
: integer
| odd
@ -129,8 +138,8 @@ rule
;
integer
: T_INT { s(:int, val[0].to_i) }
;
: T_INT { s(:int, val[0].to_i) }
;
end
---- inner

View File

@ -11,6 +11,15 @@ describe Oga::CSS::Parser do
)
end
example 'parse the > axis called on another > axis' do
parse_css('a > b > c').should == s(
:axis,
'child',
s(:axis, 'child', s(:test, nil, 'a'), s(:test, nil, 'b')),
s(:test, nil, 'c')
)
end
example 'parse the + axis' do
parse_css('x + y').should == s(
:axis,
@ -20,6 +29,15 @@ describe Oga::CSS::Parser do
)
end
example 'parse the + axis called on another + axis' do
parse_css('a + b + c').should == s(
:axis,
'following-direct',
s(:axis, 'following-direct', s(:test, nil, 'a'), s(:test, nil, 'b')),
s(:test, nil, 'c')
)
end
example 'parse the ~ axis' do
parse_css('x ~ y').should == s(
:axis,
@ -36,5 +54,32 @@ describe Oga::CSS::Parser do
s(:test, nil, 'z')
)
end
example 'parse the ~ axis called on another ~ axis' do
parse_css('a ~ b ~ c').should == s(
:axis,
'following',
s(:axis, 'following', s(:test, nil, 'a'), s(:test, nil, 'b')),
s(:test, nil, 'c')
)
end
example 'parse a pseudo class followed by the ~ axis' do
parse_css('x:root ~ a').should == s(
:axis,
'following',
s(:pseudo, 'root', s(:test, nil, 'x')),
s(:test, nil, 'a')
)
end
example 'parse the ~ axis followed by a pseudo class' do
parse_css('a ~ x:root').should == s(
:axis,
'following',
s(:test, nil, 'a'),
s(:pseudo, 'root', s(:test, nil, 'x'))
)
end
end
end

View File

@ -86,5 +86,13 @@ describe Oga::CSS::Parser do
s(:nth, s(:int, -2), s(:int, -1))
)
end
example 'parse two pseudo selectors' do
parse_css('x:focus:hover').should == s(
:pseudo,
'hover',
s(:pseudo, 'focus', s(:test, nil, 'x'))
)
end
end
end