oak_css/parser/
mod.rs

1pub mod element_type;
2use crate::{
3    language::CssLanguage,
4    lexer::{CssLexer, CssTokenType},
5};
6pub use element_type::CssElementType;
7use oak_core::{
8    GreenNode, OakError, TextEdit,
9    parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
10    source::Source,
11};
12
13pub(crate) type State<'a, S> = ParserState<'a, CssLanguage, S>;
14
15pub struct CssParser<'config> {
16    pub(crate) _config: &'config CssLanguage,
17}
18
19impl<'config> CssParser<'config> {
20    pub fn new(config: &'config CssLanguage) -> Self {
21        Self { _config: config }
22    }
23}
24
25impl<'config> Parser<CssLanguage> for CssParser<'config> {
26    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<CssLanguage>) -> ParseOutput<'a, CssLanguage> {
27        let lexer = CssLexer::new(self._config);
28        parse_with_lexer(&lexer, text, edits, cache, |state| self.parse_root_internal(state))
29    }
30}
31
32impl<'config> CssParser<'config> {
33    pub(crate) fn parse_root_internal<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<&'a GreenNode<'a, CssLanguage>, OakError> {
34        let cp = state.checkpoint();
35
36        while state.not_at_end() {
37            if state.at(CssTokenType::AtRule) || state.at(CssTokenType::AtImport) || state.at(CssTokenType::AtMedia) {
38                self.parse_at_rule(state)?;
39            }
40            else {
41                self.parse_ruleset(state)?;
42            }
43        }
44
45        Ok(state.finish_at(cp, CssElementType::SourceFile))
46    }
47
48    fn parse_at_rule<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
49        let cp = state.checkpoint();
50        state.bump(); // Consume the at-keyword
51
52        while state.not_at_end() && !state.at(CssTokenType::Semicolon) && !state.at(CssTokenType::LeftBrace) {
53            state.bump();
54        }
55
56        if state.at(CssTokenType::LeftBrace) {
57            state.expect(CssTokenType::LeftBrace).ok();
58            while state.not_at_end() && !state.at(CssTokenType::RightBrace) {
59                self.parse_ruleset(state)?;
60            }
61            state.expect(CssTokenType::RightBrace).ok();
62        }
63        else if state.at(CssTokenType::Semicolon) {
64            state.expect(CssTokenType::Semicolon).ok();
65        }
66
67        state.finish_at(cp, CssElementType::AtRule);
68        Ok(())
69    }
70
71    fn parse_ruleset<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
72        let cp = state.checkpoint();
73
74        // Parse selector(s)
75        self.parse_selectors(state)?;
76
77        // Parse declaration block
78        let cp_block = state.checkpoint();
79        state.expect(CssTokenType::LeftBrace).ok();
80        while state.not_at_end() && !state.at(CssTokenType::RightBrace) {
81            self.parse_declaration(state)?;
82            if state.at(CssTokenType::Semicolon) {
83                state.expect(CssTokenType::Semicolon).ok();
84            }
85            else if !state.at(CssTokenType::RightBrace) {
86                // Potential error, but we try to continue
87                break;
88            }
89        }
90        state.expect(CssTokenType::RightBrace).ok();
91        state.finish_at(cp_block, CssElementType::DeclarationBlock);
92
93        state.finish_at(cp, CssElementType::RuleSet);
94        Ok(())
95    }
96
97    fn parse_selectors<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
98        let cp = state.checkpoint();
99        while state.not_at_end() && !state.at(CssTokenType::LeftBrace) {
100            state.bump();
101        }
102        state.finish_at(cp, CssElementType::SelectorList);
103        Ok(())
104    }
105
106    fn parse_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
107        let cp = state.checkpoint();
108
109        // Property name
110        if state.at(CssTokenType::Identifier) || state.at(CssTokenType::PropertyName) {
111            state.bump();
112        }
113        else {
114            return Ok(());
115        }
116
117        state.expect(CssTokenType::Colon).ok();
118
119        let cp_value = state.checkpoint();
120        while state.not_at_end() && !state.at(CssTokenType::Semicolon) && !state.at(CssTokenType::RightBrace) {
121            state.bump();
122        }
123        state.finish_at(cp_value, CssElementType::Value);
124
125        state.finish_at(cp, CssElementType::Declaration);
126        Ok(())
127    }
128}