mirror of
https://github.com/github/semantic.git
synced 2025-01-09 00:56:32 +03:00
Merge branch 'master' into the-point-of-no-carriage-return
This commit is contained in:
commit
a1e0e64204
@ -1,11 +1,8 @@
|
|||||||
{-# LANGUAGE DeriveFunctor, FlexibleContexts, FlexibleInstances, LambdaCase, OverloadedStrings, RecordWildCards, ScopedTypeVariables, TypeApplications #-}
|
{-# LANGUAGE DeriveGeneric, DeriveTraversable, FlexibleContexts, FlexibleInstances, LambdaCase, OverloadedStrings, QuantifiedConstraints, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TypeApplications, TypeOperators #-}
|
||||||
module Analysis.Typecheck
|
module Analysis.Typecheck
|
||||||
( Monotype (..)
|
( Monotype (..)
|
||||||
, Meta
|
, Meta
|
||||||
, Polytype (PForAll, PBool, PFree, PArr)
|
, Polytype (..)
|
||||||
, Scope
|
|
||||||
, Analysis.Typecheck.bind
|
|
||||||
, Analysis.Typecheck.instantiate
|
|
||||||
, typecheckingFlowInsensitive
|
, typecheckingFlowInsensitive
|
||||||
, typecheckingAnalysis
|
, typecheckingAnalysis
|
||||||
) where
|
) where
|
||||||
@ -13,15 +10,16 @@ module Analysis.Typecheck
|
|||||||
import Analysis.Eval
|
import Analysis.Eval
|
||||||
import Analysis.FlowInsensitive
|
import Analysis.FlowInsensitive
|
||||||
import Control.Applicative (Alternative (..))
|
import Control.Applicative (Alternative (..))
|
||||||
import Control.Effect
|
import Control.Effect.Carrier
|
||||||
import Control.Effect.Fail
|
import Control.Effect.Fail
|
||||||
import Control.Effect.Fresh as Fresh
|
import Control.Effect.Fresh as Fresh
|
||||||
import Control.Effect.Reader hiding (Local)
|
import Control.Effect.Reader hiding (Local)
|
||||||
import Control.Effect.State
|
import Control.Effect.State
|
||||||
import Control.Monad (unless)
|
import Control.Monad (unless)
|
||||||
|
import Control.Monad.Module
|
||||||
import qualified Data.Core as Core
|
import qualified Data.Core as Core
|
||||||
import Data.File
|
import Data.File
|
||||||
import Data.Foldable (foldl', for_)
|
import Data.Foldable (for_)
|
||||||
import Data.Function (fix)
|
import Data.Function (fix)
|
||||||
import Data.Functor (($>))
|
import Data.Functor (($>))
|
||||||
import qualified Data.IntMap as IntMap
|
import qualified Data.IntMap as IntMap
|
||||||
@ -29,102 +27,84 @@ import qualified Data.IntSet as IntSet
|
|||||||
import Data.List.NonEmpty (nonEmpty)
|
import Data.List.NonEmpty (nonEmpty)
|
||||||
import Data.Loc
|
import Data.Loc
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
|
import Data.Maybe (fromJust, fromMaybe)
|
||||||
import Data.Name as Name
|
import Data.Name as Name
|
||||||
|
import Data.Scope
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import Data.Stack
|
import Data.Stack
|
||||||
import Data.Term
|
import Data.Term
|
||||||
|
import Data.Void
|
||||||
|
import GHC.Generics (Generic1)
|
||||||
import Prelude hiding (fail)
|
import Prelude hiding (fail)
|
||||||
|
|
||||||
data Monotype a
|
data Monotype f a
|
||||||
= MBool
|
= Bool
|
||||||
| MUnit
|
| Unit
|
||||||
| MString
|
| String
|
||||||
| MMeta a
|
| Arr (f a) (f a)
|
||||||
| MFree Gensym
|
| Record (Map.Map User (f a))
|
||||||
| MArr (Monotype a) (Monotype a)
|
deriving (Foldable, Functor, Generic1, Traversable)
|
||||||
| MRecord (Map.Map User (Monotype a))
|
|
||||||
deriving (Eq, Functor, Ord, Show)
|
deriving instance (Eq a, forall a . Eq a => Eq (f a), Monad f) => Eq (Monotype f a)
|
||||||
|
deriving instance (Ord a, forall a . Eq a => Eq (f a)
|
||||||
|
, forall a . Ord a => Ord (f a), Monad f) => Ord (Monotype f a)
|
||||||
|
deriving instance (Show a, forall a . Show a => Show (f a)) => Show (Monotype f a)
|
||||||
|
|
||||||
|
instance HFunctor Monotype
|
||||||
|
instance RightModule Monotype where
|
||||||
|
Unit >>=* _ = Unit
|
||||||
|
Bool >>=* _ = Bool
|
||||||
|
String >>=* _ = String
|
||||||
|
Arr a b >>=* f = Arr (a >>= f) (b >>= f)
|
||||||
|
Record m >>=* f = Record ((>>= f) <$> m)
|
||||||
|
|
||||||
type Meta = Int
|
type Meta = Int
|
||||||
|
|
||||||
data Polytype
|
newtype Polytype f a = PForAll (Scope () f a)
|
||||||
= PForAll Scope
|
deriving (Foldable, Functor, Generic1, Traversable)
|
||||||
| PUnit
|
|
||||||
| PBool
|
|
||||||
| PString
|
|
||||||
| PBound Int
|
|
||||||
| PFree Gensym
|
|
||||||
| PArr Polytype Polytype
|
|
||||||
| PRecord (Map.Map User Polytype)
|
|
||||||
deriving (Eq, Ord, Show)
|
|
||||||
|
|
||||||
newtype Scope = Scope Polytype
|
deriving instance (Eq a, forall a . Eq a => Eq (f a), Monad f) => Eq (Polytype f a)
|
||||||
deriving (Eq, Ord, Show)
|
deriving instance (Ord a, forall a . Eq a => Eq (f a)
|
||||||
|
, forall a . Ord a => Ord (f a), Monad f) => Ord (Polytype f a)
|
||||||
|
deriving instance (Show a, forall a . Show a => Show (f a)) => Show (Polytype f a)
|
||||||
|
|
||||||
forAll :: Gensym -> Polytype -> Polytype
|
instance HFunctor Polytype
|
||||||
forAll n body = PForAll (Analysis.Typecheck.bind n body)
|
instance RightModule Polytype where
|
||||||
|
PForAll b >>=* f = PForAll (b >>=* f)
|
||||||
|
|
||||||
forAlls :: Foldable t => t Gensym -> Polytype -> Polytype
|
|
||||||
|
forAll :: (Eq a, Carrier sig m, Member Polytype sig) => a -> m a -> m a
|
||||||
|
forAll n body = send (PForAll (Data.Scope.bind1 n body))
|
||||||
|
|
||||||
|
forAlls :: (Eq a, Carrier sig m, Member Polytype sig, Foldable t) => t a -> m a -> m a
|
||||||
forAlls ns body = foldr forAll body ns
|
forAlls ns body = foldr forAll body ns
|
||||||
|
|
||||||
generalize :: (Carrier sig m, Member Naming sig) => Monotype Meta -> m Polytype
|
generalize :: Term Monotype Meta -> Term (Polytype :+: Monotype) Void
|
||||||
generalize ty = namespace "generalize" $ do
|
generalize ty = fromJust (closed (forAlls (IntSet.toList (mvs ty)) (hoistTerm R ty)))
|
||||||
Gensym root _ <- Name.fresh
|
|
||||||
pure (forAlls (map (Gensym root) (IntSet.toList (mvs ty))) (fold root ty))
|
|
||||||
where fold root = \case
|
|
||||||
MUnit -> PUnit
|
|
||||||
MBool -> PBool
|
|
||||||
MString -> PString
|
|
||||||
MMeta i -> PFree (Gensym root i)
|
|
||||||
MFree n -> PFree n
|
|
||||||
MArr a b -> PArr (fold root a) (fold root b)
|
|
||||||
MRecord fs -> PRecord (fold root <$> fs)
|
|
||||||
|
|
||||||
-- | Bind occurrences of a 'Gensym' in a 'Polytype' term, producing a 'Scope' in which the 'Gensym' is bound.
|
|
||||||
bind :: Gensym -> Polytype -> Scope
|
|
||||||
bind name = Scope . substIn (\ i n -> if name == n then PBound i else PFree n) (const PBound)
|
|
||||||
|
|
||||||
-- | Substitute a 'Polytype' term for the free variable in a given 'Scope', producing a closed 'Polytype' term.
|
|
||||||
instantiate :: Polytype -> Scope -> Polytype
|
|
||||||
instantiate image (Scope body) = substIn (const PFree) (\ i j -> if i == j then image else PBound j) body
|
|
||||||
|
|
||||||
substIn :: (Int -> Gensym -> Polytype)
|
|
||||||
-> (Int -> Int -> Polytype)
|
|
||||||
-> Polytype
|
|
||||||
-> Polytype
|
|
||||||
substIn free bound = go 0
|
|
||||||
where go i (PFree name) = free i name
|
|
||||||
go i (PBound j) = bound i j
|
|
||||||
go i (PForAll (Scope body)) = PForAll (Scope (go (succ i) body))
|
|
||||||
go _ PUnit = PUnit
|
|
||||||
go _ PBool = PBool
|
|
||||||
go _ PString = PString
|
|
||||||
go i (PArr a b) = PArr (go i a) (go i b)
|
|
||||||
go i (PRecord fs) = PRecord (go i <$> fs)
|
|
||||||
|
|
||||||
|
|
||||||
typecheckingFlowInsensitive :: [File (Term Core.Core Name)] -> (Heap Name (Monotype Meta), [File (Either (Loc, String) Polytype)])
|
typecheckingFlowInsensitive :: [File (Term Core.Core Name)] -> (Heap Name (Term Monotype Meta), [File (Either (Loc, String) (Term (Polytype :+: Monotype) Void))])
|
||||||
typecheckingFlowInsensitive
|
typecheckingFlowInsensitive
|
||||||
= run
|
= run
|
||||||
. runFresh
|
. runFresh
|
||||||
. runNaming
|
. runNaming
|
||||||
. runHeap (Gen (Gensym (Nil :> "root") 0))
|
. runHeap (Gen (Gensym (Nil :> "root") 0))
|
||||||
. (>>= traverse (traverse (traverse generalize)))
|
. fmap (fmap (fmap (fmap generalize)))
|
||||||
. traverse runFile
|
. traverse runFile
|
||||||
|
|
||||||
runFile :: ( Carrier sig m
|
runFile :: ( Carrier sig m
|
||||||
, Effect sig
|
, Effect sig
|
||||||
, Member Fresh sig
|
, Member Fresh sig
|
||||||
, Member Naming sig
|
, Member Naming sig
|
||||||
, Member (State (Heap Name (Monotype Meta))) sig
|
, Member (State (Heap Name (Term Monotype Meta))) sig
|
||||||
)
|
)
|
||||||
=> File (Term Core.Core Name)
|
=> File (Term Core.Core Name)
|
||||||
-> m (File (Either (Loc, String) (Monotype Meta)))
|
-> m (File (Either (Loc, String) (Term Monotype Meta)))
|
||||||
runFile file = traverse run file
|
runFile file = traverse run file
|
||||||
where run
|
where run
|
||||||
= (\ m -> do
|
= (\ m -> do
|
||||||
(subst, t) <- m
|
(subst, t) <- m
|
||||||
modify @(Heap Name (Monotype Meta)) (substAll subst)
|
modify @(Heap Name (Term Monotype Meta)) (fmap (Set.map (substAll subst)))
|
||||||
pure (substAll subst <$> t))
|
pure (substAll subst <$> t))
|
||||||
. runState (mempty :: Substitution)
|
. runState (mempty :: Substitution)
|
||||||
. runReader (fileLoc file)
|
. runReader (fileLoc file)
|
||||||
@ -139,7 +119,7 @@ runFile file = traverse run file
|
|||||||
v <$ for_ bs (unify v))
|
v <$ for_ bs (unify v))
|
||||||
. convergeTerm (fix (cacheTerm . eval typecheckingAnalysis))
|
. convergeTerm (fix (cacheTerm . eval typecheckingAnalysis))
|
||||||
|
|
||||||
typecheckingAnalysis :: (Alternative m, Carrier sig m, Member Fresh sig, Member (State (Set.Set Constraint)) sig, Member (State (Heap Name (Monotype Meta))) sig, MonadFail m) => Analysis Name (Monotype Meta) m
|
typecheckingAnalysis :: (Alternative m, Carrier sig m, Member Fresh sig, Member (State (Set.Set Constraint)) sig, Member (State (Heap Name (Term Monotype Meta))) sig, MonadFail m) => Analysis Name (Term Monotype Meta) m
|
||||||
typecheckingAnalysis = Analysis{..}
|
typecheckingAnalysis = Analysis{..}
|
||||||
where alloc = pure
|
where alloc = pure
|
||||||
bind _ _ = pure ()
|
bind _ _ = pure ()
|
||||||
@ -152,108 +132,65 @@ typecheckingAnalysis = Analysis{..}
|
|||||||
arg <- meta
|
arg <- meta
|
||||||
assign addr arg
|
assign addr arg
|
||||||
ty <- eval body
|
ty <- eval body
|
||||||
pure (MArr arg ty)
|
pure (Term (Arr arg ty))
|
||||||
apply _ f a = do
|
apply _ f a = do
|
||||||
_A <- meta
|
_A <- meta
|
||||||
_B <- meta
|
_B <- meta
|
||||||
unify (MArr _A _B) f
|
unify (Term (Arr _A _B)) f
|
||||||
unify _A a
|
unify _A a
|
||||||
pure _B
|
pure _B
|
||||||
unit = pure MUnit
|
unit = pure (Term Unit)
|
||||||
bool _ = pure MBool
|
bool _ = pure (Term Bool)
|
||||||
asBool b = unify MBool b >> pure True <|> pure False
|
asBool b = unify (Term Bool) b >> pure True <|> pure False
|
||||||
string _ = pure MString
|
string _ = pure (Term String)
|
||||||
asString s = unify MString s $> mempty
|
asString s = unify (Term String) s $> mempty
|
||||||
frame = fail "unimplemented"
|
frame = fail "unimplemented"
|
||||||
edge _ _ = pure ()
|
edge _ _ = pure ()
|
||||||
_ ... m = m
|
_ ... m = m
|
||||||
|
|
||||||
|
|
||||||
data Constraint = Monotype Meta :===: Monotype Meta
|
data Constraint = Term Monotype Meta :===: Term Monotype Meta
|
||||||
deriving (Eq, Ord, Show)
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
infix 4 :===:
|
infix 4 :===:
|
||||||
|
|
||||||
data Solution
|
data Solution
|
||||||
= Int := Monotype Meta
|
= Int := Term Monotype Meta
|
||||||
deriving (Eq, Ord, Show)
|
deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
infix 5 :=
|
infix 5 :=
|
||||||
|
|
||||||
meta :: (Carrier sig m, Member Fresh sig) => m (Monotype Meta)
|
meta :: (Carrier sig m, Member Fresh sig) => m (Term Monotype Meta)
|
||||||
meta = MMeta <$> Fresh.fresh
|
meta = pure <$> Fresh.fresh
|
||||||
|
|
||||||
unify :: (Carrier sig m, Member (State (Set.Set Constraint)) sig) => Monotype Meta -> Monotype Meta -> m ()
|
unify :: (Carrier sig m, Member (State (Set.Set Constraint)) sig) => Term Monotype Meta -> Term Monotype Meta -> m ()
|
||||||
unify t1 t2
|
unify t1 t2
|
||||||
| t1 == t2 = pure ()
|
| t1 == t2 = pure ()
|
||||||
| otherwise = modify (<> Set.singleton (t1 :===: t2))
|
| otherwise = modify (<> Set.singleton (t1 :===: t2))
|
||||||
|
|
||||||
type Substitution = IntMap.IntMap (Monotype Meta)
|
type Substitution = IntMap.IntMap (Term Monotype Meta)
|
||||||
|
|
||||||
solve :: (Carrier sig m, Member (State Substitution) sig, MonadFail m) => Set.Set Constraint -> m ()
|
solve :: (Carrier sig m, Member (State Substitution) sig, MonadFail m) => Set.Set Constraint -> m ()
|
||||||
solve cs = for_ cs solve
|
solve cs = for_ cs solve
|
||||||
where solve = \case
|
where solve = \case
|
||||||
-- FIXME: how do we enforce proper subtyping? row polymorphism or something?
|
-- FIXME: how do we enforce proper subtyping? row polymorphism or something?
|
||||||
MRecord f1 :===: MRecord f2 -> traverse solve (Map.intersectionWith (:===:) f1 f2) $> ()
|
Term (Record f1) :===: Term (Record f2) -> traverse solve (Map.intersectionWith (:===:) f1 f2) $> ()
|
||||||
MArr a1 b1 :===: MArr a2 b2 -> solve (a1 :===: a2) *> solve (b1 :===: b2)
|
Term (Arr a1 b1) :===: Term (Arr a2 b2) -> solve (a1 :===: a2) *> solve (b1 :===: b2)
|
||||||
MMeta m1 :===: MMeta m2 | m1 == m2 -> pure ()
|
Var m1 :===: Var m2 | m1 == m2 -> pure ()
|
||||||
MMeta m1 :===: t2 -> do
|
Var m1 :===: t2 -> do
|
||||||
sol <- solution m1
|
sol <- solution m1
|
||||||
case sol of
|
case sol of
|
||||||
Just (_ := t1) -> solve (t1 :===: t2)
|
Just (_ := t1) -> solve (t1 :===: t2)
|
||||||
Nothing | m1 `IntSet.member` mvs t2 -> fail ("Occurs check failure: " <> show m1 <> " :===: " <> show t2)
|
Nothing | m1 `IntSet.member` mvs t2 -> fail ("Occurs check failure: " <> show m1 <> " :===: " <> show t2)
|
||||||
| otherwise -> modify (IntMap.insert m1 t2 . subst (m1 := t2))
|
| otherwise -> modify (IntMap.insert m1 t2 . fmap (substAll (IntMap.singleton m1 t2)))
|
||||||
t1 :===: MMeta m2 -> solve (MMeta m2 :===: t1)
|
t1 :===: Var m2 -> solve (Var m2 :===: t1)
|
||||||
t1 :===: t2 -> unless (t1 == t2) $ fail ("Type mismatch:\nexpected: " <> show t1 <> "\n actual: " <> show t2)
|
t1 :===: t2 -> unless (t1 == t2) $ fail ("Type mismatch:\nexpected: " <> show t1 <> "\n actual: " <> show t2)
|
||||||
|
|
||||||
solution m = fmap (m :=) <$> gets (IntMap.lookup m)
|
solution m = fmap (m :=) <$> gets (IntMap.lookup m)
|
||||||
|
|
||||||
substAll :: Substitutable t => Substitution -> t -> t
|
|
||||||
substAll s a = foldl' (flip subst) a (map (uncurry (:=)) (IntMap.toList s))
|
|
||||||
|
|
||||||
|
mvs :: Foldable t => t Meta -> IntSet.IntSet
|
||||||
|
mvs = foldMap IntSet.singleton
|
||||||
|
|
||||||
class FreeVariables t where
|
substAll :: Monad t => IntMap.IntMap (t Meta) -> t Meta -> t Meta
|
||||||
mvs :: t -> IntSet.IntSet
|
substAll s a = a >>= \ i -> fromMaybe (pure i) (IntMap.lookup i s)
|
||||||
|
|
||||||
instance FreeVariables (Monotype Meta) where
|
|
||||||
mvs MUnit = mempty
|
|
||||||
mvs MBool = mempty
|
|
||||||
mvs MString = mempty
|
|
||||||
mvs (MArr a b) = mvs a <> mvs b
|
|
||||||
mvs (MMeta m) = IntSet.singleton m
|
|
||||||
mvs (MFree _) = mempty
|
|
||||||
mvs (MRecord fs) = foldMap mvs fs
|
|
||||||
|
|
||||||
instance FreeVariables Constraint where
|
|
||||||
mvs (t1 :===: t2) = mvs t1 <> mvs t2
|
|
||||||
|
|
||||||
class Substitutable t where
|
|
||||||
subst :: Solution -> t -> t
|
|
||||||
|
|
||||||
instance Substitutable (Monotype Meta) where
|
|
||||||
subst s con = case con of
|
|
||||||
MUnit -> MUnit
|
|
||||||
MBool -> MBool
|
|
||||||
MString -> MString
|
|
||||||
MArr a b -> MArr (subst s a) (subst s b)
|
|
||||||
MMeta m'
|
|
||||||
| m := t <- s
|
|
||||||
, m == m' -> t
|
|
||||||
| otherwise -> MMeta m'
|
|
||||||
MFree n -> MFree n
|
|
||||||
MRecord fs -> MRecord (subst s <$> fs)
|
|
||||||
|
|
||||||
instance Substitutable Constraint where
|
|
||||||
subst s (t1 :===: t2) = subst s t1 :===: subst s t2
|
|
||||||
|
|
||||||
instance Substitutable Solution where
|
|
||||||
subst s (m := t) = m := subst s t
|
|
||||||
|
|
||||||
instance Substitutable a => Substitutable (IntMap.IntMap a) where
|
|
||||||
subst s = IntMap.map (subst s)
|
|
||||||
|
|
||||||
instance (Ord a, Substitutable a) => Substitutable (Set.Set a) where
|
|
||||||
subst s = Set.map (subst s)
|
|
||||||
|
|
||||||
instance Substitutable v => Substitutable (Map.Map k v) where
|
|
||||||
subst s = fmap (subst s)
|
|
||||||
|
@ -194,25 +194,8 @@ annWith :: (Carrier sig m, Member Core sig) => CallStack -> m a -> m a
|
|||||||
annWith callStack = maybe id (fmap send . Ann) (stackLoc callStack)
|
annWith callStack = maybe id (fmap send . Ann) (stackLoc callStack)
|
||||||
|
|
||||||
|
|
||||||
stripAnnotations :: (Member Core sig, Syntax sig) => Term sig a -> Term sig a
|
stripAnnotations :: (Member Core sig, HFunctor sig, forall g . Functor g => Functor (sig g)) => Term sig a -> Term sig a
|
||||||
stripAnnotations = iter id alg Var Var
|
stripAnnotations (Var v) = Var v
|
||||||
where alg t | Just c <- prj t, Ann _ b <- c = b
|
stripAnnotations (Term t)
|
||||||
| otherwise = Term t
|
| Just c <- prj t, Ann _ b <- c = stripAnnotations b
|
||||||
|
| otherwise = Term (hmap stripAnnotations t)
|
||||||
|
|
||||||
instance Syntax Core where
|
|
||||||
foldSyntax go k h = \case
|
|
||||||
Let a -> Let a
|
|
||||||
a :>> b -> go h a :>> go h b
|
|
||||||
Lam u b -> Lam u (foldSyntax go k h b)
|
|
||||||
a :$ b -> go h a :$ go h b
|
|
||||||
Unit -> Unit
|
|
||||||
Bool b -> Bool b
|
|
||||||
If c t e -> If (go h c) (go h t) (go h e)
|
|
||||||
String s -> String s
|
|
||||||
Load t -> Load (go h t)
|
|
||||||
Edge e t -> Edge e (go h t)
|
|
||||||
Frame -> Frame
|
|
||||||
a :. b -> go h a :. go h b
|
|
||||||
a := b -> go h a := go h b
|
|
||||||
Ann loc t -> Ann loc (go h t)
|
|
||||||
|
@ -11,7 +11,6 @@ module Data.Core.Pretty
|
|||||||
import Control.Effect.Reader
|
import Control.Effect.Reader
|
||||||
import Data.Core
|
import Data.Core
|
||||||
import Data.File
|
import Data.File
|
||||||
import Data.Functor.Const
|
|
||||||
import Data.Name
|
import Data.Name
|
||||||
import Data.Scope
|
import Data.Scope
|
||||||
import Data.Term
|
import Data.Term
|
||||||
@ -56,57 +55,58 @@ inParens amount go = do
|
|||||||
body <- with amount go
|
body <- with amount go
|
||||||
pure (encloseIf (amount >= prec) (symbol "(") (symbol ")") body)
|
pure (encloseIf (amount >= prec) (symbol "(") (symbol ")") body)
|
||||||
|
|
||||||
prettify :: (Member (Reader [AnsiDoc]) sig, Member (Reader Prec) sig, Carrier sig m)
|
prettyCore :: Style -> Term Core User -> AnsiDoc
|
||||||
=> Style
|
prettyCore style = run . runReader @Prec 0 . go (pure . name)
|
||||||
-> Core (Const (m AnsiDoc)) a
|
where go :: (Member (Reader Prec) sig, Carrier sig m) => (a -> m AnsiDoc) -> Term Core a -> m AnsiDoc
|
||||||
-> m AnsiDoc
|
go var = \case
|
||||||
prettify style = \case
|
Var v -> var v
|
||||||
Let a -> pure $ keyword "let" <+> name a
|
Term t -> case t of
|
||||||
Const a :>> Const b -> do
|
Let a -> pure $ keyword "let" <+> name a
|
||||||
prec <- ask @Prec
|
a :>> b -> do
|
||||||
fore <- with 12 a
|
prec <- ask @Prec
|
||||||
aft <- with 12 b
|
fore <- with 12 (go var a)
|
||||||
|
aft <- with 12 (go var b)
|
||||||
|
|
||||||
let open = symbol ("{" <> softline)
|
let open = symbol ("{" <> softline)
|
||||||
close = symbol (softline <> "}")
|
close = symbol (softline <> "}")
|
||||||
separator = ";" <> Pretty.line
|
separator = ";" <> Pretty.line
|
||||||
body = fore <> separator <> aft
|
body = fore <> separator <> aft
|
||||||
|
|
||||||
pure . Pretty.align $ encloseIf (12 > prec) open close (Pretty.align body)
|
pure . Pretty.align $ encloseIf (12 > prec) open close (Pretty.align body)
|
||||||
|
|
||||||
Lam n f -> inParens 11 $ do
|
Lam n f -> inParens 11 $ do
|
||||||
(x, body) <- bind n f
|
(x, body) <- bind n f
|
||||||
pure (lambda <> x <+> arrow <+> body)
|
pure (lambda <> x <+> arrow <+> body)
|
||||||
|
|
||||||
Frame -> pure $ primitive "frame"
|
Frame -> pure $ primitive "frame"
|
||||||
Unit -> pure $ primitive "unit"
|
Unit -> pure $ primitive "unit"
|
||||||
Bool b -> pure $ primitive (if b then "true" else "false")
|
Bool b -> pure $ primitive (if b then "true" else "false")
|
||||||
String s -> pure . strlit $ Pretty.viaShow s
|
String s -> pure . strlit $ Pretty.viaShow s
|
||||||
|
|
||||||
Const f :$ Const x -> inParens 11 $ (<+>) <$> f <*> x
|
f :$ x -> inParens 11 $ (<+>) <$> go var f <*> go var x
|
||||||
|
|
||||||
If (Const con) (Const tru) (Const fal) -> do
|
If con tru fal -> do
|
||||||
con' <- "if" `appending` con
|
con' <- "if" `appending` go var con
|
||||||
tru' <- "then" `appending` tru
|
tru' <- "then" `appending` go var tru
|
||||||
fal' <- "else" `appending` fal
|
fal' <- "else" `appending` go var fal
|
||||||
pure $ Pretty.sep [con', tru', fal']
|
pure $ Pretty.sep [con', tru', fal']
|
||||||
|
|
||||||
Load (Const p) -> "load" `appending` p
|
Load p -> "load" `appending` go var p
|
||||||
Edge Lexical (Const n) -> "lexical" `appending` n
|
Edge Lexical n -> "lexical" `appending` go var n
|
||||||
Edge Import (Const n) -> "import" `appending` n
|
Edge Import n -> "import" `appending` go var n
|
||||||
Const item :. Const body -> inParens 4 $ do
|
item :. body -> inParens 4 $ do
|
||||||
f <- item
|
f <- go var item
|
||||||
g <- body
|
g <- go var body
|
||||||
pure (f <> symbol "." <> g)
|
pure (f <> symbol "." <> g)
|
||||||
|
|
||||||
Const lhs := Const rhs -> inParens 3 $ do
|
lhs := rhs -> inParens 3 $ do
|
||||||
f <- lhs
|
f <- go var lhs
|
||||||
g <- rhs
|
g <- go var rhs
|
||||||
pure (f <+> symbol "=" <+> g)
|
pure (f <+> symbol "=" <+> g)
|
||||||
|
|
||||||
-- Annotations are not pretty-printed, as it lowers the signal/noise ratio too profoundly.
|
-- Annotations are not pretty-printed, as it lowers the signal/noise ratio too profoundly.
|
||||||
Ann _ (Const c) -> c
|
Ann _ c -> go var c
|
||||||
where bind (Ignored x) f = let x' = name x in (,) x' <$> local (x':) (getConst (unScope f))
|
where bind (Ignored x) f = let x' = name x in (,) x' <$> go (incr (const (pure x')) var) (fromScope f)
|
||||||
lambda = case style of
|
lambda = case style of
|
||||||
Unicode -> symbol "λ"
|
Unicode -> symbol "λ"
|
||||||
Ascii -> symbol "\\"
|
Ascii -> symbol "\\"
|
||||||
@ -117,8 +117,3 @@ prettify style = \case
|
|||||||
|
|
||||||
appending :: Functor f => AnsiDoc -> f AnsiDoc -> f AnsiDoc
|
appending :: Functor f => AnsiDoc -> f AnsiDoc -> f AnsiDoc
|
||||||
appending k item = (keyword k <+>) <$> item
|
appending k item = (keyword k <+>) <$> item
|
||||||
|
|
||||||
prettyCore :: Style -> Term Core User -> AnsiDoc
|
|
||||||
prettyCore s = run . runReader @Prec 0 . runReader @[AnsiDoc] [] . cata id (prettify s) bound (pure . name)
|
|
||||||
where bound (Z _) = asks (head @AnsiDoc)
|
|
||||||
bound (S n) = local (tail @AnsiDoc) n
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
module Data.Scope
|
module Data.Scope
|
||||||
( Incr(..)
|
( Incr(..)
|
||||||
, incr
|
, incr
|
||||||
|
, closed
|
||||||
, Scope(..)
|
, Scope(..)
|
||||||
, fromScope
|
, fromScope
|
||||||
, toScope
|
, toScope
|
||||||
@ -44,6 +45,10 @@ incr :: (a -> c) -> (b -> c) -> Incr a b -> c
|
|||||||
incr z s = \case { Z a -> z a ; S b -> s b }
|
incr z s = \case { Z a -> z a ; S b -> s b }
|
||||||
|
|
||||||
|
|
||||||
|
closed :: Traversable f => f a -> Maybe (f b)
|
||||||
|
closed = traverse (const Nothing)
|
||||||
|
|
||||||
|
|
||||||
newtype Scope a f b = Scope { unScope :: f (Incr a (f b)) }
|
newtype Scope a f b = Scope { unScope :: f (Incr a (f b)) }
|
||||||
deriving (Foldable, Functor, Traversable)
|
deriving (Foldable, Functor, Traversable)
|
||||||
|
|
||||||
|
@ -1,18 +1,12 @@
|
|||||||
{-# LANGUAGE DeriveTraversable, FlexibleInstances, LambdaCase, MultiParamTypeClasses, QuantifiedConstraints, RankNTypes, ScopedTypeVariables, StandaloneDeriving, TypeOperators, UndecidableInstances #-}
|
{-# LANGUAGE DeriveTraversable, FlexibleInstances, MultiParamTypeClasses, QuantifiedConstraints, RankNTypes, StandaloneDeriving, UndecidableInstances #-}
|
||||||
module Data.Term
|
module Data.Term
|
||||||
( Term(..)
|
( Term(..)
|
||||||
, Syntax(..)
|
, hoistTerm
|
||||||
, iter
|
|
||||||
, cata
|
|
||||||
, interpret
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Effect.Carrier
|
import Control.Effect.Carrier
|
||||||
import Control.Monad (ap)
|
import Control.Monad (ap)
|
||||||
import Control.Monad.Module
|
import Control.Monad.Module
|
||||||
import Data.Coerce (coerce)
|
|
||||||
import Data.Functor.Const
|
|
||||||
import Data.Scope
|
|
||||||
|
|
||||||
data Term sig a
|
data Term sig a
|
||||||
= Var a
|
= Var a
|
||||||
@ -50,44 +44,7 @@ instance RightModule sig => Carrier sig (Term sig) where
|
|||||||
eff = Term
|
eff = Term
|
||||||
|
|
||||||
|
|
||||||
class (HFunctor sig, forall g . Functor g => Functor (sig g)) => Syntax sig where
|
hoistTerm :: (HFunctor sig, forall g . Functor g => Functor (sig g)) => (forall m x . sig m x -> sig' m x) -> Term sig a -> Term sig' a
|
||||||
foldSyntax :: (forall x y . (x -> m y) -> f x -> n y)
|
hoistTerm f = go
|
||||||
-> (forall a . Incr () (n a) -> m (Incr () (n a)))
|
where go (Var v) = Var v
|
||||||
-> (a -> m b)
|
go (Term t) = Term (f (hmap (hoistTerm f) t))
|
||||||
-> sig f a
|
|
||||||
-> sig n b
|
|
||||||
|
|
||||||
instance Syntax (Scope ()) where
|
|
||||||
foldSyntax go bound free = Scope . go (bound . fmap (go free)) . unScope
|
|
||||||
|
|
||||||
instance (Syntax l, Syntax r) => Syntax (l :+: r) where
|
|
||||||
foldSyntax go bound free (L l) = L (foldSyntax go bound free l)
|
|
||||||
foldSyntax go bound free (R r) = R (foldSyntax go bound free r)
|
|
||||||
|
|
||||||
|
|
||||||
iter :: forall m n sig a b
|
|
||||||
. Syntax sig
|
|
||||||
=> (forall a . m a -> n a)
|
|
||||||
-> (forall a . sig n a -> n a)
|
|
||||||
-> (forall a . Incr () (n a) -> m (Incr () (n a)))
|
|
||||||
-> (a -> m b)
|
|
||||||
-> Term sig a
|
|
||||||
-> n b
|
|
||||||
iter var alg bound = go
|
|
||||||
where go :: forall x y . (x -> m y) -> Term sig x -> n y
|
|
||||||
go free = \case
|
|
||||||
Var a -> var (free a)
|
|
||||||
Term t -> alg (foldSyntax go bound free t)
|
|
||||||
|
|
||||||
cata :: Syntax sig
|
|
||||||
=> (a -> b)
|
|
||||||
-> (forall a . sig (Const b) a -> b)
|
|
||||||
-> (Incr () b -> a)
|
|
||||||
-> (x -> a)
|
|
||||||
-> Term sig x
|
|
||||||
-> b
|
|
||||||
cata var alg k h = getConst . iter (coerce var) (Const . alg) (coerce k) (Const . h)
|
|
||||||
|
|
||||||
|
|
||||||
interpret :: (Carrier sig m, Member eff sig, Syntax eff) => (forall a . Incr () (m a) -> m (Incr () (m a))) -> (a -> m b) -> Term eff a -> m b
|
|
||||||
interpret = iter id send
|
|
||||||
|
Loading…
Reference in New Issue
Block a user