1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
//! Types and functions to parse a token stream into a cascade.

use derivative::Derivative;

use crate::stream::{Delimiter, Group, Punct, Spanned, TokenStream, TokenTree};

/// Represents a CSS rule that starts with a preamble which usually contains the
/// selectors and ends with a `{ ... }` delimited group.
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Rule<'a, S>
where
    S: TokenStream,
{
    /// This is the preamble: a slice of tokens that was encountered before the
    /// `{ ... }` delimited group and that didn't match any other grammar.
    pub selectors: &'a [TokenTree<S>],

    /// This is the `{ ... }` delimited group. The last rule in a parse might
    /// not have a group in the case of a syntax error.
    pub group: Option<&'a S::Group>,
}

/// Represents a CSS property that starts with a sequence of tokens which
/// usually is the name, a colon, another sequence of tokens which usually is
/// the value and finally ends with a semicolon. The semicolon is the parser's
/// anchor point: it starts by locating the semicolon and then parses backwards
/// until it has collected the name.
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Property<'a, S>
where
    S: TokenStream,
{
    /// This is the preamble: a slice of tokens that was encountered before the
    /// colon and that didn't match any other grammar. The validator will emit
    /// an error if this slice is empty. Apart from that, this slice might
    /// contain any kind of token (including more colons).
    pub name: &'a [TokenTree<S>],

    /// This is the colon. This is missing if we find a semicolon, search
    /// backwards for the colon and can't find it. The validator will emit an
    /// error if this field is missing.
    pub colon: Option<&'a S::Punct>,

    /// This is a slice of tokens that were encountered between the semicolon
    /// and the colon. Although the property is parsed from right to left, this
    /// slice has the same order as the parser input. The validator will emit an
    /// error if this slice is empty. Part from that, this slice might contain
    /// any kind of token, except a colon.
    pub value: &'a [TokenTree<S>],

    /// This is the semicolon. This field is always present because it serves as
    /// the anchor point for our parser.
    pub semicolon: &'a S::Punct,
}

/// Represents a CSS item: either a property or a rule.
#[derive(Derivative)]
#[derivative(Debug)]
pub enum Item<'a, S>
where
    S: TokenStream,
{
    /// This represents a CSS rule that starts with a preamble which usually
    /// contains the selectors and ends with a `{ ... }` delimited group.
    Property(Property<'a, S>),

    /// This represents a CSS property that starts with a sequence of tokens
    /// which usually is the name, a colon, another sequence of tokens which
    /// usually is the value and finally ends with a semicolon. The semicolon is
    /// the parser's anchor point: it starts by locating the semicolon and then
    /// parses backwards until it has collected the name.
    Rule(Rule<'a, S>),
}

/// Parses zero or more rules from the given slice of tokens.
pub fn parse_rules<'a, S>(mut tokens: &'a [TokenTree<S>]) -> Vec<Rule<'a, S>>
where
    S: TokenStream,
{
    let mut rules = vec![];

    while !tokens.is_empty() {
        match tokens
            .iter()
            .enumerate()
            .find_map(|(i, token)| match token {
                TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => {
                    Some((i, group))
                }
                _ => None,
            }) {
            Some((position, group)) => {
                rules.push(Rule {
                    selectors: &tokens[..position],
                    group: Some(group),
                });

                tokens = &tokens[position + 1..];
            }
            None => {
                rules.push(Rule {
                    selectors: tokens,
                    group: None,
                });

                break;
            }
        }
    }

    rules
}

/// Parses a single property and zero or more preceeding rules from the given
/// slice of tokens.
pub fn parse_property<'a, S>(tokens: &'a [TokenTree<S>]) -> (Vec<Rule<'a, S>>, Property<'a, S>)
where
    S: TokenStream,
{
    let semicolon = match tokens.last() {
        Some(TokenTree::Punct(punct)) if punct.as_char() == ';' => punct.into(),
        _ => unreachable!(),
    };

    let colon_position = match tokens.iter().rposition(|token| match token {
        TokenTree::Punct(punct) if punct.as_char() == ':' => true,
        _ => false,
    }) {
        Some(position) => position,
        None => {
            return (
                vec![],
                Property {
                    name: &[],
                    colon: None,
                    value: &tokens[..tokens.len() - 1],
                    semicolon,
                },
            )
        }
    };

    let value = &tokens[colon_position + 1..tokens.len() - 1];

    let colon = match &tokens[colon_position] {
        TokenTree::Punct(punct) if punct.as_char() == ':' => punct,
        _ => unreachable!(),
    };

    let tokens = &tokens[..colon_position];

    let (rules, name) = match tokens.iter().rposition(|token| match token {
        TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => true,
        _ => false,
    }) {
        Some(position) => (
            parse_rules(&tokens[..position + 1]),
            &tokens[position + 1..],
        ),
        None => (vec![], tokens),
    };

    (
        rules,
        Property {
            name,
            colon: Some(colon),
            value,
            semicolon,
        },
    )
}

/// Parses the given token stream and returns a vector of items. These items
/// might be missing fields. Use `validate(items)` to check for any errors. In
/// addition, this function does not recursively parse rules.
pub fn parse<'a, S>(mut tokens: &'a [TokenTree<S>]) -> Vec<Item<'a, S>>
where
    S: TokenStream,
{
    let mut items = vec![];

    while !tokens.is_empty() {
        // Read until we find a `;`.
        let end = tokens.iter().position(|token| match token {
            TokenTree::Punct(punct) if punct.as_char() == ';' => true,
            _ => false,
        });

        match end {
            // If we find a `;`, we assume that [0 .. end) consists of zero or
            // more rules and one property.
            Some(end) => {
                // This parses zero or more rules and one property.
                let (rules, property) = parse_property(&tokens[..end + 1]);
                items.extend(rules.into_iter().map(|rule| Item::Rule(rule)));
                items.push(Item::Property(property));
                tokens = &tokens[end + 1..];
            }
            // If we don't find a `;`, we assume that [0 .. end) consists of
            // zero or more rules of which the last rule might not have a block.
            None => {
                // This parses zero or more rules.
                let rules = parse_rules(tokens);
                items.extend(rules.into_iter().map(|rule| Item::Rule(rule)));
                break;
            }
        };
    }

    items
}

/// Type of error that is returned during validation.
#[derive(Derivative)]
#[derivative(Debug)]
pub enum Error<S>
where
    S: TokenStream,
{
    /// This error is returned when a property's name is missing. The span
    /// points to the token that was encountered instead.
    MissingPropertyName(S::Span),

    /// This error is returned when a : is missing. The span points to the token
    /// that was encountered instead.
    MissingColon(S::Span),

    /// This error is returned when a property's value is missing. The span
    /// points to the token that was encountered instead.
    MissingPropertyValue(S::Span),

    /// This error is returned when a selector is missing. The span points to
    /// the token that was encountered instead (which will always be the `{ ...
    /// }` group).
    MissingSelector(S::Span),

    /// This error is returned when a rule block is missing. The span points to
    /// the last selector that was encountered before the rule group was
    /// expected.
    MissingRuleGroup(S::Span),
}

/// Validates the given parse and returns any errors that it encounters.
pub fn validate<'a, S>(items: &[Item<S>]) -> Vec<Error<S>>
where
    S: TokenStream,
{
    let mut errors = vec![];

    for item in items {
        match item {
            Item::Property(property) => {
                if property.name.is_empty() {
                    errors.push(Error::MissingPropertyName(
                        property
                            .colon
                            .map(|colon| colon.span())
                            .or_else(|| property.value.first().map(|value| value.span()))
                            .unwrap_or(property.semicolon.span()),
                    ));
                } else if property.colon.is_none() {
                    errors.push(Error::MissingPropertyName(
                        property
                            .value
                            .first()
                            .map(|value| value.span())
                            .unwrap_or(property.semicolon.span()),
                    ));
                } else if property.value.is_empty() {
                    errors.push(Error::MissingPropertyValue(property.semicolon.span()));
                }
            }
            Item::Rule(rule) => {
                if let Some(selector) = rule.selectors.last() {
                    if rule.group.is_none() {
                        errors.push(Error::MissingRuleGroup(selector.span()));
                    }
                } else {
                    if let Some(group) = rule.group {
                        errors.push(Error::MissingSelector(group.span()));
                    }
                }
            }
        }
    }

    errors
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::lexer::lex;

    #[test]
    fn test_parser() {
        let stream = lex(":initial, :hover {
            background-color: { Color::red() };
        }")
        .unwrap();

        let parse = parse(stream.tokens.as_slice());
        let errors = validate(&parse[..]);
        println!("Result: {:#?} ({:#?})", parse, errors);
    }
}