Initial
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
.lake/
|
||||||
|
build/
|
||||||
|
lakefile.olean
|
||||||
|
lake-manifest.json
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import BidirTT.Syntax
|
||||||
|
import BidirTT.Value
|
||||||
|
import BidirTT.Pretty
|
||||||
|
import BidirTT.Eval
|
||||||
|
import BidirTT.Context
|
||||||
|
import BidirTT.Check
|
||||||
|
import BidirTT.Examples
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import BidirTT.Context
|
||||||
|
import BidirTT.Eval
|
||||||
|
import BidirTT.Pretty
|
||||||
|
|
||||||
|
namespace BidirTT
|
||||||
|
|
||||||
|
abbrev TCM := Except String
|
||||||
|
|
||||||
|
private def showTy (l : Lvl) (v : Val) : String :=
|
||||||
|
match quote l v with
|
||||||
|
| Except.ok tm => prettyTm tm
|
||||||
|
| Except.error err => s!"<ill-scoped value: {err}>"
|
||||||
|
|
||||||
|
mutual
|
||||||
|
partial def check (cxt : Cxt) : Raw → Val → TCM Tm
|
||||||
|
| .lam x t, .pi a c => do
|
||||||
|
let bodyTy ← cApp c (.var cxt.lvl)
|
||||||
|
let t' ← check (cxt.bind x a) t bodyTy
|
||||||
|
pure (.lam t')
|
||||||
|
| .pair t u, .sig a c => do
|
||||||
|
let t' ← check cxt t a
|
||||||
|
let vt ← eval cxt.env t'
|
||||||
|
let bodyTy ← cApp c vt
|
||||||
|
let u' ← check cxt u bodyTy
|
||||||
|
pure (.pair t' u')
|
||||||
|
| .letE x a t u, ty => do
|
||||||
|
let (a', _) ← inferUniverse cxt a
|
||||||
|
let va := eval cxt.env a'
|
||||||
|
let va ← va
|
||||||
|
let t' ← check cxt t va
|
||||||
|
let vt ← eval cxt.env t'
|
||||||
|
let u' ← check (cxt.define x vt va) u ty
|
||||||
|
pure (.letE a' t' u')
|
||||||
|
| r, ty => do
|
||||||
|
let (t', ty') ← infer cxt r
|
||||||
|
if (← conv cxt.lvl ty' ty) then
|
||||||
|
pure t'
|
||||||
|
else
|
||||||
|
throw s!"type mismatch: expected {showTy cxt.lvl ty}, got {showTy cxt.lvl ty'}"
|
||||||
|
|
||||||
|
partial def infer (cxt : Cxt) : Raw → TCM (Tm × Val)
|
||||||
|
| .var x =>
|
||||||
|
match cxt.lookup x with
|
||||||
|
| some (i, a) => pure (.var i, a)
|
||||||
|
| none => throw s!"unknown variable {x}"
|
||||||
|
| .univ i => pure (.univ i, .univ (i + 1))
|
||||||
|
| .app t u => do
|
||||||
|
let (t', tty) ← infer cxt t
|
||||||
|
match tty with
|
||||||
|
| .pi a c => do
|
||||||
|
let u' ← check cxt u a
|
||||||
|
let vu ← eval cxt.env u'
|
||||||
|
let bodyTy ← cApp c vu
|
||||||
|
pure (.app t' u', bodyTy)
|
||||||
|
| _ => throw s!"expected Pi type in application, got {showTy cxt.lvl tty}"
|
||||||
|
| .fst t => do
|
||||||
|
let (t', tty) ← infer cxt t
|
||||||
|
match tty with
|
||||||
|
| .sig a _ => pure (.fst t', a)
|
||||||
|
| _ => throw s!"expected Sigma type in .1, got {showTy cxt.lvl tty}"
|
||||||
|
| .snd t => do
|
||||||
|
let (t', tty) ← infer cxt t
|
||||||
|
match tty with
|
||||||
|
| .sig _ c => do
|
||||||
|
let vt ← eval cxt.env t'
|
||||||
|
let fstv ← vFst vt
|
||||||
|
let bodyTy ← cApp c fstv
|
||||||
|
pure (.snd t', bodyTy)
|
||||||
|
| _ => throw s!"expected Sigma type in .2, got {showTy cxt.lvl tty}"
|
||||||
|
| .pi x a b => do
|
||||||
|
let (a', i) ← inferUniverse cxt a
|
||||||
|
let va ← eval cxt.env a'
|
||||||
|
let (b', j) ← inferUniverse (cxt.bind x va) b
|
||||||
|
pure (.pi a' b', .univ (Nat.max i j))
|
||||||
|
| .sig x a b => do
|
||||||
|
let (a', i) ← inferUniverse cxt a
|
||||||
|
let va ← eval cxt.env a'
|
||||||
|
let (b', j) ← inferUniverse (cxt.bind x va) b
|
||||||
|
pure (.sig a' b', .univ (Nat.max i j))
|
||||||
|
| .ann t a => do
|
||||||
|
let (a', _) ← inferUniverse cxt a
|
||||||
|
let va ← eval cxt.env a'
|
||||||
|
let t' ← check cxt t va
|
||||||
|
pure (t', va)
|
||||||
|
| .letE x a t u => do
|
||||||
|
let (a', _) ← inferUniverse cxt a
|
||||||
|
let va ← eval cxt.env a'
|
||||||
|
let t' ← check cxt t va
|
||||||
|
let vt ← eval cxt.env t'
|
||||||
|
let (u', uty) ← infer (cxt.define x vt va) u
|
||||||
|
pure (.letE a' t' u', uty)
|
||||||
|
| .lam _ _ => throw "cannot infer type of lambda, use an annotation"
|
||||||
|
| .pair _ _ => throw "cannot infer type of pair, use an annotation"
|
||||||
|
|
||||||
|
partial def inferUniverse (cxt : Cxt) (r : Raw) : TCM (Tm × Nat) := do
|
||||||
|
let (t, ty) ← infer cxt r
|
||||||
|
match ty with
|
||||||
|
| .univ level => pure (t, level)
|
||||||
|
| _ => throw s!"expected a universe, got {showTy cxt.lvl ty}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def checkTop (r : Raw) : TCM (Tm × Val) :=
|
||||||
|
infer Cxt.empty r
|
||||||
|
|
||||||
|
end BidirTT
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import BidirTT.Value
|
||||||
|
|
||||||
|
namespace BidirTT
|
||||||
|
|
||||||
|
structure Cxt where
|
||||||
|
env : Env
|
||||||
|
types : List (Name × Val)
|
||||||
|
lvl : Lvl
|
||||||
|
deriving Inhabited
|
||||||
|
|
||||||
|
def Cxt.empty : Cxt := ⟨[], [], 0⟩
|
||||||
|
|
||||||
|
def Cxt.bind (cxt : Cxt) (x : Name) (a : Val) : Cxt :=
|
||||||
|
{ env := .var cxt.lvl :: cxt.env
|
||||||
|
, types := (x, a) :: cxt.types
|
||||||
|
, lvl := cxt.lvl + 1 }
|
||||||
|
|
||||||
|
def Cxt.define (cxt : Cxt) (x : Name) (v a : Val) : Cxt :=
|
||||||
|
{ env := v :: cxt.env
|
||||||
|
, types := (x, a) :: cxt.types
|
||||||
|
, lvl := cxt.lvl + 1 }
|
||||||
|
|
||||||
|
private def lookupGo : Name → List (Name × Val) → Nat → Option (Nat × Val)
|
||||||
|
| _, [], _ => none
|
||||||
|
| x, (y, a) :: rest, i => if x == y then some (i, a) else lookupGo x rest (i+1)
|
||||||
|
|
||||||
|
def Cxt.lookup (cxt : Cxt) (x : Name) : Option (Nat × Val) :=
|
||||||
|
lookupGo x cxt.types 0
|
||||||
|
|
||||||
|
end BidirTT
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import BidirTT.Value
|
||||||
|
|
||||||
|
namespace BidirTT
|
||||||
|
|
||||||
|
abbrev EvalM := Except String
|
||||||
|
|
||||||
|
mutual
|
||||||
|
partial def eval : Env → Tm → EvalM Val
|
||||||
|
| env, .var i =>
|
||||||
|
match env[i]? with
|
||||||
|
| some v => pure v
|
||||||
|
| none =>
|
||||||
|
throw s!"bad de Bruijn index {i} in environment of size {env.length}"
|
||||||
|
| env, .lam t => pure (.lam (.mk env t))
|
||||||
|
| env, .app t u => do
|
||||||
|
let vt ← eval env t
|
||||||
|
let vu ← eval env u
|
||||||
|
vApp vt vu
|
||||||
|
| env, .pi a b => do
|
||||||
|
let va ← eval env a
|
||||||
|
pure (.pi va (.mk env b))
|
||||||
|
| env, .sig a b => do
|
||||||
|
let va ← eval env a
|
||||||
|
pure (.sig va (.mk env b))
|
||||||
|
| env, .pair t u => do
|
||||||
|
let vt ← eval env t
|
||||||
|
let vu ← eval env u
|
||||||
|
pure (.pair vt vu)
|
||||||
|
| env, .fst t => do
|
||||||
|
let vt ← eval env t
|
||||||
|
vFst vt
|
||||||
|
| env, .snd t => do
|
||||||
|
let vt ← eval env t
|
||||||
|
vSnd vt
|
||||||
|
| _, .univ i => pure (.univ i)
|
||||||
|
| env, .letE _ t u => do
|
||||||
|
let vt ← eval env t
|
||||||
|
eval (vt :: env) u
|
||||||
|
|
||||||
|
partial def vApp : Val → Val → EvalM Val
|
||||||
|
| .lam c, u => cApp c u
|
||||||
|
| t, u => pure (.app t u)
|
||||||
|
|
||||||
|
partial def vFst : Val → EvalM Val
|
||||||
|
| .pair a _ => pure a
|
||||||
|
| t => pure (.fst t)
|
||||||
|
|
||||||
|
partial def vSnd : Val → EvalM Val
|
||||||
|
| .pair _ b => pure b
|
||||||
|
| t => pure (.snd t)
|
||||||
|
|
||||||
|
partial def cApp : Closure → Val → EvalM Val
|
||||||
|
| .mk env body, v => eval (v :: env) body
|
||||||
|
end
|
||||||
|
|
||||||
|
partial def quote : Lvl → Val → EvalM Tm
|
||||||
|
| l, .var x =>
|
||||||
|
if x < l then
|
||||||
|
pure (.var (l - x - 1))
|
||||||
|
else
|
||||||
|
throw s!"bad level {x} while quoting at level {l}"
|
||||||
|
| l, .app t u => do
|
||||||
|
let qt ← quote l t
|
||||||
|
let qu ← quote l u
|
||||||
|
pure (.app qt qu)
|
||||||
|
| l, .fst t => do
|
||||||
|
let qt ← quote l t
|
||||||
|
pure (.fst qt)
|
||||||
|
| l, .snd t => do
|
||||||
|
let qt ← quote l t
|
||||||
|
pure (.snd qt)
|
||||||
|
| l, .lam c => do
|
||||||
|
let body ← cApp c (.var l)
|
||||||
|
let qb ← quote (l + 1) body
|
||||||
|
pure (.lam qb)
|
||||||
|
| l, .pi a c => do
|
||||||
|
let qa ← quote l a
|
||||||
|
let body ← cApp c (.var l)
|
||||||
|
let qb ← quote (l + 1) body
|
||||||
|
pure (.pi qa qb)
|
||||||
|
| l, .sig a c => do
|
||||||
|
let qa ← quote l a
|
||||||
|
let body ← cApp c (.var l)
|
||||||
|
let qb ← quote (l + 1) body
|
||||||
|
pure (.sig qa qb)
|
||||||
|
| l, .pair a b => do
|
||||||
|
let qa ← quote l a
|
||||||
|
let qb ← quote l b
|
||||||
|
pure (.pair qa qb)
|
||||||
|
| _, .univ i => pure (.univ i)
|
||||||
|
|
||||||
|
private def andThen (lhs : EvalM Bool) (rhs : Unit → EvalM Bool) : EvalM Bool := do
|
||||||
|
if (← lhs) then
|
||||||
|
rhs ()
|
||||||
|
else
|
||||||
|
pure false
|
||||||
|
|
||||||
|
partial def conv : Lvl → Val → Val → EvalM Bool
|
||||||
|
| _, .univ i, .univ j => pure (i == j)
|
||||||
|
| l, .pi a c, .pi a' c' =>
|
||||||
|
andThen (conv l a a') fun _ => do
|
||||||
|
let b ← cApp c (.var l)
|
||||||
|
let b' ← cApp c' (.var l)
|
||||||
|
conv (l + 1) b b'
|
||||||
|
| l, .sig a c, .sig a' c' =>
|
||||||
|
andThen (conv l a a') fun _ => do
|
||||||
|
let b ← cApp c (.var l)
|
||||||
|
let b' ← cApp c' (.var l)
|
||||||
|
conv (l + 1) b b'
|
||||||
|
| l, .lam c, .lam c' =>
|
||||||
|
do
|
||||||
|
let body ← cApp c (.var l)
|
||||||
|
let body' ← cApp c' (.var l)
|
||||||
|
conv (l + 1) body body'
|
||||||
|
| l, .lam c, t =>
|
||||||
|
do
|
||||||
|
let body ← cApp c (.var l)
|
||||||
|
let apped ← vApp t (.var l)
|
||||||
|
conv (l + 1) body apped
|
||||||
|
| l, t, .lam c =>
|
||||||
|
do
|
||||||
|
let apped ← vApp t (.var l)
|
||||||
|
let body ← cApp c (.var l)
|
||||||
|
conv (l + 1) apped body
|
||||||
|
| l, .pair a b, .pair a' b' =>
|
||||||
|
andThen (conv l a a') fun _ => conv l b b'
|
||||||
|
| l, .pair a b, p =>
|
||||||
|
andThen
|
||||||
|
(do
|
||||||
|
let fstp ← vFst p
|
||||||
|
conv l a fstp)
|
||||||
|
fun _ => do
|
||||||
|
let sndp ← vSnd p
|
||||||
|
conv l b sndp
|
||||||
|
| l, p, .pair a b =>
|
||||||
|
andThen
|
||||||
|
(do
|
||||||
|
let fstp ← vFst p
|
||||||
|
conv l fstp a)
|
||||||
|
fun _ => do
|
||||||
|
let sndp ← vSnd p
|
||||||
|
conv l sndp b
|
||||||
|
| _, .var x, .var y => pure (x == y)
|
||||||
|
| l, .app t u, .app t' u' =>
|
||||||
|
andThen (conv l t t') fun _ => conv l u u'
|
||||||
|
| l, .fst t, .fst t' => conv l t t'
|
||||||
|
| l, .snd t, .snd t' => conv l t t'
|
||||||
|
| _, _, _ => pure false
|
||||||
|
|
||||||
|
end BidirTT
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import BidirTT.Syntax
|
||||||
|
|
||||||
|
namespace BidirTT.Examples
|
||||||
|
|
||||||
|
open BidirTT
|
||||||
|
|
||||||
|
def idTy : Raw :=
|
||||||
|
.pi "A" (.univ 0) (.pi "_" (.var "A") (.var "A"))
|
||||||
|
|
||||||
|
def idTm : Raw :=
|
||||||
|
.lam "A" (.lam "x" (.var "x"))
|
||||||
|
|
||||||
|
def idAnn : Raw := .ann idTm idTy
|
||||||
|
|
||||||
|
def constTy : Raw :=
|
||||||
|
.pi "A" (.univ 0) (.pi "B" (.univ 0)
|
||||||
|
(.pi "_" (.var "A") (.pi "_" (.var "B") (.var "A"))))
|
||||||
|
|
||||||
|
def constTm : Raw :=
|
||||||
|
.lam "A" (.lam "B" (.lam "x" (.lam "_" (.var "x"))))
|
||||||
|
|
||||||
|
def constAnn : Raw := .ann constTm constTy
|
||||||
|
|
||||||
|
def swapTy : Raw :=
|
||||||
|
.pi "A" (.univ 0) (.pi "B" (.univ 0)
|
||||||
|
(.pi "_" (.sig "_" (.var "A") (.var "B"))
|
||||||
|
(.sig "_" (.var "B") (.var "A"))))
|
||||||
|
|
||||||
|
def swapTm : Raw :=
|
||||||
|
.lam "A" (.lam "B" (.lam "p"
|
||||||
|
(.pair (.snd (.var "p")) (.fst (.var "p")))))
|
||||||
|
|
||||||
|
def swapAnn : Raw := .ann swapTm swapTy
|
||||||
|
|
||||||
|
def depPairTy : Raw :=
|
||||||
|
.sig "A" (.univ 2) (.var "A")
|
||||||
|
|
||||||
|
def depPairTm : Raw :=
|
||||||
|
.pair
|
||||||
|
(.univ 1)
|
||||||
|
(.pi "_" (.univ 0) (.univ 0))
|
||||||
|
|
||||||
|
def depPairAnn : Raw := .ann depPairTm depPairTy
|
||||||
|
|
||||||
|
def fstDepPair : Raw := .fst depPairAnn
|
||||||
|
|
||||||
|
def sndDepPair : Raw := .snd depPairAnn
|
||||||
|
|
||||||
|
def omegaTy : Raw :=
|
||||||
|
.pi "A" (.univ 0) (.var "A")
|
||||||
|
|
||||||
|
def omegaTm : Raw :=
|
||||||
|
.lam "x" (.app (.var "x") (.var "x"))
|
||||||
|
|
||||||
|
def omegaAnn : Raw := .ann omegaTm omegaTy
|
||||||
|
|
||||||
|
def unknownVar : Raw := .var "nope"
|
||||||
|
|
||||||
|
def pairMismatch : Raw :=
|
||||||
|
.ann (.pair (.univ 1) (.univ 1))
|
||||||
|
(.sig "A" (.univ 2) (.var "A"))
|
||||||
|
|
||||||
|
def badFst : Raw := .fst (.univ 0)
|
||||||
|
|
||||||
|
def letUniverse : Raw :=
|
||||||
|
.ann
|
||||||
|
(.letE "A" (.univ 1) (.pi "_" (.univ 0) (.univ 0)) (.var "A"))
|
||||||
|
(.univ 1)
|
||||||
|
|
||||||
|
def univ0 : Raw := .univ 0
|
||||||
|
|
||||||
|
end BidirTT.Examples
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import BidirTT.Syntax
|
||||||
|
|
||||||
|
namespace BidirTT
|
||||||
|
|
||||||
|
mutual
|
||||||
|
partial def prettyTm : Tm → String
|
||||||
|
| .var i => s!"#{i}"
|
||||||
|
| .lam t => s!"(fun => {prettyTm t})"
|
||||||
|
| .app t u => s!"({prettyTm t} {prettyTm u})"
|
||||||
|
| .pi a b => s!"(Pi {prettyTm a} -> {prettyTm b})"
|
||||||
|
| .sig a b => s!"(Sigma {prettyTm a} * {prettyTm b})"
|
||||||
|
| .pair t u => s!"({prettyTm t}, {prettyTm u})"
|
||||||
|
| .fst t => s!"({prettyTm t}.1)"
|
||||||
|
| .snd t => s!"({prettyTm t}.2)"
|
||||||
|
| .univ i => s!"U{i}"
|
||||||
|
| .letE a t u => s!"(let : {prettyTm a} := {prettyTm t}; {prettyTm u})"
|
||||||
|
end
|
||||||
|
|
||||||
|
end BidirTT
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
namespace BidirTT
|
||||||
|
|
||||||
|
abbrev Name := String
|
||||||
|
|
||||||
|
inductive Raw where
|
||||||
|
| var : Name → Raw
|
||||||
|
| lam : Name → Raw → Raw
|
||||||
|
| app : Raw → Raw → Raw
|
||||||
|
| pi : Name → Raw → Raw → Raw
|
||||||
|
| sig : Name → Raw → Raw → Raw
|
||||||
|
| pair : Raw → Raw → Raw
|
||||||
|
| fst : Raw → Raw
|
||||||
|
| snd : Raw → Raw
|
||||||
|
| univ : Nat → Raw
|
||||||
|
| letE : Name → Raw → Raw → Raw → Raw
|
||||||
|
| ann : Raw → Raw → Raw
|
||||||
|
deriving Repr, Inhabited, BEq, DecidableEq
|
||||||
|
|
||||||
|
inductive Tm where
|
||||||
|
| var : Nat → Tm
|
||||||
|
| lam : Tm → Tm
|
||||||
|
| app : Tm → Tm → Tm
|
||||||
|
| pi : Tm → Tm → Tm
|
||||||
|
| sig : Tm → Tm → Tm
|
||||||
|
| pair : Tm → Tm → Tm
|
||||||
|
| fst : Tm → Tm
|
||||||
|
| snd : Tm → Tm
|
||||||
|
| univ : Nat → Tm
|
||||||
|
| letE : Tm → Tm → Tm → Tm
|
||||||
|
deriving Repr, Inhabited, BEq, DecidableEq
|
||||||
|
|
||||||
|
end BidirTT
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import BidirTT.Syntax
|
||||||
|
|
||||||
|
namespace BidirTT
|
||||||
|
|
||||||
|
mutual
|
||||||
|
inductive Val where
|
||||||
|
| var : Nat → Val
|
||||||
|
| app : Val → Val → Val
|
||||||
|
| fst : Val → Val
|
||||||
|
| snd : Val → Val
|
||||||
|
| lam : Closure → Val
|
||||||
|
| pi : Val → Closure → Val
|
||||||
|
| sig : Val → Closure → Val
|
||||||
|
| pair : Val → Val → Val
|
||||||
|
| univ : Nat → Val
|
||||||
|
|
||||||
|
inductive Closure where
|
||||||
|
| mk : List Val → Tm → Closure
|
||||||
|
end
|
||||||
|
|
||||||
|
abbrev Env := List Val
|
||||||
|
abbrev Lvl := Nat
|
||||||
|
|
||||||
|
instance : Inhabited Val := ⟨.univ 0⟩
|
||||||
|
instance : Inhabited Closure := ⟨.mk [] (.univ 0)⟩
|
||||||
|
|
||||||
|
end BidirTT
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import BidirTT
|
||||||
|
|
||||||
|
open BidirTT
|
||||||
|
|
||||||
|
def runOne (label : String) (r : Raw) : IO Unit := do
|
||||||
|
match checkTop r with
|
||||||
|
| .ok (t, ty) =>
|
||||||
|
IO.println s!"[ok] {label}"
|
||||||
|
IO.println s!" term : {BidirTT.prettyTm t}"
|
||||||
|
match quote 0 ty with
|
||||||
|
| Except.ok qt =>
|
||||||
|
IO.println s!" type : {BidirTT.prettyTm qt}"
|
||||||
|
| Except.error err =>
|
||||||
|
IO.println s!" type : <error: {err}>"
|
||||||
|
| .error e =>
|
||||||
|
IO.println s!"[err] {label}: {e}"
|
||||||
|
|
||||||
|
def main : IO Unit := do
|
||||||
|
runOne "U0" Examples.univ0
|
||||||
|
runOne "id" Examples.idAnn
|
||||||
|
runOne "const" Examples.constAnn
|
||||||
|
runOne "swap" Examples.swapAnn
|
||||||
|
runOne "depPair" Examples.depPairAnn
|
||||||
|
runOne "depPair.1" Examples.fstDepPair
|
||||||
|
runOne "depPair.2" Examples.sndDepPair
|
||||||
|
runOne "let universe" Examples.letUniverse
|
||||||
|
runOne "omega (bad)" Examples.omegaAnn
|
||||||
|
runOne "unknown var" Examples.unknownVar
|
||||||
|
runOne "pair mismatch" Examples.pairMismatch
|
||||||
|
runOne "bad fst" Examples.badFst
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# iris
|
||||||
|
|
||||||
|
Small (toy) dependently typed core w. a bidirectional typechecker. It takes a named raw syntax, checks and elaborates it into a de Bruijn core and evaluates terms into semantic values, quotes them back for diagnostics and uses conversion checking to compare normal forms. The current kernel has explicit universe levels `U0`, `U1`, `U2`, dependent function and pair types, projections, annotations, and `let`
|
||||||
|
|
||||||
|
One of the checked examples is:
|
||||||
|
|
||||||
|
```lean
|
||||||
|
def depPairTy : Raw :=
|
||||||
|
.sig "A" (.univ 2) (.var "A")
|
||||||
|
|
||||||
|
def depPairTm : Raw :=
|
||||||
|
.pair
|
||||||
|
(.univ 1)
|
||||||
|
(.pi "_" (.univ 0) (.univ 0))
|
||||||
|
```
|
||||||
|
|
||||||
|
which elaborates to a pair whose first component is the type `U1` and whose second component inhabits it
|
||||||
+106
@@ -0,0 +1,106 @@
|
|||||||
|
import BidirTT
|
||||||
|
|
||||||
|
open BidirTT
|
||||||
|
|
||||||
|
inductive Expectation where
|
||||||
|
| okTy : Tm → Expectation
|
||||||
|
| errContains : String → Expectation
|
||||||
|
|
||||||
|
private def renderType (ty : Val) : Except String Tm :=
|
||||||
|
BidirTT.quote 0 ty
|
||||||
|
|
||||||
|
private def containsText (haystack needle : String) : Bool :=
|
||||||
|
needle.isEmpty || (haystack.splitOn needle).length > 1
|
||||||
|
|
||||||
|
structure TestCase where
|
||||||
|
name : String
|
||||||
|
input : Raw
|
||||||
|
expect : Expectation
|
||||||
|
|
||||||
|
def cases : List TestCase := [
|
||||||
|
⟨"U0 is typed by U1", Examples.univ0,
|
||||||
|
.okTy (.univ 1)⟩,
|
||||||
|
⟨"id typechecks", Examples.idAnn,
|
||||||
|
.okTy (.pi (.univ 0) (.pi (.var 0) (.var 1)))⟩,
|
||||||
|
⟨"const typechecks", Examples.constAnn,
|
||||||
|
.okTy (.pi (.univ 0) (.pi (.univ 0) (.pi (.var 1) (.pi (.var 1) (.var 3)))))⟩,
|
||||||
|
⟨"swap typechecks", Examples.swapAnn,
|
||||||
|
.okTy (.pi (.univ 0) (.pi (.univ 0) (.pi (.sig (.var 1) (.var 1)) (.sig (.var 1) (.var 3)))))⟩,
|
||||||
|
⟨"dependent pair typechecks", Examples.depPairAnn,
|
||||||
|
.okTy (.sig (.univ 2) (.var 0))⟩,
|
||||||
|
⟨"fst infers the first projection", Examples.fstDepPair,
|
||||||
|
.okTy (.univ 2)⟩,
|
||||||
|
⟨"snd infers the dependent second projection", Examples.sndDepPair,
|
||||||
|
.okTy (.univ 1)⟩,
|
||||||
|
⟨"let infers through definitions", Examples.letUniverse,
|
||||||
|
.okTy (.univ 1)⟩,
|
||||||
|
⟨"self application rejected", Examples.omegaAnn,
|
||||||
|
.errContains "expected Pi type in application"⟩,
|
||||||
|
⟨"unknown variable rejected", Examples.unknownVar,
|
||||||
|
.errContains "unknown variable nope"⟩,
|
||||||
|
⟨"pair mismatch rejected at the Sigma body", Examples.pairMismatch,
|
||||||
|
.errContains "type mismatch: expected U1, got U2"⟩,
|
||||||
|
⟨"bad fst rejected", Examples.badFst,
|
||||||
|
.errContains "expected Sigma type in .1, got U1"⟩
|
||||||
|
]
|
||||||
|
|
||||||
|
def runCase (tc : TestCase) : IO Bool := do
|
||||||
|
match tc.expect, checkTop tc.input with
|
||||||
|
| .okTy expectedTy, .ok (_, ty) =>
|
||||||
|
match renderType ty with
|
||||||
|
| Except.ok actualTy =>
|
||||||
|
if actualTy == expectedTy then
|
||||||
|
IO.println s!"PASS {tc.name}"
|
||||||
|
pure true
|
||||||
|
else
|
||||||
|
IO.println s!"FAIL {tc.name} (expected type {BidirTT.prettyTm expectedTy}, got {BidirTT.prettyTm actualTy})"
|
||||||
|
pure false
|
||||||
|
| Except.error err =>
|
||||||
|
IO.println s!"FAIL {tc.name} (could not quote type: {err})"
|
||||||
|
pure false
|
||||||
|
| .okTy expectedTy, .error err =>
|
||||||
|
IO.println s!"FAIL {tc.name} (expected type {BidirTT.prettyTm expectedTy}, got error {err})"
|
||||||
|
pure false
|
||||||
|
| .errContains needle, .error err =>
|
||||||
|
if containsText err needle then
|
||||||
|
IO.println s!"PASS {tc.name}"
|
||||||
|
pure true
|
||||||
|
else
|
||||||
|
IO.println s!"FAIL {tc.name} (expected error containing {needle}, got {err})"
|
||||||
|
pure false
|
||||||
|
| .errContains needle, .ok (_, ty) =>
|
||||||
|
match renderType ty with
|
||||||
|
| Except.ok actualTy =>
|
||||||
|
IO.println s!"FAIL {tc.name} (expected error containing {needle}, got type {BidirTT.prettyTm actualTy})"
|
||||||
|
pure false
|
||||||
|
| Except.error err =>
|
||||||
|
IO.println s!"FAIL {tc.name} (expected error containing {needle}, got quote failure {err})"
|
||||||
|
pure false
|
||||||
|
|
||||||
|
def runInternalSafetyChecks : IO Bool := do
|
||||||
|
let malformedEvalOk :=
|
||||||
|
match eval [] (.var 0) with
|
||||||
|
| Except.error err => containsText err "bad de Bruijn index 0"
|
||||||
|
| Except.ok _ => false
|
||||||
|
let malformedQuoteOk :=
|
||||||
|
match quote 0 (.var 0) with
|
||||||
|
| Except.error err => containsText err "bad level 0"
|
||||||
|
| Except.ok _ => false
|
||||||
|
if malformedEvalOk && malformedQuoteOk then
|
||||||
|
IO.println "PASS malformed core terms are rejected safely"
|
||||||
|
pure true
|
||||||
|
else
|
||||||
|
IO.println "FAIL malformed core terms are rejected safely"
|
||||||
|
pure false
|
||||||
|
|
||||||
|
def main : IO UInt32 := do
|
||||||
|
let results ← cases.mapM runCase
|
||||||
|
let safetyOk ← runInternalSafetyChecks
|
||||||
|
let allResults := results ++ [safetyOk]
|
||||||
|
let failed := allResults.countP (· == false)
|
||||||
|
if failed == 0 then
|
||||||
|
IO.println s!"\n{allResults.length} passed, 0 failed"
|
||||||
|
pure 0
|
||||||
|
else
|
||||||
|
IO.println s!"\n{allResults.length - failed} passed, {failed} failed"
|
||||||
|
pure 1
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import Lake
|
||||||
|
open Lake DSL
|
||||||
|
|
||||||
|
package bidirtt where
|
||||||
|
leanOptions := #[
|
||||||
|
⟨`pp.unicode.fun, true⟩,
|
||||||
|
⟨`autoImplicit, false⟩
|
||||||
|
]
|
||||||
|
|
||||||
|
@[default_target]
|
||||||
|
lean_lib BidirTT where
|
||||||
|
|
||||||
|
lean_exe bidirtt where
|
||||||
|
root := `Main
|
||||||
|
|
||||||
|
lean_exe tests where
|
||||||
|
root := `Tests
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
leanprover/lean4:v4.15.0
|
||||||
Reference in New Issue
Block a user