Skip to main content

oak_sass/parser/
mod.rs

1/// Element types for the Sass language.
2pub 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
17/// Parser for the Sass language.
18pub struct SassParser<'config> {
19    pub(crate) config: &'config SassLanguage,
20}
21
22impl<'config> SassParser<'config> {
23    /// Creates a new Sass parser with the given configuration.
24    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        // Parse selector
60        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}