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 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 options no_result_var
prechigh
left T_COLON
left T_CHILD T_FOLLOWING T_FOLLOWING_DIRECT
preclow
rule rule
css css
: path { val[0] } : expression { val[0] }
| /* none */ { nil } | /* 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 path_member
: node_test : node_test
| axis | axis
| pseudo_class | 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_test
: node_name { s(:test, *val[0]) } : node_name { s(:test, *val[0]) }
| node_name predicate { s(:test, *val[0], val[1]) } | node_name predicate { s(:test, *val[0], val[1]) }
@ -68,19 +82,19 @@ rule
axis axis
# x > y # x > y
: node_test T_CHILD node_test : path_member T_CHILD path_member
{ {
s(:axis, 'child', val[0], val[2]) s(:axis, 'child', val[0], val[2])
} }
# x + y # x + y
| node_test T_FOLLOWING node_test | path_member T_FOLLOWING path_member
{ {
s(:axis, 'following', val[0], val[2]) s(:axis, 'following', val[0], val[2])
} }
# x ~ y # x ~ y
| node_test T_FOLLOWING_DIRECT node_test | path_member T_FOLLOWING_DIRECT path_member
{ {
s(:axis, 'following-direct', val[0], val[2]) s(:axis, 'following-direct', val[0], val[2])
} }
@ -88,20 +102,15 @@ rule
pseudo_class pseudo_class
# x:root # 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) # 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 pseudo_arg
: integer : integer
| odd | odd
@ -129,8 +138,8 @@ rule
; ;
integer integer
: T_INT { s(:int, val[0].to_i) } : T_INT { s(:int, val[0].to_i) }
; ;
end end
---- inner ---- inner

View File

@ -11,6 +11,15 @@ describe Oga::CSS::Parser do
) )
end 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 example 'parse the + axis' do
parse_css('x + y').should == s( parse_css('x + y').should == s(
:axis, :axis,
@ -20,6 +29,15 @@ describe Oga::CSS::Parser do
) )
end 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 example 'parse the ~ axis' do
parse_css('x ~ y').should == s( parse_css('x ~ y').should == s(
:axis, :axis,
@ -36,5 +54,32 @@ describe Oga::CSS::Parser do
s(:test, nil, 'z') s(:test, nil, 'z')
) )
end 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
end end

View File

@ -86,5 +86,13 @@ describe Oga::CSS::Parser do
s(:nth, s(:int, -2), s(:int, -1)) s(:nth, s(:int, -2), s(:int, -1))
) )
end 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
end end