diff --git a/src/internal/execute.rs b/src/internal/execute.rs index 4c31904..59ae703 100644 --- a/src/internal/execute.rs +++ b/src/internal/execute.rs @@ -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.then); execute_(out, nodes, value, arena); } } diff --git a/src/internal/parse.rs b/src/internal/parse.rs index f31e2a8..fa7808d 100644 --- a/src/internal/parse.rs +++ b/src/internal/parse.rs @@ -31,7 +31,8 @@ pub struct For { #[derive(Debug, PartialEq, Eq, Hash)] pub struct Has { pub path: Path, - pub body: Vec, + pub then: Vec, + pub else_: Option>, } #[derive(Debug, PartialEq, Eq, Hash)] @@ -78,6 +79,8 @@ fn parse_block(rest: &str) -> ParseResult> { 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("has ") { + parse_has(rest).map(|v| v.map(AstNodeKind::Has)) } else if rest_.starts_with("with ") { parse_with(rest).map(|v| v.map(AstNodeKind::With)) } else { @@ -310,6 +313,57 @@ fn test_parse_if_else_basic() { ) } +fn parse_has(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("has ") { + 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("{/has}") || 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("{/has}")); + let else_ = match else_ { + Some(b) => b, + None => return ParseResult(rest, None), + }; + let rest = rest.strip_prefix("{/has}").unwrap(); + + ParseResult(rest, Some(else_)) + } else { + let rest = rest.strip_prefix("{/has}").unwrap(); + + ParseResult(rest, None) + }; + + ParseResult(rest, Some(Has { path, then, else_ })) + } else { + ParseResult(rest, None) + } +} + fn parse_path(rest: &str) -> ParseResult> { if !rest.starts_with('.') { return ParseResult(rest, None); @@ -468,7 +522,9 @@ fn test_parse_many_until() { #[test] fn test_parse_complex() { assert_eq!( - parse("{for .}{if .}{with .}{.}{/with}{else}Yo{/if}{/for}"), + parse( + "{for .}{if .}{with .}{.}{/with}{else}{has .}Yo{else}{/has}{/if}{/for}" + ), vec![AstNodeKind::For(For { path: Path(vec![]), body: vec![AstNodeKind::If(If { @@ -479,7 +535,11 @@ fn test_parse_complex() { path: Path(vec![]) })] })], - else_: Some(vec![AstNodeKind::Text("Yo".to_string())]) + else_: Some(vec![AstNodeKind::Has(Has { + path: Path(vec![]), + then: vec![AstNodeKind::Text("Yo".to_string())], + else_: Some(vec![]), + })]) })] })] ) diff --git a/src/internal/parse_backup.rs b/src/internal/parse_backup.rs new file mode 100644 index 0000000..c5da372 --- /dev/null +++ b/src/internal/parse_backup.rs @@ -0,0 +1,188 @@ +/* +#[cfg(test)] +mod test { + use super::{ + parse, AstMany, AstNodeKind, AstNodeRef, For, Has, If, Path, Print, + }; + + 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), + }), + }), + ], + ); + } +} +*/ + +use super::parse::AstNodeKind; + +#[derive(Debug, Eq, PartialEq)] +pub struct AstArena(Vec); +impl AstArena { + pub const fn new() -> Self { + Self(Vec::new()) + } + + 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] + } + */ + pub fn deref_many(&self, rf: &Vec) -> &[AstNodeKind] { + todo!() + } +} +#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +pub struct AstNodeRef(u32); + +#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +pub struct AstMany { + start: AstNodeRef, + end: AstNodeRef, +} + +pub(crate) struct Ast { + pub(crate) arena: AstArena, + pub(crate) root: Vec, +}