summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <mike@xagasoft.com>2026-06-09 14:28:36 -0700
committerMike Buland <mike@xagasoft.com>2026-06-09 14:28:36 -0700
commit5d014484a4a34146bc13a578e81ed0a0080d4697 (patch)
treee97a598b46260d1ee0849758b462fd29cd5abb32
parent61b54e8f6b177f33a1428ab6a3622503a63cc1d6 (diff)
downloadcrimtag-5d014484a4a34146bc13a578e81ed0a0080d4697.tar.gz
crimtag-5d014484a4a34146bc13a578e81ed0a0080d4697.tar.bz2
crimtag-5d014484a4a34146bc13a578e81ed0a0080d4697.tar.xz
crimtag-5d014484a4a34146bc13a578e81ed0a0080d4697.zip
Loops seem to work!?
-rw-r--r--src/lexer.rs2
-rw-r--r--src/lib.rs55
-rw-r--r--src/parser.rs11
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> {
19 View, 19 View,
20 Output, 20 Output,
21 Show, 21 Show,
22 Loop,
22 Equals, 23 Equals,
23 Period, 24 Period,
24 Token(&'a str), 25 Token(&'a str),
@@ -244,6 +245,7 @@ impl<'a> Lexer<'a> {
244 "view" => SymbolType::View, 245 "view" => SymbolType::View,
245 "show" => SymbolType::Show, 246 "show" => SymbolType::Show,
246 "output" => SymbolType::Output, 247 "output" => SymbolType::Output,
248 "loop" => SymbolType::Loop,
247 _ => SymbolType::Token(s) 249 _ => SymbolType::Token(s)
248 }, start_pos, end_pos )) 250 }, start_pos, end_pos ))
249 } 251 }
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<String, String>;
59 59
60#[derive(Debug)] 60#[derive(Debug)]
61enum Token { 61enum Token {
62 Loop(Identifier, Vec<Token>),
62 Show(Identifier,Properties), 63 Show(Identifier,Properties),
63 Text(String), 64 Text(String),
64} 65}
65 66
66#[derive(Debug,Clone)] 67#[derive(Debug,Clone)]
67pub enum Value { 68pub enum Value {
68 Dictionary(HashMap<String,Value>), 69 Dictionary(Context),
69 List(Vec<Value>), 70 List(Vec<Value>),
70 String(String), 71 String(String),
71 Int(i64), 72 Int(i64),
@@ -232,6 +233,16 @@ impl Crimtag {
232fn exec(input: &impl MappedStructure, tokens: &Vec<Token>, buf: &mut String) -> Result<(),ParseError> { 233fn exec(input: &impl MappedStructure, tokens: &Vec<Token>, buf: &mut String) -> Result<(),ParseError> {
233 for token in tokens { 234 for token in tokens {
234 match token { 235 match token {
236 Token::Loop(ident,code) => {
237 if let Some(value) = input.get_value( &ident ) &&
238 let Value::List(list) = value {
239 for rec in &list {
240 if let Value::Dictionary(d) = rec {
241 exec( d, code, buf )?
242 }
243 }
244 }
245 }
235 Token::Show(ident,p) => { 246 Token::Show(ident,p) => {
236 let format = if let Some(s) = p.get("format") { 247 let format = if let Some(s) = p.get("format") {
237 s 248 s
@@ -333,4 +344,46 @@ Now with explicit outputs:
333 } 344 }
334 } 345 }
335 } 346 }
347
348 #[test]
349 fn loops() {
350 let mut ct = Crimtag::new();
351 if let Err(e) = ct.load_static(r#"Looping code:
352[|view "index"|>We will enumerate people here:[|loop people|>
353 - [|show last_name|], [|show first_name|]: [|show title|]<|loop|]
354<|view|]
355"#) {
356 println!("Error: {:?}", e );
357 return;
358 }
359
360 let ctx = Context::from([
361 ("people".to_string(), crate::Value::List(vec![
362 crate::Value::Dictionary(Context::from([
363 ("first_name".to_string(), Value::String("Joe".to_string())),
364 ("last_name".to_string(), Value::String("Smith".to_string())),
365 ("title".to_string(), Value::String("CEO".to_string())),
366 ])),
367 crate::Value::Dictionary(Context::from([
368 ("first_name".to_string(), Value::String("Chris".to_string())),
369 ("last_name".to_string(), Value::String("Perkens".to_string())),
370 ("title".to_string(), Value::String("Baconeer".to_string())),
371 ])),
372 crate::Value::Dictionary(Context::from([
373 ("first_name".to_string(), Value::String("Will".to_string())),
374 ("last_name".to_string(), Value::String("Power".to_string())),
375 ("title".to_string(), Value::String("CFO".to_string())),
376 ])),
377 crate::Value::Dictionary(Context::from([
378 ("first_name".to_string(), Value::String("Justin".to_string())),
379 ("last_name".to_string(), Value::String("Time".to_string())),
380 ("title".to_string(), Value::String("CIO".to_string())),
381 ])),
382 ])),
383 ]);
384 match ct.render("index", &ctx) {
385 Ok(s) => println!("View result: {}", s ),
386 Err(e) => println!("Error: {:?}", e ),
387 }
388 }
336} 389}
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<Symbol<'a>> {
103 SymbolType::Token(_) | 103 SymbolType::Token(_) |
104 SymbolType::View | 104 SymbolType::View |
105 SymbolType::Output | 105 SymbolType::Output |
106 SymbolType::Loop |
106 SymbolType::Show => true, 107 SymbolType::Show => true,
107 _ => false 108 _ => false
108 }) 109 })
@@ -327,7 +328,7 @@ impl Parser {
327 328
328 if ctx.next().is_some() { 329 if ctx.next().is_some() {
329 match ctx.cur().unwrap().symbol() { 330 match ctx.cur().unwrap().symbol() {
330 SymbolType::View | SymbolType::Show | 331 SymbolType::View | SymbolType::Show | SymbolType::Loop |
331 SymbolType::Output => { 332 SymbolType::Output => {
332 let name_sym = ctx.cur().unwrap(); 333 let name_sym = ctx.cur().unwrap();
333 ctx.next(); 334 ctx.next();
@@ -640,6 +641,14 @@ impl<'a> TagBuilder<'a> {
640 pub fn build(mut self) -> ParseResult<Token> { 641 pub fn build(mut self) -> ParseResult<Token> {
641 if let Some(sym) = self.name { 642 if let Some(sym) = self.name {
642 match sym.symbol() { 643 match sym.symbol() {
644 SymbolType::Loop => {
645 let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) {
646 id
647 } else {
648 return Err(ParseError::new(Position::none(), "Identifier for loop variable name."));
649 };
650 Ok(Token::Loop(id, self.children))
651 }
643 SymbolType::Show => { 652 SymbolType::Show => {
644 let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) { 653 let id = if let ParamValue::Identifier(id) = self.params.swap_remove(0) {
645 id 654 id