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