Source code on Github
{-# OPTIONS --safe --without-K #-}

----------------------------------------------------------------------
-- Anti-Unification
--
-- This is a modified version of anti-unification as implemented by
-- the Agda stdlib.
----------------------------------------------------------------------

module Reflection.AntiUnification where

open import Meta.Prelude
open import Meta.Init

import Data.Maybe as Maybe
import Data.Nat as Nat
open import Data.Product

open import Reflection.AlphaEquality

open import Class.DecEq

----------------------------------------------------------------------
-- Utilities
----------------------------------------------------------------------

module VarDescend (♯vars : ) where
  -- Descend past a variable.
  varDescend :     
  varDescend ϕ x = if ϕ Nat.≤ᵇ x then ♯vars + x else x

  -- Descend a variable underneath pattern variables.
  patternDescend :   Pattern  Pattern × 
  patternsDescend :   Args Pattern  Args Pattern × 

  patternDescend ϕ (con c ps) = map₁ (con c) (patternsDescend ϕ ps)
  patternDescend ϕ (dot t)    = dot t , ϕ
  patternDescend ϕ (var x)    = var (varDescend ϕ x) , suc ϕ
  patternDescend ϕ (lit l)    = lit l , ϕ
  patternDescend ϕ (proj f)   = proj f , ϕ
  patternDescend ϕ (absurd x) = absurd (varDescend ϕ x) , suc ϕ

  patternsDescend ϕ ((arg i p)  ps) =
    let (p' , ϕ') = patternDescend ϕ p
        (ps' , ϕ'') = patternsDescend ϕ' ps
    in (arg i p  ps' , ϕ'')
  patternsDescend ϕ []       =
    [] , ϕ

----------------------------------------------------------------------
-- Anti-Unification
----------------------------------------------------------------------

-- `cmp` compares two different terms and assigns them a variable
-- `♯vars` is the successor of the maximum value that `cmp` should take
module AntiUnification (cmp : Term  Term  ) (♯vars : ) where
  open VarDescend ♯vars

  antiUnify        :   Term  Term  Term
  antiUnifyArgs    :   Args Term  Args Term  Maybe (Args Term)
  antiUnifyClauses :   Clauses  Clauses  Maybe Clauses
  antiUnifyClause  :   Clause  Clause  Maybe Clause

  antiUnify ϕ t@(var x args) t'@(var y args') with x ≡ᵇ y | antiUnifyArgs ϕ args args'
  ... | _     | nothing    = var (cmp t t' + ϕ) []
  ... | false | just uargs = var (cmp t t' + ϕ) uargs
  ... | true  | just uargs = var (varDescend ϕ x) uargs
  antiUnify ϕ t@(con c args) t'@(con c' args') with c ≡ᵇ c' | antiUnifyArgs ϕ args args'
  ... | _     | nothing    = var (cmp t t' + ϕ) []
  ... | false | just uargs = var (cmp t t' + ϕ) []
  ... | true  | just uargs = con c uargs
  antiUnify ϕ t@(def f args) t'@(def f' args') with f ≡ᵇ f' | antiUnifyArgs ϕ args args'
  ... | _     | nothing    = var (cmp t t' + ϕ) []
  ... | false | just uargs = var (cmp t t' + ϕ) []
  ... | true  | just uargs = def f uargs
  antiUnify ϕ (lam v (abs s t)) (lam _ (abs _ t')) =
    lam v (abs s (antiUnify (suc ϕ) t t'))
  antiUnify ϕ t@(pat-lam cs args) t'@(pat-lam cs' args') with antiUnifyClauses ϕ cs cs' | antiUnifyArgs ϕ args args'
  ... | nothing  | _       = var (cmp t t' + ϕ) []
  ... | _        | nothing = var (cmp t t' + ϕ) []
  ... | just ucs | just uargs = pat-lam ucs uargs
  antiUnify ϕ (Π[ s  arg i a ] b) (Π[ _  arg _ a' ] b') =
    Π[ s  arg i (antiUnify ϕ a a') ] antiUnify (suc ϕ) b b'
  antiUnify ϕ (sort (set t)) (sort (set t')) =
    sort (set (antiUnify ϕ t t'))
  antiUnify ϕ t@(sort (lit n)) t'@(sort (lit n')) with n ≡ᵇ n'
  ... | true  = sort (lit n)
  ... | false = var (cmp t t' + ϕ) []
  antiUnify ϕ t@(sort (propLit n)) t'@(sort (propLit n')) with n ≡ᵇ n'
  ... | true  = sort (propLit n)
  ... | false = var (cmp t t' + ϕ) []
  antiUnify ϕ t@(sort (inf n)) t'@(sort (inf n')) with n ≡ᵇ n'
  ... | true  = sort (inf n)
  ... | false = var (cmp t t' + ϕ) []
  antiUnify ϕ (sort unknown) (sort unknown) =
    sort unknown
  antiUnify ϕ t@(lit l) t'@(lit l') with l == l'
  ... | true  = lit l
  ... | false = var (cmp t t' + ϕ) []
  antiUnify ϕ t@(meta x args) t'@(meta x' args') with x == x' | antiUnifyArgs ϕ args args'
  ... | _     | nothing    = var (cmp t t' + ϕ) []
  ... | false | _          = var (cmp t t' + ϕ) []
  ... | true  | just uargs = meta x uargs
  antiUnify ϕ unknown unknown = unknown
  antiUnify ϕ t@_ t'@_ = var (cmp t t' + ϕ) []

  antiUnifyArgs ϕ (arg i t  args) (arg _ t'  args') =
    Maybe.map (arg i (antiUnify ϕ t t') ∷_) (antiUnifyArgs ϕ args args')
  antiUnifyArgs ϕ [] [] = just []
  antiUnifyArgs ϕ _  _  = nothing

  antiUnifyClause ϕ (clause Γ pats t) (clause Δ pats' t') =
    Maybe.when ((Γ =α= Δ)  (pats =α= pats'))
      let (upats , ϕ') = patternsDescend ϕ pats
      in clause Γ upats (antiUnify ϕ' t t')
  antiUnifyClause ϕ (absurd-clause Γ pats) (absurd-clause Δ pats') =
    Maybe.when ((Γ =α= Δ)  (pats =α= pats')) $
      absurd-clause Γ pats
  antiUnifyClause ϕ _ _ =
    nothing

  antiUnifyClauses ϕ (c  cs) (c'  cs') =
    Maybe.ap (Maybe.map _∷_ (antiUnifyClause ϕ c c')) (antiUnifyClauses ϕ cs cs')
  antiUnifyClauses ϕ _ _ =
    just []