1use oak_core::{
5 parser::{ParseCache, ParseOutput, Parser, ParserState, parse_with_lexer},
6 source::{Source, TextEdit},
7};
8
9pub mod element_type;
11use crate::{
12 language::LiquidLanguage,
13 lexer::{LiquidLexer, token_type::LiquidTokenType},
14};
15use element_type::LiquidElementType;
16
17pub(crate) type State<'a, S> = ParserState<'a, LiquidLanguage, S>;
18
19#[derive(Debug, Clone)]
21pub struct LiquidParser<'config> {
22 config: &'config LiquidLanguage,
24}
25
26impl<'config> LiquidParser<'config> {
27 pub fn new(config: &'config LiquidLanguage) -> Self {
29 Self { config }
30 }
31
32 fn parse_node<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
33 match state.peek_kind() {
34 Some(LiquidTokenType::DoubleLeftBrace) => self.parse_variable(state),
35 Some(LiquidTokenType::LeftBracePercent) => self.parse_tag_statement(state),
36 Some(LiquidTokenType::Comment) => {
37 let cp = state.checkpoint();
38 state.bump();
39 state.finish_at(cp, LiquidElementType::Comment);
40 Ok(())
41 }
42 _ => {
43 let cp = state.checkpoint();
44 state.advance();
45 state.finish_at(cp, LiquidElementType::Text);
46 Ok(())
47 }
48 }
49 }
50
51 fn parse_variable<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
52 let cp = state.checkpoint();
53 state.expect(LiquidTokenType::DoubleLeftBrace)?;
54 self.parse_expression(state)?;
55 state.expect(LiquidTokenType::DoubleRightBrace)?;
56 state.finish_at(cp, LiquidElementType::Variable);
57 Ok(())
58 }
59
60 fn parse_tag_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
61 let cp = state.checkpoint();
62 state.expect(LiquidTokenType::LeftBracePercent)?;
63
64 let kind = state.peek_kind();
65 match kind {
66 Some(LiquidTokenType::Identifier) => {
67 let text = state.peek_text().unwrap_or_default();
68 match text.as_ref() {
69 "if" => self.parse_if_statement(state, cp),
70 "for" => self.parse_for_statement(state, cp),
71 "block" => self.parse_block_statement(state, cp),
72 "macro" => self.parse_macro_definition(state, cp),
73 _ => {
74 while state.not_at_end() && !state.at(LiquidTokenType::PercentRightBrace) {
75 state.advance();
76 }
77 state.expect(LiquidTokenType::PercentRightBrace)?;
78 state.finish_at(cp, LiquidElementType::Tag);
79 Ok(())
80 }
81 }
82 }
83 _ => {
84 while state.not_at_end() && !state.at(LiquidTokenType::PercentRightBrace) {
85 state.advance();
86 }
87 state.expect(LiquidTokenType::PercentRightBrace)?;
88 state.finish_at(cp, LiquidElementType::Tag);
89 Ok(())
90 }
91 }
92 }
93
94 fn parse_if_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, cp: (usize, usize)) -> Result<(), oak_core::OakError> {
95 state.expect(LiquidTokenType::Identifier)?; self.parse_expression(state)?;
97 state.expect(LiquidTokenType::PercentRightBrace)?;
98
99 while state.not_at_end() {
100 if state.at(LiquidTokenType::LeftBracePercent) {
101 if let Some(LiquidTokenType::Identifier) = state.peek_kind_at(1) {
102 let text = state.tokens.peek_at(1).map(|t| state.source.get_text_in(t.span)).unwrap_or_default();
103 if text == "endif" || text == "elif" || text == "else" {
104 break;
105 }
106 }
107 }
108 self.parse_node(state)?;
109 }
110
111 if state.at(LiquidTokenType::LeftBracePercent) {
112 let text = state.tokens.peek_at(1).map(|t| state.source.get_text_in(t.span)).unwrap_or_default();
113 if text == "elif" {
114 state.expect(LiquidTokenType::LeftBracePercent)?;
115 self.parse_if_statement(state, state.checkpoint())?;
116 }
117 else if text == "else" {
118 state.expect(LiquidTokenType::LeftBracePercent)?;
119 state.expect(LiquidTokenType::Identifier)?; state.expect(LiquidTokenType::PercentRightBrace)?;
121 while state.not_at_end() && !(state.at(LiquidTokenType::LeftBracePercent) && state.tokens.peek_at(1).map(|t| state.source.get_text_in(t.span)).map(|t| t == "endif").unwrap_or(false)) {
122 self.parse_node(state)?;
123 }
124 }
125 }
126
127 if state.at(LiquidTokenType::LeftBracePercent) && state.tokens.peek_at(1).map(|t| state.source.get_text_in(t.span)).map(|t| t == "endif").unwrap_or(false) {
128 state.expect(LiquidTokenType::LeftBracePercent)?;
129 state.expect(LiquidTokenType::Identifier)?; state.expect(LiquidTokenType::PercentRightBrace)?;
131 }
132
133 state.finish_at(cp, LiquidElementType::IfStatement);
134 Ok(())
135 }
136
137 fn parse_for_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, cp: (usize, usize)) -> Result<(), oak_core::OakError> {
138 state.expect(LiquidTokenType::Identifier)?; self.parse_expression(state)?; if state.peek_text().map(|t| t == "in").unwrap_or(false) {
142 state.advance();
143 self.parse_expression(state)?; }
145 state.expect(LiquidTokenType::PercentRightBrace)?;
146
147 while state.not_at_end() && !(state.at(LiquidTokenType::LeftBracePercent) && state.tokens.peek_at(1).map(|t| state.source.get_text_in(t.span)).map(|t| t == "endfor").unwrap_or(false)) {
148 self.parse_node(state)?;
149 }
150
151 state.expect(LiquidTokenType::LeftBracePercent)?;
152 state.expect(LiquidTokenType::Identifier)?; state.expect(LiquidTokenType::PercentRightBrace)?;
154
155 state.finish_at(cp, LiquidElementType::ForStatement);
156 Ok(())
157 }
158
159 fn parse_block_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, cp: (usize, usize)) -> Result<(), oak_core::OakError> {
160 state.expect(LiquidTokenType::Identifier)?; state.expect(LiquidTokenType::Identifier)?; state.expect(LiquidTokenType::PercentRightBrace)?;
163
164 while state.not_at_end() && !(state.at(LiquidTokenType::LeftBracePercent) && state.tokens.peek_at(1).map(|t| state.source.get_text_in(t.span)).map(|t| t == "endblock").unwrap_or(false)) {
165 self.parse_node(state)?;
166 }
167
168 state.expect(LiquidTokenType::LeftBracePercent)?;
169 state.expect(LiquidTokenType::Identifier)?; if state.at(LiquidTokenType::Identifier) {
171 state.advance();
172 }
173 state.expect(LiquidTokenType::PercentRightBrace)?;
174
175 state.finish_at(cp, LiquidElementType::Block);
176 Ok(())
177 }
178
179 fn parse_macro_definition<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, cp: (usize, usize)) -> Result<(), oak_core::OakError> {
180 state.expect(LiquidTokenType::Identifier)?; state.expect(LiquidTokenType::Identifier)?; if state.at(LiquidTokenType::LeftParen) {
184 state.advance();
185 while state.not_at_end() && !state.at(LiquidTokenType::RightParen) {
186 if state.at(LiquidTokenType::Identifier) {
187 state.advance();
188 }
189 if state.at(LiquidTokenType::Comma) {
190 state.advance();
191 }
192 }
193 state.expect(LiquidTokenType::RightParen)?;
194 }
195
196 state.expect(LiquidTokenType::PercentRightBrace)?;
197
198 while state.not_at_end() && !(state.at(LiquidTokenType::LeftBracePercent) && state.tokens.peek_at(1).map(|t| state.source.get_text_in(t.span)).map(|t| t == "endmacro").unwrap_or(false)) {
199 self.parse_node(state)?;
200 }
201
202 state.expect(LiquidTokenType::LeftBracePercent)?;
203 state.expect(LiquidTokenType::Identifier)?; state.expect(LiquidTokenType::PercentRightBrace)?;
205
206 state.finish_at(cp, LiquidElementType::MacroDefinition);
207 Ok(())
208 }
209
210 fn parse_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
211 self.parse_binary_expression(state, 0)
212 }
213
214 fn parse_binary_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, min_precedence: i32) -> Result<(), oak_core::OakError> {
215 let cp = state.checkpoint();
216 self.parse_primary_expression(state)?;
217
218 while let Some(kind) = state.peek_kind() {
219 let precedence = self.get_precedence(kind);
220 if precedence < min_precedence {
221 break;
222 }
223
224 state.advance();
225 self.parse_binary_expression(state, precedence + 1)?;
226 state.finish_at(cp, if kind == LiquidTokenType::Pipe { LiquidElementType::Filter } else { LiquidElementType::Expression });
227 }
228
229 Ok(())
230 }
231
232 fn get_precedence(&self, kind: LiquidTokenType) -> i32 {
233 match kind {
234 LiquidTokenType::Pipe => 1,
235 LiquidTokenType::Plus | LiquidTokenType::Minus => 2,
236 LiquidTokenType::Star | LiquidTokenType::Slash => 3,
237 _ => -1,
238 }
239 }
240
241 fn parse_primary_expression<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), oak_core::OakError> {
242 let cp = state.checkpoint();
243
244 match state.peek_kind() {
245 Some(LiquidTokenType::Identifier) => {
246 state.advance();
247 if state.at(LiquidTokenType::LeftParen) {
248 state.advance();
249 while state.not_at_end() && !state.at(LiquidTokenType::RightParen) {
250 self.parse_expression(state)?;
251 if state.at(LiquidTokenType::Comma) {
252 state.advance();
253 }
254 }
255 state.expect(LiquidTokenType::RightParen)?;
256 state.finish_at(cp, LiquidElementType::Function);
257 }
258 else {
259 state.finish_at(cp, LiquidElementType::Identifier);
260 }
261 }
262 Some(LiquidTokenType::String) | Some(LiquidTokenType::Number) | Some(LiquidTokenType::Boolean) => {
263 state.advance();
264 state.finish_at(cp, LiquidElementType::Literal);
265 }
266 Some(LiquidTokenType::LeftParen) => {
267 state.advance();
268 self.parse_expression(state)?;
269 state.expect(LiquidTokenType::RightParen)?;
270 }
271 _ => {
272 while state.not_at_end() && !state.at(LiquidTokenType::PercentRightBrace) && !state.at(LiquidTokenType::DoubleRightBrace) && !state.at(LiquidTokenType::RightParen) && !state.at(LiquidTokenType::Comma) {
273 state.advance();
274 }
275 }
276 }
277 Ok(())
278 }
279}
280
281impl<'config> Parser<LiquidLanguage> for LiquidParser<'config> {
282 fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<LiquidLanguage>) -> ParseOutput<'a, LiquidLanguage> {
283 let lexer = LiquidLexer::new(&self.config);
284 parse_with_lexer(&lexer, text, edits, cache, |state| {
285 let checkpoint = state.checkpoint();
286 while state.not_at_end() {
287 self.parse_node(state)?;
288 }
289 Ok(state.finish_at(checkpoint, LiquidElementType::Root))
290 })
291 }
292}