Skip to main content

oak_less/parser/
mod.rs

1#![doc = include_str!("readme.md")]
2/// Less element types and role definitions.
3pub mod element_type;
4use crate::{
5    language::LessLanguage,
6    lexer::{LessLexer, LessTokenType},
7};
8pub use element_type::LessElementType;
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, LessLanguage, S>;
16
17/// Parser for the Less language.
18pub struct LessParser<'config> {
19    /// Language configuration.
20    pub(crate) config: &'config LessLanguage,
21}
22
23impl<'config> LessParser<'config> {
24    /// Creates a new `LessParser` with the given language configuration.
25    pub fn new(config: &'config LessLanguage) -> Self {
26        Self { config }
27    }
28}
29
30impl<'config> Parser<LessLanguage> for LessParser<'config> {
31    /// Parses the Less source code into a green tree.
32    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<LessLanguage>) -> ParseOutput<'a, LessLanguage> {
33        let lexer = LessLexer::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(LessTokenType::AtRule) || state.at(LessTokenType::AtImport) || state.at(LessTokenType::AtMedia) { self.parse_at_rule(state)? } else { self.parse_ruleset(state)? }
39            }
40
41            Ok(state.finish_at(cp, LessElementType::SourceFile))
42        })
43    }
44}
45
46impl<'config> LessParser<'config> {
47    /// Parses a Less at-rule (e.g., `@import`, `@media`).
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(); // Consume the at-keyword
51
52        while state.not_at_end() && !state.at(LessTokenType::Semicolon) && !state.at(LessTokenType::LeftBrace) {
53            state.bump()
54        }
55
56        if state.at(LessTokenType::LeftBrace) {
57            state.expect(LessTokenType::LeftBrace).ok();
58            while state.not_at_end() && !state.at(LessTokenType::RightBrace) {
59                self.parse_ruleset(state)?
60            }
61            state.expect(LessTokenType::RightBrace).ok();
62        }
63        else if state.at(LessTokenType::Semicolon) {
64            state.expect(LessTokenType::Semicolon).ok();
65        }
66
67        state.finish_at(cp, LessElementType::AtRule);
68        Ok(())
69    }
70
71    /// Parses a Less rule set (selector + declaration block).
72    fn parse_ruleset<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
73        let cp = state.checkpoint();
74
75        // Parse selector(s)
76        self.parse_selectors(state)?;
77
78        // Parse declaration block
79        let cp_block = state.checkpoint();
80        state.expect(LessTokenType::LeftBrace).ok();
81        while state.not_at_end() && !state.at(LessTokenType::RightBrace) {
82            self.parse_declaration(state)?;
83            if state.at(LessTokenType::Semicolon) {
84                state.expect(LessTokenType::Semicolon).ok();
85            }
86            else if !state.at(LessTokenType::RightBrace) {
87                // Potential error, but we try to continue
88                break;
89            }
90        }
91        state.expect(LessTokenType::RightBrace).ok();
92        state.finish_at(cp_block, LessElementType::DeclarationBlock);
93
94        state.finish_at(cp, LessElementType::RuleSet);
95        Ok(())
96    }
97
98    /// Parses Less selectors.
99    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(LessTokenType::LeftBrace) {
102            state.bump()
103        }
104        state.finish_at(cp, LessElementType::SelectorList);
105        Ok(())
106    }
107
108    /// Parses a Less declaration (property: value).
109    fn parse_declaration<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
110        let cp = state.checkpoint();
111
112        // Property
113        let cp_prop = state.checkpoint();
114        while state.not_at_end() && !state.at(LessTokenType::Colon) && !state.at(LessTokenType::Semicolon) && !state.at(LessTokenType::RightBrace) {
115            state.bump()
116        }
117        state.finish_at(cp_prop, LessElementType::Property);
118
119        if state.at(LessTokenType::Colon) {
120            state.expect(LessTokenType::Colon).ok();
121
122            // Value
123            let cp_val = state.checkpoint();
124            while state.not_at_end() && !state.at(LessTokenType::Semicolon) && !state.at(LessTokenType::RightBrace) {
125                state.bump()
126            }
127            state.finish_at(cp_val, LessElementType::Value);
128        }
129
130        state.finish_at(cp, LessElementType::Declaration);
131        Ok(())
132    }
133}