Skip to main content

oak_handlebars/parser/
mod.rs

1pub mod element_type;
2
3use crate::{
4    language::HandlebarsLanguage,
5    lexer::{HandlebarsLexer, token_type::HandlebarsTokenType},
6};
7use oak_core::{
8    Parser,
9    errors::OakError,
10    parser::{ParseCache, ParseOutput, ParserState, parse_with_lexer},
11    source::{Source, TextEdit},
12};
13
14pub(crate) type State<'a, S> = ParserState<'a, HandlebarsLanguage, S>;
15
16/// Handlebars parser.
17pub struct HandlebarsParser<'config> {
18    /// Language configuration.
19    pub(crate) config: &'config HandlebarsLanguage,
20}
21
22impl<'config> HandlebarsParser<'config> {
23    /// Creates a new `HandlebarsParser`.
24    pub fn new(config: &'config HandlebarsLanguage) -> Self {
25        Self { config }
26    }
27}
28
29impl<'config> Parser<HandlebarsLanguage> for HandlebarsParser<'config> {
30    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<HandlebarsLanguage>) -> ParseOutput<'a, HandlebarsLanguage> {
31        let lexer = HandlebarsLexer::new(&self.config);
32        parse_with_lexer(&lexer, text, edits, cache, |state| {
33            let checkpoint = state.checkpoint();
34
35            while state.not_at_end() {
36                self.parse_item(state)?
37            }
38
39            Ok(state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Root))
40        })
41    }
42}
43
44impl<'config> HandlebarsParser<'config> {
45    fn parse_item<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
46        let token = match state.current() {
47            Some(t) => t,
48            None => {
49                let err = OakError::unexpected_eof(state.tokens.index(), state.source_id());
50                state.errors.push(err.clone());
51                return Err(err);
52            }
53        };
54
55        match token.kind {
56            HandlebarsTokenType::Content => {
57                let checkpoint = state.checkpoint();
58                state.advance();
59                state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::ContentNode);
60            }
61            HandlebarsTokenType::Open => self.parse_mustache(state)?,
62            HandlebarsTokenType::OpenUnescaped => self.parse_mustache_unescaped(state)?,
63            HandlebarsTokenType::OpenBlock => self.parse_block(state)?,
64            HandlebarsTokenType::OpenInverseBlock => self.parse_inverse_block(state)?,
65            HandlebarsTokenType::OpenRawBlock => self.parse_raw_block(state)?,
66            HandlebarsTokenType::OpenPartial => self.parse_partial(state)?,
67            HandlebarsTokenType::OpenComment | HandlebarsTokenType::OpenCommentBlock => self.parse_comment(state)?,
68            HandlebarsTokenType::Whitespace | HandlebarsTokenType::Newline => state.advance(),
69            _ => {
70                // For anything else, treat as error or skip
71                state.advance()
72            }
73        }
74
75        Ok(())
76    }
77
78    fn parse_mustache<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
79        let checkpoint = state.checkpoint();
80        state.expect(HandlebarsTokenType::Open)?;
81
82        self.skip_trivia(state);
83        self.parse_expression(state)?;
84        self.skip_trivia(state);
85
86        state.expect(HandlebarsTokenType::Close)?;
87        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Mustache);
88        Ok(())
89    }
90
91    fn parse_mustache_unescaped<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
92        let checkpoint = state.checkpoint();
93        state.expect(HandlebarsTokenType::OpenUnescaped)?;
94
95        self.skip_trivia(state);
96        self.parse_expression(state)?;
97        self.skip_trivia(state);
98
99        state.expect(HandlebarsTokenType::CloseUnescaped)?;
100        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Mustache);
101        Ok(())
102    }
103
104    fn parse_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
105        let checkpoint = state.checkpoint();
106        state.expect(HandlebarsTokenType::OpenBlock)?;
107
108        self.skip_trivia(state);
109        self.parse_expression(state)?;
110        self.skip_trivia(state);
111
112        state.expect(HandlebarsTokenType::Close)?;
113
114        // Parse block content until closing tag or else
115        while state.not_at_end() && !state.at(HandlebarsTokenType::CloseBlock) {
116            if state.at(HandlebarsTokenType::Open) {
117                // Check if it's an {{else}}
118                let next = state.peek_at(1);
119                if let Some(token) = next {
120                    if token.kind == HandlebarsTokenType::Else {
121                        self.parse_else_block(state)?;
122                        continue;
123                    }
124                }
125            }
126            self.parse_item(state)?
127        }
128
129        if state.at(HandlebarsTokenType::CloseBlock) {
130            state.expect(HandlebarsTokenType::CloseBlock)?;
131            self.skip_trivia(state);
132            self.parse_path(state)?;
133            self.skip_trivia(state);
134            state.expect(HandlebarsTokenType::Close)?
135        }
136
137        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Block);
138        Ok(())
139    }
140
141    fn parse_inverse_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
142        let checkpoint = state.checkpoint();
143        state.expect(HandlebarsTokenType::OpenInverseBlock)?;
144
145        self.skip_trivia(state);
146        if !state.at(HandlebarsTokenType::Close) {
147            self.parse_expression(state)?;
148            self.skip_trivia(state)
149        }
150
151        state.expect(HandlebarsTokenType::Close)?;
152
153        while state.not_at_end() && !state.at(HandlebarsTokenType::CloseBlock) {
154            self.parse_item(state)?
155        }
156
157        if state.at(HandlebarsTokenType::CloseBlock) {
158            state.expect(HandlebarsTokenType::CloseBlock)?;
159            self.skip_trivia(state);
160            self.parse_path(state)?;
161            self.skip_trivia(state);
162            state.expect(HandlebarsTokenType::Close)?
163        }
164
165        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::InverseBlock);
166        Ok(())
167    }
168
169    fn parse_else_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
170        let checkpoint = state.checkpoint();
171        state.expect(HandlebarsTokenType::Open)?;
172        state.expect(HandlebarsTokenType::Else)?;
173        state.expect(HandlebarsTokenType::Close)?;
174
175        while state.not_at_end() && !state.at(HandlebarsTokenType::CloseBlock) {
176            self.parse_item(state)?
177        }
178
179        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::ElseBlock);
180        Ok(())
181    }
182
183    fn parse_raw_block<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
184        let checkpoint = state.checkpoint();
185        state.expect(HandlebarsTokenType::OpenRawBlock)?;
186
187        self.skip_trivia(state);
188        self.parse_expression(state)?;
189        self.skip_trivia(state);
190
191        state.expect(HandlebarsTokenType::CloseRawBlock)?;
192
193        // In raw blocks, everything is content until the end raw block tag
194        while state.not_at_end() && !state.at(HandlebarsTokenType::OpenEndRawBlock) {
195            state.advance()
196        }
197
198        if state.at(HandlebarsTokenType::OpenEndRawBlock) {
199            state.expect(HandlebarsTokenType::OpenEndRawBlock)?;
200            self.skip_trivia(state);
201            self.parse_path(state)?;
202            self.skip_trivia(state);
203            state.expect(HandlebarsTokenType::CloseRawBlock)?
204        }
205
206        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Block);
207        Ok(())
208    }
209
210    fn parse_partial<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
211        let checkpoint = state.checkpoint();
212        state.expect(HandlebarsTokenType::OpenPartial)?;
213
214        self.skip_trivia(state);
215        self.parse_path(state)?;
216        self.skip_trivia(state);
217
218        while state.not_at_end() && !state.at(HandlebarsTokenType::Close) {
219            self.parse_parameter(state)?;
220            self.skip_trivia(state)
221        }
222
223        state.expect(HandlebarsTokenType::Close)?;
224        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Partial);
225        Ok(())
226    }
227
228    fn parse_comment<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
229        let checkpoint = state.checkpoint();
230        if state.at(HandlebarsTokenType::OpenComment) {
231            state.expect(HandlebarsTokenType::OpenComment)?;
232            while state.not_at_end() && !state.at(HandlebarsTokenType::Close) {
233                state.advance()
234            }
235            state.expect(HandlebarsTokenType::Close)?
236        }
237        else {
238            state.expect(HandlebarsTokenType::OpenCommentBlock)?;
239            while state.not_at_end() && !state.at(HandlebarsTokenType::CloseCommentBlock) {
240                state.advance()
241            }
242            state.expect(HandlebarsTokenType::CloseCommentBlock)?
243        }
244        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::CommentNode);
245        Ok(())
246    }
247
248    fn parse_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
249        let checkpoint = state.checkpoint();
250
251        self.parse_path(state)?;
252
253        while state.not_at_end() && !state.at(HandlebarsTokenType::Close) && !state.at(HandlebarsTokenType::CloseUnescaped) {
254            self.skip_trivia(state);
255            if state.at(HandlebarsTokenType::Identifier) || state.at(HandlebarsTokenType::StringLiteral) || state.at(HandlebarsTokenType::NumberLiteral) || state.at(HandlebarsTokenType::BooleanLiteral) { self.parse_parameter(state)? } else { break }
256        }
257
258        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Expression);
259        Ok(())
260    }
261
262    fn parse_parameter<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
263        if state.at(HandlebarsTokenType::Identifier) {
264            // Check if it's a named parameter: key=value
265            let next = state.peek_at(1);
266            if let Some(token) = next {
267                if token.kind == HandlebarsTokenType::Equal {
268                    let checkpoint = state.checkpoint();
269                    state.expect(HandlebarsTokenType::Identifier)?;
270                    state.expect(HandlebarsTokenType::Equal)?;
271                    self.parse_value(state)?;
272                    state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Parameter);
273                    return Ok(());
274                }
275            }
276        }
277
278        self.parse_value(state)
279    }
280
281    fn parse_value<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
282        if state.at(HandlebarsTokenType::Identifier) {
283            self.parse_path(state)?
284        }
285        else if state.at(HandlebarsTokenType::StringLiteral) || state.at(HandlebarsTokenType::NumberLiteral) || state.at(HandlebarsTokenType::BooleanLiteral) {
286            state.advance()
287        }
288        else if state.at(HandlebarsTokenType::LeftParen) {
289            self.parse_sub_expression(state)?
290        }
291        else {
292            let token = state.current();
293            let err = oak_core::errors::OakError::unexpected_token(format!("{:?}", token.map(|t| t.kind)), state.tokens.index(), state.source_id());
294            state.errors.push(err.clone());
295            return Err(err);
296        }
297        Ok(())
298    }
299
300    fn parse_path<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
301        let checkpoint = state.checkpoint();
302        state.expect(HandlebarsTokenType::Identifier)?;
303
304        while state.at(HandlebarsTokenType::Dot) || state.at(HandlebarsTokenType::Slash) {
305            state.advance();
306            state.expect(HandlebarsTokenType::Identifier)?
307        }
308
309        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::Path);
310        Ok(())
311    }
312
313    fn parse_sub_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
314        let checkpoint = state.checkpoint();
315        state.expect(HandlebarsTokenType::LeftParen)?;
316
317        self.skip_trivia(state);
318        self.parse_expression(state)?;
319        self.skip_trivia(state);
320
321        state.expect(HandlebarsTokenType::RightParen)?;
322        state.finish_at(checkpoint, crate::parser::element_type::HandlebarsElementType::SubExpression);
323        Ok(())
324    }
325
326    fn skip_trivia<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
327        while state.at(HandlebarsTokenType::Whitespace) || state.at(HandlebarsTokenType::Newline) {
328            state.advance()
329        }
330    }
331}