tinytemplate/
compiler.rs

1#![allow(deprecated)]
2
3/// The compiler module houses the code which parses and compiles templates. TinyTemplate implements
4/// a simple bytecode interpreter (see the [instruction] module for more details) to render templates.
5/// The [`TemplateCompiler`](struct.TemplateCompiler.html) struct is responsible for parsing the
6/// template strings and generating the appropriate bytecode instructions.
7use error::Error::*;
8use error::{get_offset, Error, Result};
9use instruction::{Instruction, Path, PathStep};
10
11/// The end point of a branch or goto instruction is not known.
12const UNKNOWN: usize = ::std::usize::MAX;
13
14/// The compiler keeps a stack of the open blocks so that it can ensure that blocks are closed in
15/// the right order. The Block type is a simple enumeration of the kinds of blocks that could be
16/// open. It may contain the instruction index corresponding to the start of the block.
17enum Block {
18    Branch(usize),
19    For(usize),
20    With,
21}
22
23/// List of the known @-keywords so that we can error if the user spells them wrong.
24static KNOWN_KEYWORDS: [&str; 4] = ["@index", "@first", "@last", "@root"];
25
26/// The TemplateCompiler struct is responsible for parsing a template string and generating bytecode
27/// instructions based on it. The parser is a simple hand-written pattern-matching parser with no
28/// recursion, which makes it relatively easy to read.
29pub(crate) struct TemplateCompiler<'template> {
30    original_text: &'template str,
31    remaining_text: &'template str,
32    instructions: Vec<Instruction<'template>>,
33    block_stack: Vec<(&'template str, Block)>,
34
35    /// When we see a `{foo -}` or similar, we need to remember to left-trim the next text block we
36    /// encounter.
37    trim_next: bool,
38}
39impl<'template> TemplateCompiler<'template> {
40    /// Create a new template compiler to parse and compile the given template.
41    pub fn new(text: &'template str) -> TemplateCompiler<'template> {
42        TemplateCompiler {
43            original_text: text,
44            remaining_text: text,
45            instructions: vec![],
46            block_stack: vec![],
47            trim_next: false,
48        }
49    }
50
51    /// Consume the template compiler to parse the template and return the generated bytecode.
52    pub fn compile(mut self) -> Result<Vec<Instruction<'template>>> {
53        while !self.remaining_text.is_empty() {
54            // Comment, denoted by {# comment text #}
55            if self.remaining_text.starts_with("{#") {
56                self.trim_next = false;
57
58                let tag = self.consume_tag("#}")?;
59                let comment = tag[2..(tag.len() - 2)].trim();
60                if comment.starts_with('-') {
61                    self.trim_last_whitespace();
62                }
63                if comment.ends_with('-') {
64                    self.trim_next_whitespace();
65                }
66            // Block tag. Block tags are wrapped in {{ }} and always have one word at the start
67            // to identify which kind of tag it is. Depending on the tag type there may be more.
68            } else if self.remaining_text.starts_with("{{") {
69                self.trim_next = false;
70
71                let (discriminant, rest) = self.consume_block()?;
72                match discriminant {
73                    "if" => {
74                        let (path, negated) = if rest.starts_with("not") {
75                            (self.parse_path(&rest[4..])?, true)
76                        } else {
77                            (self.parse_path(rest)?, false)
78                        };
79                        self.block_stack
80                            .push((discriminant, Block::Branch(self.instructions.len())));
81                        self.instructions
82                            .push(Instruction::Branch(path, !negated, UNKNOWN));
83                    }
84                    "else" => {
85                        self.expect_empty(rest)?;
86                        let num_instructions = self.instructions.len() + 1;
87                        self.close_branch(num_instructions, discriminant)?;
88                        self.block_stack
89                            .push((discriminant, Block::Branch(self.instructions.len())));
90                        self.instructions.push(Instruction::Goto(UNKNOWN))
91                    }
92                    "endif" => {
93                        self.expect_empty(rest)?;
94                        let num_instructions = self.instructions.len();
95                        self.close_branch(num_instructions, discriminant)?;
96                    }
97                    "with" => {
98                        let (path, name) = self.parse_with(rest)?;
99                        let instruction = Instruction::PushNamedContext(path, name);
100                        self.instructions.push(instruction);
101                        self.block_stack.push((discriminant, Block::With));
102                    }
103                    "endwith" => {
104                        self.expect_empty(rest)?;
105                        if let Some((_, Block::With)) = self.block_stack.pop() {
106                            self.instructions.push(Instruction::PopContext)
107                        } else {
108                            return Err(self.parse_error(
109                                discriminant,
110                                "Found a closing endwith that doesn't match with a preceeding with.".to_string()
111                            ));
112                        }
113                    }
114                    "for" => {
115                        let (path, name) = self.parse_for(rest)?;
116                        self.instructions
117                            .push(Instruction::PushIterationContext(path, name));
118                        self.block_stack
119                            .push((discriminant, Block::For(self.instructions.len())));
120                        self.instructions.push(Instruction::Iterate(UNKNOWN));
121                    }
122                    "endfor" => {
123                        self.expect_empty(rest)?;
124                        let num_instructions = self.instructions.len() + 1;
125                        let goto_target = self.close_for(num_instructions, discriminant)?;
126                        self.instructions.push(Instruction::Goto(goto_target));
127                        self.instructions.push(Instruction::PopContext);
128                    }
129                    "call" => {
130                        let (name, path) = self.parse_call(rest)?;
131                        self.instructions.push(Instruction::Call(name, path));
132                    }
133                    _ => {
134                        return Err(self.parse_error(
135                            discriminant,
136                            format!("Unknown block type '{}'", discriminant),
137                        ));
138                    }
139                }
140            // Values, of the form { dotted.path.to.value.in.context }
141            // Note that it is not (currently) possible to escape curly braces in the templates to
142            // prevent them from being interpreted as values.
143            } else if self.remaining_text.starts_with('{') {
144                self.trim_next = false;
145
146                let (path, name) = self.consume_value()?;
147                let instruction = match name {
148                    Some(name) => Instruction::FormattedValue(path, name),
149                    None => Instruction::Value(path),
150                };
151                self.instructions.push(instruction);
152            // All other text - just consume characters until we see a {
153            } else {
154                let mut escaped = false;
155                loop {
156                    let mut text = self.consume_text(escaped);
157                    if self.trim_next {
158                        text = text.trim_left();
159                        self.trim_next = false;
160                    }
161                    escaped = text.ends_with('\\');
162                    if escaped {
163                        text = &text[0..(text.len() - 1)];
164                    }
165                    self.instructions.push(Instruction::Literal(text));
166
167                    if !escaped {
168                        break;
169                    }
170                }
171            }
172        }
173
174        if let Some((text, _)) = self.block_stack.pop() {
175            return Err(self.parse_error(
176                text,
177                "Expected block-closing tag, but reached the end of input.".to_string(),
178            ));
179        }
180
181        Ok(self.instructions)
182    }
183
184    /// Splits a string into a list of named segments which can later be used to look up values in the
185    /// context.
186    fn parse_path(&self, text: &'template str) -> Result<Path<'template>> {
187        if !text.starts_with('@') {
188            Ok(text
189                .split('.')
190                .map(|s| match s.parse::<usize>() {
191                    Ok(n) => PathStep::Index(s, n),
192                    Err(_) => PathStep::Name(s),
193                })
194                .collect::<Vec<_>>())
195        } else if KNOWN_KEYWORDS.iter().any(|k| *k == text) {
196            Ok(vec![PathStep::Name(text)])
197        } else {
198            Err(self.parse_error(text, format!("Invalid keyword name '{}'", text)))
199        }
200    }
201
202    /// Finds the line number and column where an error occurred. Location is the substring of
203    /// self.original_text where the error was found, and msg is the error message.
204    fn parse_error(&self, location: &str, msg: String) -> Error {
205        let (line, column) = get_offset(self.original_text, location);
206        ParseError { msg, line, column }
207    }
208
209    /// Tags which should have no text after the discriminant use this to raise an error if
210    /// text is found.
211    fn expect_empty(&self, text: &str) -> Result<()> {
212        if text.is_empty() {
213            Ok(())
214        } else {
215            Err(self.parse_error(text, format!("Unexpected text '{}'", text)))
216        }
217    }
218
219    /// Close the branch that is on top of the block stack by setting its target instruction
220    /// and popping it from the stack. Returns an error if the top of the block stack is not a
221    /// branch.
222    fn close_branch(&mut self, new_target: usize, discriminant: &str) -> Result<()> {
223        let branch_block = self.block_stack.pop();
224        if let Some((_, Block::Branch(index))) = branch_block {
225            match &mut self.instructions[index] {
226                Instruction::Branch(_, _, target) => {
227                    *target = new_target;
228                    Ok(())
229                }
230                Instruction::Goto(target) => {
231                    *target = new_target;
232                    Ok(())
233                }
234                _ => panic!(),
235            }
236        } else {
237            Err(self.parse_error(
238                discriminant,
239                "Found a closing endif or else which doesn't match with a preceding if."
240                    .to_string(),
241            ))
242        }
243    }
244
245    /// Close the for loop that is on top of the block stack by setting its target instruction and
246    /// popping it from the stack. Returns an error if the top of the stack is not a for loop.
247    /// Returns the index of the loop's Iterate instruction for further processing.
248    fn close_for(&mut self, new_target: usize, discriminant: &str) -> Result<usize> {
249        let branch_block = self.block_stack.pop();
250        if let Some((_, Block::For(index))) = branch_block {
251            match &mut self.instructions[index] {
252                Instruction::Iterate(target) => {
253                    *target = new_target;
254                    Ok(index)
255                }
256                _ => panic!(),
257            }
258        } else {
259            Err(self.parse_error(
260                discriminant,
261                "Found a closing endfor which doesn't match with a preceding for.".to_string(),
262            ))
263        }
264    }
265
266    /// Advance the cursor to the next { and return the consumed text. If `escaped` is true, skips
267    /// a { at the start of the text.
268    fn consume_text(&mut self, escaped: bool) -> &'template str {
269        let search_substr = if escaped {
270            &self.remaining_text[1..]
271        } else {
272            self.remaining_text
273        };
274
275        let mut position = search_substr
276            .find('{')
277            .unwrap_or_else(|| search_substr.len());
278        if escaped {
279            position += 1;
280        }
281
282        let (text, remaining) = self.remaining_text.split_at(position);
283        self.remaining_text = remaining;
284        text
285    }
286
287    /// Advance the cursor to the end of the value tag and return the value's path and optional
288    /// formatter name.
289    fn consume_value(&mut self) -> Result<(Path<'template>, Option<&'template str>)> {
290        let tag = self.consume_tag("}")?;
291        let mut tag = tag[1..(tag.len() - 1)].trim();
292        if tag.starts_with('-') {
293            tag = tag[1..].trim();
294            self.trim_last_whitespace();
295        }
296        if tag.ends_with('-') {
297            tag = tag[0..tag.len() - 1].trim();
298            self.trim_next_whitespace();
299        }
300
301        if let Some(index) = tag.find('|') {
302            let (path_str, name_str) = tag.split_at(index);
303            let name = name_str[1..].trim();
304            let path = self.parse_path(path_str.trim())?;
305            Ok((path, Some(name)))
306        } else {
307            Ok((self.parse_path(tag)?, None))
308        }
309    }
310
311    /// Right-trim whitespace from the last text block we parsed.
312    fn trim_last_whitespace(&mut self) {
313        if let Some(Instruction::Literal(text)) = self.instructions.last_mut() {
314            *text = text.trim_right();
315        }
316    }
317
318    /// Make a note to left-trim whitespace from the next text block we parse.
319    fn trim_next_whitespace(&mut self) {
320        self.trim_next = true;
321    }
322
323    /// Advance the cursor to the end of the current block tag and return the discriminant substring
324    /// and the rest of the text in the tag. Also handles trimming whitespace where needed.
325    fn consume_block(&mut self) -> Result<(&'template str, &'template str)> {
326        let tag = self.consume_tag("}}")?;
327        let mut block = tag[2..(tag.len() - 2)].trim();
328        if block.starts_with('-') {
329            block = block[1..].trim();
330            self.trim_last_whitespace();
331        }
332        if block.ends_with('-') {
333            block = block[0..block.len() - 1].trim();
334            self.trim_next_whitespace();
335        }
336        let discriminant = block.split_whitespace().next().unwrap_or(block);
337        let rest = block[discriminant.len()..].trim();
338        Ok((discriminant, rest))
339    }
340
341    /// Advance the cursor to after the given expected_close string and return the text in between
342    /// (including the expected_close characters), or return an error message if we reach the end
343    /// of a line of text without finding it.
344    fn consume_tag(&mut self, expected_close: &str) -> Result<&'template str> {
345        if let Some(line) = self.remaining_text.lines().next() {
346            if let Some(pos) = line.find(expected_close) {
347                let (tag, remaining) = self.remaining_text.split_at(pos + expected_close.len());
348                self.remaining_text = remaining;
349                Ok(tag)
350            } else {
351                Err(self.parse_error(
352                    line,
353                    format!(
354                        "Expected a closing '{}' but found end-of-line instead.",
355                        expected_close
356                    ),
357                ))
358            }
359        } else {
360            Err(self.parse_error(
361                self.remaining_text,
362                format!(
363                    "Expected a closing '{}' but found end-of-text instead.",
364                    expected_close
365                ),
366            ))
367        }
368    }
369
370    /// Parse a with tag to separate the value path from the (optional) name.
371    fn parse_with(&self, with_text: &'template str) -> Result<(Path<'template>, &'template str)> {
372        if let Some(index) = with_text.find(" as ") {
373            let (path_str, name_str) = with_text.split_at(index);
374            let path = self.parse_path(path_str.trim())?;
375            let name = name_str[" as ".len()..].trim();
376            Ok((path, name))
377        } else {
378            Err(self.parse_error(
379                with_text,
380                format!(
381                    "Expected 'as <path>' in with block, but found \"{}\" instead",
382                    with_text
383                ),
384            ))
385        }
386    }
387
388    /// Parse a for tag to separate the value path from the name.
389    fn parse_for(&self, for_text: &'template str) -> Result<(Path<'template>, &'template str)> {
390        if let Some(index) = for_text.find(" in ") {
391            let (name_str, path_str) = for_text.split_at(index);
392            let name = name_str.trim();
393            let path = self.parse_path(path_str[" in ".len()..].trim())?;
394            Ok((path, name))
395        } else {
396            Err(self.parse_error(
397                for_text,
398                format!("Unable to parse for block text '{}'", for_text),
399            ))
400        }
401    }
402
403    /// Parse a call tag to separate the template name and context value.
404    fn parse_call(&self, call_text: &'template str) -> Result<(&'template str, Path<'template>)> {
405        if let Some(index) = call_text.find(" with ") {
406            let (name_str, path_str) = call_text.split_at(index);
407            let name = name_str.trim();
408            let path = self.parse_path(path_str[" with ".len()..].trim())?;
409            Ok((name, path))
410        } else {
411            Err(self.parse_error(
412                call_text,
413                format!("Unable to parse call block text '{}'", call_text),
414            ))
415        }
416    }
417}
418
419#[cfg(test)]
420mod test {
421    use super::*;
422    use instruction::Instruction::*;
423
424    fn compile(text: &'static str) -> Result<Vec<Instruction<'static>>> {
425        TemplateCompiler::new(text).compile()
426    }
427
428    #[test]
429    fn test_compile_literal() {
430        let text = "Test String";
431        let instructions = compile(text).unwrap();
432        assert_eq!(1, instructions.len());
433        assert_eq!(&Literal(text), &instructions[0]);
434    }
435
436    #[test]
437    fn test_compile_value() {
438        let text = "{ foobar }";
439        let instructions = compile(text).unwrap();
440        assert_eq!(1, instructions.len());
441        assert_eq!(&Value(vec![PathStep::Name("foobar")]), &instructions[0]);
442    }
443
444    #[test]
445    fn test_compile_value_with_formatter() {
446        let text = "{ foobar | my_formatter }";
447        let instructions = compile(text).unwrap();
448        assert_eq!(1, instructions.len());
449        assert_eq!(
450            &FormattedValue(vec![PathStep::Name("foobar")], "my_formatter"),
451            &instructions[0]
452        );
453    }
454
455    #[test]
456    fn test_dotted_path() {
457        let text = "{ foo.bar }";
458        let instructions = compile(text).unwrap();
459        assert_eq!(1, instructions.len());
460        assert_eq!(
461            &Value(vec![PathStep::Name("foo"), PathStep::Name("bar")]),
462            &instructions[0]
463        );
464    }
465
466    #[test]
467    fn test_indexed_path() {
468        let text = "{ foo.0.bar }";
469        let instructions = compile(text).unwrap();
470        assert_eq!(1, instructions.len());
471        assert_eq!(
472            &Value(vec![
473                PathStep::Name("foo"),
474                PathStep::Index("0", 0),
475                PathStep::Name("bar")
476            ]),
477            &instructions[0]
478        );
479    }
480
481    #[test]
482    fn test_mixture() {
483        let text = "Hello { name }, how are you?";
484        let instructions = compile(text).unwrap();
485        assert_eq!(3, instructions.len());
486        assert_eq!(&Literal("Hello "), &instructions[0]);
487        assert_eq!(&Value(vec![PathStep::Name("name")]), &instructions[1]);
488        assert_eq!(&Literal(", how are you?"), &instructions[2]);
489    }
490
491    #[test]
492    fn test_if_endif() {
493        let text = "{{ if foo }}Hello!{{ endif }}";
494        let instructions = compile(text).unwrap();
495        assert_eq!(2, instructions.len());
496        assert_eq!(
497            &Branch(vec![PathStep::Name("foo")], true, 2),
498            &instructions[0]
499        );
500        assert_eq!(&Literal("Hello!"), &instructions[1]);
501    }
502
503    #[test]
504    fn test_if_not_endif() {
505        let text = "{{ if not foo }}Hello!{{ endif }}";
506        let instructions = compile(text).unwrap();
507        assert_eq!(2, instructions.len());
508        assert_eq!(
509            &Branch(vec![PathStep::Name("foo")], false, 2),
510            &instructions[0]
511        );
512        assert_eq!(&Literal("Hello!"), &instructions[1]);
513    }
514
515    #[test]
516    fn test_if_else_endif() {
517        let text = "{{ if foo }}Hello!{{ else }}Goodbye!{{ endif }}";
518        let instructions = compile(text).unwrap();
519        assert_eq!(4, instructions.len());
520        assert_eq!(
521            &Branch(vec![PathStep::Name("foo")], true, 3),
522            &instructions[0]
523        );
524        assert_eq!(&Literal("Hello!"), &instructions[1]);
525        assert_eq!(&Goto(4), &instructions[2]);
526        assert_eq!(&Literal("Goodbye!"), &instructions[3]);
527    }
528
529    #[test]
530    fn test_with() {
531        let text = "{{ with foo as bar }}Hello!{{ endwith }}";
532        let instructions = compile(text).unwrap();
533        assert_eq!(3, instructions.len());
534        assert_eq!(
535            &PushNamedContext(vec![PathStep::Name("foo")], "bar"),
536            &instructions[0]
537        );
538        assert_eq!(&Literal("Hello!"), &instructions[1]);
539        assert_eq!(&PopContext, &instructions[2]);
540    }
541
542    #[test]
543    fn test_foreach() {
544        let text = "{{ for foo in bar.baz }}{ foo }{{ endfor }}";
545        let instructions = compile(text).unwrap();
546        assert_eq!(5, instructions.len());
547        assert_eq!(
548            &PushIterationContext(vec![PathStep::Name("bar"), PathStep::Name("baz")], "foo"),
549            &instructions[0]
550        );
551        assert_eq!(&Iterate(4), &instructions[1]);
552        assert_eq!(&Value(vec![PathStep::Name("foo")]), &instructions[2]);
553        assert_eq!(&Goto(1), &instructions[3]);
554        assert_eq!(&PopContext, &instructions[4]);
555    }
556
557    #[test]
558    fn test_strip_whitespace_value() {
559        let text = "Hello,     {- name -}   , how are you?";
560        let instructions = compile(text).unwrap();
561        assert_eq!(3, instructions.len());
562        assert_eq!(&Literal("Hello,"), &instructions[0]);
563        assert_eq!(&Value(vec![PathStep::Name("name")]), &instructions[1]);
564        assert_eq!(&Literal(", how are you?"), &instructions[2]);
565    }
566
567    #[test]
568    fn test_strip_whitespace_block() {
569        let text = "Hello,     {{- if name -}}    {name}    {{- endif -}}   , how are you?";
570        let instructions = compile(text).unwrap();
571        assert_eq!(6, instructions.len());
572        assert_eq!(&Literal("Hello,"), &instructions[0]);
573        assert_eq!(
574            &Branch(vec![PathStep::Name("name")], true, 5),
575            &instructions[1]
576        );
577        assert_eq!(&Literal(""), &instructions[2]);
578        assert_eq!(&Value(vec![PathStep::Name("name")]), &instructions[3]);
579        assert_eq!(&Literal(""), &instructions[4]);
580        assert_eq!(&Literal(", how are you?"), &instructions[5]);
581    }
582
583    #[test]
584    fn test_comment() {
585        let text = "Hello, {# foo bar baz #} there!";
586        let instructions = compile(text).unwrap();
587        assert_eq!(2, instructions.len());
588        assert_eq!(&Literal("Hello, "), &instructions[0]);
589        assert_eq!(&Literal(" there!"), &instructions[1]);
590    }
591
592    #[test]
593    fn test_strip_whitespace_comment() {
594        let text = "Hello, \t\n    {#- foo bar baz -#} \t  there!";
595        let instructions = compile(text).unwrap();
596        assert_eq!(2, instructions.len());
597        assert_eq!(&Literal("Hello,"), &instructions[0]);
598        assert_eq!(&Literal("there!"), &instructions[1]);
599    }
600
601    #[test]
602    fn test_strip_whitespace_followed_by_another_tag() {
603        let text = "{value -}{value} Hello";
604        let instructions = compile(text).unwrap();
605        assert_eq!(3, instructions.len());
606        assert_eq!(&Value(vec![PathStep::Name("value")]), &instructions[0]);
607        assert_eq!(&Value(vec![PathStep::Name("value")]), &instructions[1]);
608        assert_eq!(&Literal(" Hello"), &instructions[2]);
609    }
610
611    #[test]
612    fn test_call() {
613        let text = "{{ call my_macro with foo.bar }}";
614        let instructions = compile(text).unwrap();
615        assert_eq!(1, instructions.len());
616        assert_eq!(
617            &Call(
618                "my_macro",
619                vec![PathStep::Name("foo"), PathStep::Name("bar")]
620            ),
621            &instructions[0]
622        );
623    }
624
625    #[test]
626    fn test_curly_brace_escaping() {
627        let text = "body \\{ \nfont-size: {fontsize} \n}";
628        let instructions = compile(text).unwrap();
629        assert_eq!(4, instructions.len());
630        assert_eq!(&Literal("body "), &instructions[0]);
631        assert_eq!(&Literal("{ \nfont-size: "), &instructions[1]);
632        assert_eq!(&Value(vec![PathStep::Name("fontsize")]), &instructions[2]);
633        assert_eq!(&Literal(" \n}"), &instructions[3]);
634    }
635
636    #[test]
637    fn test_unclosed_tags() {
638        let tags = vec![
639            "{",
640            "{ foo.bar",
641            "{ foo.bar\n }",
642            "{{",
643            "{{ if foo.bar",
644            "{{ if foo.bar \n}}",
645            "{#",
646            "{# if foo.bar",
647            "{# if foo.bar \n#}",
648        ];
649        for tag in tags {
650            compile(tag).unwrap_err();
651        }
652    }
653
654    #[test]
655    fn test_mismatched_blocks() {
656        let text = "{{ if foo }}{{ with bar }}{{ endif }} {{ endwith }}";
657        compile(text).unwrap_err();
658    }
659
660    #[test]
661    fn test_disallows_invalid_keywords() {
662        let text = "{ @foo }";
663        compile(text).unwrap_err();
664    }
665
666    #[test]
667    fn test_diallows_unknown_block_type() {
668        let text = "{{ foobar }}";
669        compile(text).unwrap_err();
670    }
671
672    #[test]
673    fn test_parse_error_line_column_num() {
674        let text = "\n\n\n{{ foobar }}";
675        let err = compile(text).unwrap_err();
676        if let ParseError { line, column, .. } = err {
677            assert_eq!(4, line);
678            assert_eq!(3, column);
679        } else {
680            panic!("Should have returned a parse error");
681        }
682    }
683
684    #[test]
685    fn test_parse_error_on_unclosed_if() {
686        let text = "{{ if foo }}";
687        compile(text).unwrap_err();
688    }
689
690    #[test]
691    fn test_parse_escaped_open_curly_brace() {
692        let text: &str = r"hello \{world}";
693        let instructions = compile(text).unwrap();
694        assert_eq!(2, instructions.len());
695        assert_eq!(&Literal("hello "), &instructions[0]);
696        assert_eq!(&Literal("{world}"), &instructions[1]);
697    }
698}