packages feed

language-dickinson-0.1.1.2: src/Language/Dickinson/Type.hs

{-# LANGUAGE DeriveAnyClass    #-}
{-# LANGUAGE DeriveFunctor     #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}

module Language.Dickinson.Type ( Dickinson (..)
                               , Declaration (..)
                               , Import (..)
                               , Expression (..)
                               , Pattern (..)
                               , DickinsonTy (..)
                               ) where

import           Control.DeepSeq                    (NFData)
import           Data.Binary                        (Binary)
import           Data.Foldable                      (toList)
import           Data.List.NonEmpty                 (NonEmpty (..))
import qualified Data.List.NonEmpty                 as NE
import           Data.Semigroup                     ((<>))
import qualified Data.Text                          as T
import           Data.Text.Prettyprint.Doc          (Doc, Pretty (pretty), align, brackets, colon, concatWith, dquotes,
                                                     group, hardline, hsep, indent, parens, pipe, rangle, tupled, vsep,
                                                     (<+>))
import           Data.Text.Prettyprint.Doc.Ext      (hardSep, (<#*>), (<#>), (<^>))
import           Data.Text.Prettyprint.Doc.Internal (unsafeTextWithoutNewlines)
import           GHC.Generics                       (Generic)
import           Language.Dickinson.Name

data Dickinson a = Dickinson { modImports :: [Import a]
                             , modDefs    :: [Declaration a]
                             } deriving (Generic, NFData, Binary, Functor, Show)

data Declaration a = Define { declAnn :: a
                            , defName :: Name a
                            , defExpr :: Expression a
                            }
                   | TyDecl { declAnn :: a
                            , tyName  :: Name a
                            , tyCons  :: NonEmpty (TyName a)
                            }
                   deriving (Generic, NFData, Binary, Functor, Show)

data Import a = Import { importAnn :: a
                       , declMod   :: Name a
                       }
                       deriving (Generic, NFData, Binary, Functor, Show)

data Pattern a = PatternVar a (Name a)
               | PatternTuple a (NonEmpty (Pattern a))
               | PatternCons a (TyName a)
               | Wildcard a
               deriving (Generic, NFData, Binary, Functor, Show)

data Expression a = Literal { exprAnn :: a, litText :: T.Text }
                  | StrChunk { exprAnn :: a, chunkText :: T.Text }
                  | Choice { exprAnn :: a
                           , choices :: NonEmpty (Double, Expression a)
                           }
                  | Let { exprAnn  :: a
                        , letBinds :: NonEmpty (Name a, Expression a)
                        , letExpr  :: Expression a
                        }
                  | Var { exprAnn :: a, exprVar :: Name a }
                  | Interp { exprAnn :: a, exprInterp :: [Expression a] }
                  | MultiInterp { exprAnn :: a, exprMultiInterp :: [Expression a] }
                  | Lambda { exprAnn    :: a
                           , lambdaVar  :: Name a
                           , lambdaTy   :: DickinsonTy a
                           , lambdaExpr :: Expression a
                           }
                  | Apply { exprAnn :: a
                          , exprFun :: Expression a
                          , exprArg :: Expression a
                          }
                  | Concat { exprAnn :: a, exprConcats :: [Expression a] }
                  | Tuple { exprAnn :: a, exprTup :: NonEmpty (Expression a) }
                  | Match { exprAnn   :: a
                          , exprMatch :: Expression a
                          , exprPat   :: Pattern a
                          , exprIn    :: Expression a
                          }
                  | Flatten { exprAnn :: a, exprFlat :: Expression a }
                  | Annot { exprAnn :: a
                          , expr    :: Expression a
                          , exprTy  :: DickinsonTy a
                          }
                  | Constructor { exprAnn :: a, constructorName :: TyName a }
                  deriving (Generic, NFData, Binary, Functor, Show)
                  -- TODO: builtins?

data DickinsonTy a = TyText a
                   | TyFun a (DickinsonTy a) (DickinsonTy a)
                   | TyTuple a (NonEmpty (DickinsonTy a))
                   | TyNamed a (Name a)
                   deriving (Generic, NFData, Binary, Show, Functor)

instance Eq (DickinsonTy a) where
    (==) TyText{} TyText{}                     = True
    (==) (TyFun _ ty ty') (TyFun _ ty'' ty''') = (ty == ty'') && (ty' == ty''')
    (==) (TyTuple _ tys) (TyTuple _ tys')      = and (NE.zipWith (==) tys tys')
    (==) (TyNamed _ n) (TyNamed _ n')          = n == n'
    (==) _ _                                   = False

instance Pretty (Declaration a) where
    pretty (Define _ n e)  = parens (":def" <+> pretty n <#> indent 2 (pretty e))
    pretty (TyDecl _ n cs) = "tydecl" <+> pretty n <+> "=" <+> concatWith (\x y -> x <+> pipe <+> y) (toList (pretty <$> cs))

instance Pretty (Import a) where
    pretty (Import _ n)   = parens (":include" <+> pretty n)

instance Pretty (Dickinson a) where
    pretty (Dickinson is ds) = concatWith (\x y -> x <> hardline <> hardline <> y) (fmap pretty is <> ["%-"] <> fmap pretty ds)

prettyLetLeaf :: (Name a, Expression a) -> Doc b
prettyLetLeaf (n, e) = group (brackets (pretty n <+> pretty e))

prettyChoiceBranch :: (Double, Expression a) -> Doc b
prettyChoiceBranch (d, e) = parens (pipe <+> pretty d <+> pretty e)

prettyChoiceOneof :: Expression a -> Doc b
prettyChoiceOneof e = parens (pipe <+> pretty e)

prettyInterp :: Expression a -> Doc b
prettyInterp (StrChunk _ t) = pretty (escReplace t)
prettyInterp e              = "${" <> pretty e <> "}"

prettyMultiInterp :: [Expression a] -> Doc b
prettyMultiInterp = mconcat . fmap prettyChunk
    where prettyChunk (StrChunk _ t) = textHard t -- FIXME: convert "\n" to hardline...
          prettyChunk e              = "${" <> pretty e <> "}"

textHard :: T.Text -> Doc a
textHard = concatWith (<#>) . map unsafeTextWithoutNewlines . T.splitOn "\n"

instance Pretty (Pattern a) where
    pretty (PatternVar _ n)    = pretty n
    pretty (PatternTuple _ ps) = tupled (toList (pretty <$> ps))
    pretty Wildcard{}          = "_"
    pretty (PatternCons _ c)   = pretty c

escReplace :: T.Text -> T.Text
escReplace =
      T.replace "\"" "\\\""
    . T.replace "\n" "\\n"
    . T.replace "${" "\\${"

allEq :: Eq a => NonEmpty a -> Bool
allEq (x :| xs) = all (== x) xs

-- figure out indentation
instance Pretty (Expression a) where
    pretty (Var _ n)          = pretty n
    pretty (Literal _ l)      = dquotes $ pretty (escReplace l)
    pretty (Let _ ls e)       = group (parens (":let" <^> vsep (toList (fmap prettyLetLeaf ls) ++ [pretty e])))
    -- also comments lol
    pretty (Choice _ brs)
        | allEq (fst <$> brs) = parens (":oneof" <#> indent 2 (hardSep (toList $ fmap prettyChoiceOneof (snd <$> brs))))
        | otherwise           = parens (":branch" <#> indent 2 (hardSep (toList $ fmap prettyChoiceBranch brs)))
    pretty (Lambda _ n ty e)  = parens (":lambda" <+> pretty n <+> pretty ty <#*> pretty e)
    pretty (Apply _ e e')     = parens ("$" <+> pretty e <+> pretty e')
    pretty (Interp _ es)      = group (dquotes (foldMap prettyInterp es))
    pretty (MultiInterp _ es) = group (align ("'''" <> prettyMultiInterp es <> "'''"))
    pretty (Concat _ es)      = parens (rangle <+> hsep (pretty <$> es))
    pretty StrChunk{}         = error "Internal error: naked StrChunk"
    pretty (Tuple _ es)       = tupled (toList (pretty <$> es))
    pretty (Match _ n p e)    = parens (":match" <+> pretty n <^> pretty p <^> pretty e)
    pretty (Flatten _ e)      = parens (":flatten" <^> pretty e)
    pretty (Annot _ e ty)     = pretty e <+> colon <+> pretty ty
    pretty (Constructor _ tn) = pretty tn

instance Pretty (DickinsonTy a) where
    pretty TyText{}       = "text"
    pretty (TyFun _ t t') = parens ("⟶" <+> pretty t <+> pretty t')
    pretty (TyTuple _ ts) = tupled (toList (pretty <$> ts))
    pretty (TyNamed _ n)  = pretty n