inferno-types-0.1.0.0: src/Inferno/Types/Syntax.hs
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Inferno.Types.Syntax
( Ident (..),
ExtIdent (..),
ImplExpl (..),
Import (..),
ModuleName (..),
InfixFixity (..),
Fixity (..),
Comment (..),
IStr (..),
OpsTable,
SomeIStr (..),
toEitherList,
fromEitherList,
Lit (..),
Pat (..),
PatF (..),
TV (..),
BaseType (..),
InfernoType (..),
Expr
( ..,
Var_,
OpVar_,
TypeRep_,
Enum_,
App_,
Lam_,
Let_,
Lit_,
InterpolatedString_,
If_,
Op_,
PreOp_,
Tuple_,
One_,
Empty_,
Assert_,
Case_,
Array_,
ArrayComp_,
Bracketed_,
RenameModule_,
OpenModule_
),
BlockUtils (..),
ElementPosition (..),
TList (..),
SigVar (..),
SourcePos (..),
Scoped (..),
Dependencies (..),
arbitraryName,
collectArrs,
extractArgsAndPrettyPrint,
tListToList,
tListFromList,
sigVarToIdent,
sigVarToExpr,
patternToExpr,
incSourceCol,
fromScoped,
rws,
punctuate',
hideInternalIdents,
substInternalIdents,
getIdentifierPositions,
)
where
import Control.Applicative (liftA, liftA2, liftA3)
import Control.DeepSeq (NFData (..))
import Control.Monad (replicateM)
import Data.Aeson (FromJSON (..), FromJSONKey (..), FromJSONKeyFunction (FromJSONKeyTextParser), ToJSON (..), ToJSONKey (..))
import Data.Aeson.Types (toJSONKeyText)
import Data.Bifunctor.TH (deriveBifunctor)
import Data.Data (Constr, Data (..), Typeable, gcast1, mkConstr, mkDataType)
import qualified Data.Data as Data
import Data.Functor.Foldable (ana, cata, project)
import Data.Functor.Foldable.TH (makeBaseFunctor)
import Data.Hashable (Hashable (hashWithSalt))
import Data.Int (Int64)
import qualified Data.IntMap as IntMap
import Data.List.NonEmpty (NonEmpty ((:|)), toList)
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Map as Map
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Serialize (Serialize)
import qualified Data.Serialize as Serialize
import qualified Data.Set as Set
import Data.String (IsString)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import Data.Word (Word64)
import GHC.Generics (Generic)
import Inferno.Utils.Prettyprinter (renderPretty)
import Numeric (showHex)
import Prettyprinter
( Doc,
Pretty (pretty),
align,
concatWith,
enclose,
flatAlt,
group,
hardline,
indent,
lbracket,
line,
line',
lparen,
nest,
rbracket,
rparen,
sep,
vsep,
(<+>),
)
import qualified Prettyprinter.Internal as Pretty
import Test.QuickCheck (Arbitrary (..), Gen, elements, listOf, oneof, recursivelyShrink, shrinkNothing, sized, suchThat)
import Test.QuickCheck.Arbitrary.ADT (ToADTArbitrary)
import Test.QuickCheck.Instances.Text ()
import Text.Megaparsec (Pos, SourcePos (..), mkPos, unPos)
import Text.Read (readMaybe)
newtype TV = TV {unTV :: Int}
deriving stock (Eq, Ord, Show, Data, Generic)
deriving newtype (ToJSON, FromJSON, ToJSONKey, FromJSONKey, NFData, Hashable, Arbitrary, Serialize)
deriving anyclass (ToADTArbitrary)
data BaseType
= TInt
| TDouble
| TWord16
| TWord32
| TWord64
| TText
| TTime
| TTimeDiff
| TResolution
| TEnum Text (Set.Set Ident)
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON, NFData, ToADTArbitrary)
instance Arbitrary BaseType where
shrink = shrinkNothing
arbitrary =
oneof $
(TEnum <$> (Text.pack <$> arbitrary) <*> (Set.fromList <$> listOf arbitrary))
: ( map
pure
[ TInt,
TDouble,
TWord16,
TWord32,
TWord64,
TText,
TTime,
TTimeDiff,
TResolution
]
)
instance Serialize BaseType where
get =
Serialize.getInt8 >>= \case
0 -> pure TInt
1 -> pure TDouble
2 -> pure TWord16
3 -> pure TWord32
4 -> pure TWord64
5 -> pure TText
6 -> pure TTime
7 -> pure TTimeDiff
8 -> pure TResolution
_ -> do
nm <- Serialize.get
ids <- Serialize.get
pure $ TEnum (Text.decodeUtf8 nm) $ Set.fromList $ map (Ident . Text.decodeUtf8) ids
put = \case
TInt -> Serialize.putInt8 0
TDouble -> Serialize.putInt8 1
TWord16 -> Serialize.putInt8 2
TWord32 -> Serialize.putInt8 3
TWord64 -> Serialize.putInt8 4
TText -> Serialize.putInt8 5
TTime -> Serialize.putInt8 6
TTimeDiff -> Serialize.putInt8 7
TResolution -> Serialize.putInt8 8
TEnum nm ids -> do
Serialize.putInt8 9
Serialize.put $ Text.encodeUtf8 nm
Serialize.put $ map (Text.encodeUtf8 . unIdent) $ Set.toList ids
instance Hashable BaseType where
hashWithSalt s TInt = hashWithSalt s (1 :: Int)
hashWithSalt s TDouble = hashWithSalt s (2 :: Int)
hashWithSalt s TWord16 = hashWithSalt s (3 :: Int)
hashWithSalt s TWord32 = hashWithSalt s (4 :: Int)
hashWithSalt s TWord64 = hashWithSalt s (5 :: Int)
hashWithSalt s TText = hashWithSalt s (6 :: Int)
hashWithSalt s TTime = hashWithSalt s (7 :: Int)
hashWithSalt s TTimeDiff = hashWithSalt s (8 :: Int)
hashWithSalt s TResolution = hashWithSalt s (9 :: Int)
hashWithSalt s (TEnum nm cs) = hashWithSalt s (10 :: Int, nm, Set.toList cs)
data InfernoType
= TVar TV
| TBase BaseType
| TArr InfernoType InfernoType
| TArray InfernoType
| TSeries InfernoType
| TOptional InfernoType
| TTuple (TList InfernoType)
| TRep InfernoType
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON, NFData, Hashable, ToADTArbitrary)
deriving anyclass (Serialize)
instance Arbitrary InfernoType where
shrink = recursivelyShrink
arbitrary = sized arbitrarySized
where
arbitraryVar =
TVar <$> arbitrary
arbitraryArr n =
TArr
<$> (arbitrarySized $ n `div` 3)
<*> (arbitrarySized $ n `div` 3)
arbitraryTTuple n =
oneof
[ pure $ TTuple TNil,
TTuple <$> (TCons <$> (arbitrarySized $ n `div` 3) <*> (arbitrarySized $ n `div` 3) <*> listOf (arbitrarySized $ n `div` 3))
]
arbitraryBase = TBase <$> arbitrary
arbitraryRest n = do
constr <- elements [TArray, TSeries, TOptional, TRep]
constr <$> (arbitrarySized $ n `div` 3)
arbitrarySized 0 =
oneof
[ arbitraryVar,
arbitraryBase
]
arbitrarySized n =
oneof
[ arbitraryVar,
arbitraryBase,
arbitraryArr n,
arbitraryTTuple n,
arbitraryRest n
]
punctuate' :: Doc ann -> [Doc ann] -> [Doc ann]
punctuate' _ [] = []
punctuate' _ [d] = [d]
punctuate' p (d : ds) = (d <+> p) : punctuate' p ds
collectArrs :: InfernoType -> [InfernoType]
collectArrs (TArr ty1 ty2) = ty1 : collectArrs ty2
collectArrs t = [t]
letters :: [Text]
letters = map Text.pack $ [1 ..] >>= flip replicateM ['a' .. 'z']
instance Pretty TV where
pretty (TV i) = "'" <> pretty (letters !! i)
instance Pretty BaseType where
pretty = \case
TInt -> "int"
TDouble -> "double"
TWord16 -> "word16"
TWord32 -> "word32"
TWord64 -> "word64"
TText -> "text"
TTime -> "time"
TTimeDiff -> "timeDiff"
TResolution -> "resolution"
TEnum t _ -> pretty t
instance Pretty InfernoType where
pretty = \case
TVar v -> pretty v
TBase b -> pretty b
t@(TArr _ _) ->
let prettyType = align . sep . punctuate' "→"
in prettyType $
map (\t' -> case t' of TArr _ _ -> enclose lparen rparen $ pretty t'; _ -> pretty t') $
collectArrs t
TArray ty@(TVar _) -> "array of" <+> align (pretty ty)
TArray ty@(TBase _) -> "array of" <+> align (pretty ty)
TArray ty@(TTuple _) -> "array of" <+> align (pretty ty)
TArray ty -> "array of" <+> align (enclose lparen rparen $ pretty ty)
TSeries ty@(TVar _) -> "series of" <+> align (pretty ty)
TSeries ty@(TBase _) -> "series of" <+> align (pretty ty)
TSeries ty@(TTuple _) -> "series of" <+> align (pretty ty)
TSeries ty -> "series of" <+> align (enclose lparen rparen $ pretty ty)
TOptional ty@(TVar _) -> "option of" <+> align (pretty ty)
TOptional ty@(TBase _) -> "option of" <+> align (pretty ty)
TOptional ty@(TTuple _) -> "option of" <+> align (pretty ty)
TOptional ty -> "option of" <+> align (enclose lparen rparen $ pretty ty)
TTuple tys -> Pretty.tupled (map pretty $ tListToList tys)
TRep ty@(TVar _) -> "rep of" <+> align (pretty ty)
TRep ty@(TBase _) -> "rep of" <+> align (pretty ty)
TRep ty@(TTuple _) -> "rep of" <+> align (pretty ty)
TRep ty -> "rep of" <+> align (enclose lparen rparen $ pretty ty)
incSourceCol :: SourcePos -> Int -> SourcePos
incSourceCol pos 0 = pos
incSourceCol (SourcePos n l c) i = SourcePos n l (c <> mkPos i)
rws :: [Text] -- list of reserved words
rws = ["if", "then", "else", "let", "module", "in", "match", "with", "Some", "None", "assert", "fun", "infixr", "infixl", "infix", "enum", "open"]
newtype Ident = Ident {unIdent :: Text}
deriving stock (Eq, Ord, Show, Data, Generic)
deriving newtype (ToJSON, FromJSON, ToJSONKey, FromJSONKey, IsString, NFData, Hashable)
deriving anyclass (ToADTArbitrary)
arbitraryName :: Gen Text
arbitraryName =
( (\a as -> Text.pack $ a : as)
<$> (elements ['a' .. 'z'])
<*> (listOf $ elements $ ['0' .. '9'] ++ ['a' .. 'z'] ++ ['_'])
)
`suchThat` (\i -> not $ i `elem` rws)
instance Arbitrary Ident where
shrink = shrinkNothing
arbitrary = Ident <$> arbitraryName
newtype ModuleName = ModuleName {unModuleName :: Text}
deriving stock (Eq, Ord, Show, Data, Generic)
deriving newtype (ToJSON, FromJSON, IsString)
deriving anyclass (ToADTArbitrary)
instance Arbitrary ModuleName where
shrink = shrinkNothing
arbitrary = ModuleName <$> arbitraryName
class ElementPosition a where
elementPosition :: SourcePos -> a -> (SourcePos, SourcePos)
instance ElementPosition Ident where
elementPosition pos (Ident a) = (pos, incSourceCol pos $ Text.length a)
instance ElementPosition ModuleName where
elementPosition pos (ModuleName a) = (pos, incSourceCol pos $ Text.length a)
instance ElementPosition (Maybe Ident) where
elementPosition pos = \case
Just i -> elementPosition pos i
Nothing -> (pos, incSourceCol pos 1)
-- | An extended identifier; either an internal (e.g., var$4) or a regular variable
newtype ExtIdent = ExtIdent (Either Int Text)
deriving (Show, Eq, Ord, Data, Generic)
deriving newtype (ToJSON, FromJSON)
instance Arbitrary ExtIdent where
shrink = shrinkNothing
arbitrary =
ExtIdent <$> oneof [Left <$> (arbitrary `suchThat` ((<) 0)), Right <$> arbitraryName]
instance ToJSONKey ExtIdent where
toJSONKey = toJSONKeyText $ \case
ExtIdent (Left i) -> "var$" <> (Text.pack $ show i)
ExtIdent (Right k) -> "reg$" <> k
instance FromJSONKey ExtIdent where
fromJSONKey = FromJSONKeyTextParser $ \t ->
case Text.take 4 t of
"var$" -> case readMaybe $ Text.unpack $ Text.drop 4 t of
Just i -> pure $ ExtIdent $ Left i
Nothing -> fail "Could not read internal var"
"reg$" -> pure $ ExtIdent $ Right $ Text.drop 4 t
_ -> fail "Invalid ExtIdent key"
data ImplExpl = Impl ExtIdent | Expl ExtIdent
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
instance Pretty ExtIdent where
pretty (ExtIdent i) = case i of
Left n -> "var$" <> pretty n
Right x -> pretty x
instance Pretty ImplExpl where
pretty = \case
Impl a -> "?" <> pretty a
Expl a -> pretty a
instance ElementPosition ImplExpl where
elementPosition pos = \case
Impl (ExtIdent (Left _)) -> (pos, pos)
Impl (ExtIdent (Right a)) -> (pos, incSourceCol pos $ Text.length a + 1)
Expl (ExtIdent (Left _)) -> (pos, pos)
Expl (ExtIdent (Right a)) -> (pos, incSourceCol pos $ Text.length a)
data Fixity = InfixOp InfixFixity | PrefixOp deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
data InfixFixity = NoFix | LeftFix | RightFix deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
instance ToJSON Pos where
toJSON = toJSON . unPos
deriving instance ToJSON SourcePos
instance FromJSON Pos where
parseJSON = fmap mkPos . parseJSON
deriving instance FromJSON SourcePos
data Comment pos
= LineComment pos Text pos
| BlockComment pos Text pos
deriving (Show, Eq, Ord, Data, Generic, Functor, Foldable, ToJSON, FromJSON)
instance Pretty (Comment a) where
pretty = \case
LineComment _ str _ -> ("//" <+> pretty str)
BlockComment _ str _ -> encloseComment $ map pretty $ Text.splitOn "\n" $ Text.strip str
where
encloseComment ds = case ds of
[] -> "/* */"
[d] -> "/*" <+> d <+> "*/"
_ -> hardVcat (zipWith (<>) ("/* " : repeat mempty) ds) <> " */"
hardVcat :: [Doc ann] -> Doc ann
hardVcat = concatWith (\x y -> x <> hardline <> y)
data Lit
= LInt Int64
| LDouble Double
| LText Text
| LHex Word64
deriving (Show, Eq, Ord, Data, Generic, ToJSON, FromJSON)
instance Pretty Lit where
pretty = \case
LInt i -> if i < 0 then "(" <> pretty i <> ")" else pretty i
LDouble d -> if d < 0 then "(" <> pretty d <> ")" else pretty d
LText t -> pretty $ show t
LHex w -> "0x" <> (pretty $ showHex w "")
instance ElementPosition Lit where
elementPosition pos l = (pos, incSourceCol pos $ length $ show $ pretty l)
data TList a = TNil | TCons a a [a]
deriving (Show, Eq, Ord, Functor, Foldable, Data, Generic, ToJSON, FromJSON, NFData, Hashable, ToADTArbitrary)
deriving anyclass (Serialize)
instance Arbitrary a => Arbitrary (TList a) where
arbitrary =
oneof
[ pure TNil,
TCons <$> arbitrary <*> arbitrary <*> listOf arbitrary
]
instance Traversable TList where
{-# INLINE traverse #-} -- so that traverse can fuse
traverse f = \case
TNil -> pure TNil
TCons x y zs -> liftA3 TCons (f x) (f y) (traverse f zs)
tListToList :: TList a -> [a]
tListToList = \case
TNil -> []
TCons a b cs -> a : b : cs
tListFromList :: [a] -> TList a
tListFromList = \case
[] -> TNil
(a : b : cs) -> TCons a b cs
_ -> error "undefined TList"
data IStr (f :: Bool) e where
ISEmpty :: IStr 'True e
ISStr :: Text -> IStr 'True e -> IStr 'False e
ISExpr :: Typeable f => e -> IStr f e -> IStr 'True e
instance (Typeable f, Data e) => Data (IStr f e) where
gfoldl _ z ISEmpty = z ISEmpty
gfoldl k z (ISStr s xs) = z ISStr `k` s `k` xs
gfoldl k z (ISExpr e xs) = z ISExpr `k` e `k` xs
gunfold _ _ _ =
error $
"Cannot derive a gunfold instance without unsafeCoerce.\n"
<> "If this function is needed, try uncommenting the lines below. However, this definition might not be correct."
-- where
-- gunfold' :: forall c. (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (IStr f e)
-- gunfold' k z c = case constrIndex c of
-- 1 -> unsafeCoerce $ z ISEmpty
-- 2 -> unsafeCoerce $ k (k (z ISStr) :: Typeable f => c (IStr 'True e -> IStr 'False e))
-- _ -> unsafeCoerce $ k (k (z ISExpr) :: Typeable f => c (IStr f e -> IStr 'True e))
toConstr ISEmpty = con_ISEmpty
toConstr (ISStr _ _) = con_ISStr
toConstr (ISExpr _ _) = con_ISExpr
dataTypeOf _ = ty_IStr
dataCast1 f = gcast1 f
con_ISEmpty, con_ISStr, con_ISExpr :: Constr
con_ISEmpty = mkConstr ty_IStr "ISEmpty" [] Data.Prefix
con_ISStr = mkConstr ty_IStr "ISStr" [] Data.Prefix
con_ISExpr = mkConstr ty_IStr "ISExpr" [] Data.Prefix
ty_IStr :: Data.DataType
ty_IStr = mkDataType "Inferno.Syntax.IStr" [con_ISEmpty, con_ISStr, con_ISExpr]
deriving instance Show e => Show (IStr f e)
deriving instance Functor (IStr f)
deriving instance Foldable (IStr f)
instance Traversable (IStr f) where
{-# INLINE traverse #-} -- so that traverse can fuse
traverse f = \case
ISEmpty -> pure ISEmpty
ISStr s xs -> liftA (ISStr s) (traverse f xs)
ISExpr e xs -> liftA2 ISExpr (f e) (traverse f xs)
data SomeIStr e = forall f. Typeable f => SomeIStr (IStr f e)
instance Data e => Data (SomeIStr e) where
gfoldl k z (SomeIStr xs) = z SomeIStr `k` xs
gunfold _ _ _ =
error $
"Cannot derive a gunfold instance without unsafeCoerce.\n"
<> "If this function is needed, try uncommenting the lines below. However, this definition might not be correct."
-- where
-- gunfold' :: forall c. (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (SomeIStr e)
-- gunfold' k z _ = k (z SomeIStr :: c (IStr 'False e -> SomeIStr e))
toConstr _ = con_SomeIStr
dataTypeOf _ = ty_SomeIStr
dataCast1 f = gcast1 f
con_SomeIStr :: Constr
con_SomeIStr = mkConstr ty_SomeIStr "SomeIStr" [] Data.Prefix
ty_SomeIStr :: Data.DataType
ty_SomeIStr = mkDataType "Inferno.Syntax.SomeIStr" [con_SomeIStr]
deriving instance Show e => Show (SomeIStr e)
instance Eq e => Eq (SomeIStr e) where
(SomeIStr ISEmpty) == (SomeIStr ISEmpty) = True
(SomeIStr (ISStr s1 xs)) == (SomeIStr (ISStr s2 ys)) =
(s1 == s2) && (SomeIStr xs) == (SomeIStr ys)
(SomeIStr (ISExpr e1 xs)) == (SomeIStr (ISExpr e2 ys)) =
(e1 == e2) && (SomeIStr xs) == (SomeIStr ys)
_ == _ = False
instance Ord e => Ord (SomeIStr e) where
compare (SomeIStr ISEmpty) (SomeIStr ISEmpty) = EQ
compare (SomeIStr ISEmpty) _ = LT
compare _ (SomeIStr ISEmpty) = GT
compare (SomeIStr (ISStr _ _)) (SomeIStr (ISExpr _ _)) = LT
compare (SomeIStr (ISExpr _ _)) (SomeIStr (ISStr _ _)) = GT
compare (SomeIStr (ISStr s xs)) (SomeIStr (ISStr t ys)) = case compare s t of
EQ -> compare (SomeIStr xs) (SomeIStr ys)
other -> other
compare (SomeIStr (ISExpr e xs)) (SomeIStr (ISExpr f ys)) = case compare e f of
EQ -> compare (SomeIStr xs) (SomeIStr ys)
other -> other
deriving instance Functor SomeIStr
deriving instance Foldable SomeIStr
toEitherList :: SomeIStr e -> [Either Text e]
toEitherList = \case
SomeIStr ISEmpty -> []
SomeIStr (ISStr s ys) -> Left s : toEitherList (SomeIStr ys)
SomeIStr (ISExpr e ys) -> Right e : toEitherList (SomeIStr ys)
fromEitherList :: [Either Text e] -> SomeIStr e
fromEitherList = \case
[] -> SomeIStr (ISEmpty :: IStr 'True e)
Left s : xs -> case fromEitherList xs of
SomeIStr ISEmpty -> SomeIStr $ ISStr s ISEmpty
SomeIStr (ISStr s' xs') -> SomeIStr $ ISStr (s <> s') xs'
SomeIStr rest@(ISExpr _ _) -> SomeIStr $ ISStr s rest
Right e : xs -> case fromEitherList xs of
SomeIStr rest -> SomeIStr $ ISExpr e rest
instance FromJSON e => FromJSON (SomeIStr e) where
parseJSON = fmap fromEitherList . parseJSON
instance ToJSON e => ToJSON (SomeIStr e) where
toJSON = toJSON . toEitherList
instance Traversable SomeIStr where
{-# INLINE traverse #-} -- so that traverse can fuse
traverse f = \case
SomeIStr (ISEmpty :: IStr f a) -> pure $ SomeIStr (ISEmpty :: IStr f b)
SomeIStr (ISStr s xs) -> do
res <- traverse f xs
pure $ case res of
ISEmpty -> SomeIStr $ ISStr s ISEmpty
rest@(ISExpr _ _) -> SomeIStr $ ISStr s rest
SomeIStr (ISExpr e xs) -> do
e' <- f e
res <- traverse f xs
pure $ SomeIStr $ ISExpr e' res
data Import pos
= IVar pos Ident
| IOpVar pos Ident
| IEnum
pos -- position of `enum`
pos -- position of ident
Ident
| ICommentAbove (Comment pos) (Import pos)
| ICommentAfter (Import pos) (Comment pos)
| ICommentBelow (Import pos) (Comment pos)
deriving (Show, Eq, Ord, Functor, Foldable, Generic, Data, ToJSON, FromJSON)
makeBaseFunctor ''Import
data Scoped a = LocalScope | Scope a
deriving (Show, Eq, Ord, Functor, Foldable, Traversable, Data, Generic, ToJSON, FromJSON)
fromScoped :: a -> Scoped a -> a
fromScoped d = \case
Scope a -> a
LocalScope -> d
data Expr hash pos
= Var
pos
hash
(Scoped ModuleName)
ImplExpl
| OpVar pos hash (Scoped ModuleName) Ident -- infix ops like `+` used as prefix, i.e. `(+)`
| TypeRep pos InfernoType
| Enum pos hash (Scoped ModuleName) Ident
| App (Expr hash pos) (Expr hash pos)
| Lam
pos -- position of `fun`
( NonEmpty
( pos, -- position of variable,
Maybe ExtIdent
)
)
pos -- position of `->`
(Expr hash pos)
| Let
pos -- position of `let`
pos -- position of variable
ImplExpl
pos -- position of `=`
(Expr hash pos)
pos -- position of `in`
(Expr hash pos)
| Lit pos Lit
| InterpolatedString
pos -- position of string start
(SomeIStr (pos, Expr hash pos, pos))
pos -- position of string end
| If
pos -- position of `if`
(Expr hash pos)
pos -- position of then`
(Expr hash pos)
pos -- position of `else`
(Expr hash pos)
| Op
(Expr hash pos)
pos -- position of operator
hash
(Int, InfixFixity) -- level and fixity of the operaror
(Scoped ModuleName)
Ident
(Expr hash pos)
| PreOp
pos -- position of operator
hash
Int -- level of the operaror
(Scoped ModuleName)
Ident
(Expr hash pos)
| Tuple
pos -- position of `(`
(TList (Expr hash pos, Maybe pos))
pos -- position of `)`
-- NOTE: the frontend syntax is Some/None but internally we use one/empty for legacy reasons
| One
pos -- position of `Some`
(Expr hash pos)
| Empty pos -- position of `None`
| Assert
pos -- position of `assert`
(Expr hash pos)
pos -- position of `in`
(Expr hash pos)
| Case
pos -- position of `match`
(Expr hash pos)
pos -- position of `{`
( NonEmpty
( pos, -- position of `|`
Pat hash pos,
pos, -- position of `->`
Expr hash pos
)
)
pos -- position of `}`
| Array
pos -- position of `[`
[ ( Expr hash pos,
Maybe pos -- position of `,`
)
]
pos -- position of `]`
| ArrayComp
pos -- position of `[`
(Expr hash pos)
pos -- position of `|`
( NonEmpty
( pos, -- position of identifier
Ident,
pos, -- position of `<-`
Expr hash pos,
Maybe pos -- position of `,`
)
)
( Maybe
( pos, -- position of `if`
Expr hash pos
)
)
pos -- position of `]`
| CommentAbove
(Comment pos)
(Expr hash pos)
| CommentAfter
(Expr hash pos)
(Comment pos)
| CommentBelow
(Expr hash pos)
(Comment pos)
| Bracketed pos (Expr hash pos) pos
| RenameModule
pos -- pos of new name
ModuleName
pos -- pos of old name
ModuleName
pos -- pos of `in`
(Expr hash pos)
| OpenModule
pos
hash
ModuleName
[(Import pos, Maybe pos)]
pos -- pos of `in`
(Expr hash pos)
deriving (Show, Eq, Ord, Functor, Foldable, Generic, Data, ToJSON, FromJSON)
{-# COMPLETE
Var_,
OpVar_,
TypeRep_,
Enum_,
App_,
Lam_,
Let_,
Lit_,
InterpolatedString_,
If_,
Op_,
PreOp_,
Tuple_,
One_,
Empty_,
Assert_,
Case_,
Array_,
ArrayComp_,
CommentAbove,
CommentAfter,
CommentBelow,
Bracketed_,
RenameModule_,
OpenModule_
#-}
pattern Var_ :: forall hash pos. hash -> Scoped ModuleName -> ImplExpl -> Expr hash pos
pattern Var_ h ns x <- Var _ h ns x
pattern OpVar_ :: forall hash pos. hash -> Scoped ModuleName -> Ident -> Expr hash pos
pattern OpVar_ h ns x <- OpVar _ h ns x
pattern TypeRep_ :: forall hash pos. InfernoType -> Expr hash pos
pattern TypeRep_ ty <- TypeRep _ ty
pattern Enum_ :: forall hash pos. hash -> Scoped ModuleName -> Ident -> Expr hash pos
pattern Enum_ h ns x <- Enum _ h ns x
pattern App_ :: forall hash pos. Expr hash pos -> Expr hash pos -> Expr hash pos
pattern App_ e1 e2 <- App e1 e2
pattern Lam_ :: forall hash pos. NonEmpty (pos, Maybe ExtIdent) -> Expr hash pos -> Expr hash pos
pattern Lam_ xs e <- Lam _ xs _ e
pattern Let_ :: forall hash pos. ImplExpl -> Expr hash pos -> Expr hash pos -> Expr hash pos
pattern Let_ x e1 e2 <- Let _ _ x _ e1 _ e2
pattern Lit_ :: forall hash pos. Lit -> Expr hash pos
pattern Lit_ l <- Lit _ l
pattern InterpolatedString_ :: forall hash pos. SomeIStr (pos, Expr hash pos, pos) -> Expr hash pos
pattern InterpolatedString_ xs <- InterpolatedString _ xs _
pattern If_ :: forall hash pos. Expr hash pos -> Expr hash pos -> Expr hash pos -> Expr hash pos
pattern If_ c t f <- If _ c _ t _ f
pattern Op_ :: forall hash pos. Expr hash pos -> hash -> Scoped ModuleName -> Ident -> Expr hash pos -> Expr hash pos
pattern Op_ e1 h ns op e2 <- Op e1 _ h _ ns op e2
pattern PreOp_ :: forall hash pos. hash -> Scoped ModuleName -> Ident -> Expr hash pos -> Expr hash pos
pattern PreOp_ h ns op e <- PreOp _ h _ ns op e
pattern Tuple_ :: forall hash pos. TList (Expr hash pos, Maybe pos) -> Expr hash pos
pattern Tuple_ xs <- Tuple _ xs _
pattern One_ :: forall hash pos. Expr hash pos -> Expr hash pos
pattern One_ e <- One _ e
pattern Empty_ :: forall hash pos. Expr hash pos
pattern Empty_ <- Empty _
pattern Assert_ :: forall hash pos. Expr hash pos -> Expr hash pos -> Expr hash pos
pattern Assert_ c e <- Assert _ c _ e
pattern Case_ :: forall hash pos. Expr hash pos -> NonEmpty (pos, Pat hash pos, pos, Expr hash pos) -> Expr hash pos
pattern Case_ e xs <- Case _ e _ xs _
pattern Array_ :: forall hash pos. [(Expr hash pos, Maybe pos)] -> Expr hash pos
pattern Array_ xs <- Array _ xs _
pattern ArrayComp_ ::
forall hash pos.
Expr hash pos ->
NonEmpty (pos, Ident, pos, Expr hash pos, Maybe pos) ->
Maybe (pos, Expr hash pos) ->
Expr hash pos
pattern ArrayComp_ e xs c <- ArrayComp _ e _ xs c _
pattern Bracketed_ :: forall hash pos. Expr hash pos -> Expr hash pos
pattern Bracketed_ e <- Bracketed _ e _
pattern RenameModule_ :: forall hash pos. ModuleName -> ModuleName -> Expr hash pos -> Expr hash pos
pattern RenameModule_ n1 n2 e <- RenameModule _ n1 _ n2 _ e
pattern OpenModule_ :: forall hash pos. ModuleName -> [(Import pos, Maybe pos)] -> Expr hash pos -> Expr hash pos
pattern OpenModule_ n1 ns e <- OpenModule _ _ n1 ns _ e
data Pat hash pos
= PVar pos (Maybe Ident)
| PEnum pos hash (Scoped ModuleName) Ident
| PLit pos Lit
| POne pos (Pat hash pos)
| PEmpty pos
| PTuple pos (TList (Pat hash pos, Maybe pos)) pos
| PCommentAbove
(Comment pos)
(Pat hash pos)
| PCommentAfter
(Pat hash pos)
(Comment pos)
| PCommentBelow
(Pat hash pos)
(Comment pos)
deriving (Show, Eq, Ord, Functor, Foldable, Data, Generic, ToJSON, FromJSON)
makeBaseFunctor ''Pat
makeBaseFunctor ''Expr
deriveBifunctor ''Pat
deriveBifunctor ''Expr
patternToExpr :: Pat () () -> Expr () ()
patternToExpr = \case
PVar _ Nothing -> Var () () LocalScope $ Expl $ ExtIdent $ Right "_"
PVar _ (Just (Ident i)) -> Var () () LocalScope $ Expl $ ExtIdent $ Right i
PEnum _ _ modNm i -> Enum () () modNm i
PLit _ l -> Lit () l
POne _ p -> One () $ patternToExpr p
PEmpty _ -> Empty ()
PTuple _ ps _ -> Tuple () (fmap (\(pat, pos) -> (patternToExpr pat, pos)) ps) ()
PCommentAbove c p -> CommentAbove c $ patternToExpr p
PCommentAfter p c -> CommentAfter (patternToExpr p) c
PCommentBelow p c -> CommentBelow (patternToExpr p) c
getIdentifierPositions :: Ident -> Expr a SourcePos -> [(SourcePos, SourcePos)]
getIdentifierPositions (Ident i) = cata go
where
go :: ExprF a SourcePos [(SourcePos, SourcePos)] -> [(SourcePos, SourcePos)]
go = \case
VarF pos _ _ v@(Expl (ExtIdent (Right a))) -> if i == a then let (sPos, ePos) = elementPosition pos v in [(sPos, ePos)] else []
rest -> foldr (++) [] rest
class BlockUtils f where
blockPosition :: f SourcePos -> (SourcePos, SourcePos)
removeComments :: f pos -> f pos
hasLeadingComment :: f pos -> Bool
hasTrailingComment :: f pos -> Bool
renameModule :: Scoped ModuleName -> f pos -> f pos
instance BlockUtils Comment where
blockPosition = \case
LineComment s _ e -> (s, e)
BlockComment s _ e -> (s, e)
removeComments = id
hasLeadingComment _ = True
hasTrailingComment _ = True
renameModule _ = id
instance BlockUtils Import where
blockPosition p = cata go p
where
go = \case
IVarF pos v -> elementPosition pos v
IOpVarF pos (Ident i) -> (pos, incSourceCol pos $ Text.length i + 2)
IEnumF pos1 pos2 (Ident i) -> (pos1, incSourceCol pos2 $ Text.length i)
ICommentAboveF c (_, pos2) -> let (pos1, _) = blockPosition c in (pos1, pos2)
ICommentAfterF (pos1, _) c -> let (_, pos2) = blockPosition c in (pos1, pos2)
ICommentBelowF (pos1, _) c -> let (_, pos2) = blockPosition c in (pos1, pos2)
removeComments = ana $ \case
ICommentAbove _ p -> project $ removeComments p
ICommentAfter p _ -> project $ removeComments p
ICommentBelow p _ -> project $ removeComments p
other -> project other
renameModule _ = id
hasLeadingComment = head . cata go
where
go = \case
ICommentAboveF _ _ -> [True]
rest -> foldr (++) [False] rest
hasTrailingComment = last . cata go
where
go = \case
ICommentAfterF _ _ -> [True]
ICommentBelowF _ _ -> [True]
rest -> foldl (++) [False] rest
instance BlockUtils (Pat hash) where
blockPosition p = cata go p
where
go :: PatF hash SourcePos (SourcePos, SourcePos) -> (SourcePos, SourcePos)
go = \case
PVarF pos v -> elementPosition pos v
PEnumF pos _ ns (Ident i) -> (pos, incSourceCol pos $ Text.length i + 1 + (fromScoped 0 $ (+ 1) . Text.length . unModuleName <$> ns))
PLitF pos l -> elementPosition pos l
PEmptyF pos -> (pos, incSourceCol pos 5)
POneF pos1 (_, pos2) -> (pos1, pos2)
PTupleF pos1 _ pos2 -> (pos1, incSourceCol pos2 1)
PCommentAboveF c (_, pos2) -> let (pos1, _) = blockPosition c in (pos1, pos2)
PCommentAfterF (pos1, _) c -> let (_, pos2) = blockPosition c in (pos1, pos2)
PCommentBelowF (pos1, _) c -> let (_, pos2) = blockPosition c in (pos1, pos2)
removeComments = ana $ \case
PCommentAbove _ p -> project $ removeComments p
PCommentAfter p _ -> project $ removeComments p
PCommentBelow p _ -> project $ removeComments p
other -> project other
renameModule newNs = ana $ \case
PEnum pos hash _ns i -> project $ PEnum pos hash newNs i
other -> project other
hasLeadingComment = head . cata go
where
go :: PatF hash pos [Bool] -> [Bool]
go = \case
PCommentAboveF _ _ -> [True]
rest -> foldr (++) [False] rest
hasTrailingComment = last . cata go
where
go :: PatF hash pos [Bool] -> [Bool]
go = \case
PCommentAfterF _ _ -> [True]
PCommentBelowF _ _ -> [True]
rest -> foldl (++) [False] rest
instance BlockUtils (Expr hash) where
blockPosition e = cata go e
where
go :: ExprF hash SourcePos (SourcePos, SourcePos) -> (SourcePos, SourcePos)
go = \case
VarF pos _ ns v -> let (sPos, ePos) = elementPosition pos v in (sPos, incSourceCol ePos $ fromScoped 0 $ (+ 1) . Text.length . unModuleName <$> ns)
OpVarF pos _ ns v -> let (sPos, ePos) = elementPosition pos v in (sPos, incSourceCol ePos $ fromScoped 2 $ (+ 3) . Text.length . unModuleName <$> ns)
EnumF pos _ ns (Ident i) -> (pos, incSourceCol pos $ Text.length i + 1 + (fromScoped 0 $ (+ 1) . Text.length . unModuleName <$> ns))
AppF (pos1, _) (_, pos2) -> (pos1, pos2)
LamF pos1 _ _ (_, pos2) -> (pos1, pos2)
LetF pos1 _ _ _ _ _ (_, pos2) -> (pos1, pos2)
LitF pos l -> (pos, incSourceCol pos $ length $ show $ pretty l)
InterpolatedStringF pos1 _ pos2 -> (pos1, pos2)
IfF pos1 _ _ _ _ (_, pos2) -> (pos1, pos2)
OpF (pos1, _) _ _ _ _ _ (_, pos2) -> (pos1, pos2)
PreOpF pos1 _ _ _ _ (_, pos2) -> (pos1, pos2)
TupleF pos1 _ pos2 -> (pos1, incSourceCol pos2 1)
OneF pos1 (_, pos2) -> (pos1, pos2)
EmptyF pos -> (pos, incSourceCol pos 5)
AssertF pos1 _ _ (_, pos2) -> (pos1, pos2)
CaseF pos1 _ _ _ pos2 -> (pos1, incSourceCol pos2 1)
ArrayF pos1 _ pos2 -> (pos1, incSourceCol pos2 1)
ArrayCompF pos1 _ _ _ _ pos2 -> (pos1, incSourceCol pos2 1)
CommentAboveF c (_, pos2) -> let (pos1, _) = blockPosition c in (pos1, pos2)
CommentAfterF (pos1, _) c -> let (_, pos2) = blockPosition c in (pos1, pos2)
CommentBelowF (pos1, _) c -> let (_, pos2) = blockPosition c in (pos1, pos2)
BracketedF pos1 _ pos2 -> (pos1, pos2)
RenameModuleF pos1 _ _ _ _ (_, pos2) -> (pos1, pos2)
OpenModuleF pos1 _ _ _ _ (_, pos2) -> (pos1, pos2)
TypeRepF pos _ -> (pos, pos)
removeComments = ana $ \case
CommentAbove _ p -> project $ removeComments p
CommentAfter p _ -> project $ removeComments p
CommentBelow p _ -> project $ removeComments p
Case p1 e1 p2 xs p3 ->
project $
Case
p1
(removeComments e1)
p2
(fmap (\(p4, pat, p5, e2) -> (p4, removeComments pat, p5, removeComments e2)) xs)
p3
other -> project other
renameModule newNs = ana $ \e -> project $ case e of
Var pos hash _ns i -> Var pos hash newNs i
OpVar pos hash _ns i -> OpVar pos hash newNs i
Enum pos hash _ns i -> Enum pos hash newNs i
Op e1 p1 hash meta _ns op e2 -> Op e1 p1 hash meta newNs op e2
Case p1 e1 p2 xs p3 ->
Case
p1
(renameModule newNs e1)
p2
(fmap (\(p4, pat, p5, e2) -> (p4, renameModule newNs pat, p5, renameModule newNs e2)) xs)
p3
other -> other
hasLeadingComment = head . cata go
where
go :: ExprF hash pos [Bool] -> [Bool]
go = \case
CommentAboveF _ _ -> [True]
rest -> foldr (++) [False] rest
hasTrailingComment = last . cata go
where
go :: ExprF hash pos [Bool] -> [Bool]
go = \case
CommentAfterF _ _ -> [True]
CommentBelowF _ _ -> [True]
rest -> foldl (++) [False] rest
collectApps :: Expr hash pos -> [Expr hash pos]
collectApps (App x@(App _ _) y) = collectApps x ++ [y]
collectApps (App x y) = [x, y]
collectApps _ = undefined
-- | Filter out any var$n/?var$n variables and their let/lambda bindings
-- This is used when pretty printing for the front-end, as we don't want the
-- users to see these auto-generated internal variables.
hideInternalIdents :: Expr hash pos -> Expr hash pos
hideInternalIdents = ana $ \case
App e (Var _ _ _ (Impl (ExtIdent (Left _)))) -> project $ hideInternalIdents e
App e (Var _ _ _ (Expl (ExtIdent (Left _)))) -> project $ hideInternalIdents e
App e (TypeRep _ _) -> project $ hideInternalIdents e
Let _ _ (Impl (ExtIdent (Left _))) _ _ _ e -> project $ hideInternalIdents e
Let _ _ (Expl (ExtIdent (Left _))) _ _ _ e -> project $ hideInternalIdents e
Lam p1 xs p2 e ->
let filteredXs = flip NonEmpty.filter xs $ \case
(_, Just (ExtIdent (Left _))) -> False
_ -> True
in case filteredXs of
[] -> project $ hideInternalIdents e
(x' : xs') -> project $ Lam p1 (x' NonEmpty.:| xs') p2 $ hideInternalIdents e
other -> project other
-- | Extract the arguments of a script and pretty print the script body.
-- This hides the internal variable arguments.
extractArgsAndPrettyPrint :: Expr hash pos -> ([Maybe Ident], Text)
extractArgsAndPrettyPrint expr =
extract [] (hideInternalIdents expr)
where
extract args = \case
Lam _ (x :| xs) _ e -> extract (args <> map snd (x : xs)) e
e -> (mapMaybe extIdentToIdent args, renderPretty e)
-- Strip the runtime type rep arguments, and convert others to Ident
extIdentToIdent = \case
(Just (ExtIdent (Left _))) -> Nothing
(Just (ExtIdent (Right i))) -> Just $ Just $ Ident {unIdent = i}
Nothing -> Just Nothing
-- | Substitute every variable occurrence of `?var$i` with `var$j`
-- if `(i, Left j)` is in in the supplied map.
-- otherwise replace `?var$i` with `@t` if (i, Right t) \in m`
substInternalIdents :: Map.Map Int (Either Int InfernoType) -> Expr hash pos -> Expr hash pos
substInternalIdents m = ana $ \case
Var pos h ns (Impl (ExtIdent (Left i))) ->
project $ case Map.lookup i m of
Just (Left j) -> Var pos h ns (Expl (ExtIdent (Left j)))
Just (Right t) -> TypeRep pos t
Nothing -> Var pos h ns (Impl (ExtIdent (Left i)))
other -> project other
instance Pretty (Import a) where
pretty = \case
IVar _ (Ident x) -> pretty x
IOpVar _ (Ident x) -> "(" <> pretty x <> ")"
IEnum _ _ (Ident x) -> "enum" <+> pretty x
ICommentAbove c e -> pretty c <> hardline <> pretty e
ICommentAfter e c -> pretty e <+> pretty c
ICommentBelow e c -> pretty e <> line <> pretty c
instance Pretty (Pat hash a) where
pretty = \case
PVar _ (Just (Ident x)) -> pretty x
PVar _ Nothing -> "_"
PEnum _ _ ns (Ident n) -> (fromScoped mempty $ (<> ".") . pretty . unModuleName <$> ns) <> "#" <> pretty n
PLit _ l -> pretty l
PTuple _ TNil _ -> "()"
PTuple _ ps _ -> group $ (flatAlt "( " "(") <> prettyTuple True (tListToList ps)
POne _ e -> "Some" <+> align (pretty e)
PEmpty _ -> "None"
PCommentAbove c e -> pretty c <> hardline <> pretty e
PCommentAfter e c -> pretty e <+> pretty c
PCommentBelow e c -> pretty e <> line <> pretty c
where
prettyTuple firstElement = \case
[] -> mempty
[(e, _)] ->
align (pretty e)
<> (if hasTrailingComment e then hardline <> ")" else flatAlt " )" ")")
(e, _) : es ->
(if not firstElement && hasLeadingComment e then line else mempty)
<> align (pretty e)
<> (if hasTrailingComment e then hardline else line')
<> ", "
<> prettyTuple False es
instance Pretty (Expr hash pos) where
pretty = prettyPrec False 0
prettyPrec :: Bool -> Int -> Expr hash pos -> Doc ann
prettyPrec isBracketed prec expr =
case expr of
Var _ _ ns x -> (fromScoped mempty $ (<> ".") . pretty . unModuleName <$> ns) <> pretty x
TypeRep _ ty -> "@" <> pretty ty
OpVar _ _ ns (Ident x) -> (fromScoped mempty $ (<> ".") . pretty . unModuleName <$> ns) <> "(" <> pretty x <> ")"
Enum _ _ ns (Ident n) -> (fromScoped mempty $ (<> ".") . pretty . unModuleName <$> ns) <> "#" <> pretty n
App _ _ -> group $ nest 2 $ prettyApp $ collectApps expr
where
prettyAppAux m p = case m of
Var _ _ _ _ -> p
OpVar _ _ _ _ -> p
Enum _ _ _ _ -> p
Lit _ _ -> p
InterpolatedString _ _ _ -> p
Tuple _ _ _ -> p
Empty _ -> p
Array _ _ _ -> p
ArrayComp _ _ _ _ _ _ -> p
Bracketed _ _ _ -> p
_ -> enclose lparen rparen $ if hasTrailingComment m then p <> hardline else p
prettyApp = \case
[] -> mempty
[x] -> prettyAppAux x $ prettyPrec True 0 x
(x : xs) -> (prettyAppAux x $ prettyPrec True 0 x) <> (if hasTrailingComment x then hardline else line) <> prettyApp xs
Lam _ xs _ e ->
let fun = "fun" <+> align (sep $ map (fromMaybe "_" . fmap pretty . snd) $ toList xs) <+> "->"
body = align $ prettyPrec False 0 e
in group $ nest 2 $ vsep [fun, body]
Let _ _ x _ e1 _ e2 ->
let letPretty = "let" <+> align (pretty x <+> "=" <+> align (prettyPrec False 0 e1))
body = "in" <+> prettyPrec False 0 e2
in letPretty <> (if hasTrailingComment e1 then hardline else line) <> body
Lit _ l -> pretty l
InterpolatedString _ istr _ -> enclose "`" "`" $
group $
cat' $
case istr of
SomeIStr ISEmpty -> []
SomeIStr xs@(ISStr _ _) -> prettyISStr xs
SomeIStr xs@(ISExpr _ _) -> "${" : prettyISExpr xs
where
prettyISExpr :: IStr 'True (a, Expr hash a, a) -> [Doc ann]
prettyISExpr = \case
ISEmpty -> []
ISExpr (_, e, _) ISEmpty -> (indentE $ prettyPrec False 0 e) : if hasTrailingComment e then [hardline, "}"] else ["}"]
ISExpr (_, e, _) xs@(ISExpr _ _) ->
((indentE $ prettyPrec False 0 e) : if hasTrailingComment e then [hardline, "}${"] else ["}${"]) ++ prettyISExpr xs
ISExpr (_, e, _) (ISStr str xs@(ISExpr _ _)) ->
let str' = vsepHard $ addToLast $ addToFirst $ map pretty $ Text.splitOn "\n" str
in ((indentE $ prettyPrec False 0 e) : if hasTrailingComment e then [hardline, str'] else [str']) ++ prettyISExpr xs
ISExpr (_, e, _) (ISStr str xs) ->
let str' = vsepHard $ addToFirst $ map pretty $ Text.splitOn "\n" str
in ((indentE $ prettyPrec False 0 e) : if hasTrailingComment e then [hardline, str'] else [str']) ++ prettyISExpr xs
prettyISStr :: IStr 'False (a, Expr hash a, a) -> [Doc ann]
prettyISStr = \case
ISStr str ISEmpty -> [pretty str]
ISStr str xs ->
let str' = vsepHard $ addToLast $ map pretty $ Text.splitOn "\n" str
in str' : prettyISExpr xs
addToLast, addToFirst :: [Doc ann] -> [Doc ann]
addToLast = \case
[] -> []
[s] -> [s <> "${"]
s : xs -> s : addToLast xs
addToFirst = \case
[] -> []
s : xs -> ("}" <> s) : xs
If _ c _ t _ f ->
let ifPretty = "if" <+> align (prettyPrec False 0 c)
thenPretty = "then" <+> align (prettyPrec False 0 t)
elsePretty = "else" <+> align (prettyPrec False 0 f)
in nest 2 $
ifPretty
<> (if hasTrailingComment c then hardline else line)
<> thenPretty
<> (if hasTrailingComment t then hardline else line)
<> elsePretty
Op e1 _ _ (n, NoFix) ns (Ident op) e2 ->
bracketWhen e2 (prec > n) $
prettyOpAux (n + 1) e1 <> (if hasTrailingComment e1 then hardline else mempty)
<+> prettyOp ns op
<+> (if hasLeadingComment e2 then line else mempty) <> prettyOpAux (n + 1) e2
Op e1 _ _ (n, LeftFix) ns (Ident op) e2 ->
bracketWhen e2 (prec > n) $
prettyOpAux n e1 <> (if hasTrailingComment e1 then hardline else mempty)
<+> prettyOp ns op
<+> (if hasLeadingComment e2 then line else mempty) <> prettyOpAux (n + 1) e2
Op e1 _ _ (n, RightFix) ns (Ident op) e2 ->
bracketWhen e2 (prec > n) $
prettyOpAux (n + 1) e1 <> (if hasTrailingComment e1 then hardline else mempty)
<+> prettyOp ns op
<+> (if hasLeadingComment e2 then line else mempty) <> prettyOpAux n e2
PreOp _ _ n ns (Ident op) e ->
bracketWhen e (prec > n) $
prettyOp ns op
<+> (if hasLeadingComment e then line else mempty) <> prettyOpAux (n + 1) e
Tuple _ TNil _ -> "()"
Tuple _ xs _ -> group $ (flatAlt "( " "(") <> prettyTuple True (tListToList xs)
where
prettyTuple firstElement = \case
[] -> mempty
[(e, _)] ->
align (prettyPrec False 0 e)
<> (if hasTrailingComment e then hardline <> ")" else flatAlt " )" ")")
(e, _) : es ->
(if not firstElement && hasLeadingComment e then line else mempty)
<> align (prettyPrec False 0 e)
<> (if hasTrailingComment e then hardline else line')
<> ", "
<> prettyTuple False es
One _ e -> "Some" <+> align (prettyPrec False 0 e)
Empty _ -> "None"
Assert _ c _ e ->
let assertPretty = "assert" <+> align (prettyPrec False 0 c)
body = (flatAlt " in" "in") <+> align (prettyPrec False 0 e)
in assertPretty <> (if hasTrailingComment c then hardline else line) <> body
Case _ e_case _ patExprs _ ->
group $
nest 2 $
vsep
[ "match" <+> align (prettyPrec False 0 e_case <> if hasTrailingComment e_case then hardline else mempty) <+> "with" <+> "{",
(align $ prettyCase True $ toList patExprs) <> flatAlt " }" "}"
]
where
prettyCase :: Bool -> [(a, Pat hash a, a, Expr hash a)] -> Doc ann
prettyCase firstElement = \case
[] -> mempty
[(_, pat, _, e)] ->
group
( "|"
<+> align
( pretty pat <> (if hasTrailingComment pat then hardline else mempty)
<+> "->"
<> line
<> (prettyPrec False 0 e)
)
)
<> (if hasTrailingComment e then hardline else mempty)
(_, pat, _, e) : es ->
(if not firstElement && hasLeadingComment pat then hardline else mempty)
<> group ("|" <+> align (pretty pat <> (if hasTrailingComment pat then hardline else mempty) <+> "->" <> line <> (prettyPrec False 0 e)))
<> (if hasTrailingComment e then hardline else line)
<> prettyCase False es
Array _ [] _ -> "[]"
Array _ xs _ -> group $ (flatAlt "[ " "[") <> prettyArray True xs
where
prettyArray firstElement = \case
[] -> mempty
[(e, _)] ->
align (prettyPrec False 0 e)
<> (if hasTrailingComment e then hardline <> "]" else flatAlt " ]" "]")
(e, _) : es ->
(if not firstElement && hasLeadingComment e then line else mempty)
<> align (prettyPrec False 0 e)
<> (if hasTrailingComment e then hardline else line')
<> ", "
<> prettyArray False es
ArrayComp _ e_body _ args e_cond _ ->
enclose lbracket rbracket $
align $
(align $ prettyPrec False 0 e_body <> if hasTrailingComment e_body then hardline else mempty) <+> align ("|" <+> (argsPretty $ toList args))
where
argsPretty = \case
[] -> mempty
[(_, Ident n, _, e, _)] ->
pretty n
<+> "<-"
<+> align (prettyPrec False 0 e)
<> case e_cond of
Just (_, c) -> (if hasTrailingComment e then hardline else line') <> "," <+> "if" <+> align (prettyPrec False 0 c) <> (if hasTrailingComment c then hardline else mempty)
Nothing -> if hasTrailingComment e then hardline else mempty
(_, Ident n, _, e, _) : xs ->
pretty n
<+> "<-"
<+> align (prettyPrec False 0 e)
<> (if hasTrailingComment e then hardline else line')
<> ", "
<> argsPretty xs
CommentAbove c e -> pretty c <> hardline <> prettyPrec isBracketed prec e
CommentAfter e c -> prettyPrec isBracketed prec e <+> pretty c
CommentBelow e c -> prettyPrec isBracketed prec e <> line <> pretty c
Bracketed _ e _ -> enclose lparen rparen $ if hasTrailingComment e then prettyPrec True prec e <> hardline else prettyPrec True prec e
RenameModule _ (ModuleName nNew) _ (ModuleName nOld) _ e ->
let letPretty = "let" <+> align ("module" <+> pretty nNew <+> "=" <+> pretty nOld)
body = (flatAlt " in" "in") <+> align (prettyPrec False 0 e)
in letPretty <> line <> body
OpenModule _ _ (ModuleName n) ns _ e ->
"open"
<+> pretty n
<> ( case ns of
[] -> line
_ -> (align $ group $ (flatAlt "( " "(") <> prettyImports True (map fst ns)) <> (if hasTrailingComment $ fst (last ns) then hardline else line)
)
<> (flatAlt " in" "in")
<+> align (prettyPrec False 0 e)
where
prettyImports firstElement = \case
[] -> mempty
[i] ->
align (pretty i)
<> (if hasTrailingComment i then hardline <> ")" else flatAlt " )" ")")
i : is ->
(if not firstElement && hasLeadingComment i then line else mempty)
<> align (pretty i)
<> (if hasTrailingComment i then hardline else line')
<> ", "
<> prettyImports False is
where
indentE e = flatAlt (indent 2 e) e
vsepHard :: [Doc ann] -> Doc ann
vsepHard = concatWith (\x y -> x <> hardline <> y)
cat' :: [Doc ann] -> Doc ann
cat' [] = mempty
cat' [x] = x
cat' (x : Pretty.Line : xs) = x <> hardline <> cat' xs
cat' (x : xs) = x <> line' <> cat' xs
bracketWhen e b =
if isBracketed
then id
else
if b
then (\x -> enclose lparen rparen $ x <> if hasTrailingComment e then hardline else mempty)
else id
prettyOp ns op = (fromScoped mempty $ (<> ".") . pretty . unModuleName <$> ns) <> pretty op
prettyOpAux n e = case e of
Var _ _ _ _ -> prettyPrec False n e
OpVar _ _ _ _ -> prettyPrec False n e
Enum _ _ _ _ -> prettyPrec False n e
Lit _ _ -> prettyPrec False n e
InterpolatedString _ _ _ -> prettyPrec False n e
Tuple _ _ _ -> prettyPrec False n e
Empty _ -> prettyPrec False n e
Array _ _ _ -> prettyPrec False n e
ArrayComp _ _ _ _ _ _ -> prettyPrec False n e
Bracketed _ _ _ -> prettyPrec False n e
Op _ _ _ _ _ _ _ -> prettyPrec False n e
PreOp _ _ _ _ _ _ -> prettyPrec False n e
_ -> enclose lparen rparen $ if hasTrailingComment e then prettyPrec False n e <> hardline else prettyPrec False n e
data SigVar = SigVar Text | SigOpVar Text deriving (Eq, Show, Data)
sigVarToIdent :: SigVar -> Ident
sigVarToIdent x = Ident $ case x of
SigVar i -> i
SigOpVar i -> i
sigVarToExpr :: Scoped ModuleName -> SigVar -> Expr () ()
sigVarToExpr modNm = \case
SigVar i -> Var () () modNm $ Expl $ ExtIdent $ Right i
SigOpVar i -> OpVar () () modNm $ Ident i
type OpsTable = IntMap.IntMap [(Fixity, Scoped ModuleName, Text)]
class Dependencies f hash where
getDependencies :: Ord hash => f -> Set.Set hash
instance Dependencies (Pat hash pos) hash where
getDependencies = cata $ \case
PEnumF _ h _ _ -> Set.singleton h
rest -> foldr Set.union mempty rest
instance Dependencies (Expr hash pos) hash where
getDependencies = cata $ \case
VarF _ h _ _ -> Set.singleton h
OpVarF _ h _ _ -> Set.singleton h
EnumF _ h _ _ -> Set.singleton h
OpF _ _ h _ _ _ _ -> Set.singleton h
rest -> foldr Set.union mempty rest