egglog 2.0.0

egglog is a language that combines the benefits of equality saturation and datalog. It can be used for analysis, optimization, and synthesis of programs. It is the successor to the popular rust library egg.
Documentation
;; Example showing how to use multisets to hold associative & commutative operations

(datatype*
  (Math
    (Num i64)
    (Var String)
    (Add Math Math)
    (Mul Math Math)
    (Product MathMultiSet)
    (Sum MathMultiSet))
  (sort MathToMath (UnstableFn (Math) Math))
  (sort MathMultiSet (MultiSet Math)))

;; expr1 = 2 * (x + 3)
(let $expr1 (Mul (Num 2) (Add (Var "x") (Num 3))))
;; expr2 = 6 + 2 * x
(let $expr2 (Add (Num 6) (Mul (Num 2) (Var "x"))))

(rewrite (Add a b) (Sum (multiset-of a b)))
(rewrite (Mul a b) (Product (multiset-of a b)))

;; 0 or 1 elements sums/products also can be extracted back to numbers
(rule
  (
    (= sum (Sum sum-inner))
    (= 0 (multiset-length sum-inner))
  )
  ((union sum (Num 0)))
)
(rule
  (
    (= sum (Sum sum-inner))
    (= 1 (multiset-length sum-inner))
  )
  ((union sum (multiset-pick sum-inner)))
)

(rule
  (
    (= product (Product product-inner))
    (= 0 (multiset-length product-inner))
  )
  ((union product (Num 1)))
)
(rule
  (
    (= product (Product product-inner))
    (= 1 (multiset-length product-inner))
  )
  ((union product (multiset-pick product-inner)))
)

; (rewrite (Mul a (Add b c))
;          (Add (Mul a b) (Mul a c)))

; -> we would like to write it like this, but cannot (yet) bc we can't match on the inner structure of the multisets
;    and we don't support anonymous functions

; (rewrite (Product (multiset-insert a (Sum bc)))
;          (Sum (multiset-map (lambda (x) (Product (multiset-insert a x))) bc)))


;; so instead we can define a function and partially apply it to get the same function as the lambda
(constructor tmp-fn (MathMultiSet Math) Math)
(rewrite (tmp-fn xs x) (Product (multiset-insert xs x)))

(rule
  (
    ;; and we can do a cross product search of all possible pairs of products/sums to find one we want
    (= sum (Sum bc))
    (= product (Product product-inner))
    (multiset-contains product-inner sum)
    (> (multiset-length product-inner) 1)
    (= a (multiset-remove product-inner sum))
  )
  (
    (union product (Sum
      (unstable-multiset-map
        (unstable-fn "tmp-fn" a)
        bc)
    ))
  )
)

; (rewrite (Add (Num a) (Num b))
;          (Num (+ a b)))

(rule
  (
    (= sum (Sum sum-inner))
    (= num-a (Num a))
    (multiset-contains sum-inner num-a)
    (= without-a (multiset-remove sum-inner num-a))
    (= num-b (Num b))
    (multiset-contains without-a num-b)
  )
  (
    (union sum
     (Sum (multiset-insert (multiset-remove without-a num-b) (Num (+ a b))))
   )
  )
)

; (rewrite (Mul (Num a) (Num b))
;          (Num (* a b)))

(rule
  (
    (= product (Product product-inner))
    (= num-a (Num a))
    (multiset-contains product-inner num-a)
    (= without-a (multiset-remove product-inner num-a))
    (= num-b (Num b))
    (multiset-contains without-a num-b)
  )
  (
    (union product
     (Product (multiset-insert (multiset-remove without-a num-b) (Num (* a b))))
   )
  )
)

(run 100)
(check (= $expr1 $expr2))