1 //! Parser
2 //!
3 //! This module contains functions than can be used for writing plugins
4 //! but should be ignored for simple usage.
5
6 use crate::error::{Error, Result, ResultLiquidExt};
7 use crate::model::Value;
8 use crate::runtime::Expression;
9 use crate::runtime::Renderable;
10 use crate::runtime::Variable;
11
12 use super::Language;
13 use super::Text;
14 use super::{Filter, FilterArguments, FilterChain};
15
16 use pest::Parser;
17
18 mod inner {
19 #[derive(Parser)]
20 #[grammar = "parser/grammar.pest"]
21 pub struct LiquidParser;
22 }
23
24 use self::inner::*;
25
26 type Pair<'a> = ::pest::iterators::Pair<'a, Rule>;
27 type Pairs<'a> = ::pest::iterators::Pairs<'a, Rule>;
28
29 /// Converts a `pest::Error` into a `liquid::Error`.
convert_pest_error(err: ::pest::error::Error<Rule>) -> Error30 fn convert_pest_error(err: ::pest::error::Error<Rule>) -> Error {
31 let err = err.renamed_rules(|&rule| match rule {
32 Rule::LesserThan => "\"<\"".to_string(),
33 Rule::GreaterThan => "\">\"".to_string(),
34 Rule::LesserThanEquals => "\"<=\"".to_string(),
35 Rule::GreaterThanEquals => "\">=\"".to_string(),
36 Rule::Equals => "\"==\"".to_string(),
37 Rule::NotEquals => "\"!=\"".to_string(),
38 Rule::LesserThanGreaterThan => "\"<>\"".to_string(),
39 Rule::Assign => "\"=\"".to_string(),
40 Rule::Comma => "\",\"".to_string(),
41 Rule::Colon => "\":\"".to_string(),
42 other => format!("{:?}", other),
43 });
44 Error::with_msg(err.to_string())
45 }
46
47 /// Generates a `liquid::Error` with the given message pointing to
48 /// the pest
error_from_pair(pair: Pair, msg: String) -> Error49 fn error_from_pair(pair: Pair, msg: String) -> Error {
50 let pest_error = ::pest::error::Error::new_from_span(
51 ::pest::error::ErrorVariant::CustomError { message: msg },
52 pair.as_span(),
53 );
54 convert_pest_error(pest_error)
55 }
56
57 /// Parses the provided &str into a number of Renderable items.
parse(text: &str, options: &Language) -> Result<Vec<Box<dyn Renderable>>>58 pub fn parse(text: &str, options: &Language) -> Result<Vec<Box<dyn Renderable>>> {
59 let mut liquid = LiquidParser::parse(Rule::LaxLiquidFile, text)
60 .expect("Parsing with Rule::LaxLiquidFile should not raise errors, but InvalidLiquid tokens instead.")
61 .next()
62 .expect("Unwrapping LiquidFile to access the elements.")
63 .into_inner();
64
65 let mut renderables = Vec::new();
66
67 while let Some(element) = liquid.next() {
68 if element.as_rule() == Rule::EOI {
69 break;
70 }
71
72 renderables.push(BlockElement::parse_pair(
73 element.into(),
74 &mut liquid,
75 options,
76 )?);
77 }
78 Ok(renderables)
79 }
80
81 /// Parses a `Scalar` from a `Pair` with a literal value.
82 /// This `Pair` must be `Rule::Literal`.
parse_literal(literal: Pair) -> Value83 fn parse_literal(literal: Pair) -> Value {
84 if literal.as_rule() != Rule::Literal {
85 panic!("Expected literal.");
86 }
87
88 let literal = literal
89 .into_inner()
90 .next()
91 .expect("Get into the rule inside literal.");
92
93 match literal.as_rule() {
94 Rule::NilLiteral => Value::Nil,
95 Rule::EmptyLiteral => Value::State(crate::model::State::Empty),
96 Rule::BlankLiteral => Value::State(crate::model::State::Blank),
97 Rule::StringLiteral => {
98 let literal = literal.as_str();
99 let trim_quotes = &literal[1..literal.len() - 1];
100
101 Value::scalar(trim_quotes.to_owned())
102 }
103 Rule::IntegerLiteral => Value::scalar(
104 literal
105 .as_str()
106 .parse::<i64>()
107 .expect("Grammar ensures matches are parseable as integers."),
108 ),
109 Rule::FloatLiteral => Value::scalar(
110 literal
111 .as_str()
112 .parse::<f64>()
113 .expect("Grammar ensures matches are parseable as floats."),
114 ),
115 Rule::BooleanLiteral => Value::scalar(
116 literal
117 .as_str()
118 .parse::<bool>()
119 .expect("Grammar ensures matches are parseable as bools."),
120 ),
121 _ => unreachable!(),
122 }
123 }
124
125 /// Parses a `Variable` from a `Pair` with a variable.
126 /// This `Pair` must be `Rule::Variable`.
parse_variable(variable: Pair) -> Variable127 fn parse_variable(variable: Pair) -> Variable {
128 if variable.as_rule() != Rule::Variable {
129 panic!("Expected variable.");
130 }
131
132 let mut indexes = variable.into_inner();
133
134 let first_identifier = indexes
135 .next()
136 .expect("A variable starts with an identifier.")
137 .as_str()
138 .to_owned();
139 let mut variable = Variable::with_literal(first_identifier);
140
141 let indexes = indexes.map(|index| match index.as_rule() {
142 Rule::Identifier => Expression::with_literal(index.as_str().to_owned()),
143 Rule::Value => parse_value(index),
144 _ => unreachable!(),
145 });
146
147 variable.extend(indexes);
148 variable
149 }
150
151 /// Parses an `Expression` from a `Pair` with a value.
152 ///
153 /// Do not confuse this value with `liquid-value`'s `Value`.
154 /// In this runtime, value refers to either a literal value or a variable.
155 ///
156 /// This `Pair` must be `Rule::Value`.
parse_value(value: Pair) -> Expression157 fn parse_value(value: Pair) -> Expression {
158 if value.as_rule() != Rule::Value {
159 panic!("Expected value.");
160 }
161
162 let value = value.into_inner().next().expect("Get inside the value.");
163
164 match value.as_rule() {
165 Rule::Literal => Expression::Literal(parse_literal(value)),
166 Rule::Variable => Expression::Variable(parse_variable(value)),
167 _ => unreachable!(),
168 }
169 }
170
171 /// Parses a `FilterCall` from a `Pair` with a filter.
172 /// This `Pair` must be `Rule::Filter`.
parse_filter(filter: Pair, options: &Language) -> Result<Box<dyn Filter>>173 fn parse_filter(filter: Pair, options: &Language) -> Result<Box<dyn Filter>> {
174 if filter.as_rule() != Rule::Filter {
175 panic!("Expected a filter.");
176 }
177
178 let filter_str = filter.as_str();
179 let mut filter = filter.into_inner();
180 let name = filter.next().expect("A filter always has a name.").as_str();
181
182 let mut keyword_args = Vec::new();
183 let mut positional_args = Vec::new();
184
185 for arg in filter {
186 match arg.as_rule() {
187 Rule::PositionalFilterArgument => {
188 let value = arg.into_inner().next().expect("Rule ensures value.");
189 let value = parse_value(value);
190 positional_args.push(value);
191 }
192 Rule::KeywordFilterArgument => {
193 let mut arg = arg.into_inner();
194 let key = arg.next().expect("Rule ensures identifier.").as_str();
195 let value = arg.next().expect("Rule ensures value.");
196 let value = parse_value(value);
197 keyword_args.push((key, value));
198 }
199 _ => unreachable!(),
200 }
201 }
202
203 let args = FilterArguments {
204 positional: Box::new(positional_args.into_iter()),
205 keyword: Box::new(keyword_args.into_iter()),
206 };
207
208 let f = options.filters.get(name).ok_or_else(|| {
209 let mut available: Vec<_> = options.filters.plugin_names().collect();
210 available.sort_unstable();
211 let available = itertools::join(available, ", ");
212 Error::with_msg("Unknown filter")
213 .context("requested filter", name.to_owned())
214 .context("available filters", available)
215 })?;
216
217 let f = f
218 .parse(args)
219 .trace("Filter parsing error")
220 .context_key("filter")
221 .value_with(|| filter_str.to_string().into())?;
222
223 Ok(f)
224 }
225
226 /// Parses a `FilterChain` from a `Pair` with a filter chain.
227 /// This `Pair` must be `Rule::FilterChain`.
parse_filter_chain(chain: Pair, options: &Language) -> Result<FilterChain>228 fn parse_filter_chain(chain: Pair, options: &Language) -> Result<FilterChain> {
229 if chain.as_rule() != Rule::FilterChain {
230 panic!("Expected an expression with filters.");
231 }
232
233 let mut chain = chain.into_inner();
234 let entry = parse_value(
235 chain
236 .next()
237 .expect("A filterchain always has starts by a value."),
238 );
239 let filters: Result<Vec<_>> = chain.map(|f| parse_filter(f, options)).collect();
240 let filters = filters?;
241
242 let filters = FilterChain::new(entry, filters);
243 Ok(filters)
244 }
245
246 /// An interface to access elements inside a block.
247 pub struct TagBlock<'a: 'b, 'b> {
248 name: &'b str,
249 iter: &'b mut dyn Iterator<Item = Pair<'a>>,
250 closed: bool,
251 }
252
253 impl<'a, 'b> TagBlock<'a, 'b> {
new(name: &'b str, next_elements: &'b mut dyn Iterator<Item = Pair<'a>>) -> Self254 fn new(name: &'b str, next_elements: &'b mut dyn Iterator<Item = Pair<'a>>) -> Self {
255 TagBlock {
256 name,
257 iter: next_elements,
258 closed: false,
259 }
260 }
261
262 /// Returns the next element of the block, if any, similarly to an iterator.
263 ///
264 /// However, if the input text reaches its end and the block is not closed,
265 /// an error is returned instead.
266 #[allow(clippy::should_implement_trait)]
next(&mut self) -> Result<Option<BlockElement<'a>>>267 pub fn next(&mut self) -> Result<Option<BlockElement<'a>>> {
268 if self.closed {
269 return Ok(None);
270 }
271
272 let element = self.iter.next().expect("File shouldn't end before EOI.");
273
274 if element.as_rule() == Rule::EOI {
275 return error_from_pair(
276 element,
277 format!("Unclosed block. {{% end{} %}} tag expected.", self.name),
278 )
279 .into_err();
280 }
281
282 // Tags are treated separately so as to check for a possible `{% endtag %}`
283 if element.as_rule() == Rule::Tag {
284 let as_str = element.as_str();
285 let mut tag = element
286 .into_inner()
287 .next()
288 .expect("Unwrapping TagInner")
289 .into_inner();
290 let name = tag.next().expect("Tags start by their identifier.");
291 let name_str = name.as_str();
292
293 // The name of the closing tag is "end" followed by the tag's name.
294 if name_str.len() > 3 && &name_str[0..3] == "end" && &name_str[3..] == self.name {
295 // Then this is a block ending tag and will close the block.
296
297 // no more arguments should be supplied, trying to supply them is an error
298 if let Some(token) = tag.next() {
299 return TagToken::from(token).raise_error().into_err();
300 }
301
302 self.closed = true;
303 return Ok(None);
304 } else {
305 // Then this is a regular tag
306 let tokens = TagTokenIter::new(&name, tag);
307 return Ok(Some(BlockElement::Tag(Tag {
308 name,
309 tokens,
310 as_str,
311 })));
312 }
313 }
314 Ok(Some(element.into()))
315 }
316
317 /// Retrieves all the content of this block as a String, regardless of
318 /// being valid liquid or not.
319 ///
320 /// Do not use this method in a block you already called `.next()` on.
321 ///
322 /// Set the parameter `allow_nesting` of this function to true if you
323 /// still want these tags to nest (so the number of `{% name %}` must
324 /// be equal to the number of `{% endname %}`) of false if you don't
325 /// (only the first `{% name %}` is parsed, a single `{% endname %}`
326 /// will always close the tag).
327 ///
328 /// # Panics
329 ///
330 /// Will panic if used in a closed block.
escape_liquid(&mut self, allow_nesting: bool) -> Result<&'a str>331 pub fn escape_liquid(&mut self, allow_nesting: bool) -> Result<&'a str> {
332 if self.closed {
333 panic!("`escape_liquid` must be used in an open tag.")
334 }
335
336 let mut nesting_level = 1;
337
338 // Working with pest positions allows returning a `&str` instead of a `String`
339 let mut start_pos = None;
340 let mut end_pos = None;
341
342 while let Some(element) = self.iter.next() {
343 let element_as_span = element.as_span();
344 if start_pos.is_none() {
345 start_pos = Some(element_as_span.start_pos());
346 }
347
348 if element.as_rule() == Rule::EOI {
349 return error_from_pair(
350 element,
351 format!("Unclosed block. {{% end{} %}} tag expected.", self.name),
352 )
353 .into_err();
354 }
355
356 // Tags are potentially `{% endtag %}`
357 if element.as_rule() == Rule::Tag {
358 let mut tag = element
359 .into_inner()
360 .next()
361 .expect("Unwrapping TagInner")
362 .into_inner();
363 let name = tag.next().expect("Tags start by their identifier.");
364 let name_str = name.as_str();
365
366 // The name of the closing tag is "end" followed by the tag's name.
367 if name_str.len() > 3 && &name_str[0..3] == "end" && &name_str[3..] == self.name {
368 // No more arguments should be supplied. If they are, it is
369 // assumed not to be a tag closer.
370 if tag.next().is_none() {
371 nesting_level -= 1;
372 if nesting_level == 0 {
373 self.closed = true;
374 let start_pos = start_pos.expect("Will be `Some` inside this loop.");
375 let output = match end_pos {
376 Some(end_pos) => start_pos.span(&end_pos).as_str(),
377 None => "",
378 };
379
380 return Ok(output);
381 }
382 }
383 } else if name_str == self.name && allow_nesting {
384 // Going deeper in the nested blocks.
385 nesting_level += 1;
386 }
387 }
388
389 end_pos = Some(element_as_span.end_pos());
390 }
391
392 panic!("Function must eventually find either a Rule::EOI or a closing tag.")
393 }
394
395 /// A convenient method that parses every element remaining in the block.
parse_all(&mut self, options: &Language) -> Result<Vec<Box<dyn Renderable>>>396 pub fn parse_all(&mut self, options: &Language) -> Result<Vec<Box<dyn Renderable>>> {
397 let mut renderables = Vec::new();
398 while let Some(r) = self.parse_next(options)? {
399 renderables.push(r);
400 }
401 Ok(renderables)
402 }
403
404 /// Parses the next element in the block just as if it weren't inside any block.
405 ///
406 /// Returns none if no element is left and raises the same errors as `next()`.
parse_next(&mut self, options: &Language) -> Result<Option<Box<dyn Renderable>>>407 pub fn parse_next(&mut self, options: &Language) -> Result<Option<Box<dyn Renderable>>> {
408 match self.next()? {
409 None => Ok(None),
410 Some(element) => Ok(Some(element.parse(self, options)?)),
411 }
412 }
413
414 /// Checks whether the block was fully parsed its elements.
415 ///
416 /// This must be added at the end of every block right before returning, so as
417 /// to ensure that it doesn't leave any unparsed element by accident.
assert_empty(self)418 pub fn assert_empty(self) {
419 assert!(
420 self.closed,
421 "Block {{% {} %}} doesn't exhaust its iterator of elements.",
422 self.name
423 )
424 }
425 }
426
427 /// An element that is raw text.
428 pub struct Raw<'a> {
429 text: &'a str,
430 }
431 impl<'a> From<Pair<'a>> for Raw<'a> {
from(element: Pair<'a>) -> Self432 fn from(element: Pair<'a>) -> Self {
433 if element.as_rule() != Rule::Raw {
434 panic!("Only rule Raw can be converted to Raw.");
435 }
436 Raw {
437 text: element.as_str(),
438 }
439 }
440 }
441 impl<'a> Into<&'a str> for Raw<'a> {
into(self) -> &'a str442 fn into(self) -> &'a str {
443 self.as_str()
444 }
445 }
446 impl<'a> Raw<'a> {
447 /// Turns the text into a Renderable.
into_renderable(self) -> Box<dyn Renderable>448 pub fn into_renderable(self) -> Box<dyn Renderable> {
449 Box::new(Text::new(self.as_str()))
450 }
451
452 /// Returns the text as a str.
as_str(&self) -> &'a str453 pub fn as_str(&self) -> &'a str {
454 self.text
455 }
456 }
457
458 /// An element that is a tag.
459 pub struct Tag<'a> {
460 name: Pair<'a>,
461 tokens: TagTokenIter<'a>,
462 as_str: &'a str,
463 }
464
465 impl<'a> From<Pair<'a>> for Tag<'a> {
from(element: Pair<'a>) -> Self466 fn from(element: Pair<'a>) -> Self {
467 if element.as_rule() != Rule::Tag {
468 panic!("Only rule Tag can be converted to Tag.");
469 }
470 let as_str = element.as_str();
471 let mut tag = element
472 .into_inner()
473 .next()
474 .expect("Unwrapping TagInner.")
475 .into_inner();
476 let name = tag.next().expect("A tag starts with an identifier.");
477 let tokens = TagTokenIter::new(&name, tag);
478
479 Tag {
480 name,
481 tokens,
482 as_str,
483 }
484 }
485 }
486
487 impl<'a> Tag<'a> {
488 /// Creates a new tag from a string such as "{% tagname tagtoken1 tagtoken2 ... %}".
489 ///
490 /// This is used as a debug tool. It allows to easily build tags in unit tests.
new(text: &'a str) -> Result<Self>491 pub fn new(text: &'a str) -> Result<Self> {
492 let tag = LiquidParser::parse(Rule::Tag, text)
493 .map_err(convert_pest_error)?
494 .next()
495 .ok_or_else(|| Error::with_msg("Tried to create a Tag from an invalid string."))?;
496
497 Ok(tag.into())
498 }
499
500 /// Returns the name of this tag.
name(&self) -> &str501 pub fn name(&self) -> &str {
502 self.name.as_str()
503 }
504
505 /// Returns the tokens of this tag.
tokens(&mut self) -> &mut TagTokenIter<'a>506 pub fn tokens(&mut self) -> &mut TagTokenIter<'a> {
507 &mut self.tokens
508 }
509
510 /// Consumes this structure to obtain ownership over its tokens.
into_tokens(self) -> TagTokenIter<'a>511 pub fn into_tokens(self) -> TagTokenIter<'a> {
512 self.tokens
513 }
514
515 /// Returns the tag as a str.
as_str(&self) -> &str516 pub fn as_str(&self) -> &str {
517 self.as_str
518 }
519
520 /// Parses the tag just as if it weren't inside any block.
parse( self, tag_block: &mut TagBlock, options: &Language, ) -> Result<Box<dyn Renderable>>521 pub fn parse(
522 self,
523 tag_block: &mut TagBlock,
524 options: &Language,
525 ) -> Result<Box<dyn Renderable>> {
526 self.parse_pair(&mut tag_block.iter, options)
527 }
528
529 /// The same as `parse`, but directly takes an iterator over `Pair`s instead of a TagBlock.
parse_pair( self, next_elements: &mut dyn Iterator<Item = Pair>, options: &Language, ) -> Result<Box<dyn Renderable>>530 fn parse_pair(
531 self,
532 next_elements: &mut dyn Iterator<Item = Pair>,
533 options: &Language,
534 ) -> Result<Box<dyn Renderable>> {
535 let (name, tokens) = (self.name, self.tokens);
536 let position = name.as_span();
537 let name = name.as_str();
538
539 if let Some(plugin) = options.tags.get(name) {
540 plugin.parse(tokens, options)
541 } else if let Some(plugin) = options.blocks.get(name) {
542 let block = TagBlock::new(name, next_elements);
543 let renderables = plugin.parse(tokens, block, options)?;
544 Ok(renderables)
545 } else {
546 let pest_error = ::pest::error::Error::new_from_span(
547 ::pest::error::ErrorVariant::CustomError {
548 message: "Unknown tag.".to_string(),
549 },
550 position,
551 );
552 let mut all_tags: Vec<_> = options.tags.plugin_names().collect();
553 all_tags.sort_unstable();
554 let all_tags = itertools::join(all_tags, ", ");
555 let mut all_blocks: Vec<_> = options.blocks.plugin_names().collect();
556 all_blocks.sort_unstable();
557 let all_blocks = itertools::join(all_blocks, ", ");
558 let error = convert_pest_error(pest_error)
559 .context("requested", name.to_owned())
560 .context("available tags", all_tags)
561 .context("available blocks", all_blocks);
562 Err(error)
563 }
564 }
565 }
566
567 /// An element that is an expression.
568 pub struct Exp<'a> {
569 element: Pair<'a>,
570 }
571
572 impl<'a> From<Pair<'a>> for Exp<'a> {
from(element: Pair<'a>) -> Self573 fn from(element: Pair<'a>) -> Self {
574 if element.as_rule() != Rule::Expression {
575 panic!("Only rule Expression can be converted to Expression.");
576 }
577 Exp { element }
578 }
579 }
580
581 impl<'a> Exp<'a> {
582 /// Parses the expression just as if it weren't inside any block.
parse(self, options: &Language) -> Result<Box<dyn Renderable>>583 pub fn parse(self, options: &Language) -> Result<Box<dyn Renderable>> {
584 let filter_chain = self
585 .element
586 .into_inner()
587 .next()
588 .expect("Unwrapping ExpressionInner")
589 .into_inner()
590 .next()
591 .expect("An expression consists of one filterchain.");
592
593 let filter_chain = parse_filter_chain(filter_chain, options)?;
594 Ok(Box::new(filter_chain))
595 }
596
597 /// Returns the expression as a str.
as_str(&self) -> &str598 pub fn as_str(&self) -> &str {
599 self.element.as_str()
600 }
601 }
602
603 /// This token could not be recognized as valid liquid.
604 /// If parsed, will raise an error.
605 pub struct InvalidLiquidToken<'a> {
606 element: Pair<'a>,
607 }
608 impl<'a> InvalidLiquidToken<'a> {
609 /// Returns the expression as a str.
610 // TODO consider removing this
as_str(&self) -> &str611 pub fn as_str(&self) -> &str {
612 self.element.as_str()
613 }
614
615 /// Tries to parse this as valid liquid, which will inevitably raise an error.
616 /// This is needed in order to raise the right error message.
parse(self, tag_block: &mut TagBlock) -> Result<Box<dyn Renderable>>617 pub fn parse(self, tag_block: &mut TagBlock) -> Result<Box<dyn Renderable>> {
618 self.parse_pair(&mut tag_block.iter)
619 }
620
621 /// Tries to parse this as valid liquid, which will inevitably raise an error.
622 /// This is needed in order to raise the correct error message.
parse_pair( self, next_elements: &mut dyn Iterator<Item = Pair>, ) -> Result<Box<dyn Renderable>>623 fn parse_pair(
624 self,
625 next_elements: &mut dyn Iterator<Item = Pair>,
626 ) -> Result<Box<dyn Renderable>> {
627 use pest::error::LineColLocation;
628
629 let invalid_token_span = self.element.as_span();
630 let invalid_token_position = invalid_token_span.start_pos();
631 let (offset_l, offset_c) = invalid_token_position.line_col();
632 let offset_l = offset_l - 1;
633 let offset_c = offset_c - 1;
634
635 let end_position = match next_elements.last() {
636 Some(element) => element.as_span().end_pos(),
637 None => invalid_token_span.end_pos(),
638 };
639
640 let mut text = String::from(&invalid_token_position.line_of()[..offset_c]);
641 text.push_str(invalid_token_position.span(&end_position).as_str());
642
643 // Reparses from the line where invalid liquid started, in order
644 // to raise the error.
645 let mut error = match LiquidParser::parse(Rule::LiquidFile, &text) {
646 Ok(_) => panic!("`LiquidParser::parse` should fail in InvalidLiquidTokens."),
647 Err(error) => error,
648 };
649
650 // Adds an offset to the line of the error, in order to show the right line
651 // TODO when liquid::error is able to handle line/col information by itself
652 // make this operation on the liquid Error type instead.
653 error.line_col = match error.line_col {
654 LineColLocation::Span((ls, cs), (le, ce)) => {
655 LineColLocation::Span((ls + offset_l, cs), (le + offset_l, ce))
656 }
657 LineColLocation::Pos((ls, cs)) => LineColLocation::Pos((ls + offset_l, cs)),
658 };
659
660 Err(convert_pest_error(error))
661 }
662 }
663 impl<'a> From<Pair<'a>> for InvalidLiquidToken<'a> {
from(element: Pair<'a>) -> Self664 fn from(element: Pair<'a>) -> Self {
665 if element.as_rule() != Rule::InvalidLiquid {
666 panic!("Tried to parse a valid liquid token as invalid.");
667 }
668 InvalidLiquidToken { element }
669 }
670 }
671
672 /// An element that can be raw text, a tag, or an expression.
673 ///
674 /// This is the result of calling `next()` on a `TagBlock`.
675 pub enum BlockElement<'a> {
676 Raw(Raw<'a>),
677 Tag(Tag<'a>),
678 Expression(Exp<'a>),
679 Invalid(InvalidLiquidToken<'a>),
680 }
681 impl<'a> From<Pair<'a>> for BlockElement<'a> {
from(element: Pair<'a>) -> Self682 fn from(element: Pair<'a>) -> Self {
683 match element.as_rule() {
684 Rule::Raw => BlockElement::Raw(element.into()),
685 Rule::Tag => BlockElement::Tag(element.into()),
686 Rule::Expression => BlockElement::Expression(element.into()),
687 Rule::InvalidLiquid => BlockElement::Invalid(element.into()),
688 _ => panic!(
689 "Only rules Raw | Tag | Expression can be converted to BlockElement. Found {:?}",
690 element.as_rule()
691 ),
692 }
693 }
694 }
695
696 impl<'a> BlockElement<'a> {
697 /// Parses the element in the block just as if it weren't inside any block.
parse( self, block: &mut TagBlock<'a, '_>, options: &Language, ) -> Result<Box<dyn Renderable>>698 pub fn parse(
699 self,
700 block: &mut TagBlock<'a, '_>,
701 options: &Language,
702 ) -> Result<Box<dyn Renderable>> {
703 match self {
704 BlockElement::Raw(raw) => Ok(raw.into_renderable()),
705 BlockElement::Tag(tag) => tag.parse(block, options),
706 BlockElement::Expression(exp) => exp.parse(options),
707 BlockElement::Invalid(invalid) => invalid.parse(block),
708 }
709 }
710
711 /// The same as `parse`, but directly takes an iterator over `Pair`s instead of a TagBlock.
parse_pair( self, next_elements: &mut dyn Iterator<Item = Pair>, options: &Language, ) -> Result<Box<dyn Renderable>>712 fn parse_pair(
713 self,
714 next_elements: &mut dyn Iterator<Item = Pair>,
715 options: &Language,
716 ) -> Result<Box<dyn Renderable>> {
717 match self {
718 BlockElement::Raw(raw) => Ok(raw.into_renderable()),
719 BlockElement::Tag(tag) => tag.parse_pair(next_elements, options),
720 BlockElement::Expression(exp) => exp.parse(options),
721 BlockElement::Invalid(invalid) => invalid.parse_pair(next_elements),
722 }
723 }
724
725 /// Returns the element as a str.
as_str(&self) -> &str726 pub fn as_str(&self) -> &str {
727 match self {
728 BlockElement::Raw(raw) => raw.as_str(),
729 BlockElement::Tag(tag) => tag.as_str(),
730 BlockElement::Expression(exp) => exp.as_str(),
731 BlockElement::Invalid(invalid) => invalid.as_str(),
732 }
733 }
734 }
735
736 /// An iterator over `TagToken`s that is aware of their position in the file.
737 ///
738 /// The awareness of the position allows more precise error messages.
739 pub struct TagTokenIter<'a> {
740 iter: Box<dyn Iterator<Item = TagToken<'a>> + 'a>,
741 position: ::pest::Position<'a>,
742 }
743 impl<'a> Iterator for TagTokenIter<'a> {
744 type Item = TagToken<'a>;
next(&mut self) -> Option<Self::Item>745 fn next(&mut self) -> Option<Self::Item> {
746 self.iter.next().map(|next| {
747 self.position = next.token.as_span().end_pos();
748 next
749 })
750 }
751 }
752 impl<'a> TagTokenIter<'a> {
new(name: &Pair<'a>, tokens: Pairs<'a>) -> Self753 fn new(name: &Pair<'a>, tokens: Pairs<'a>) -> Self {
754 TagTokenIter {
755 iter: Box::new(tokens.map(TagToken::from)),
756 position: name.as_span().end_pos(),
757 }
758 }
759
760 /// Creates an error with the given message pointing at the current
761 /// position of the iterator.
raise_error(&mut self, error_msg: &str) -> Error762 pub fn raise_error(&mut self, error_msg: &str) -> Error {
763 let pest_error = ::pest::error::Error::new_from_pos(
764 ::pest::error::ErrorVariant::CustomError {
765 message: error_msg.to_string(),
766 },
767 self.position.clone(),
768 );
769 convert_pest_error(pest_error)
770 }
771
772 /// Returns the next tag token or raises an error if there is none.
expect_next(&mut self, error_msg: &str) -> Result<TagToken<'a>>773 pub fn expect_next(&mut self, error_msg: &str) -> Result<TagToken<'a>> {
774 self.next().ok_or_else(|| self.raise_error(error_msg))
775 }
776
777 /// Returns `Ok` if the iterator is empty, an error otherwise
expect_nothing(&mut self) -> Result<()>778 pub fn expect_nothing(&mut self) -> Result<()> {
779 if let Some(token) = self.next() {
780 Err(token.raise_error())
781 } else {
782 Ok(())
783 }
784 }
785 }
786
787 /// The result of calling `TagToken`'s `try`.
788 ///
789 /// If the token is successfuly matched, the match is returned;
790 /// otherwise, the TagToken is returned back.
791 pub enum TryMatchToken<'a, T> {
792 Matches(T),
793 Fails(TagToken<'a>),
794 }
795
796 impl<'a, T> TryMatchToken<'a, T> {
into_result(self) -> Result<T>797 pub fn into_result(self) -> Result<T> {
798 match self {
799 TryMatchToken::Matches(t) => Ok(t),
800 TryMatchToken::Fails(t) => Err(t.raise_error()),
801 }
802 }
803
into_result_custom_msg(self, msg: &str) -> Result<T>804 pub fn into_result_custom_msg(self, msg: &str) -> Result<T> {
805 match self {
806 TryMatchToken::Matches(t) => Ok(t),
807 TryMatchToken::Fails(t) => Err(t.raise_custom_error(msg)),
808 }
809 }
810 }
811
812 /// An interface to access tokens inside a tag.
813 pub struct TagToken<'a> {
814 token: Pair<'a>,
815 expected: Vec<Rule>,
816 }
817
818 impl<'a> From<Pair<'a>> for TagToken<'a> {
from(token: Pair<'a>) -> Self819 fn from(token: Pair<'a>) -> Self {
820 TagToken {
821 token,
822 expected: Vec::new(),
823 }
824 }
825 }
826
827 impl<'a> TagToken<'a> {
828 /// Raises an error from this TagToken.
829 ///
830 /// The error message will be based on the expected tokens,
831 /// which this structure tracks when using the methods starting
832 /// with 'expect'.
833 ///
834 /// For example, if one calls `expect_value` and that function fails
835 /// to give an `Ok` value, calling this would show `Expected Value`
836 /// on the error message.
raise_error(self) -> Error837 pub fn raise_error(self) -> Error {
838 let pest_error = ::pest::error::Error::new_from_span(
839 ::pest::error::ErrorVariant::ParsingError {
840 positives: self.expected,
841 negatives: vec![self.token.as_rule()],
842 },
843 self.token.as_span(),
844 );
845 convert_pest_error(pest_error)
846 }
847
848 /// Raises an error from this TagToken.
849 ///
850 /// The error will have the given error message.
raise_custom_error(self, msg: &str) -> Error851 pub fn raise_custom_error(self, msg: &str) -> Error {
852 let pest_error = ::pest::error::Error::new_from_span(
853 ::pest::error::ErrorVariant::CustomError {
854 message: msg.to_string(),
855 },
856 self.token.as_span(),
857 );
858 convert_pest_error(pest_error)
859 }
860
unwrap_filter_chain(&mut self) -> std::result::Result<Pair<'a>, ()>861 fn unwrap_filter_chain(&mut self) -> std::result::Result<Pair<'a>, ()> {
862 let token = self.token.clone();
863
864 if token.as_rule() != Rule::FilterChain {
865 return Err(());
866 }
867
868 Ok(token)
869 }
870
unwrap_value(&mut self) -> std::result::Result<Pair<'a>, ()>871 fn unwrap_value(&mut self) -> std::result::Result<Pair<'a>, ()> {
872 let filterchain = self.unwrap_filter_chain()?;
873
874 let mut chain = filterchain.into_inner();
875 let value = chain.next().expect("Unwrapping value out of Filterchain.");
876 if chain.next().is_some() {
877 // There are filters: it can't be a value
878 return Err(());
879 }
880
881 Ok(value)
882 }
883
unwrap_variable(&mut self) -> std::result::Result<Pair<'a>, ()>884 fn unwrap_variable(&mut self) -> std::result::Result<Pair<'a>, ()> {
885 let value = self.unwrap_value()?;
886
887 let variable = value
888 .into_inner()
889 .next()
890 .expect("A value is made of one token.");
891
892 if variable.as_rule() != Rule::Variable {
893 return Err(());
894 }
895
896 Ok(variable)
897 }
898
unwrap_identifier(&mut self) -> std::result::Result<Pair<'a>, ()>899 fn unwrap_identifier(&mut self) -> std::result::Result<Pair<'a>, ()> {
900 let variable = self.unwrap_variable()?;
901
902 let mut indexes = variable.into_inner();
903 let identifier = indexes
904 .next()
905 .expect("Unwrapping identifier out of variable.");
906 if indexes.next().is_some() {
907 // There are indexes: it can't be a value
908 return Err(());
909 }
910
911 Ok(identifier)
912 }
913
unwrap_literal(&mut self) -> std::result::Result<Pair<'a>, ()>914 fn unwrap_literal(&mut self) -> std::result::Result<Pair<'a>, ()> {
915 let value = self.unwrap_value()?;
916
917 let literal = value
918 .into_inner()
919 .next()
920 .expect("A value is made of one token.");
921
922 if literal.as_rule() != Rule::Literal {
923 return Err(());
924 }
925
926 Ok(literal)
927 }
928
929 /// Tries to obtain a `FilterChain` from this token.
expect_filter_chain(mut self, options: &Language) -> TryMatchToken<'a, FilterChain>930 pub fn expect_filter_chain(mut self, options: &Language) -> TryMatchToken<'a, FilterChain> {
931 match self.expect_filter_chain_err(options) {
932 Ok(t) => TryMatchToken::Matches(t),
933 Err(_) => {
934 self.expected.push(Rule::FilterChain);
935 TryMatchToken::Fails(self)
936 }
937 }
938 }
939
expect_filter_chain_err(&mut self, options: &Language) -> Result<FilterChain>940 fn expect_filter_chain_err(&mut self, options: &Language) -> Result<FilterChain> {
941 let t = self
942 .unwrap_filter_chain()
943 .map_err(|_| Error::with_msg("failed to parse"))?;
944 let f = parse_filter_chain(t, options)?;
945 Ok(f)
946 }
947
948 /// Tries to obtain a value from this token.
949 ///
950 /// Do not confuse this value with `liquid-value`'s `Value`.
951 /// In this runtime, value refers to either a literal value or a variable.
expect_value(mut self) -> TryMatchToken<'a, Expression>952 pub fn expect_value(mut self) -> TryMatchToken<'a, Expression> {
953 match self.unwrap_value() {
954 Ok(t) => TryMatchToken::Matches(parse_value(t)),
955 Err(_) => {
956 self.expected.push(Rule::Value);
957 TryMatchToken::Fails(self)
958 }
959 }
960 }
961
962 /// Tries to obtain a `Variable` from this token.
expect_variable(mut self) -> TryMatchToken<'a, Variable>963 pub fn expect_variable(mut self) -> TryMatchToken<'a, Variable> {
964 match self.unwrap_variable() {
965 Ok(t) => TryMatchToken::Matches(parse_variable(t)),
966 Err(_) => {
967 self.expected.push(Rule::Variable);
968 TryMatchToken::Fails(self)
969 }
970 }
971 }
972
973 /// Tries to obtain an identifier from this token.
974 ///
975 /// The identifier is returned as a str.
expect_identifier(mut self) -> TryMatchToken<'a, &'a str>976 pub fn expect_identifier(mut self) -> TryMatchToken<'a, &'a str> {
977 match self.unwrap_identifier() {
978 Ok(t) => TryMatchToken::Matches(t.as_str()),
979 Err(_) => {
980 self.expected.push(Rule::Identifier);
981 TryMatchToken::Fails(self)
982 }
983 }
984 }
985
986 /// Tries to obtain a literal value from this token.
987 ///
988 /// The value is returned as a `Value`.
expect_literal(mut self) -> TryMatchToken<'a, Value>989 pub fn expect_literal(mut self) -> TryMatchToken<'a, Value> {
990 match self.unwrap_literal() {
991 Ok(t) => TryMatchToken::Matches(parse_literal(t)),
992 Err(_) => {
993 self.expected.push(Rule::Literal);
994 TryMatchToken::Fails(self)
995 }
996 }
997 }
998 /// Tries to obtain a range from this token.
999 ///
1000 /// The range is returned as a pair `(Expression, Expression)`.
expect_range(mut self) -> TryMatchToken<'a, (Expression, Expression)>1001 pub fn expect_range(mut self) -> TryMatchToken<'a, (Expression, Expression)> {
1002 let token = self.token.clone();
1003
1004 if token.as_rule() != Rule::Range {
1005 self.expected.push(Rule::Range);
1006 return TryMatchToken::Fails(self);
1007 }
1008
1009 let mut range = token.into_inner();
1010 TryMatchToken::Matches((
1011 parse_value(range.next().expect("start")),
1012 parse_value(range.next().expect("end")),
1013 ))
1014 }
1015
1016 /// Returns `Ok` if and only if the tokens' str is equal to the given str.
expect_str(self, expected: &str) -> TryMatchToken<'a, ()>1017 pub fn expect_str(self, expected: &str) -> TryMatchToken<'a, ()> {
1018 if self.as_str() == expected {
1019 TryMatchToken::Matches(())
1020 } else {
1021 // TODO change `self`'s state to be aware that `expected` was expected.
1022 TryMatchToken::Fails(self)
1023 }
1024 }
1025
1026 /// Returns token as a str.
as_str(&self) -> &str1027 pub fn as_str(&self) -> &str {
1028 self.token.as_str().trim()
1029 }
1030 }
1031
1032 #[cfg(test)]
1033 mod test {
1034 use super::*;
1035 use crate::runtime::{Runtime, RuntimeBuilder, Template};
1036
1037 #[test]
test_parse_literal()1038 fn test_parse_literal() {
1039 let nil = LiquidParser::parse(Rule::Literal, "nil")
1040 .unwrap()
1041 .next()
1042 .unwrap();
1043 assert_eq!(parse_literal(nil), Value::Nil);
1044 let nil = LiquidParser::parse(Rule::Literal, "null")
1045 .unwrap()
1046 .next()
1047 .unwrap();
1048 assert_eq!(parse_literal(nil), Value::Nil);
1049
1050 let blank = LiquidParser::parse(Rule::Literal, "blank")
1051 .unwrap()
1052 .next()
1053 .unwrap();
1054 assert_eq!(
1055 parse_literal(blank),
1056 Value::State(crate::model::State::Blank)
1057 );
1058
1059 let empty = LiquidParser::parse(Rule::Literal, "empty")
1060 .unwrap()
1061 .next()
1062 .unwrap();
1063 assert_eq!(
1064 parse_literal(empty),
1065 Value::State(crate::model::State::Empty)
1066 );
1067
1068 let integer = LiquidParser::parse(Rule::Literal, "42")
1069 .unwrap()
1070 .next()
1071 .unwrap();
1072 assert_eq!(parse_literal(integer), Value::scalar(42));
1073
1074 let negative_int = LiquidParser::parse(Rule::Literal, "-42")
1075 .unwrap()
1076 .next()
1077 .unwrap();
1078 assert_eq!(parse_literal(negative_int), Value::scalar(-42));
1079
1080 let float = LiquidParser::parse(Rule::Literal, "4321.032")
1081 .unwrap()
1082 .next()
1083 .unwrap();
1084 assert_eq!(parse_literal(float), Value::scalar(4321.032));
1085
1086 let negative_float = LiquidParser::parse(Rule::Literal, "-4321.032")
1087 .unwrap()
1088 .next()
1089 .unwrap();
1090 assert_eq!(parse_literal(negative_float), Value::scalar(-4321.032));
1091
1092 let boolean = LiquidParser::parse(Rule::Literal, "true")
1093 .unwrap()
1094 .next()
1095 .unwrap();
1096 assert_eq!(parse_literal(boolean), Value::scalar(true));
1097
1098 let string_double_quotes = LiquidParser::parse(Rule::Literal, "\"Hello world!\"")
1099 .unwrap()
1100 .next()
1101 .unwrap();
1102 assert_eq!(
1103 parse_literal(string_double_quotes),
1104 Value::scalar("Hello world!")
1105 );
1106
1107 let string_single_quotes = LiquidParser::parse(Rule::Literal, "'Liquid'")
1108 .unwrap()
1109 .next()
1110 .unwrap();
1111 assert_eq!(parse_literal(string_single_quotes), Value::scalar("Liquid"));
1112 }
1113
1114 #[test]
test_parse_variable()1115 fn test_parse_variable() {
1116 let variable = LiquidParser::parse(Rule::Variable, "foo[0].bar.baz[foo.bar]")
1117 .unwrap()
1118 .next()
1119 .unwrap();
1120
1121 let indexes = vec![
1122 Expression::Literal(Value::scalar(0)),
1123 Expression::Literal(Value::scalar("bar")),
1124 Expression::Literal(Value::scalar("baz")),
1125 Expression::Variable(Variable::with_literal("foo").push_literal("bar")),
1126 ];
1127
1128 let mut expected = Variable::with_literal("foo");
1129 expected.extend(indexes);
1130
1131 assert_eq!(parse_variable(variable), expected);
1132 }
1133
1134 #[test]
test_whitespace_control()1135 fn test_whitespace_control() {
1136 let options = Language::default();
1137
1138 let runtime = RuntimeBuilder::new().build();
1139 runtime.set_global("exp".into(), Value::scalar(5));
1140
1141 let text = " \n {{ exp }} \n ";
1142 let template = parse(text, &options).map(Template::new).unwrap();
1143 let output = template.render(&runtime).unwrap();
1144
1145 assert_eq!(output, " \n 5 \n ");
1146
1147 let text = " \n {{- exp }} \n ";
1148 let template = parse(text, &options).map(Template::new).unwrap();
1149 let output = template.render(&runtime).unwrap();
1150
1151 assert_eq!(output, "5 \n ");
1152
1153 let text = " \n {{ exp -}} \n ";
1154 let template = parse(text, &options).map(Template::new).unwrap();
1155 let output = template.render(&runtime).unwrap();
1156
1157 assert_eq!(output, " \n 5");
1158
1159 let text = " \n {{- exp -}} \n ";
1160 let template = parse(text, &options).map(Template::new).unwrap();
1161 let output = template.render(&runtime).unwrap();
1162
1163 assert_eq!(output, "5");
1164 }
1165 }
1166