2024-10-05 13:31:09 -07:00
|
|
|
module Expr where
|
|
|
|
|
|
2024-12-08 12:40:52 -08:00
|
|
|
import qualified Data.List.NonEmpty as NE
|
|
|
|
|
import Prettyprinter
|
|
|
|
|
import Prettyprinter.Render.Text
|
|
|
|
|
import Prelude hiding (group)
|
|
|
|
|
|
2024-10-05 13:31:09 -07:00
|
|
|
data Expr where
|
2024-11-22 19:44:31 -08:00
|
|
|
Var :: Text -> Integer -> Expr
|
2024-11-17 01:57:53 -08:00
|
|
|
Free :: Text -> Expr
|
2024-11-30 23:43:17 -08:00
|
|
|
Axiom :: Text -> Expr
|
2024-12-02 20:39:56 -08:00
|
|
|
Star :: Expr
|
2024-11-28 13:39:23 -08:00
|
|
|
Level :: Integer -> Expr
|
2024-10-06 14:02:35 -07:00
|
|
|
App :: Expr -> Expr -> Expr
|
2024-12-11 14:12:56 -08:00
|
|
|
Abs :: Text -> Expr -> Expr -> Expr
|
|
|
|
|
Pi :: Text -> Expr -> Expr -> Expr
|
2024-11-30 22:36:27 -08:00
|
|
|
Let :: Text -> Maybe Expr -> Expr -> Expr -> Expr
|
2024-11-17 01:57:53 -08:00
|
|
|
deriving (Show, Ord)
|
2024-11-11 23:38:10 -08:00
|
|
|
|
2024-12-08 12:40:52 -08:00
|
|
|
instance Pretty Expr where
|
2024-12-08 16:11:21 -08:00
|
|
|
pretty = prettyExpr 0 . cleanNames . dedupNames
|
2024-12-08 12:40:52 -08:00
|
|
|
|
2024-11-11 23:38:10 -08:00
|
|
|
instance Eq Expr where
|
2024-11-22 19:44:31 -08:00
|
|
|
(Var _ n) == (Var _ m) = n == m
|
2024-11-20 07:37:49 -08:00
|
|
|
(Free s) == (Free t) = s == t
|
2024-11-30 23:43:17 -08:00
|
|
|
(Axiom s) == (Axiom t) = s == t
|
2024-12-02 20:39:56 -08:00
|
|
|
Star == Star = True
|
2024-11-28 13:39:23 -08:00
|
|
|
(Level i) == (Level j) = i == j
|
2024-11-11 23:38:10 -08:00
|
|
|
(App e1 e2) == (App f1 f2) = e1 == f1 && e2 == f2
|
2024-12-11 14:12:56 -08:00
|
|
|
(Abs _ t1 b1) == (Abs _ t2 b2) = t1 == t2 && b1 == b2
|
|
|
|
|
(Pi _ t1 b1) == (Pi _ t2 b2) = t1 == t2 && b1 == b2
|
2024-11-30 22:36:27 -08:00
|
|
|
(Let _ _ v1 b1) == (Let _ _ v2 b2) = v1 == v2 && b1 == b2
|
2024-11-11 23:38:10 -08:00
|
|
|
_ == _ = False
|
2024-10-06 14:02:35 -07:00
|
|
|
|
2024-11-11 13:43:28 -08:00
|
|
|
occursFree :: Integer -> Expr -> Bool
|
2024-11-22 19:44:31 -08:00
|
|
|
occursFree n (Var _ k) = n == k
|
2024-11-17 01:57:53 -08:00
|
|
|
occursFree _ (Free _) = False
|
2024-11-30 23:43:17 -08:00
|
|
|
occursFree _ (Axiom _) = False
|
2024-12-02 20:39:56 -08:00
|
|
|
occursFree _ Star = False
|
2024-11-28 13:39:23 -08:00
|
|
|
occursFree _ (Level _) = False
|
2024-11-11 13:52:50 -08:00
|
|
|
occursFree n (App a b) = on (||) (occursFree n) a b
|
2024-12-11 14:12:56 -08:00
|
|
|
occursFree n (Abs _ a b) = occursFree n a || occursFree (n + 1) b
|
|
|
|
|
occursFree n (Pi _ a b) = occursFree n a || occursFree (n + 1) b
|
2024-11-30 22:36:27 -08:00
|
|
|
occursFree n (Let _ _ v b) = occursFree n v || occursFree (n + 1) b
|
2024-11-11 13:43:28 -08:00
|
|
|
|
2024-11-17 01:57:53 -08:00
|
|
|
shiftIndices :: Integer -> Integer -> Expr -> Expr
|
2024-11-22 19:44:31 -08:00
|
|
|
shiftIndices d c (Var x k)
|
|
|
|
|
| k >= c = Var x (k + d)
|
|
|
|
|
| otherwise = Var x k
|
2024-11-17 01:57:53 -08:00
|
|
|
shiftIndices _ _ (Free s) = Free s
|
2024-11-30 23:43:17 -08:00
|
|
|
shiftIndices _ _ (Axiom s) = Axiom s
|
2024-12-02 20:39:56 -08:00
|
|
|
shiftIndices _ _ Star = Star
|
2024-11-28 13:39:23 -08:00
|
|
|
shiftIndices _ _ (Level i) = Level i
|
2024-11-17 01:57:53 -08:00
|
|
|
shiftIndices d c (App m n) = App (shiftIndices d c m) (shiftIndices d c n)
|
2024-12-11 14:12:56 -08:00
|
|
|
shiftIndices d c (Abs x m n) = Abs x (shiftIndices d c m) (shiftIndices d (c + 1) n)
|
|
|
|
|
shiftIndices d c (Pi x m n) = Pi x (shiftIndices d c m) (shiftIndices d (c + 1) n)
|
2024-11-30 22:36:27 -08:00
|
|
|
shiftIndices d c (Let x t v b) = Let x t (shiftIndices d c v) (shiftIndices d (c + 1) b)
|
2024-11-17 01:57:53 -08:00
|
|
|
|
|
|
|
|
incIndices :: Expr -> Expr
|
|
|
|
|
incIndices = shiftIndices 1 0
|
|
|
|
|
|
2024-10-06 14:02:35 -07:00
|
|
|
{- --------------------- PRETTY PRINTING ----------------------------- -}
|
|
|
|
|
|
2024-12-08 16:11:21 -08:00
|
|
|
dedupNames :: Expr -> Expr
|
|
|
|
|
dedupNames = go []
|
|
|
|
|
where
|
|
|
|
|
varName :: [Text] -> Text -> Int -> Text
|
2024-12-11 13:26:05 -08:00
|
|
|
varName bs x k =
|
|
|
|
|
if x == ""
|
|
|
|
|
then x
|
|
|
|
|
else case bs !!? k of
|
|
|
|
|
Nothing -> x
|
|
|
|
|
Just x' ->
|
|
|
|
|
let count = (length $ filter (== x') $ drop (k + 1) bs)
|
|
|
|
|
in if count > 0
|
|
|
|
|
then x <> printLevel count
|
|
|
|
|
else x
|
2024-12-08 16:11:21 -08:00
|
|
|
|
|
|
|
|
go :: [Text] -> Expr -> Expr
|
|
|
|
|
go bs (Var x k) = Var (varName bs x (fromIntegral k)) k
|
|
|
|
|
go bs (App m n) = App (go bs m) (go bs n)
|
2024-12-11 14:12:56 -08:00
|
|
|
go bs (Abs x ty b) = Abs (varName (x : bs) x 0) (go bs ty) (go (x : bs) b)
|
|
|
|
|
go bs (Pi x ty b) = Pi (varName (x : bs) x 0) (go bs ty) (go (x : bs) b)
|
2024-12-08 16:11:21 -08:00
|
|
|
go bs (Let x ascr val b) = Let (varName (x : bs) x 0) (go bs <$> ascr) (go bs val) (go (x : bs) b)
|
|
|
|
|
go _ e = e
|
|
|
|
|
|
2024-12-08 12:40:52 -08:00
|
|
|
data Param = Param Text Expr
|
|
|
|
|
data ParamGroup = ParamGroup [Text] Expr
|
|
|
|
|
data Binding = Binding Text [ParamGroup] Expr
|
|
|
|
|
|
|
|
|
|
instance Pretty Param where
|
|
|
|
|
pretty (Param x ty) = group $ parens $ pretty x <+> ":" <+> pretty ty
|
|
|
|
|
|
|
|
|
|
instance Pretty ParamGroup where
|
|
|
|
|
pretty (ParamGroup vars ty) = group $ parens $ align (sep (map pretty vars)) <+> ":" <+> pretty ty
|
2024-11-11 14:34:55 -08:00
|
|
|
|
2024-12-08 12:40:52 -08:00
|
|
|
instance Pretty Binding where
|
|
|
|
|
pretty (Binding var [] body) = group $ parens $ pretty var <+> ":=" <+> pretty body
|
|
|
|
|
pretty (Binding var params body) = group $ parens $ pretty var <+> align (sep (map pretty params)) <+> ":=" <+> pretty body
|
2024-11-23 10:35:58 -08:00
|
|
|
|
|
|
|
|
collectLambdas :: Expr -> ([Param], Expr)
|
2024-12-11 14:12:56 -08:00
|
|
|
collectLambdas (Abs x ty body) = (Param x ty : params, final)
|
2024-11-11 16:38:46 -08:00
|
|
|
where
|
|
|
|
|
(params, final) = collectLambdas body
|
|
|
|
|
collectLambdas e = ([], e)
|
|
|
|
|
|
2024-11-23 10:35:58 -08:00
|
|
|
collectLets :: Expr -> ([Binding], Expr)
|
2024-12-08 12:40:52 -08:00
|
|
|
collectLets (Let x _ val body) = (Binding x params' val' : bindings, final)
|
2024-11-23 09:16:32 -08:00
|
|
|
where
|
2024-11-23 10:35:58 -08:00
|
|
|
(bindings, final) = collectLets body
|
|
|
|
|
(params, val') = collectLambdas val
|
|
|
|
|
params' = groupParams params
|
2024-11-23 09:16:32 -08:00
|
|
|
collectLets e = ([], e)
|
2024-11-17 18:33:14 -08:00
|
|
|
|
2024-11-23 10:35:58 -08:00
|
|
|
collectPis :: Expr -> ([Param], Expr)
|
2024-12-11 14:12:56 -08:00
|
|
|
collectPis p@(Pi "" _ _) = ([], p)
|
|
|
|
|
collectPis (Pi x ty body) = (Param x ty : params, final)
|
2024-11-11 16:38:46 -08:00
|
|
|
where
|
|
|
|
|
(params, final) = collectPis body
|
|
|
|
|
collectPis e = ([], e)
|
|
|
|
|
|
2024-12-08 16:11:21 -08:00
|
|
|
collectArrows :: Expr -> NonEmpty Expr
|
2024-12-11 14:12:56 -08:00
|
|
|
collectArrows (Pi "" t1 t2) = t1 :| toList rest
|
2024-12-08 16:11:21 -08:00
|
|
|
where
|
|
|
|
|
rest = collectArrows t2
|
|
|
|
|
collectArrows e = pure e
|
|
|
|
|
|
2024-12-08 12:40:52 -08:00
|
|
|
collectApps :: Expr -> NonEmpty Expr
|
|
|
|
|
collectApps (App e1 e2) = e2 :| toList rest
|
|
|
|
|
where
|
|
|
|
|
rest = collectApps e1
|
|
|
|
|
collectApps e = pure e
|
|
|
|
|
|
2024-11-23 09:16:32 -08:00
|
|
|
cleanNames :: Expr -> Expr
|
|
|
|
|
cleanNames (App m n) = App (cleanNames m) (cleanNames n)
|
2024-12-11 14:12:56 -08:00
|
|
|
cleanNames (Abs x ty body) = Abs x (cleanNames ty) (cleanNames body)
|
|
|
|
|
cleanNames (Pi x ty body)
|
|
|
|
|
| occursFree 0 body = Pi x (cleanNames ty) (cleanNames body)
|
|
|
|
|
| otherwise = Pi "" ty (cleanNames body)
|
2024-11-23 09:16:32 -08:00
|
|
|
cleanNames e = e
|
|
|
|
|
|
2024-11-23 10:35:58 -08:00
|
|
|
groupParams :: [Param] -> [ParamGroup]
|
2024-11-11 16:38:46 -08:00
|
|
|
groupParams = foldr addParam []
|
|
|
|
|
where
|
2024-12-08 12:40:52 -08:00
|
|
|
addParam :: Param -> [ParamGroup] -> [ParamGroup]
|
|
|
|
|
addParam (Param x t) [] = [ParamGroup [x] t]
|
|
|
|
|
addParam (Param x t) l@(ParamGroup xs s : rest)
|
|
|
|
|
| incIndices t == s = ParamGroup (x : xs) t : rest
|
|
|
|
|
| otherwise = ParamGroup [x] t : l
|
|
|
|
|
|
2024-12-08 16:11:21 -08:00
|
|
|
printLevel :: (IsString s, Semigroup s, Integral i) => i -> s
|
2024-12-02 20:39:56 -08:00
|
|
|
printLevel k
|
|
|
|
|
| k == 0 = "₀"
|
|
|
|
|
| k == 1 = "₁"
|
|
|
|
|
| k == 2 = "₂"
|
|
|
|
|
| k == 3 = "₃"
|
|
|
|
|
| k == 4 = "₄"
|
|
|
|
|
| k == 5 = "₅"
|
|
|
|
|
| k == 6 = "₆"
|
|
|
|
|
| k == 7 = "₇"
|
|
|
|
|
| k == 8 = "₈"
|
|
|
|
|
| k == 9 = "₉"
|
2024-12-08 16:11:21 -08:00
|
|
|
| k < 0 = printLevel k
|
2024-12-02 20:39:56 -08:00
|
|
|
| otherwise = printLevel (k `div` 10) <> printLevel (k `mod` 10)
|
|
|
|
|
|
2024-12-08 12:40:52 -08:00
|
|
|
prettyExpr :: Integer -> Expr -> Doc ann
|
|
|
|
|
prettyExpr k expr = case expr of
|
|
|
|
|
Var s _ -> pretty s
|
|
|
|
|
Free s -> pretty s
|
|
|
|
|
Axiom s -> pretty s
|
2024-12-13 22:45:37 -08:00
|
|
|
Star -> "★"
|
2024-12-08 12:40:52 -08:00
|
|
|
Level i
|
|
|
|
|
| i == 0 -> "□"
|
|
|
|
|
| otherwise -> "□" <> printLevel i
|
|
|
|
|
App{}
|
|
|
|
|
| k > 3 -> parens application
|
|
|
|
|
| otherwise -> application
|
|
|
|
|
where
|
|
|
|
|
(top :| rest) = NE.reverse $ collectApps expr
|
|
|
|
|
application = group $ hang 4 $ prettyExpr 3 top <> line <> align (sep $ map (prettyExpr 4) rest)
|
2024-12-11 14:12:56 -08:00
|
|
|
Pi "" _ _
|
2024-12-08 12:40:52 -08:00
|
|
|
| k > 2 -> parens piType
|
|
|
|
|
| otherwise -> piType
|
|
|
|
|
where
|
2024-12-08 16:11:21 -08:00
|
|
|
(top :| rest) = collectArrows expr
|
|
|
|
|
piType = group $ hang 4 $ prettyExpr 3 top <+> align (sep $ map (("->" <+>) . prettyExpr 2) rest)
|
2024-12-08 12:40:52 -08:00
|
|
|
Pi{} -> group $ hang 4 $ "∏" <+> align (sep grouped) <> "," <> line <> align (prettyExpr 0 body)
|
|
|
|
|
where
|
|
|
|
|
(params, body) = collectPis expr
|
|
|
|
|
grouped = pretty <$> groupParams params
|
|
|
|
|
Abs{} ->
|
|
|
|
|
if k >= 1
|
|
|
|
|
then parens lambdaForm
|
|
|
|
|
else lambdaForm
|
|
|
|
|
where
|
|
|
|
|
(params, body) = collectLambdas expr
|
|
|
|
|
grouped = pretty <$> groupParams params
|
|
|
|
|
lambdaForm = group $ hang 4 $ "λ" <+> align (sep grouped) <+> "=>" <> line <> align (prettyExpr 0 body)
|
|
|
|
|
Let{} ->
|
|
|
|
|
group $
|
|
|
|
|
vsep
|
|
|
|
|
[ "let" <+> align bindings
|
|
|
|
|
, "in" <+> align (prettyExpr 0 body)
|
|
|
|
|
, "end"
|
|
|
|
|
]
|
|
|
|
|
where
|
|
|
|
|
(binds, body) = collectLets expr
|
|
|
|
|
bindings = sep $ map pretty binds
|
|
|
|
|
|
|
|
|
|
prettyT :: Expr -> Text
|
|
|
|
|
prettyT = renderStrict . layoutSmart defaultLayoutOptions . pretty
|
2024-10-06 14:02:35 -07:00
|
|
|
|
2024-11-14 22:02:04 -08:00
|
|
|
prettyS :: Expr -> String
|
2024-12-08 12:40:52 -08:00
|
|
|
prettyS = toString . prettyT
|