pivot templates
authorBen Pfaff <blp@cs.stanford.edu>
Wed, 1 Jan 2025 19:57:55 +0000 (11:57 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Wed, 1 Jan 2025 19:57:55 +0000 (11:57 -0800)
rust/pspp/src/output/pivot/mod.rs

index 772a96d48032d6ff0c880348cd61cd678be2f652..c3af6c182269a5c25a46bdf48def8bcf1573ee7f 100644 (file)
@@ -1125,21 +1125,73 @@ impl<'a, 'b> DisplayValue<'a, 'b> {
                 }
                 b'^' => {
                     let (index, rest) = consume_int(iter.as_slice());
-                    if (1..=args.len()).contains(&index) && !args[index - 1].is_empty() {
-                        write!(f, "{}", args[index - 1][0].display(&self.table))?;
-                    }
                     iter = rest.iter();
+                    let Some(arg) = args.get(index.wrapping_sub(1)) else {
+                        continue;
+                    };
+                    if let Some(arg) = arg.get(0) {
+                        write!(f, "{}", arg.display(&self.table))?;
+                    }
                 }
                 b'[' => {
                     let (a, rest) = extract_inner_template(iter.as_slice());
                     let (b, rest) = extract_inner_template(rest);
                     let rest = rest.strip_prefix(b"]").unwrap_or(rest);
+                    let (index, rest) = consume_int(rest);
+                    iter = rest.iter();
+
+                    let Some(mut args) = args.get(index.wrapping_sub(1)).map(|vec| vec.as_slice())
+                    else {
+                        continue;
+                    };
+                    let (mut template, mut escape) =
+                        if !a.is_empty() { (a, b'%') } else { (b, b'^') };
+                    while !args.is_empty() {
+                        let n_consumed = self.inner_template(f, template, escape, args)?;
+                        if n_consumed == 0 {
+                            break;
+                        }
+                        args = &args[n_consumed..];
+
+                        template = b;
+                        escape = b'^';
+                    }
+                }
+                c => write!(f, "{c}")?,
+            }
+        }
+        Ok(())
+    }
+
+    fn inner_template<'c>(
+        &self,
+        f: &mut std::fmt::Formatter<'_>,
+        template: &[u8],
+        escape: u8,
+        args: &[Value],
+    ) -> Result<usize, std::fmt::Error> {
+        let mut iter = template.iter();
+        let mut args_consumed = 0;
+        while let Some(c) = iter.next() {
+            match c {
+                b'\\' => {
+                    let c = *iter.next().unwrap_or(&b'\\') as char;
+                    let c = if c == 'n' { '\n' } else { c };
+                    write!(f, "{c}")?;
+                }
+                c if *c == escape => {
+                    let (index, rest) = consume_int(iter.as_slice());
                     iter = rest.iter();
+                    let Some(arg) = args.get(index.wrapping_sub(1)) else {
+                        continue;
+                    };
+                    args_consumed = args_consumed.max(index);
+                    write!(f, "{}", arg.display(&self.table))?;
                 }
                 c => write!(f, "{c}")?,
             }
         }
-        todo!()
+        Ok(args_consumed)
     }
 }
 
@@ -1155,7 +1207,12 @@ fn consume_int(input: &[u8]) -> (usize, &[u8]) {
 }
 
 fn extract_inner_template(input: &[u8]) -> (&[u8], &[u8]) {
-    todo!()
+    for (index, c) in input.iter().copied().enumerate() {
+        if c == b':' && (index == 0 || input[index-1] != b'\\') {
+            return input.split_at(index);
+        }
+    }
+    (input, &[])
 }
 
 fn interpret_show(