342 lines
10 KiB
Idris
342 lines
10 KiB
Idris
module Quox.Syntax.Term
|
|
|
|
import public Quox.Syntax.Var
|
|
import public Quox.Syntax.Shift
|
|
import public Quox.Syntax.Subst
|
|
import public Quox.Syntax.Universe
|
|
import public Quox.Syntax.Qty
|
|
import public Quox.Syntax.Dim
|
|
import public Quox.Name
|
|
|
|
import Quox.Pretty
|
|
|
|
import public Data.DPair
|
|
import Data.List
|
|
import Data.Maybe
|
|
import Data.Nat
|
|
import public Data.So
|
|
import Data.String
|
|
|
|
%default total
|
|
|
|
|
|
infixl 8 :#
|
|
infixl 9 :@
|
|
mutual
|
|
public export
|
|
TSubst : Nat -> Nat -> Nat -> Type
|
|
TSubst d = Subst (\n => Elim d n)
|
|
|
|
||| first argument `d` is dimension scope size, second `n` is term scope size
|
|
public export
|
|
data Term : (d, n : Nat) -> Type where
|
|
||| type of types
|
|
TYPE : (l : Universe) -> Term d n
|
|
|
|
||| function type
|
|
Pi : (qty, qtm : Qty) -> (x : Name) ->
|
|
(arg : Term d n) -> (res : Term d (S n)) -> Term d n
|
|
||| function term
|
|
Lam : (x : Name) -> (body : Term d (S n)) -> Term d n
|
|
|
|
||| elimination
|
|
E : (e : Elim d n) -> Term d n
|
|
|
|
||| term closure/suspended substitution
|
|
CloT : (tm : Term d from) -> (th : Lazy (TSubst d from to)) -> Term d to
|
|
||| dimension closure/suspended substitution
|
|
DCloT : (tm : Term dfrom n) -> (th : Lazy (DSubst dfrom dto)) -> Term dto n
|
|
|
|
||| first argument `d` is dimension scope size, second `n` is term scope size
|
|
public export
|
|
data Elim : (d, n : Nat) -> Type where
|
|
||| free variable
|
|
F : (x : Name) -> Elim d n
|
|
||| bound variable
|
|
B : (i : Var n) -> Elim d n
|
|
|
|
||| term application
|
|
(:@) : (fun : Elim d n) -> (arg : Term d n) -> Elim d n
|
|
|
|
||| type-annotated term
|
|
(:#) : (tm, ty : Term d n) -> Elim d n
|
|
|
|
||| term closure/suspended substitution
|
|
CloE : (el : Elim d from) -> (th : Lazy (TSubst d from to)) -> Elim d to
|
|
||| dimension closure/suspended substitution
|
|
DCloE : (el : Elim dfrom n) -> (th : Lazy (DSubst dfrom dto)) -> Elim dto n
|
|
|
|
%name Term s, t, r
|
|
%name Elim e, f
|
|
|
|
|
|
||| same as `F` but as a term
|
|
public export %inline
|
|
FT : Name -> Term d n
|
|
FT = E . F
|
|
|
|
||| abbreviation for a bound variable like `BV 4` instead of
|
|
||| `B (VS (VS (VS (VS VZ))))`
|
|
public export %inline
|
|
BV : (i : Nat) -> {auto 0 p : LT i n} -> Elim d n
|
|
BV i = B $ V i
|
|
|
|
||| same as `BV` but as a term
|
|
public export %inline
|
|
BVT : (i : Nat) -> {auto 0 p : LT i n} -> Term d n
|
|
BVT i = E $ BV i
|
|
|
|
|
|
infixl 9 :@@
|
|
||| apply multiple arguments at once
|
|
public export %inline
|
|
(:@@) : Elim d n -> List (Term d n) -> Elim d n
|
|
f :@@ ss = foldl (:@) f ss
|
|
|
|
private
|
|
getArgs' : Elim d n -> List (Term d n) -> (Elim d n, List (Term d n))
|
|
getArgs' (f :@ s) args = getArgs' f (s :: args)
|
|
getArgs' f args = (f, args)
|
|
|
|
||| splits an application into its head and arguments. if it's not an
|
|
||| application then the list is just empty
|
|
export %inline
|
|
getArgs : Elim d n -> (Elim d n, List (Term d n))
|
|
getArgs e = getArgs' e []
|
|
|
|
|
|
parameters {auto _ : Pretty.HasEnv m}
|
|
private %inline arrowD : m (Doc HL)
|
|
arrowD = hlF Syntax $ ifUnicode "→" "->"
|
|
|
|
private %inline lamD : m (Doc HL)
|
|
lamD = hlF Syntax $ ifUnicode "λ" "fun"
|
|
|
|
private %inline annD : m (Doc HL)
|
|
annD = hlF Syntax $ ifUnicode "⦂" "::"
|
|
|
|
private %inline typeD : Doc HL
|
|
typeD = hl Syntax "Type"
|
|
|
|
private %inline colonD : Doc HL
|
|
colonD = hl Syntax ":"
|
|
|
|
mutual
|
|
export covering
|
|
PrettyHL (Term d n) where
|
|
prettyM (TYPE l) =
|
|
parensIfM App $ typeD <//> !(withPrec Arg $ prettyM l)
|
|
prettyM (Pi qty qtm x s t) =
|
|
parensIfM Outer $ hang 2 $
|
|
!(prettyBinder [qty, qtm] x s) <++> !arrowD
|
|
<//> !(under T x $ prettyM t)
|
|
prettyM (Lam x t) =
|
|
parensIfM Outer $
|
|
sep [!lamD, hl TVar !(prettyM x), !arrowD]
|
|
<//> !(under T x $ prettyM t)
|
|
prettyM (E e) =
|
|
prettyM e
|
|
prettyM (CloT s th) =
|
|
parensIfM SApp . hang 2 =<<
|
|
[|withPrec SApp (prettyM s) </> prettyTSubst th|]
|
|
prettyM (DCloT s th) =
|
|
parensIfM SApp . hang 2 =<<
|
|
[|withPrec SApp (prettyM s) </> prettyDSubst th|]
|
|
|
|
export covering
|
|
PrettyHL (Elim d n) where
|
|
prettyM (F x) =
|
|
hl' Free <$> prettyM x
|
|
prettyM (B i) =
|
|
prettyVar TVar TVarErr (!ask).tnames i
|
|
prettyM (e :@ s) =
|
|
let (f, args) = getArgs' e [s] in
|
|
parensIfM App =<< withPrec Arg
|
|
[|prettyM f <//> (align . sep <$> traverse prettyM args)|]
|
|
prettyM (s :# a) =
|
|
parensIfM Ann $ hang 2 $
|
|
!(withPrec AnnL $ prettyM s) <++> !annD
|
|
<//> !(withPrec Ann $ prettyM a)
|
|
prettyM (CloE e th) =
|
|
parensIfM SApp . hang 2 =<<
|
|
[|withPrec SApp (prettyM e) </> prettyTSubst th|]
|
|
prettyM (DCloE e th) =
|
|
parensIfM SApp . hang 2 =<<
|
|
[|withPrec SApp (prettyM e) </> prettyDSubst th|]
|
|
|
|
export covering
|
|
prettyTSubst : Pretty.HasEnv m => TSubst d from to -> m (Doc HL)
|
|
prettyTSubst s = prettySubstM prettyM (!ask).tnames TVar "[" "]" s
|
|
|
|
export covering
|
|
prettyBinder : Pretty.HasEnv m => List Qty -> Name -> Term d n -> m (Doc HL)
|
|
prettyBinder pis x a =
|
|
pure $ parens $ hang 2 $
|
|
!(prettyQtyBinds pis) <//>
|
|
hsep [hl TVar !(prettyM x), colonD, !(withPrec Outer $ prettyM a)]
|
|
|
|
|
|
export FromVar (Elim d) where fromVar = B
|
|
export FromVar (Term d) where fromVar = E . fromVar
|
|
|
|
||| does the minimal reasonable work:
|
|
||| - deletes the closure around a free name since it doesn't do anything
|
|
||| - deletes an identity substitution
|
|
||| - composes (lazily) with an existing top-level closure
|
|
||| - immediately looks up a bound variable
|
|
||| - otherwise, wraps in a new closure
|
|
export
|
|
CanSubst (Elim d) (Elim d) where
|
|
F x // _ = F x
|
|
B i // th = th !! i
|
|
CloE e ph // th = CloE e $ assert_total $ ph . th
|
|
e // Shift SZ = e
|
|
e // th = CloE e th
|
|
|
|
||| does the minimal reasonable work:
|
|
||| - deletes the closure around an atomic constant like `TYPE`
|
|
||| - deletes an identity substitution
|
|
||| - composes (lazily) with an existing top-level closure
|
|
||| - goes inside `E` in case it is a simple variable or something
|
|
||| - otherwise, wraps in a new closure
|
|
export
|
|
CanSubst (Elim d) (Term d) where
|
|
TYPE l // _ = TYPE l
|
|
E e // th = E $ e // th
|
|
CloT s ph // th = CloT s $ ph . th
|
|
s // Shift SZ = s
|
|
s // th = CloT s th
|
|
|
|
|
|
infixl 8 ///
|
|
mutual
|
|
namespace Term
|
|
||| applies a dimension substitution with the same behaviour as `(//)`
|
|
||| above
|
|
export
|
|
(///) : Term dfrom n -> DSubst dfrom dto -> Term dto n
|
|
TYPE l /// _ = TYPE l
|
|
E e /// th = E $ e /// th
|
|
DCloT s ph /// th = DCloT s $ ph . th
|
|
s /// Shift SZ = s
|
|
s /// th = DCloT s th
|
|
|
|
||| applies a term and dimension substitution
|
|
public export %inline
|
|
subs : Term dfrom from -> DSubst dfrom dto -> TSubst dto from to ->
|
|
Term dto to
|
|
subs s th ph = s /// th // ph
|
|
|
|
namespace Elim
|
|
||| applies a dimension substitution with the same behaviour as `(//)`
|
|
||| above
|
|
export
|
|
(///) : Elim dfrom n -> DSubst dfrom dto -> Elim dto n
|
|
F x /// _ = F x
|
|
B i /// _ = B i
|
|
DCloE e ph /// th = DCloE e $ ph . th
|
|
e /// Shift SZ = e
|
|
e /// th = DCloE e th
|
|
|
|
||| applies a term and dimension substitution
|
|
public export %inline
|
|
subs : Elim dfrom from -> DSubst dfrom dto -> TSubst dto from to ->
|
|
Elim dto to
|
|
subs e th ph = e /// th // ph
|
|
|
|
|
|
private %inline
|
|
comp' : DSubst dfrom dto -> TSubst dfrom from mid -> TSubst dto mid to ->
|
|
TSubst dto from to
|
|
comp' th ps ph = map (/// th) ps . ph
|
|
|
|
|
|
||| true if an elimination has a closure or dimension closure at the top level
|
|
public export %inline
|
|
isCloE : Elim d n -> Bool
|
|
isCloE (CloE _ _) = True
|
|
isCloE (DCloE _ _) = True
|
|
isCloE _ = False
|
|
|
|
||| true if a term has a closure or dimension closure at the top level,
|
|
||| or is `E` applied to such an elimination
|
|
public export %inline
|
|
isCloT : Term d n -> Bool
|
|
isCloT (CloT _ _) = True
|
|
isCloT (DCloT _ _) = True
|
|
isCloT (E e) = isCloE e
|
|
isCloT _ = False
|
|
|
|
||| an elimination which is not a top level closure
|
|
public export NotCloElim : Nat -> Nat -> Type
|
|
NotCloElim d n = Subset (Elim d n) $ So . not . isCloE
|
|
|
|
||| a term which is not a top level closure
|
|
public export NotCloTerm : Nat -> Nat -> Type
|
|
NotCloTerm d n = Subset (Term d n) $ So . not . isCloT
|
|
|
|
|
|
mutual
|
|
export
|
|
pushSubstsT : Term d n -> NotCloTerm d n
|
|
pushSubstsT s = pushSubstsT' id id s
|
|
|
|
export
|
|
pushSubstsE : Elim d n -> NotCloElim d n
|
|
pushSubstsE e = pushSubstsE' id id e
|
|
|
|
private
|
|
pushSubstsT' : DSubst dfrom dto -> TSubst dto from to ->
|
|
Term dfrom from -> NotCloTerm dto to
|
|
pushSubstsT' th ph (TYPE l) =
|
|
Element (TYPE l) Oh
|
|
pushSubstsT' th ph (Pi qty qtm x a b) =
|
|
Element (Pi qty qtm x (subs a th ph) (subs b th (push ph))) Oh
|
|
pushSubstsT' th ph (Lam x t) =
|
|
Element (Lam x $ subs t th $ push ph) Oh
|
|
pushSubstsT' th ph (E e) =
|
|
let Element e prf = pushSubstsE' th ph e in Element (E e) prf
|
|
pushSubstsT' th ph (CloT s ps) =
|
|
pushSubstsT' th (comp' th ps ph) s
|
|
pushSubstsT' th ph (DCloT s ps) =
|
|
pushSubstsT' (ps . th) ph s
|
|
|
|
private
|
|
pushSubstsE' : DSubst dfrom dto -> TSubst dto from to ->
|
|
Elim dfrom from -> NotCloElim dto to
|
|
pushSubstsE' th ph (F x) =
|
|
Element (F x) Oh
|
|
pushSubstsE' th ph (B i) =
|
|
assert_total pushSubstsE $ ph !! i
|
|
pushSubstsE' th ph (f :@ s) =
|
|
Element (subs f th ph :@ subs s th ph) Oh
|
|
pushSubstsE' th ph (s :# a) =
|
|
Element (subs s th ph :# subs a th ph) Oh
|
|
pushSubstsE' th ph (CloE e ps) =
|
|
pushSubstsE' th (comp' th ps ph) e
|
|
pushSubstsE' th ph (DCloE e ps) =
|
|
pushSubstsE' (ps . th) ph e
|
|
|
|
|
|
parameters {auto _ : Alternative f}
|
|
||| `(λx. t ⦂ (x: A) → B) s >>> (t ⦂ B)[x ≔ (s ⦂ A)]`
|
|
export
|
|
betaLam1 : Elim d n -> f (Elim d n)
|
|
betaLam1 ((Lam {body, _} :# Pi {arg, res, _}) :@ s) =
|
|
pure $ (body :# res) // (s :# arg ::: id)
|
|
betaLam1 _ = empty
|
|
|
|
||| `(e ⦂ A) >>> e` [if `e` is an elim]
|
|
export
|
|
upsilon1 : Elim d n -> f (Elim d n)
|
|
upsilon1 (E e :# _) = pure e
|
|
upsilon1 _ = empty
|
|
|
|
export
|
|
step : Elim d n -> f (Elim d n)
|
|
step e = betaLam1 e <|> upsilon1 e
|
|
|
|
export
|
|
step' : Elim d n -> Elim d n
|
|
step' e = fromMaybe e $ step e
|