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(); 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 self.parse_selectors(state)?;
76
77 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 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 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}