1pub mod element_type;
3
4use crate::{
5 language::SassLanguage,
6 lexer::{SassLexer, token_type::SassTokenType},
7 parser::element_type::SassElementType,
8};
9use oak_core::{
10 errors::OakError,
11 parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
12 source::{Source, TextEdit},
13};
14
15pub(crate) type State<'a, S> = ParserState<'a, SassLanguage, S>;
16
17pub struct SassParser<'config> {
19 pub(crate) config: &'config SassLanguage,
20}
21
22impl<'config> SassParser<'config> {
23 pub fn new(config: &'config SassLanguage) -> Self {
25 Self { config }
26 }
27
28 fn parse_node<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
29 match state.peek_kind() {
30 Some(SassTokenType::Variable) => self.parse_variable_declaration(state),
31 Some(SassTokenType::Identifier) | Some(SassTokenType::Dot) | Some(SassTokenType::Hash) => self.parse_rule(state),
32 Some(SassTokenType::LineComment) | Some(SassTokenType::BlockComment) => {
33 let cp = state.checkpoint();
34 state.bump();
35 state.finish_at(cp, SassElementType::Comment);
36 Ok(())
37 }
38 _ => {
39 state.advance();
40 Ok(())
41 }
42 }
43 }
44
45 fn parse_variable_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
46 let cp = state.checkpoint();
47 state.expect(SassTokenType::Variable)?;
48 state.expect(SassTokenType::Colon)?;
49 while state.not_at_end() && !state.at(SassTokenType::Semicolon) && !state.at(SassTokenType::Newline) {
50 state.advance();
51 }
52 state.eat(SassTokenType::Semicolon);
53 state.finish_at(cp, SassElementType::Variable);
54 Ok(())
55 }
56
57 fn parse_rule<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
58 let cp = state.checkpoint();
59 while state.not_at_end() && !state.at(SassTokenType::LeftBrace) {
61 state.advance();
62 }
63
64 if state.eat(SassTokenType::LeftBrace) {
65 while state.not_at_end() && !state.at(SassTokenType::RightBrace) {
66 self.parse_node(state)?;
67 }
68 state.expect(SassTokenType::RightBrace).ok();
69 }
70
71 state.finish_at(cp, SassElementType::Selector);
72 Ok(())
73 }
74}
75
76impl<'config> Parser<SassLanguage> for SassParser<'config> {
77 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<SassLanguage>) -> ParseOutput<'a, SassLanguage> {
78 let lexer = SassLexer::new(&self.config);
79 parse_with_lexer(&lexer, text, edits, cache, |state| {
80 let checkpoint = state.checkpoint();
81
82 while state.not_at_end() {
83 self.parse_node(state)?;
84 }
85
86 Ok(state.finish_at(checkpoint, SassElementType::SourceFile))
87 })
88 }
89}