Browse Source

Start on the transform module

Ian Goldberg 4 months ago
parent
commit
42ec7dd989
2 changed files with 241 additions and 0 deletions
  1. 1 0
      sigma_compiler_core/src/lib.rs
  2. 240 0
      sigma_compiler_core/src/transform.rs

+ 1 - 0
sigma_compiler_core/src/lib.rs

@@ -12,6 +12,7 @@ pub mod sigma {
     pub mod types;
     pub mod types;
 }
 }
 mod syntax;
 mod syntax;
+mod transform;
 
 
 pub use syntax::{SigmaCompSpec, TaggedIdent, TaggedPoint, TaggedScalar, TaggedVarDict};
 pub use syntax::{SigmaCompSpec, TaggedIdent, TaggedPoint, TaggedScalar, TaggedVarDict};
 
 

+ 240 - 0
sigma_compiler_core/src/transform.rs

@@ -0,0 +1,240 @@
+//! A module for operations that transform a [`StatementTree`].
+//! Every transformation must maintain the [disjunction invariant].
+//!
+//! [disjunction invariant]: StatementTree::check_disjunction_invariant
+
+use super::sigma::combiners::*;
+use syn::{parse_quote, Expr};
+
+/// Produce a [`StatementTree`] that represents the constant `true`
+fn leaf_true() -> StatementTree {
+    StatementTree::Leaf(parse_quote! { true })
+}
+
+/// Test if the given [`StatementTree`] represents the constant `true`
+fn is_leaf_true(st: &StatementTree) -> bool {
+    if let StatementTree::Leaf(Expr::Lit(exprlit)) = st {
+        if let syn::Lit::Bool(syn::LitBool { value: true, .. }) = exprlit.lit {
+            return true;
+        }
+    }
+    false
+}
+
+/// Simplify a [`StatementTree`] by pruning leaves that are the constant
+/// `true`, and simplifying `And`, `Or`, and `Thresh` combiners that
+/// have fewer than two children.
+pub fn prune_statement_tree(st: &mut StatementTree) {
+    match st {
+        // If the StatementTree is just a Leaf, just keep it unmodified,
+        // even if it is leaf_true.
+        StatementTree::Leaf(_) => {}
+
+        // For the And combiner, recursively simplify each child, and then
+        // prune the child if it is leaf_true.  If we end up with 1
+        // child replace ourselves with that child.  If we end up with 0
+        // children, replace ourselves with leaf_true.
+        StatementTree::And(v) => {
+            let mut i: usize = 0;
+            // Note that v.len _can change_ during this loop
+            while i < v.len() {
+                prune_statement_tree(&mut v[i]);
+                if is_leaf_true(&v[i]) {
+                    // Remove this child, and _do not_ increment i
+                    v.remove(i);
+                } else {
+                    i += 1;
+                }
+            }
+            if v.is_empty() {
+                *st = leaf_true();
+            } else if v.len() == 1 {
+                let child = v.remove(0);
+                *st = child;
+            }
+        }
+
+        // For the Or combiner, recursively simplify each child, and if
+        // it ends up leaf_true, replace ourselves with leaf_true.
+        // If we end up with 1 child, we must have started wth 1 child.
+        // Replace ourselves with that child anyway.
+        StatementTree::Or(v) => {
+            let mut i: usize = 0;
+            // Note that v.len _can change_ during this loop
+            while i < v.len() {
+                prune_statement_tree(&mut v[i]);
+                if is_leaf_true(&v[i]) {
+                    *st = leaf_true();
+                    return;
+                } else {
+                    i += 1;
+                }
+            }
+            if v.len() == 1 {
+                let child = v.remove(0);
+                *st = child;
+            }
+        }
+
+        // For the Thresh combiner, recursively simplify each child, and
+        // if it ends up leaf_true, prune it, and subtract 1 from the
+        // thresh.  If the thresh hits 0, replace ourselves with
+        // leaf_true.  If we end up with 1 child and thresh is 1,
+        // replace ourselves with that child.
+        StatementTree::Thresh(thresh, v) => {
+            let mut i: usize = 0;
+            // Note that v.len _can change_ during this loop
+            while i < v.len() {
+                prune_statement_tree(&mut v[i]);
+                if is_leaf_true(&v[i]) {
+                    // Remove this child, and _do not_ increment i
+                    v.remove(i);
+                    // But decrement thresh
+                    *thresh -= 1;
+                    if *thresh == 0 {
+                        *st = leaf_true();
+                        return;
+                    }
+                } else {
+                    i += 1;
+                }
+            }
+            if v.len() == 1 {
+                // If thresh == 0, we would have exited above
+                assert!(*thresh == 1);
+                let child = v.remove(0);
+                *st = child;
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn leaf_true_test() {
+        assert!(is_leaf_true(&leaf_true()));
+        assert!(!is_leaf_true(&StatementTree::Leaf(parse_quote! { false })));
+        assert!(!is_leaf_true(&StatementTree::Leaf(parse_quote! { 1 })));
+        assert!(!is_leaf_true(
+            &StatementTree::parse(&parse_quote! {
+                OR(1=1, a=b)
+            })
+            .unwrap()
+        ));
+    }
+
+    fn prune_tester(e: Expr, pruned_e: Expr) {
+        let mut st = StatementTree::parse(&e).unwrap();
+        prune_statement_tree(&mut st);
+        assert_eq!(st, StatementTree::parse(&pruned_e).unwrap());
+    }
+
+    #[test]
+    fn prune_statement_tree_test() {
+        prune_tester(
+            parse_quote! {
+                AND (
+                    true,
+                    e = f,
+                )
+            },
+            parse_quote! {
+                e = f
+            },
+        );
+        prune_tester(
+            parse_quote! {
+                AND (
+                    e = f,
+                    true,
+                )
+            },
+            parse_quote! {
+                e = f
+            },
+        );
+        prune_tester(
+            parse_quote! {
+                AND (
+                    e = f,
+                    true,
+                    b = c,
+                )
+            },
+            parse_quote! {
+                AND (
+                    e = f,
+                    b = c,
+                )
+            },
+        );
+        prune_tester(
+            parse_quote! {
+                OR (
+                    true,
+                    e = f,
+                )
+            },
+            parse_quote! {
+                true
+            },
+        );
+        prune_tester(
+            parse_quote! {
+                AND (
+                    a = b,
+                    true,
+                    OR (
+                        c = d,
+                        true,
+                        e = f
+                    )
+                )
+            },
+            parse_quote! {
+                a = b
+            },
+        );
+        prune_tester(
+            parse_quote! {
+                THRESH (3,
+                    a = b,
+                    true,
+                    THRESH (1,
+                        c = d,
+                        true,
+                        e = f
+                    )
+                )
+            },
+            parse_quote! {
+                a = b
+            },
+        );
+        prune_tester(
+            parse_quote! {
+                THRESH (3,
+                    a = b,
+                    true,
+                    THRESH (2,
+                        c = d,
+                        true,
+                        e = f
+                    )
+                )
+            },
+            parse_quote! {
+                THRESH (2,
+                    a = b,
+                    THRESH (1,
+                        c = d,
+                        e = f
+                    )
+                )
+            },
+        );
+    }
+}