Skip to content

Have at-rule without block handled in two stages as well #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/css-parsing-tests/one_rule.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
"@foo", ["at-rule", "foo", [], null],

"@foo bar; \t/* comment */", ["at-rule", "foo", [" ", ["ident", "bar"]], null],
" /**/ @foo bar{[(4", ["at-rule", "foo",
" /**/ @foo-with-block bar{[(4", ["at-rule", "foo-with-block",
[" ", ["ident", "bar"]],
[["[]", ["()", ["number", "4", 4, "integer"]]]]
],

"@foo { bar", ["at-rule", "foo", [" "], [" ", ["ident", "bar"]]],
"@foo [ bar", ["at-rule", "foo", [" ", ["[]", " ", ["ident", "bar"]]], null],
"@foo-with-block { bar", ["at-rule", "foo-with-block", [" "], [
" ", ["ident", "bar"]]
],
"@foo [ bar", ["at-rule", "foo",
[" ", ["[]", " ", ["ident", "bar"]]], null
],

" /**/ div > p { color: #aaa; } /**/ ", ["qualified rule",
[["ident", "div"], " ", ">", " ", ["ident", "p"], " "],
Expand Down
8 changes: 5 additions & 3 deletions src/css-parsing-tests/rule_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@
"@foo", [["at-rule", "foo", [], null]],

"@charset; @foo", [
["at-rule", "charset", [], null],
["error", "invalid"],
["at-rule", "foo", [], null]
],

"@foo bar; \t/* comment */", [["at-rule", "foo", [" ", ["ident", "bar"]], null]],

" /**/ @foo bar{[(4", [["at-rule", "foo",
" /**/ @foo-with-block bar{[(4", [["at-rule", "foo-with-block",
[" ", ["ident", "bar"]],
[["[]", ["()", ["number", "4", 4, "integer"]]]]
]],

"@foo { bar", [["at-rule", "foo", [" "], [" ", ["ident", "bar"]]]],
"@foo-with-block { bar", [["at-rule", "foo-with-block", [" "], [
" ", ["ident", "bar"]]]
],
"@foo [ bar", [["at-rule", "foo", [" ", ["[]", " ", ["ident", "bar"]]], null]],

" /**/ div > p { color: #aaa; } /**/ ", [["qualified rule",
Expand Down
8 changes: 5 additions & 3 deletions src/css-parsing-tests/stylesheet.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@

"@foo; @charset 4 {}", [
["at-rule", "foo", [], null],
["at-rule", "charset", [" ", ["number", "4", 4, "integer"], " "], []]
["error", "invalid"]
],

"@foo bar; \t/* comment */", [["at-rule", "foo", [" ", ["ident", "bar"]], null]],

" /**/ @foo bar{[(4", [["at-rule", "foo",
" /**/ @foo-with-block bar{[(4", [["at-rule", "foo-with-block",
[" ", ["ident", "bar"]],
[["[]", ["()", ["number", "4", 4, "integer"]]]]
]],

"@foo { bar", [["at-rule", "foo", [" "], [" ", ["ident", "bar"]]]],
"@foo-with-block { bar", [["at-rule", "foo-with-block", [" "], [
" ", ["ident", "bar"]]]
],
"@foo [ bar", [["at-rule", "foo", [" ", ["[]", " ", ["ident", "bar"]]], null]],

" /**/ div > p { color: #aaa; } /**/ ", [["qualified rule",
Expand Down
74 changes: 29 additions & 45 deletions src/rules_and_declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,17 @@ pub fn parse_important<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), BasicPa
/// The return value for `AtRuleParser::parse_prelude`.
/// Indicates whether the at-rule is expected to have a `{ /* ... */ }` block
/// or end with a `;` semicolon.
pub enum AtRuleType<P, R> {
pub enum AtRuleType<P, PB> {
/// The at-rule is expected to end with a `;` semicolon. Example: `@import`.
///
/// The value is the finished representation of an at-rule
/// as returned by `RuleListParser::next` or `DeclarationListParser::next`.
WithoutBlock(R),
/// The value is the representation of all data of the rule which would be
/// handled in rule_without_block.
WithoutBlock(P),

/// The at-rule is expected to have a a `{ /* ... */ }` block. Example: `@media`
///
/// The value is the representation of the "prelude" part of the rule.
WithBlock(P),

/// The at-rule may either have a block or end with a semicolon.
///
/// This is mostly for testing. As of this writing no real CSS at-rule behaves like this.
///
/// The value is the representation of the "prelude" part of the rule.
OptionalBlock(P),
WithBlock(PB),
}

/// A trait to provide various parsing of declaration values.
Expand Down Expand Up @@ -85,8 +78,11 @@ pub trait DeclarationParser<'i> {
/// so that `impl AtRuleParser<(), ()> for ... {}` can be used
/// for using `DeclarationListParser` to parse a declartions list with only qualified rules.
pub trait AtRuleParser<'i> {
/// The intermediate representation of an at-rule prelude.
type Prelude;
/// The intermediate representation of prelude of an at-rule without block;
type PreludeNoBlock;

/// The intermediate representation of prelude of an at-rule with block;
type PreludeBlock;

/// The finished representation of an at-rule.
type AtRule;
Expand All @@ -112,36 +108,39 @@ pub trait AtRuleParser<'i> {
/// that ends wherever the prelude should end.
/// (Before the next semicolon, the next `{`, or the end of the current block.)
fn parse_prelude<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
-> Result<AtRuleType<Self::Prelude, Self::AtRule>, ParseError<'i, Self::Error>> {
-> Result<AtRuleType<Self::PreludeNoBlock, Self::PreludeBlock>,
ParseError<'i, Self::Error>> {
let _ = name;
let _ = input;
Err(ParseError::Basic(BasicParseError::AtRuleInvalid(name)))
}

/// End an at-rule which doesn't have block. Return the finished
/// representation of the at-rule.
///
/// This is only called when `parse_prelude` returned `WithoutBlock`, and
/// either the `;` semicolon indeed follows the prelude, or parser is at
/// the end of the input.
fn rule_without_block(&mut self, prelude: Self::PreludeNoBlock) -> Self::AtRule {
let _ = prelude;
panic!("The `AtRuleParser::rule_without_block` method must be overriden \
if `AtRuleParser::parse_prelude` ever returns `AtRuleType::WithoutBlock`.")
}

/// Parse the content of a `{ /* ... */ }` block for the body of the at-rule.
///
/// Return the finished representation of the at-rule
/// as returned by `RuleListParser::next` or `DeclarationListParser::next`,
/// or `Err(())` to ignore the entire at-rule as invalid.
///
/// This is only called when `parse_prelude` returned `WithBlock` or `OptionalBlock`,
/// and a block was indeed found following the prelude.
fn parse_block<'t>(&mut self, prelude: Self::Prelude, input: &mut Parser<'i, 't>)
/// This is only called when `parse_prelude` returned `WithBlock`, and a block
/// was indeed found following the prelude.
fn parse_block<'t>(&mut self, prelude: Self::PreludeBlock, input: &mut Parser<'i, 't>)
-> Result<Self::AtRule, ParseError<'i, Self::Error>> {
let _ = prelude;
let _ = input;
Err(ParseError::Basic(BasicParseError::AtRuleBodyInvalid))
}

/// An `OptionalBlock` prelude was followed by `;`.
///
/// Convert the prelude into the finished representation of the at-rule
/// as returned by `RuleListParser::next` or `DeclarationListParser::next`.
fn rule_without_block(&mut self, prelude: Self::Prelude) -> Self::AtRule {
let _ = prelude;
panic!("The `AtRuleParser::rule_without_block` method must be overriden \
if `AtRuleParser::parse_prelude` ever returns `AtRuleType::OptionalBlock`.")
}
}

/// A trait to provide various parsing of qualified rules.
Expand Down Expand Up @@ -460,9 +459,9 @@ fn parse_at_rule<'i: 't, 't, P, E>(start: &ParserState, name: CowRcStr<'i>,
parser.parse_prelude(name, input)
});
match result {
Ok(AtRuleType::WithoutBlock(rule)) => {
Ok(AtRuleType::WithoutBlock(prelude)) => {
match input.next() {
Ok(&Token::Semicolon) | Err(_) => Ok(rule),
Ok(&Token::Semicolon) | Err(_) => Ok(parser.rule_without_block(prelude)),
Ok(&Token::CurlyBracketBlock) => Err(PreciseParseError {
error: ParseError::Basic(BasicParseError::UnexpectedToken(Token::CurlyBracketBlock)),
slice: input.slice_from(start.position()),
Expand Down Expand Up @@ -495,21 +494,6 @@ fn parse_at_rule<'i: 't, 't, P, E>(start: &ParserState, name: CowRcStr<'i>,
Ok(_) => unreachable!()
}
}
Ok(AtRuleType::OptionalBlock(prelude)) => {
match input.next() {
Ok(&Token::Semicolon) | Err(_) => Ok(parser.rule_without_block(prelude)),
Ok(&Token::CurlyBracketBlock) => {
// FIXME: https://github.com/rust-lang/rust/issues/42508
parse_nested_block::<'i, 't, _, _, _>(input, move |input| parser.parse_block(prelude, input))
.map_err(|e| PreciseParseError {
error: e,
slice: input.slice_from(start.position()),
location: start.source_location(),
})
}
_ => unreachable!()
}
}
Err(error) => {
let end_position = input.position();
match input.next() {
Expand Down
24 changes: 15 additions & 9 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,29 +745,35 @@ impl<'i> DeclarationParser<'i> for JsonParser {
}

impl<'i> AtRuleParser<'i> for JsonParser {
type Prelude = Vec<Json>;
type PreludeNoBlock = Vec<Json>;
type PreludeBlock = Vec<Json>;
type AtRule = Json;
type Error = ();

fn parse_prelude<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
-> Result<AtRuleType<Vec<Json>, Json>, ParseError<'i, ()>> {
Ok(AtRuleType::OptionalBlock(vec![
-> Result<AtRuleType<Vec<Json>, Vec<Json>>, ParseError<'i, ()>> {
let prelude = vec![
"at-rule".to_json(),
name.to_json(),
Json::Array(component_values_to_json(input)),
]))
];
match_ignore_ascii_case! { &*name,
"media" | "foo-with-block" => Ok(AtRuleType::WithBlock(prelude)),
"charset" => Err(BasicParseError::AtRuleInvalid(name.clone()).into()),
_ => Ok(AtRuleType::WithoutBlock(prelude)),
}
}

fn rule_without_block(&mut self, mut prelude: Vec<Json>) -> Json {
prelude.push(Json::Null);
Json::Array(prelude)
}

fn parse_block<'t>(&mut self, mut prelude: Vec<Json>, input: &mut Parser<'i, 't>)
-> Result<Json, ParseError<'i, ()>> {
prelude.push(Json::Array(component_values_to_json(input)));
Ok(Json::Array(prelude))
}

fn rule_without_block(&mut self, mut prelude: Vec<Json>) -> Json {
prelude.push(Json::Null);
Json::Array(prelude)
}
}

impl<'i> QualifiedRuleParser<'i> for JsonParser {
Expand Down