From 5d014484a4a34146bc13a578e81ed0a0080d4697 Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Tue, 9 Jun 2026 14:28:36 -0700 Subject: Loops seem to work!? --- src/lexer.rs | 2 ++ src/lib.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/parser.rs | 11 ++++++++++- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/lexer.rs b/src/lexer.rs index 2e43914..7228866 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -19,6 +19,7 @@ pub enum SymbolType<'a> { View, Output, Show, + Loop, Equals, Period, Token(&'a str), @@ -244,6 +245,7 @@ impl<'a> Lexer<'a> { "view" => SymbolType::View, "show" => SymbolType::Show, "output" => SymbolType::Output, + "loop" => SymbolType::Loop, _ => SymbolType::Token(s) }, start_pos, end_pos )) } diff --git a/src/lib.rs b/src/lib.rs index a2bfc37..c900e39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,13 +59,14 @@ type Properties = HashMap; #[derive(Debug)] enum Token { + Loop(Identifier, Vec), Show(Identifier,Properties), Text(String), } #[derive(Debug,Clone)] pub enum Value { - Dictionary(HashMap), + Dictionary(Context), List(Vec), String(String), Int(i64), @@ -232,6 +233,16 @@ impl Crimtag { fn exec(input: &impl MappedStructure, tokens: &Vec, buf: &mut String) -> Result<(),ParseError> { for token in tokens { match token { + Token::Loop(ident,code) => { + if let Some(value) = input.get_value( &ident ) && + let Value::List(list) = value { + for rec in &list { + if let Value::Dictionary(d) = rec { + exec( d, code, buf )? + } + } + } + } Token::Show(ident,p) => { let format = if let Some(s) = p.get("format") { s @@ -333,4 +344,46 @@ Now with explicit outputs: } } } + + #[test] + fn loops() { + let mut ct = Crimtag::new(); + if let Err(e) = ct.load_static(r#"Looping code: +[|view "index"|>We will enumerate people here:[|loop people|> + - [|show last_name|], [|show first_name|]: [|show title|]<|loop|] +<|view|] +"#) { + println!("Error: {:?}", e ); + return; + } + + let ctx = Context::from([ + ("people".to_string(), crate::Value::List(vec![ + crate::Value::Dictionary(Context::from([ + ("first_name".to_string(), Value::String("Joe".to_string())), + ("last_name".to_string(), Value::String("Smith".to_string())), + ("title".to_string(), Value::String("CEO".to_string())), + ])), + crate::Value::Dictionary(Context::from([ + ("first_name".to_string(), Value::String("Chris".to_string())), + ("last_name".to_string(), Value::String("Perkens".to_string())), + ("title".to_string(), Value::String("Baconeer".to_string())), + ])), + crate::Value::Dictionary(Context::from([ + ("first_name".to_string(), Value::String("Will".to_string())), + ("last_name".to_string(), Value::String("Power".to_string())), + ("title".to_string(), Value::String("CFO".to_string())), + ])), + crate::Value::Dictionary(Context::from([ + ("first_name".to_string(), Value::String("Justin".to_string())), + ("last_name".to_string(), Value::String("Time".to_string())), + ("title".to_string(), Value::String("CIO".to_string())), + ])), + ])), + ]); + match ct.render("index", &ctx) { + Ok(s) => println!("View result: {}", s ), + Err(e) => println!("Error: {:?}", e ), + } + } } diff --git a/src/parser.rs b/src/parser.rs index 5f53557..6e479e4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -103,6 +103,7 @@ impl<'a> SymbolHelper for Option> { SymbolType::Token(_) | SymbolType::View | SymbolType::Output | + SymbolType::Loop | SymbolType::Show => true, _ => false }) @@ -327,7 +328,7 @@ impl Parser { if ctx.next().is_some() { match ctx.cur().unwrap().symbol() { - SymbolType::View | SymbolType::Show | + SymbolType::View | SymbolType::Show | SymbolType::Loop | SymbolType::Output => { let name_sym = ctx.cur().unwrap(); ctx.next(); @@ -640,6 +641,14 @@ impl<'a> TagBuilder<'a> { pub fn build(mut self) -> ParseResult { if let Some(sym) = self.name { match sym.symbol() { + SymbolType::Loop => { + let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { + id + } else { + return Err(ParseError::new(Position::none(), "Identifier for loop variable name.")); + }; + Ok(Token::Loop(id, self.children)) + } SymbolType::Show => { let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { id -- cgit v1.2.3