liquid_compiler/
parser.rs

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