1#![doc = include_str!("readme.md")]
2pub mod element_type;
4use crate::{
5 language::CssLanguage,
6 lexer::{CssLexer, CssTokenType},
7};
8pub use element_type::CssElementType;
9use oak_core::{
10 GreenNode, OakError, TextEdit,
11 parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
12 source::Source,
13};
14
15pub(crate) type State<'a, S> = ParserState<'a, CssLanguage, S>;
16
17pub struct CssParser<'config> {
19 pub(crate) config: &'config CssLanguage,
21}
22
23impl<'config> CssParser<'config> {
24 pub fn new(config: &'config CssLanguage) -> Self {
26 Self { config }
27 }
28}
29
30impl<'config> Parser<CssLanguage> for CssParser<'config> {
31 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<CssLanguage>) -> ParseOutput<'a, CssLanguage> {
33 let lexer = CssLexer::new(self.config);
34 parse_with_lexer(&lexer, text, edits, cache, |state| {
35 let cp = state.checkpoint();
36
37 while state.not_at_end() {
38 if state.at(CssTokenType::AtRule) || state.at(CssTokenType::AtImport) || state.at(CssTokenType::AtMedia) { self.parse_at_rule(state)? } else { self.parse_ruleset(state)? }
39 }
40
41 Ok(state.finish_at(cp, CssElementType::SourceFile))
42 })
43 }
44}
45
46impl<'config> CssParser<'config> {
47 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> {
73 let cp = state.checkpoint();
74
75 self.parse_selectors(state)?;
77
78 let cp_block = state.checkpoint();
80 state.expect(CssTokenType::LeftBrace).ok();
81 while state.not_at_end() && !state.at(CssTokenType::RightBrace) {
82 self.parse_declaration(state)?;
83 if state.at(CssTokenType::Semicolon) {
84 state.expect(CssTokenType::Semicolon).ok();
85 }
86 else if !state.at(CssTokenType::RightBrace) {
87 break;
89 }
90 }
91 state.expect(CssTokenType::RightBrace).ok();
92 state.finish_at(cp_block, CssElementType::DeclarationBlock);
93
94 state.finish_at(cp, CssElementType::RuleSet);
95 Ok(())
96 }
97
98 fn parse_selectors<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
100 let cp = state.checkpoint();
101 while state.not_at_end() && !state.at(CssTokenType::LeftBrace) {
102 state.bump()
103 }
104 state.finish_at(cp, CssElementType::SelectorList);
105 Ok(())
106 }
107
108 fn parse_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
110 let cp = state.checkpoint();
111
112 let cp_prop = state.checkpoint();
114 while state.not_at_end() && !state.at(CssTokenType::Colon) && !state.at(CssTokenType::Semicolon) && !state.at(CssTokenType::RightBrace) {
115 state.bump()
116 }
117 state.finish_at(cp_prop, CssElementType::Property);
118
119 if state.at(CssTokenType::Colon) {
120 state.expect(CssTokenType::Colon).ok();
121
122 let cp_val = state.checkpoint();
124 while state.not_at_end() && !state.at(CssTokenType::Semicolon) && !state.at(CssTokenType::RightBrace) {
125 state.bump()
126 }
127 state.finish_at(cp_val, CssElementType::Value);
128 }
129
130 state.finish_at(cp, CssElementType::Declaration);
131 Ok(())
132 }
133}