mxmlextrema_as3parser/parser/
css_parser.rs

1use crate::ns::*;
2use num_traits::ToPrimitive;
3
4pub struct CssParser<'input> {
5    tokenizer: CssTokenizer<'input>,
6    previous_token: (Token, Location),
7    token: (Token, Location),
8    locations: Vec<Location>,
9    expecting_token_error: bool,
10}
11
12impl<'input> CssParser<'input> {
13    /// Constructs a tokenizer.
14    pub fn new(compilation_unit: &'input Rc<CompilationUnit>, options: &ParserOptions) -> Self {
15        Self {
16            tokenizer: CssTokenizer::new(compilation_unit, options),
17            previous_token: (Token::Eof, Location::with_offset(&compilation_unit, 0)),
18            token: (Token::Eof, Location::with_offset(&compilation_unit, 0)),
19            locations: vec![],
20            expecting_token_error: false,
21        }
22    }
23
24    fn options(&self) -> ParserOptions {
25        ParserOptions {
26            ..default()
27        }
28    }
29
30    fn compilation_unit(&self) -> &Rc<CompilationUnit> {
31        self.tokenizer.compilation_unit()
32    }
33
34    fn token_location(&self) -> Location {
35        self.token.1.clone()
36    }
37
38    fn mark_location(&mut self) {
39        self.locations.push(self.token.1.clone());
40    }
41
42    fn duplicate_location(&mut self) {
43        self.locations.push(self.locations.last().unwrap().clone());
44    }
45
46    fn push_location(&mut self, location: &Location) {
47        self.locations.push(location.clone());
48    }
49
50    fn pop_location(&mut self) -> Location {
51        self.locations.pop().unwrap().combine_with(self.previous_token.1.clone())
52    }
53
54    fn add_syntax_error(&self, location: &Location, kind: DiagnosticKind, arguments: Vec<Rc<dyn DiagnosticArgument>>) {
55        if self.compilation_unit().prevent_equal_offset_error(location) {
56            return;
57        }
58        self.compilation_unit().add_diagnostic(Diagnostic::new_syntax_error(location, kind, arguments));
59    }
60
61    /*
62    fn add_warning(&self, location: &Location, kind: DiagnosticKind, arguments: Vec<Rc<dyn DiagnosticArgument>>) {
63        if self.compilation_unit().prevent_equal_offset_warning(location) {
64            return;
65        }
66        self.compilation_unit().add_diagnostic(Diagnostic::new_warning(location, kind, arguments));
67    }
68    */
69
70    fn next(&mut self) {
71        self.previous_token = self.token.clone();
72        self.token = self.tokenizer.scan();
73    }
74
75    fn peek(&self, token: Token) -> bool {
76        self.token.0 == token
77    }
78
79    fn peek_identifier(&self) -> Option<(String, Location)> {
80        if let Token::Identifier(id) = self.token.0.clone() {
81            let location = self.token.1.clone();
82            Some((id, location))
83        } else {
84            None
85        }
86    }
87
88    fn peek_keyword(&self, name: &str) -> bool {
89        if let Token::Identifier(id) = self.token.0.clone() { id == name && self.token.1.character_count() == name.len() } else { false }
90    }
91
92    fn consume(&mut self, token: Token) -> bool {
93        if self.token.0 == token {
94            self.next();
95            true
96        } else {
97            false
98        }
99    }
100
101    fn consume_identifier(&mut self) -> Option<(String, Location)> {
102        if let Token::Identifier(id) = self.token.0.clone() {
103            let location = self.token.1.clone();
104            self.next();
105            Some((id, location))
106        } else {
107            None
108        }
109    }
110
111    fn consume_keyword(&mut self, name: &str) -> bool {
112        if let Token::Identifier(name1) = self.token.0.clone() {
113            if name1 == name {
114                self.next();
115                return true;
116            }
117        }
118        false
119    }
120
121    /// Expects a token in non-greedy mode: if it fails, does not skip any token.
122    fn expect(&mut self, token: Token) {
123        if self.token.0 != token {
124            self.expecting_token_error = true;
125            self.add_syntax_error(&self.token_location(), DiagnosticKind::Expecting, diagarg![token.clone(), self.token.0.clone()]);
126        } else {
127            self.expecting_token_error = false;
128            self.next();
129        }
130    }
131
132    fn expect_identifier(&mut self) -> (String, Location) {
133        if let Token::Identifier(id) = self.token.0.clone() {
134            self.expecting_token_error = false;
135            let location = self.token.1.clone();
136            self.next();
137            (id, location)
138        } else {
139            self.expecting_token_error = true;
140            self.add_syntax_error(&self.token_location(), DiagnosticKind::ExpectingIdentifier, diagarg![self.token.0.clone()]);
141            (INVALIDATED_IDENTIFIER.to_owned(), self.tokenizer.cursor_location())
142        }
143    }
144
145    fn expect_unitless_number(&mut self) -> Option<f64> {
146        if let Token::CssNumber { value, .. } = self.token.0.clone() {
147            self.expecting_token_error = false;
148            self.next();
149            Some(value)
150        } else {
151            self.expecting_token_error = true;
152            self.add_syntax_error(&self.token_location(), DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
153            None
154        }
155    }
156
157    fn expect_string(&mut self) -> (String, Location) {
158        if let Token::String(v) = self.token.0.clone() {
159            self.expecting_token_error = false;
160            let location = self.token.1.clone();
161            self.next();
162            (v, location)
163        } else {
164            self.expecting_token_error = true;
165            self.add_syntax_error(&self.token_location(), DiagnosticKind::ExpectingStringLiteral, diagarg![self.token.0.clone()]);
166            ("".into(), self.tokenizer.cursor_location())
167        }
168    }
169
170    pub fn expect_eof(&mut self) {
171        self.expect(Token::Eof);
172    }
173
174    fn create_invalidated_directive(&self, location: &Location) -> Rc<CssDirective> {
175        Rc::new(CssDirective::Invalidated(InvalidatedNode {
176            location: location.clone(),
177        }))
178    }
179
180    fn create_invalidated_property_value(&self, location: &Location) -> Rc<CssPropertyValue> {
181        Rc::new(CssPropertyValue::Invalidated(InvalidatedNode {
182            location: location.clone(),
183        }))
184    }
185
186    fn create_invalidated_selector(&self, location: &Location) -> Rc<CssSelector> {
187        Rc::new(CssSelector::Invalidated(InvalidatedNode {
188            location: location.clone(),
189        }))
190    }
191
192    fn create_invalidated_selector_condition(&self, location: &Location) -> Rc<CssSelectorCondition> {
193        Rc::new(CssSelectorCondition::Invalidated(InvalidatedNode {
194            location: location.clone(),
195        }))
196    }
197
198    fn create_invalidated_media_query_condition(&self, location: &Location) -> Rc<CssMediaQueryCondition> {
199        Rc::new(CssMediaQueryCondition::Invalidated(InvalidatedNode {
200            location: location.clone(),
201        }))
202    }
203
204    fn eof(&self) -> bool {
205        matches!(self.token.0, Token::Eof)
206    }
207
208    pub fn parse_document(&mut self) -> Rc<CssDocument> {
209        self.mark_location();
210        let just_eof = self.peek(Token::Eof);
211        let mut directives: Vec<Rc<CssDirective>> = vec![];
212        while !self.eof() {
213            directives.push(self.parse_directive());
214        }
215        let loc = self.pop_location();
216        Rc::new(CssDocument {
217            location: if just_eof {
218                self.token.1.clone()
219            } else {
220                loc
221            },
222            directives,
223        })
224    }
225
226    fn parse_directive(&mut self) -> Rc<CssDirective> {
227        if let Some(rule) = self.parse_opt_rule() {
228            Rc::new(CssDirective::Rule(rule))
229        } else if self.peek(Token::CssAtNamespace) {
230            self.mark_location();
231            self.next();
232            let prefix = self.expect_identifier();
233            let uri = if self.expecting_token_error {
234                (String::new(), self.tokenizer.cursor_location())
235            } else {
236                self.expect_string()
237            };
238            if !self.expecting_token_error {
239                self.expect(Token::CssSemicolons);
240            }
241            let loc = self.pop_location();
242            Rc::new(CssDirective::NamespaceDefinition(CssNamespaceDefinition {
243                location: loc,
244                prefix,
245                uri,
246            }))
247        } else if self.peek(Token::CssAtMedia) {
248            self.parse_media_query()
249        } else if self.peek(Token::CssAtFontFace) {
250            self.parse_font_face()
251        } else if self.peek(Token::CssAtImport) {
252            self.parse_import()
253        } else {
254            self.add_syntax_error(&self.token.1, DiagnosticKind::ExpectingDirective, diagarg![self.token.0.clone()]);
255            let d = self.create_invalidated_directive(&self.tokenizer.cursor_location());
256            self.next();
257            d
258        }
259    }
260
261    fn parse_media_query(&mut self) -> Rc<CssDirective> {
262        self.mark_location();
263        self.next();
264        let mut conditions: Vec<Rc<CssMediaQueryCondition>> = vec![];
265        let condition = self.parse_opt_media_query_condition();
266        if let Some(condition) = condition {
267            conditions.push(condition);
268        } else {
269            self.add_syntax_error(&self.token.1, DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
270        }
271        loop {
272            if let Some(condition) = self.parse_opt_media_query_condition() {
273                conditions.push(condition);
274            } else if self.eof() || self.peek(Token::BlockOpen) {
275                break;
276            } else if !self.consume(Token::Comma) {
277                self.add_syntax_error(&self.token.1, DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
278                self.next();
279            }
280        }
281        let mut rules: Vec<Rc<CssRule>> = vec![];
282        self.expect(Token::BlockOpen);
283        if !self.expecting_token_error {
284            while !(self.eof() || self.peek(Token::BlockClose)) {
285                if let Some(rule) = self.parse_opt_rule() {
286                    rules.push(Rc::new(rule));
287                } else {
288                    self.add_syntax_error(&self.token.1, DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
289                    self.next();
290                }
291            }
292            self.expect(Token::BlockClose);
293        }
294        Rc::new(CssDirective::MediaQuery(CssMediaQuery {
295            location: self.pop_location(),
296            conditions,
297            rules,
298        }))
299    }
300
301    fn parse_font_face(&mut self) -> Rc<CssDirective> {
302        self.mark_location();
303        self.next();
304        let mut properties: Vec<Rc<CssProperty>> = vec![];
305        self.expect(Token::BlockOpen);
306        if !self.expecting_token_error {
307            self.consume(Token::CssSemicolons);
308            while !(self.eof() || self.peek(Token::BlockClose)) {
309                properties.push(self.parse_property());
310                if !self.consume(Token::CssSemicolons) {
311                    break;
312                }
313            }
314            self.expect(Token::BlockClose);
315        }
316        Rc::new(CssDirective::FontFace(CssFontFace {
317            location: self.pop_location(),
318            properties,
319        }))
320    }
321
322    fn parse_import(&mut self) -> Rc<CssDirective> {
323        self.mark_location();
324        self.next();
325
326        let mut url: Option<String> = None;
327        if self.consume_keyword("url") {
328            if let Ok(facade) = self.parse_arguments() {
329                url = Some(facade.parse_text().0);
330            }
331        }
332
333        self.expect(Token::Semicolon);
334
335        Rc::new(CssDirective::Import(CssImport {
336            location: self.pop_location(),
337            url,
338        }))
339    }
340
341    fn parse_opt_media_query_condition(&mut self) -> Option<Rc<CssMediaQueryCondition>> {
342        let mut base: Option<Rc<CssMediaQueryCondition>> = None;
343        if self.peek_keyword("only") {
344            self.mark_location();
345            self.next();
346            let id = self.expect_identifier();
347            base = Some(Rc::new(CssMediaQueryCondition::OnlyId {
348                location: self.pop_location(),
349                id,
350            }));
351        }
352        if let Some(id) = self.consume_identifier() {
353            base = Some(Rc::new(CssMediaQueryCondition::Id(id)));
354        }
355        if self.peek(Token::ParenOpen) {
356            self.mark_location();
357            let property = self.parse_arguments().unwrap().parse_property();
358            let loc = self.pop_location();
359            base = Some(Rc::new(CssMediaQueryCondition::ParenProperty((property, loc))));
360        }
361        if let Some(mut base) = base.clone() {
362            while self.consume_keyword("and") {
363                self.push_location(&base.location());
364                if let Some(right) = self.parse_opt_media_query_condition() {
365                    base = Rc::new(CssMediaQueryCondition::And {
366                        location: self.pop_location(),
367                        left: base,
368                        right,
369                    });
370                } else {
371                    self.add_syntax_error(&self.token.1, DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
372                    base = Rc::new(CssMediaQueryCondition::And {
373                        location: self.pop_location(),
374                        left: base,
375                        right: self.create_invalidated_media_query_condition(&self.tokenizer.cursor_location()),
376                    });
377                }
378            }
379            return Some(base);
380        }
381        base
382    }
383
384    fn parse_arguments(&mut self) -> Result<CssParserFacade, ParserError> {
385        if !self.peek(Token::ParenOpen) {
386            self.add_syntax_error(&self.token.1, DiagnosticKind::Expecting, diagarg![Token::ParenOpen, self.token.0.clone()]);
387            return Err(ParserError::Common);
388        }
389        let (byte_range, token) = self.tokenizer.scan_arguments();
390        self.previous_token = self.token.clone();
391        self.token = token;
392        self.next();
393        Ok(CssParserFacade(self.compilation_unit(), ParserOptions {
394            byte_range: Some(byte_range),
395            ..self.options()
396        }))
397    }
398
399    fn parse_opt_rule(&mut self) -> Option<CssRule> {
400        let mut selectors: Vec<Rc<CssSelector>> = vec![self.parse_opt_selector()?];
401        while self.consume(Token::Comma) {
402            if let Some(s) = self.parse_opt_selector() {
403                selectors.push(s);
404            } else {
405                self.add_syntax_error(&self.token.1, DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
406            }
407        }
408        let mut properties: Vec<Rc<CssProperty>> = vec![];
409        self.expect(Token::BlockOpen);
410        if !self.expecting_token_error {
411            self.consume(Token::CssSemicolons);
412            while !(self.eof() || self.peek(Token::BlockClose)) {
413                properties.push(self.parse_property());
414                if !self.consume(Token::CssSemicolons) {
415                    break;
416                }
417            }
418            self.expect(Token::BlockClose);
419        }
420        self.push_location(&selectors[0].location());
421        Some(CssRule {
422            location: self.pop_location(),
423            selectors,
424            properties,
425        })
426    }
427
428    fn parse_opt_selector(&mut self) -> Option<Rc<CssSelector>> {
429        let mut base = self.parse_opt_base_selector()?;
430        
431        // Parse combinators
432        loop {
433            // CssCombinatorType::Child
434            if self.consume(Token::Gt) {
435                self.push_location(&base.location());
436                let right: Rc<CssSelector> = self.parse_base_selector();
437                base = Rc::new(CssSelector::Combinator(CssCombinatorSelector {
438                    location: self.pop_location(),
439                    left: base,
440                    right,
441                    combinator_type: CssCombinatorType::Child,
442                }));
443            // CssCombinatorType::Descendant
444            } else if let Some(right) = self.parse_opt_base_selector() {
445                self.push_location(&base.location());
446                base = Rc::new(CssSelector::Combinator(CssCombinatorSelector {
447                    location: self.pop_location(),
448                    left: base,
449                    right,
450                    combinator_type: CssCombinatorType::Descendant,
451                }));
452            } else {
453                break;
454            }
455        }
456
457        Some(base)
458    }
459
460    fn parse_base_selector(&mut self) -> Rc<CssSelector> {
461        let s = self.tokenizer.cursor_location();
462        let r = self.parse_opt_base_selector();
463        if let Some(r) = r {
464            r
465        } else {
466            self.add_syntax_error(&s, DiagnosticKind::ExpectingCssSelector, diagarg![]);
467            self.create_invalidated_selector(&s)
468        }
469    }
470
471    fn parse_opt_base_selector(&mut self) -> Option<Rc<CssSelector>> {
472        self.mark_location();
473        let mut namespace_prefix: Option<(String, Location)> = None;
474        let mut element_name: Option<(String, Location)> = self.consume_identifier();
475        let mut conditions: Vec<Rc<CssSelectorCondition>> = vec![];
476        if self.consume(Token::Pipe) {
477            namespace_prefix = element_name.clone();
478            element_name = Some(self.expect_identifier());
479        }
480        // Parse conditions as long as they are not separated by whitespace
481        while (element_name.is_none() && conditions.is_empty()) || (self.token.1.first_offset() - self.previous_token.1.last_offset() == 0) {
482            if let Some(condition) = self.parse_opt_selector_condition() {
483                conditions.push(condition);
484            } else {
485                break;
486            }
487        }
488        if element_name.is_none() && conditions.is_empty() {
489            self.pop_location();
490            return None;
491        }
492        Some(Rc::new(CssSelector::Base(CssBaseSelector {
493            location: self.pop_location(),
494            namespace_prefix,
495            element_name,
496            conditions,
497        })))
498    }
499
500    fn parse_selector_condition(&mut self) -> Rc<CssSelectorCondition> {
501        let Some(c) = self.parse_opt_selector_condition() else {
502            self.add_syntax_error(&self.token.1, DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
503            return self.create_invalidated_selector_condition(&self.tokenizer.cursor_location());
504        };
505        c
506    }
507
508    fn parse_opt_selector_condition(&mut self) -> Option<Rc<CssSelectorCondition>> {
509        if self.peek(Token::Dot) {
510            self.mark_location();
511            self.next();
512            let class_name = self.expect_identifier().0;
513            return Some(Rc::new(CssSelectorCondition::Class((class_name, self.pop_location()))));
514        }
515        if let Token::CssHashWord(id_value) = self.token.0.clone() {
516            let loc = self.token.1.clone();
517            self.next();
518            return Some(Rc::new(CssSelectorCondition::Id((id_value, loc))));
519        }
520        if self.peek(Token::Colon) {
521            self.mark_location();
522            self.next();
523            if self.consume_keyword("nth-child") {
524                let condition = if let Ok(a) = self.parse_arguments() {
525                    a.parse_nth_child_kind()
526                } else {
527                    CssNthChildKind::Invalidated
528                };
529                return Some(Rc::new(CssSelectorCondition::NthChild((condition, self.pop_location()))));
530            } else if self.consume_keyword("not") {
531                let condition = if let Ok(a) = self.parse_arguments() {
532                    a.parse_selector_condition()
533                } else {
534                    self.duplicate_location();
535                    let loc = self.pop_location();
536                    self.create_invalidated_selector_condition(&loc)
537                };
538                return Some(Rc::new(CssSelectorCondition::Not {
539                    location: self.pop_location(),
540                    condition,
541                }));
542            } else {
543                let name = self.expect_identifier().0;
544                return Some(Rc::new(CssSelectorCondition::Pseudo((name, self.pop_location()))));
545            }
546        }
547        if self.peek(Token::ColonColon) {
548            self.mark_location();
549            self.next();
550            let name = self.expect_identifier().0;
551            return Some(Rc::new(CssSelectorCondition::PseudoElement((name, self.pop_location()))));
552        }
553        if self.peek(Token::SquareOpen) {
554            self.mark_location();
555            self.next();
556            let name = self.expect_identifier();
557            let mut operator: Option<CssAttributeOperator> = None;
558            let mut value: Option<(String, Location)> = None;
559            while let Some(t1) = self.consume_attribute_operator() {
560                operator = Some(t1);
561            }
562            while let Token::String(s1) = self.token.0.clone() {
563                value = Some((s1, self.token.1.clone()));
564                self.next();
565            }
566            self.expect(Token::SquareClose);
567            return Some(Rc::new(CssSelectorCondition::Attribute {
568                location: self.pop_location(),
569                name,
570                operator,
571                value,
572            }));
573        }
574        None
575    }
576
577    fn consume_attribute_operator(&mut self) -> Option<CssAttributeOperator> {
578        if self.consume(Token::CssBeginsWith) {
579            Some(CssAttributeOperator::BeginsWith)
580        } else if self.consume(Token::CssEndsWith) {
581            Some(CssAttributeOperator::EndsWith)
582        } else if self.consume(Token::CssContains) {
583            Some(CssAttributeOperator::Contains)
584        } else if self.consume(Token::CssListMatch) {
585            Some(CssAttributeOperator::ListMatch)
586        } else if self.consume(Token::CssHreflangMatch) {
587            Some(CssAttributeOperator::HreflangMatch)
588        } else if self.consume(Token::Assign) {
589            Some(CssAttributeOperator::Equals)
590        } else {
591            None
592        }
593    }
594
595    fn parse_property(&mut self) -> Rc<CssProperty> {
596        self.mark_location();
597        let name = self.expect_identifier();
598        let mut value = self.create_invalidated_property_value(&self.tokenizer.cursor_location());
599        if !self.expecting_token_error {
600            self.expect(Token::Colon);
601            if !self.expecting_token_error {
602                value = self.parse_property_value(CssOperatorPrecedence::Array);
603            }
604        }
605        Rc::new(CssProperty::new(self.pop_location(), name, value))
606    }
607
608    fn parse_property_value(&mut self, min_precedence: CssOperatorPrecedence) -> Rc<CssPropertyValue> {
609        let Some(v) = self.parse_opt_property_value(min_precedence) else {
610            self.add_syntax_error(&self.token.1, DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
611            return self.create_invalidated_property_value(&self.tokenizer.cursor_location());
612        };
613        v
614    }
615
616    fn parse_opt_property_value(&mut self, min_precedence: CssOperatorPrecedence) -> Option<Rc<CssPropertyValue>> {
617        let base: Option<Rc<CssPropertyValue>>;
618        let t1 = self.token.0.clone();
619
620        // #HHH
621        // #HHHHHH
622        if let Token::CssHashWord(word) = t1 {
623            self.mark_location();
624            self.next();
625            let loc = self.pop_location();
626            if let Ok(v) = CssColorPropertyValue::from_hex(loc.clone(), &word) {
627                base = Some(Rc::new(CssPropertyValue::Color(v)));
628            } else {
629                base = Some(self.create_invalidated_property_value(&loc));
630            }
631        // "..."
632        // '...'
633        } else if let Token::String(value) = t1 {
634            self.mark_location();
635            self.next();
636            base = Some(Rc::new(CssPropertyValue::String(CssStringPropertyValue {
637                location: self.pop_location(),
638                value
639            })));
640        // DECIMAL
641        } else if let Token::CssNumber { value, unit } = t1 {
642            self.mark_location();
643            self.next();
644            let loc = self.pop_location();
645            base = Some(Rc::new(CssPropertyValue::Number(CssNumberPropertyValue {
646                location: loc,
647                value,
648                unit,
649            })));
650        } else if let Some(id) = self.peek_identifier() {
651            self.mark_location();
652            self.next();
653            let color_int = css_color_constant_to_int(&id.0);
654            // COLOR_NAME such as "red"
655            if let Some(color_int) = color_int {
656                base = Some(Rc::new(CssPropertyValue::Color(CssColorPropertyValue {
657                    location: self.pop_location(),
658                    color_int,
659                })));
660            // rgb(...)
661            } else if id.0 == "rgb" && self.peek(Token::ParenOpen) {
662                if let Some(color_int) = self.parse_arguments().unwrap().parse_rgb() {
663                    base = Some(Rc::new(CssPropertyValue::RgbColor(CssRgbColorPropertyValue {
664                        location: self.pop_location(),
665                        color_int,
666                    })));
667                } else {
668                    let loc = self.pop_location();
669                    base = Some(self.create_invalidated_property_value(&loc));
670                }
671            } else if id.0 == "ClassReference" && self.peek(Token::ParenOpen) {
672                let name = self.parse_arguments().unwrap().parse_text();
673                base = Some(Rc::new(CssPropertyValue::ClassReference(CssClassReferencePropertyValue {
674                    location: self.pop_location(),
675                    name,
676                })));
677            } else if id.0 == "PropertyReference" && self.peek(Token::ParenOpen) {
678                let name = self.parse_arguments().unwrap().parse_text();
679                base = Some(Rc::new(CssPropertyValue::PropertyReference(CssPropertyReferencePropertyValue {
680                    location: self.pop_location(),
681                    name,
682                })));
683            } else if id.0 == "url" && self.peek(Token::ParenOpen) {
684                let url = self.parse_arguments().unwrap().parse_text();
685                let mut format: Option<(String, Location)> = None;
686                if self.consume_keyword("format") {
687                    if let Ok(a) = self.parse_arguments() {
688                        format = Some(a.parse_text());
689                    }
690                }
691                base = Some(Rc::new(CssPropertyValue::Url(CssUrlPropertyValue {
692                    location: self.pop_location(),
693                    url,
694                    format,
695                })));
696            } else if id.0 == "local" && self.peek(Token::ParenOpen) {
697                let name = self.parse_arguments().unwrap().parse_text();
698                base = Some(Rc::new(CssPropertyValue::Local(CssLocalPropertyValue {
699                    location: self.pop_location(),
700                    name,
701                })));
702            } else if id.0 == "Embed" && self.peek(Token::ParenOpen) {
703                let entries = self.parse_arguments().unwrap().parse_embed_entries();
704                base = Some(Rc::new(CssPropertyValue::Embed(CssEmbedPropertyValue {
705                    location: self.pop_location(),
706                    entries,
707                })));
708            } else {
709                if self.peek(Token::ParenOpen) {
710                    self.add_syntax_error(&self.token_location(), DiagnosticKind::Unexpected, diagarg![self.token.0.clone()]);
711                    self.parse_arguments().unwrap();
712                }
713                base = Some(Rc::new(CssPropertyValue::Identifier(CssIdentifierPropertyValue {
714                    location: self.pop_location(),
715                    value: id.0,
716                })));
717            }
718        } else if  self.peek(Token::Plus)  || self.peek(Token::Minus)
719                || self.peek(Token::Times) || self.peek(Token::Div) {
720            base = Some(self.create_invalidated_property_value(&self.token.1));
721            self.next();
722        } else {
723            return None;
724        }
725
726        let mut base = base.unwrap();
727
728        loop {
729            if self.peek(Token::Comma) && min_precedence.includes(&CssOperatorPrecedence::Array) {
730                self.push_location(&base.location());
731                let mut elements: Vec<Rc<CssPropertyValue>> = vec![base];
732                while self.consume(Token::Comma) {
733                    elements.push(self.parse_property_value(CssOperatorPrecedence::MultiValue));
734                }
735                base = Rc::new(CssPropertyValue::Array(CssArrayPropertyValue {
736                    location: self.pop_location(),
737                    elements,
738                }));
739            } else if min_precedence.includes(&CssOperatorPrecedence::MultiValue) {
740                if let Some(mv1) = self.parse_opt_property_value(CssOperatorPrecedence::MultiValue.add(1).unwrap()) {
741                    self.push_location(&base.location());
742                    let mut values: Vec<Rc<CssPropertyValue>> = vec![base, mv1];
743                    while let Some(mv1) = self.parse_opt_property_value(CssOperatorPrecedence::MultiValue.add(1).unwrap()) {
744                        values.push(mv1);
745                    }
746                    base = Rc::new(CssPropertyValue::MultiValue(CssMultiValuePropertyValue {
747                        location: self.pop_location(),
748                        values,
749                    }));
750                } else {
751                    break;
752                }
753            } else {
754                break;
755            }
756        }
757
758        Some(base)
759    }
760
761    fn parse_embed_entry(&mut self) -> Rc<CssEmbedEntry> {
762        self.mark_location();
763        if let Some(key) = self.consume_identifier() {
764            if self.consume(Token::Assign) {
765                let value = self.expect_string();
766                Rc::new(CssEmbedEntry {
767                    location: self.pop_location(),
768                    key: Some(key),
769                    value,
770                })
771            } else {
772                Rc::new(CssEmbedEntry {
773                    location: self.pop_location(),
774                    key: None,
775                    value: key,
776                })
777            }
778        } else {
779            let value = self.expect_string();
780            Rc::new(CssEmbedEntry {
781                location: self.pop_location(),
782                key: None,
783                value,
784            })
785        }
786    }
787}
788
789fn rgb_bytes_to_integer(r: f64, g: f64, b: f64) -> u32 {
790    (calc_rgb_byte(r) << 16) | (calc_rgb_byte(g) << 8) | calc_rgb_byte(b)
791}
792
793fn calc_rgb_byte(value: f64) -> u32 {
794    // Integer
795    if value.round() == value {
796        value.round().to_u32().unwrap_or(0).clamp(0, 255)
797    // Float
798    } else {
799        (value * 255.0).round().to_u32().unwrap_or(0).clamp(0, 255)
800    }
801}
802
803/// A simplified interface for executing the CSS parser.
804pub struct CssParserFacade<'input>(pub &'input Rc<CompilationUnit>, pub ParserOptions);
805
806impl<'input> CssParserFacade<'input> {
807    fn create_parser(&self) -> CssParser<'input> {
808        CssParser::new(self.0, &self.1)
809    }
810
811    /// Parses `CssDocument` until end-of-file.
812    pub fn parse_document(&self) -> Rc<CssDocument> {
813        let mut parser = self.create_parser();
814        parser.next();
815        parser.parse_document()
816    }
817
818    /// Parses either a string or return source text as is.
819    pub fn parse_text(&self) -> (String, Location) {
820        let mut parser = self.create_parser();
821        while parser.tokenizer.consume_whitespace() {
822            // Consumed whitespace
823        }
824        let d = parser.tokenizer.characters().peek_or_zero();
825        if ['"', '\''].contains(&d) {
826            parser.next();
827            let mut v: (String, Location) = ("".into(), parser.tokenizer.cursor_location());
828            while let Token::String(v1) = parser.token.0.clone() {
829                v = (v1, parser.token.1.clone());
830                parser.next();
831            }
832            parser.expect_eof();
833            v
834        } else {
835            let mut s = String::new();
836            let i = parser.tokenizer.characters().index();
837            while let Some(ch) = parser.tokenizer.characters_mut().next() {
838                s.push(ch);
839            }
840            let j = parser.tokenizer.characters().index();
841            (s, Location::with_offsets(parser.compilation_unit(), i, j))
842        }
843    }
844
845    /// Parses `CssSelectorCondition` until end-of-file.
846    pub fn parse_selector_condition(&self) -> Rc<CssSelectorCondition> {
847        let mut parser = self.create_parser();
848        parser.next();
849        let r = parser.parse_selector_condition();
850        parser.expect_eof();
851        r
852    }
853
854    pub fn parse_property(&self) -> Rc<CssProperty> {
855        let mut parser = self.create_parser();
856        parser.next();
857        let r = parser.parse_property();
858        parser.expect_eof();
859        r
860    }
861
862    pub fn parse_property_value(&self) -> Rc<CssPropertyValue> {
863        let mut parser = self.create_parser();
864        parser.next();
865        let r = parser.parse_property_value(CssOperatorPrecedence::Array);
866        parser.expect_eof();
867        r
868    }
869
870    pub fn parse_rgb(&self) -> Option<u32> {
871        let mut parser = self.create_parser();
872        parser.next();
873        let r = parser.expect_unitless_number()?;
874        let g: f64;
875        let b: f64;
876        if parser.consume(Token::Comma) {
877            g = parser.expect_unitless_number()?;
878            parser.expect(Token::Comma);
879            b = parser.expect_unitless_number()?;
880        } else {
881            g = parser.expect_unitless_number()?;
882            b = parser.expect_unitless_number()?;
883        }
884        parser.expect_eof();
885        Some(rgb_bytes_to_integer(r, g, b))
886    }
887
888    pub fn parse_embed_entries(&self) -> Vec<Rc<CssEmbedEntry>> {
889        let mut parser = self.create_parser();
890        let mut entries: Vec<Rc<CssEmbedEntry>> = vec![];
891        parser.next();
892        if !parser.eof() {
893            entries.push(parser.parse_embed_entry());
894        }
895        while parser.consume(Token::Comma) {
896            entries.push(parser.parse_embed_entry());
897        }
898        parser.expect_eof();
899        entries
900    }
901
902    pub fn parse_nth_child_kind(&self) -> CssNthChildKind {
903        let mut parser = self.create_parser();
904        parser.next();
905        if parser.consume_keyword("odd") {
906            return CssNthChildKind::Odd;
907        }
908        if parser.consume_keyword("even") {
909            return CssNthChildKind::Even;
910        }
911        if let Token::CssNumber { value, .. } = parser.token.0.clone() {
912            parser.next();
913            return CssNthChildKind::Number(value.round().to_u32().unwrap_or(0));
914        }
915        parser.expect_eof();
916        CssNthChildKind::Invalidated
917    }
918}