diff --git a/src/internal/execute.rs b/src/internal/execute.rs index 50ea99b..4c31904 100644 --- a/src/internal/execute.rs +++ b/src/internal/execute.rs @@ -6,7 +6,7 @@ use super::parse::{Ast, AstArena, AstNodeKind, For, Has, If, Path, Print}; pub fn execute(ast: &Ast, value: &dyn Value) -> String { let mut out = String::new(); - let nodes = ast.arena.deref_many(ast.root); + let nodes = ast.arena.deref_many(&ast.root); execute_(&mut out, nodes, value, &ast.arena); out @@ -76,7 +76,7 @@ pub fn for_(out: &mut String, f: &For, value: &dyn Value, arena: &AstArena) { let mut idx = 0; while let Some(value) = value.for_(idx) { - let nodes = arena.deref_many(f.body); + let nodes = arena.deref_many(&f.body); execute_(out, nodes, value, arena); idx += 1; } @@ -89,7 +89,7 @@ pub fn has(out: &mut String, f: &Has, value: &dyn Value, arena: &AstArena) { }; if let Some(value) = value.has() { - let nodes = arena.deref_many(f.body); + let nodes = arena.deref_many(&f.body); execute_(out, nodes, value, arena); } } @@ -101,10 +101,10 @@ pub fn if_(out: &mut String, f: &If, value: &dyn Value, arena: &AstArena) { }; if value.if_() { - let nodes = arena.deref_many(f.then); + let nodes = arena.deref_many(&f.then); execute_(out, nodes, value, arena); - } else if let Some(else_) = f.else_ { - let nodes = arena.deref_many(else_); + } else if let Some(else_) = f.else_.as_ref() { + let nodes = arena.deref_many(&else_); execute_(out, nodes, value, arena); } } diff --git a/src/internal/mod.rs b/src/internal/mod.rs index d62c0f9..671a3ea 100644 --- a/src/internal/mod.rs +++ b/src/internal/mod.rs @@ -4,3 +4,4 @@ pub mod execute; pub mod parse; +mod parse_backup; diff --git a/src/internal/parse.rs b/src/internal/parse.rs index a08932e..f31e2a8 100644 --- a/src/internal/parse.rs +++ b/src/internal/parse.rs @@ -1,50 +1,7 @@ use std::ops::ControlFlow; -#[derive(Debug, Eq, PartialEq)] -pub struct AstArena(Vec); -impl AstArena { - pub const fn new() -> Self { - Self(Vec::new()) - } +pub use super::parse_backup::*; - pub fn next_id(&self) -> AstNodeRef { - AstNodeRef( - self.0 - .len() - .try_into() - .expect("Tried to store too many AST nodes"), - ) - } - - pub fn add(&mut self, n: AstNodeKind) -> AstNodeRef { - let id = self.next_id(); - self.0.push(n); - - id - } - - pub fn add_many(&mut self, mut many: Vec) -> AstMany { - let start = self.next_id(); - let end = AstNodeRef( - (start.0 as usize + many.len()) - .try_into() - .expect("Tried to store too many AST nodes"), - ); - self.0.append(&mut many); - - AstMany { start, end } - } - - pub fn deref(&self, rf: AstNodeRef) -> &AstNodeKind { - &self.0[rf.0 as usize] - } - - pub fn deref_many(&self, rf: AstMany) -> &[AstNodeKind] { - &self.0[rf.start.0 as usize..rf.end.0 as usize] - } -} -#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] -pub struct AstNodeRef(u32); #[derive(Debug, PartialEq, Eq, Hash)] pub enum AstNodeKind { If(If), @@ -54,11 +11,6 @@ pub enum AstNodeKind { Print(Print), Text(String), } -#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] -pub struct AstMany { - start: AstNodeRef, - end: AstNodeRef, -} #[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Path(pub Vec); @@ -66,26 +18,26 @@ pub struct Path(pub Vec); #[derive(Debug, PartialEq, Eq, Hash)] pub struct If { pub path: Path, - pub then: AstMany, - pub else_: Option, + pub then: Vec, + pub else_: Option>, } #[derive(Debug, PartialEq, Eq, Hash)] pub struct For { pub path: Path, - pub body: AstMany, + pub body: Vec, } #[derive(Debug, PartialEq, Eq, Hash)] pub struct Has { pub path: Path, - pub body: AstMany, + pub body: Vec, } #[derive(Debug, PartialEq, Eq, Hash)] pub struct With { pub path: Path, - pub body: AstMany, + pub body: Vec, } #[derive(Debug, PartialEq, Eq, Hash)] @@ -93,464 +45,442 @@ pub struct Print { pub path: Path, } -pub struct Ast { - pub arena: AstArena, - pub root: AstMany, +pub fn parse(source: &str) -> Vec { + parse_many_until_eof(source).1 } -pub struct ParseCtx<'a> { - pub source: &'a str, - pub current_byte_offset: usize, - - pub arena: AstArena, -} -impl<'a> ParseCtx<'a> { - pub fn new(source: &'a str) -> Self { - Self { - source, - current_byte_offset: 0, - arena: AstArena::new(), - } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +struct ParseResult<'a, T>( + &'a str, // remaining + T, // output +); +impl<'a, T> ParseResult<'a, T> { + fn remaining(&self) -> &'a str { + self.0 } - pub fn loop_expecting_progress( - &mut self, - mut inner: impl FnMut(&mut Self) -> ControlFlow, - ) -> T { - loop { - let start = self.current_byte_offset; - let cf = inner(self); - match cf { - ControlFlow::Continue(_) => { - if self.current_byte_offset == start { - panic!("Parser didn't make progress"); - } - - continue; - }, - ControlFlow::Break(v) => break v, - } - } + fn output(&self) -> &T { + &self.1 } - pub fn at_eof(&self) -> bool { - self.current_byte_offset >= self.source.len() - } - - pub fn current(&self) -> char { - self.source[self.current_byte_offset..] - .chars() - .next() - .unwrap() - } - - pub fn remaining(&self) -> &str { - &self.source[self.current_byte_offset..] - } - - pub fn looking_at(&self, ch: char) -> bool { - !self.at_eof() && self.current() == ch - } - - pub fn forward(&mut self) { - self.current_byte_offset += self.current().len_utf8() - } - - pub fn forward_str(&mut self, str: &str) { - self.current_byte_offset += str.len() - } - - pub fn scan_until(&mut self, ch: char) { - self.loop_expecting_progress(|s| { - if s.at_eof() { - return ControlFlow::Break(()); - } - - let current = s.current(); - if current == ch { - return ControlFlow::Break(()); - } - s.current_byte_offset += current.len_utf8(); - - ControlFlow::Continue(()) - }) - } - - pub fn skip_whitespace(&mut self) { - self.loop_expecting_progress(|s| { - if s.at_eof() { - return ControlFlow::Break(()); - } - - let current = s.current(); - if !current.is_whitespace() { - return ControlFlow::Break(()); - } - s.current_byte_offset += current.len_utf8(); - - ControlFlow::Continue(()) - }) - } - - pub fn parse_until( - &mut self, - mut f: impl FnMut(&mut Self) -> bool, - ) -> Option { - let mut out = Vec::new(); - let missing_terminator = self.loop_expecting_progress(|s| { - if f(s) { - return ControlFlow::Break(false); - } - - if s.at_eof() { - return ControlFlow::Break(true); - } - - let (text, trailing) = s.parse_text_with_trailing(); - if let Some(text) = text { - out.push(AstNodeKind::Text(text)); - } - if let Some(block) = trailing { - out.push(block); - } - - ControlFlow::Continue(()) - }); - - if missing_terminator { - None - } else { - Some(self.arena.add_many(out)) - } - } - - pub fn parse_text_with_trailing( - &mut self, - ) -> (Option, Option) { - let text_starts_at = self.current_byte_offset; - - let (text, trailing) = self.loop_expecting_progress(|s| { - s.scan_until('{'); - if s.at_eof() { - return ControlFlow::Break((&s.source[text_starts_at..], None)); - } - - let text_maybe_ends_at = s.current_byte_offset; - if let Some(block) = s.parse_block() { - return ControlFlow::Break(( - &s.source[text_starts_at..text_maybe_ends_at], - Some(block), - )); - } - - ControlFlow::Continue(()) - }); - - let text = if text.is_empty() { - None - } else { - Some(text.to_string()) - }; - - (text, trailing) - } - - pub fn parse_block(&mut self) -> Option { - if !self.looking_at('{') { - return None; - } - self.forward(); - - self.skip_whitespace(); - let remaining = self.remaining(); - if remaining.starts_with(".") { - self.parse_print().map(AstNodeKind::Print) - } else if remaining.starts_with("for") { - self.parse_for().map(AstNodeKind::For) - } else if remaining.starts_with("has") { - self.parse_has().map(AstNodeKind::Has) - } else if remaining.starts_with("if") { - self.parse_if().map(AstNodeKind::If) - } else if remaining.starts_with("with") { - self.parse_with().map(AstNodeKind::With) - } else { - None - } - } - - pub fn parse_path(&mut self) -> Option { - let mut out = Vec::new(); - if !self.looking_at('.') { - return None; - } - self.forward(); - - self.loop_expecting_progress(|s| { - let start = s.current_byte_offset; - s.loop_expecting_progress(|s| { - if s.current().is_whitespace() - || s.current() == '}' - || s.current() == '.' - { - return ControlFlow::Break(()); - } - s.forward(); - - ControlFlow::Continue(()) - }); - let end = s.current_byte_offset; - let str = &s.source[start..end]; - if str.is_empty() { - return ControlFlow::Break(()); - } - out.push(str.to_string()); - - if !s.looking_at('.') { - return ControlFlow::Break(()); - } - s.forward(); - ControlFlow::Continue(()) - }); - - Some(Path(out)) - } - - pub fn parse_print(&mut self) -> Option { - let path = self.parse_path()?; - self.skip_whitespace(); - if !self.looking_at('}') { - return None; - } - self.forward(); - - Some(Print { path }) - } - - pub fn parse_if(&mut self) -> Option { - if !self.remaining().starts_with("if") { - return None; - } - self.forward_str("for"); - self.skip_whitespace(); - let path = self.parse_path()?; - self.skip_whitespace(); - if !self.looking_at('}') { - return None; - } - self.forward(); - - let then = self.parse_until(|s| { - s.remaining().starts_with("{/if}") - || s.remaining().starts_with("{else}") - })?; - if self.remaining().starts_with("{/if}") { - self.forward_str("{/if}"); - - return Some(If { - path, - then, - else_: None, - }); - } - self.forward_str("{else}"); - let else_ = self.parse_until(|s| s.remaining().starts_with("{/if}"))?; - self.forward_str("{/if}"); - - Some(If { - path, - then, - else_: Some(else_), - }) - } - - pub fn parse_for(&mut self) -> Option { - if !self.remaining().starts_with("for") { - return None; - } - self.forward_str("for"); - self.skip_whitespace(); - let path = self.parse_path()?; - self.skip_whitespace(); - if !self.looking_at('}') { - return None; - } - self.forward(); - - let many = self.parse_until(|s| s.remaining().starts_with("{/for}"))?; - self.forward_str("{/for}"); - - Some(For { path, body: many }) - } - - pub fn parse_with(&mut self) -> Option { - if !self.remaining().starts_with("with") { - return None; - } - self.forward_str("with"); - self.skip_whitespace(); - let path = self.parse_path()?; - self.skip_whitespace(); - if !self.looking_at('}') { - return None; - } - self.forward(); - - let many = - self.parse_until(|s| s.remaining().starts_with("{/with}"))?; - self.forward_str("{/with}"); - - Some(With { path, body: many }) - } - - pub fn parse_has(&mut self) -> Option { - if !self.remaining().starts_with("has") { - return None; - } - self.forward_str("has"); - self.skip_whitespace(); - let path = self.parse_path()?; - self.skip_whitespace(); - if !self.looking_at('}') { - return None; - } - self.forward(); - - let many = self.parse_until(|s| s.remaining().starts_with("{/has}"))?; - self.forward_str("{/has}"); - - Some(Has { path, body: many }) + fn map(self, f: impl FnOnce(T) -> U) -> ParseResult<'a, U> { + ParseResult(self.0, f(self.1)) } } -pub fn parse(source: &str) -> Ast { - let mut ctx = ParseCtx::new(source); - let root = ctx.parse_until(|ctx| ctx.at_eof()).unwrap(); - - Ast { - arena: ctx.arena, - root, +fn parse_block(rest: &str) -> ParseResult> { + let rest_ = match rest.strip_prefix('{') { + Some(v) => v, + None => return ParseResult(rest, None), + }; + let rest_ = rest_.trim_start(); + if rest_.starts_with("for ") { + parse_for(rest).map(|v| v.map(AstNodeKind::For)) + } else if rest_.starts_with("if ") { + parse_if(rest).map(|v| v.map(AstNodeKind::If)) + } else if rest_.starts_with("with ") { + parse_with(rest).map(|v| v.map(AstNodeKind::With)) + } else { + parse_print(rest).map(|v| v.map(AstNodeKind::Print)) } } -#[cfg(test)] -mod test { - use super::{ - parse, AstMany, AstNodeKind, AstNodeRef, For, Has, If, Path, Print, +#[test] +fn test_parse_block_print_simple() { + assert_eq!( + parse_block("{.}"), + ParseResult("", Some(AstNodeKind::Print(Print { path: Path(vec![]) }))) + ) +} + +#[test] +fn test_parse_block_print_simple_trailing() { + assert_eq!( + parse_block("{.} Text"), + ParseResult( + " Text", + Some(AstNodeKind::Print(Print { path: Path(vec![]) })) + ) + ) +} + +fn parse_print(rest: &str) -> ParseResult> { + let rest = match rest.strip_prefix('{') { + Some(r) => r, + None => return ParseResult(rest, None), + }; + let rest = rest.trim_start(); + + let ParseResult(rest, path) = parse_path(rest); + let path = match path { + Some(p) => p, + None => return ParseResult(rest, None), }; - fn go(input: &str, expected: Vec) { - assert_eq!(parse(input).arena.0, expected) - } - - #[test] - fn parse_text() { - go( - "Hello, world!", - vec![AstNodeKind::Text("Hello, world!".to_string())], - ) - } - - #[test] - fn parse_print() { - go( - "{ .test }", - vec![AstNodeKind::Print(Print { - path: Path(vec!["test".to_string()]), - })], - ); - } - - #[test] - fn parse_print_just_dot() { - go( - "{ . }", - vec![AstNodeKind::Print(Print { path: Path(vec![]) })], - ); - } - - #[test] - fn parse_print_nested_path() { - go( - "{ .foo.bar }", - vec![AstNodeKind::Print(Print { - path: Path(vec!["foo".to_string(), "bar".to_string()]), - })], - ); - } - - #[test] - fn parse_has() { - go( - "{ has .test }{.}{/has}", - vec![ - AstNodeKind::Print(Print { path: Path(vec![]) }), - AstNodeKind::Has(Has { - path: Path(vec!["test".to_string()]), - body: AstMany { - start: AstNodeRef(0), - end: AstNodeRef(1), - }, - }), - ], - ); - } - - #[test] - fn parse_for() { - go( - "{ for .test }{.}{/for}", - vec![ - AstNodeKind::Print(Print { path: Path(vec![]) }), - AstNodeKind::For(For { - path: Path(vec!["test".to_string()]), - body: AstMany { - start: AstNodeRef(0), - end: AstNodeRef(1), - }, - }), - ], - ); - } - - #[test] - fn parse_if() { - go( - "{ if .test }{.test}{/if}", - vec![ - AstNodeKind::Print(Print { - path: Path(vec!["test".to_string()]), - }), - AstNodeKind::If(If { - path: Path(vec!["test".to_string()]), - then: AstMany { - start: AstNodeRef(0), - end: AstNodeRef(1), - }, - else_: None, - }), - ], - ); - } - #[test] - fn parse_if_else() { - go( - "{ if .test }{.test}{else}Hello{/if}", - vec![ - AstNodeKind::Print(Print { - path: Path(vec!["test".to_string()]), - }), - AstNodeKind::If(If { - path: Path(vec!["test".to_string()]), - then: AstMany { - start: AstNodeRef(0), - end: AstNodeRef(1), - }, - else_: Some(AstMany { - start: AstNodeRef(0), - end: AstNodeRef(1), - }), - }), - ], - ); + let rest = rest.trim_start(); + if let Some(rest) = rest.strip_prefix('}') { + ParseResult(rest, Some(Print { path })) + } else { + ParseResult(rest, None) } } + +#[test] +fn test_parse_print_trailing() { + assert_eq!( + parse_print("{.} World"), + ParseResult(" World", Some(Print { path: Path(vec![]) })) + ) +} + +fn parse_for(rest: &str) -> ParseResult> { + let rest = match rest.strip_prefix('{') { + Some(r) => r, + None => return ParseResult(rest, None), + }; + let rest = rest.trim_start(); + + let rest = match rest.strip_prefix("for ") { + Some(r) => r, + None => return ParseResult(rest, None), + }; + + let ParseResult(rest, path) = parse_path(rest); + let path = match path { + Some(p) => p, + None => return ParseResult(rest, None), + }; + + let rest = rest.trim_start(); + if let Some(rest) = rest.strip_prefix('}') { + let ParseResult(rest, body) = + parse_many_until(rest, |s| s.starts_with("{/for}")); + let body = match body { + Some(b) => b, + None => return ParseResult(rest, None), + }; + let rest = rest.strip_prefix("{/for}").unwrap(); + + ParseResult(rest, Some(For { path, body })) + } else { + ParseResult(rest, None) + } +} + +#[test] +fn test_parse_for_basic() { + assert_eq!( + parse_for("{for .}{.}{/for}"), + ParseResult( + "", + Some(For { + path: Path(vec![]), + body: vec![AstNodeKind::Print(Print { path: Path(vec![]) })] + }) + ) + ) +} + +fn parse_with(rest: &str) -> ParseResult> { + let rest = match rest.strip_prefix('{') { + Some(r) => r, + None => return ParseResult(rest, None), + }; + let rest = rest.trim_start(); + + let rest = match rest.strip_prefix("with ") { + Some(r) => r, + None => return ParseResult(rest, None), + }; + + let ParseResult(rest, path) = parse_path(rest); + let path = match path { + Some(p) => p, + None => return ParseResult(rest, None), + }; + + let rest = rest.trim_start(); + if let Some(rest) = rest.strip_prefix('}') { + let ParseResult(rest, body) = + parse_many_until(rest, |s| s.starts_with("{/with}")); + let body = match body { + Some(b) => b, + None => return ParseResult(rest, None), + }; + let rest = rest.strip_prefix("{/with}").unwrap(); + + ParseResult(rest, Some(With { path, body })) + } else { + ParseResult(rest, None) + } +} + +#[test] +fn test_parse_with_basic() { + assert_eq!( + parse_with("{with .}{.}{/with}"), + ParseResult( + "", + Some(With { + path: Path(vec![]), + body: vec![AstNodeKind::Print(Print { path: Path(vec![]) })] + }) + ) + ) +} + +fn parse_if(rest: &str) -> ParseResult> { + let rest = match rest.strip_prefix('{') { + Some(r) => r, + None => return ParseResult(rest, None), + }; + let rest = rest.trim_start(); + + let rest = match rest.strip_prefix("if ") { + Some(r) => r, + None => return ParseResult(rest, None), + }; + + let ParseResult(rest, path) = parse_path(rest); + let path = match path { + Some(p) => p, + None => return ParseResult(rest, None), + }; + + let rest = rest.trim_start(); + if let Some(rest) = rest.strip_prefix('}') { + let ParseResult(rest, body) = parse_many_until(rest, |s| { + s.starts_with("{/if}") || s.starts_with("{else}") + }); + let then = match body { + Some(b) => b, + None => return ParseResult(rest, None), + }; + + let ParseResult(rest, else_) = + if let Some(rest) = rest.strip_prefix("{else}") { + let ParseResult(rest, else_) = + parse_many_until(rest, |s| s.starts_with("{/if}")); + let else_ = match else_ { + Some(b) => b, + None => return ParseResult(rest, None), + }; + let rest = rest.strip_prefix("{/if}").unwrap(); + + ParseResult(rest, Some(else_)) + } else { + let rest = rest.strip_prefix("{/if}").unwrap(); + + ParseResult(rest, None) + }; + + ParseResult(rest, Some(If { path, then, else_ })) + } else { + ParseResult(rest, None) + } +} + +#[test] +fn test_parse_if_basic() { + assert_eq!( + parse_if("{if .}{.}{/if}"), + ParseResult( + "", + Some(If { + path: Path(vec![]), + then: vec![AstNodeKind::Print(Print { path: Path(vec![]) })], + else_: None, + }), + ) + ) +} + +#[test] +fn test_parse_if_else_basic() { + assert_eq!( + parse_if("{if .}{.}{else}Else{/if}"), + ParseResult( + "", + Some(If { + path: Path(vec![]), + then: vec![AstNodeKind::Print(Print { path: Path(vec![]) })], + else_: Some(vec![AstNodeKind::Text("Else".to_string())]), + }), + ) + ) +} + +fn parse_path(rest: &str) -> ParseResult> { + if !rest.starts_with('.') { + return ParseResult(rest, None); + } + + let rest = &rest[1..]; + let terminator = rest + .find(|c: char| c.is_whitespace() || c == '}') + .unwrap_or(rest.len()); + + let (path, rest) = rest.split_at(terminator); + let path_parts = path + .split('.') + .filter(|s| !s.is_empty()) + .map(|s| s.to_string()) + .collect::>(); + + ParseResult(rest, Some(Path(path_parts))) +} + +#[test] +fn test_parse_path_deep() { + assert_eq!( + parse_path(".test.best"), + ParseResult( + "", + Some(Path(vec!["test".to_string(), "best".to_string()])) + ) + ) +} + +#[test] +fn test_parse_path_dot_only() { + assert_eq!(parse_path("."), ParseResult("", Some(Path(vec![])))) +} + +#[test] +fn test_parse_path_deep_trailing() { + assert_eq!( + parse_path(".test.best Rest"), + ParseResult( + " Rest", + Some(Path(vec!["test".to_string(), "best".to_string()])) + ) + ) +} + +#[test] +fn test_parse_print_simple() { + assert_eq!( + parse_block("{.}"), + ParseResult("", Some(AstNodeKind::Print(Print { path: Path(vec![]) }))) + ) +} + +fn parse_many_until_eof(rest: &str) -> ParseResult> { + parse_many_until(rest, |s| s.is_empty()).map(|v| v.unwrap()) +} + +#[test] +fn test_parse_many_until_eof_only_text() { + assert_eq!( + parse_many_until_eof("Hello "), + ParseResult("", vec![AstNodeKind::Text("Hello ".to_string()),]), + ) +} + +#[test] +fn test_parse_many_until_eof_text_then_print() { + assert_eq!( + parse_many_until_eof("Hello {.}"), + ParseResult( + "", + vec![ + AstNodeKind::Text("Hello ".to_string()), + AstNodeKind::Print(Print { path: Path(vec![]) }) + ] + ), + ) +} + +#[test] +fn test_parse_many_until_eof_text_then_print_then_text() { + assert_eq!( + parse_many_until_eof("Hello {.} world"), + ParseResult( + "", + vec![ + AstNodeKind::Text("Hello ".to_string()), + AstNodeKind::Print(Print { path: Path(vec![]) }), + AstNodeKind::Text(" world".to_string()), + ] + ), + ) +} + +fn parse_many_until( + rest: &str, + should_terminate: impl Fn(&str) -> bool, +) -> ParseResult>> { + let mut out = Vec::new(); + let mut text_start = rest; + let mut remaining = rest; + loop { + if should_terminate(remaining) { + break; + } + + if remaining == "" { + return ParseResult("", None); + } + + if remaining.starts_with('{') { + let text_end = text_start.len() - remaining.len(); + let ParseResult(rest, block) = parse_block(remaining); + if let Some(block) = block { + let text = &text_start[..text_end]; + if !text.is_empty() { + out.push(AstNodeKind::Text(text.to_string())); + } + out.push(block); + text_start = rest; + } + remaining = rest; + continue; + } + + remaining = &remaining[1..]; + } + let text_end = text_start.len() - remaining.len(); + let text = &text_start[..text_end]; + if !text.is_empty() { + out.push(AstNodeKind::Text(text.to_string())); + } + + ParseResult(remaining, Some(out)) +} + +#[test] +fn test_parse_many_until() { + assert_eq!( + parse_many_until("Hello {.} {/end}World", |rest| { + rest.starts_with("{/end}") + }), + ParseResult( + "{/end}World", + Some(vec![ + AstNodeKind::Text("Hello ".to_string()), + AstNodeKind::Print(Print { path: Path(vec![]) }), + AstNodeKind::Text(" ".to_string()), + ]) + ), + ) +} + +#[test] +fn test_parse_complex() { + assert_eq!( + parse("{for .}{if .}{with .}{.}{/with}{else}Yo{/if}{/for}"), + vec![AstNodeKind::For(For { + path: Path(vec![]), + body: vec![AstNodeKind::If(If { + path: Path(vec![]), + then: vec![AstNodeKind::With(With { + path: Path(vec![]), + body: vec![AstNodeKind::Print(Print { + path: Path(vec![]) + })] + })], + else_: Some(vec![AstNodeKind::Text("Yo".to_string())]) + })] + })] + ) +} diff --git a/src/lib.rs b/src/lib.rs index 829b229..4ef8da6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -333,7 +333,7 @@ pub struct Newt { impl Newt { pub fn build(tmpl: &str) -> Self { let ast = parse(tmpl); - Self { ast } + Self { ast: todo!() } } pub fn execute(&self, value: &dyn Value) -> String {