duet-0.0.1: src/Duet/Printer.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE Strict #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE LambdaCase #-}
-- |
module Duet.Printer where
import Data.Char
import Data.List
import qualified Data.Map.Strict as M
import Duet.Types
import Text.Printf
class PrintableType (t :: * -> *) where
printType :: Printable i => Print i l -> SpecialTypes i -> t i -> String
instance PrintableType (Predicate Type) where
printType = printPredicate
class (Eq a, Identifiable a) => Printable a where
printit :: Print i l -> a -> String
instance Printable Name where
printit printer =
\case
PrimopName primop -> printPrimop primop
ValueName i string ->
string ++
(if printNameDetails printer
then "[value:" ++ show i ++ "]"
else "")
TypeName i string ->
string ++
(if printNameDetails printer
then "[type:" ++ show i ++ "]"
else "")
ConstructorName i string ->
string ++
(if printNameDetails printer
then "[constructor:" ++ show i ++ "]"
else "")
ForallName i -> "g" ++ show i
DictName i string ->
string ++
(if printNameDetails printer
then "[dict:" ++ show i ++ "]"
else "")
ClassName i s ->
s ++
(if printNameDetails printer
then "[class:" ++ show i ++ "]"
else "")
MethodName i s ->
s ++
(if printNameDetails printer
then "[method:" ++ show i ++ "]"
else "")
printPrimop :: Primop -> [Char]
printPrimop =
\case
PrimopIntegerSubtract -> "subtract"
PrimopIntegerTimes -> "times"
PrimopIntegerPlus -> "plus"
PrimopRationalSubtract -> "subtract"
PrimopRationalTimes -> "times"
PrimopRationalPlus -> "plus"
PrimopRationalDivide -> "divide"
PrimopStringAppend -> "append"
instance Printable Identifier where
printit _ =
\case
Identifier string -> string
defaultPrint :: Print i b
defaultPrint =
Print
{ printDictionaries = False
, printTypes = const Nothing
, printNameDetails = False
}
data Print i l = Print
{ printTypes :: (l -> Maybe (SpecialTypes i, TypeSignature Type i ()))
, printDictionaries :: Bool
, printNameDetails :: Bool
}
printDataType :: (Printable i, PrintableType t) => Print i l -> SpecialTypes i -> DataType t i -> String
printDataType printer specialTypes (DataType name vars cons) =
"data " ++ printit printer name ++ " " ++ unwords (map (printTypeVariable printer) vars) ++ "\n = " ++
intercalate "\n | " (map (printConstructor printer specialTypes) cons)
printConstructor :: (Printable i, PrintableType t) => Print i l -> SpecialTypes i -> DataTypeConstructor t i -> [Char]
printConstructor printer specialTypes (DataTypeConstructor name fields) =
printit printer name ++ " " ++ unwords (map (printType printer specialTypes) fields)
printTypeSignature
:: (Printable i, Printable j)
=> Print i l -> SpecialTypes i -> TypeSignature Type i j -> String
printTypeSignature printer specialTypes (TypeSignature thing scheme) =
printit printer thing ++ " :: " ++ printScheme printer specialTypes scheme
printIdentifier :: Printable j => Print i l -> j -> String
printIdentifier printer = printit printer
printImplicitlyTypedBinding
:: (Printable i, PrintableType t)
=> Print i l -> ImplicitlyTypedBinding t i l -> String
printImplicitlyTypedBinding printer (ImplicitlyTypedBinding _ (i, _) [alt]) =
printIdentifier printer i ++ " " ++ printAlternative printer alt
printImplicitlyTypedBinding _ _ = ""
printExplicitlyTypedBinding
:: (Printable i, PrintableType t)
=> Print i l -> SpecialTypes i -> ExplicitlyTypedBinding t i l -> String
printExplicitlyTypedBinding printer specialTypes (ExplicitlyTypedBinding _ (i, _) scheme [alt]) =
printIdentifier printer i ++ " :: " ++ printScheme printer specialTypes scheme ++ "\n" ++
printIdentifier printer i ++ " " ++ printAlternative printer alt
printExplicitlyTypedBinding _ _ _ = ""
printAlternative :: (Printable i, PrintableType t) => Print i l -> Alternative t i l -> [Char]
printAlternative printer (Alternative _ patterns expression) =
concat (map (\p->printPattern printer p ++ " ") patterns) ++ "= " ++ printExpression printer expression
printPattern :: (Printable i, PrintableType t) => Print i l -> Pattern t i l -> [Char]
printPattern printer =
\case
BangPattern p -> "!" ++ printPattern printer p
VariablePattern _ i -> printIdentifier printer i
WildcardPattern _ s -> s
AsPattern _ i p -> printIdentifier printer i ++ "@" ++ printPattern printer p
LiteralPattern _ l -> printLiteral l
ConstructorPattern _ i pats ->
printIdentifier printer i ++ " " ++ unwords (map (printPattern printer) pats)
printExpression :: (Printable i, PrintableType t) => Print i l -> (Expression t i l) -> String
printExpression printer e =
wrapType
(case e of
LiteralExpression _ l -> printLiteral l
VariableExpression _ i -> printIdentifier printer i
ConstantExpression _ i -> printIdentifier printer i
ConstructorExpression _ i -> printIdentifier printer i
ParensExpression _ e -> "(" <> (printExpression printer e) <> ")"
CaseExpression _ e alts ->
"case " ++
indent 5 (printExpressionIfPred printer e) ++
" of\n" ++ indented (intercalate "\n" (map (printAlt printer) alts))
ApplicationExpression _ f x ->
case x of
VariableExpression _ (nonrenamableName -> Just (DictName {}))
| not (printDictionaries printer) -> printExpressionAppOp printer f
_ ->
if any (== '\n') inner || any (== '\n') prefix
then prefix ++ "\n" ++ indented inner
else prefix ++ " " ++ indent (length prefix + 1) inner
where prefix = printExpressionAppOp printer f
inner = printExpressionAppArg printer x
LambdaExpression _ (Alternative _ args e) ->
if null filteredArgs
then inner
else if any (== '\n') inner
then "\\" ++ prefix ++ "->\n" ++ indented inner
else "\\" ++
prefix ++ "-> " ++ indent (length prefix + 4) inner
where inner = (printExpression printer e)
filteredArgs = filter dictPred args
prefix =
concat (map (\x -> printPattern printer x ++ " ") filteredArgs)
dictPred =
if printDictionaries printer
then const True
else \case
VariablePattern _ (nonrenamableName -> Just (DictName {})) ->
False
_ -> True
IfExpression _ a b c ->
"if " ++
printExpressionIfPred printer a ++
" then " ++
printExpression printer b ++ " else " ++ printExpression printer c
InfixExpression _ f (o, ov) x ->
printExpressionAppArg printer f ++
" " ++
(if printDictionaries printer
then "`" ++ printExpression printer ov ++ "`"
else o) ++
" " ++ printExpressionAppArg printer x
_ -> "<TODO>")
where
wrapType x =
case printTypes printer (expressionLabel e) of
(Nothing) -> x
(Just (specialTypes, TypeSignature _ ty)) ->
"(" ++
parens x ++ " :: " ++ printScheme printer specialTypes ty ++ ")"
where parens k =
if any isSpace k
then "(" ++ k ++ ")"
else k
printAlt
:: (PrintableType t, Printable i)
=> Print i l -> (CaseAlt t i l) -> [Char]
printAlt printer =
\(CaseAlt _ p e') ->
let inner = printExpression printer e'
in if any (== '\n') inner
then printPat printer p ++ " ->\n" ++ indented inner
else printPat printer p ++ " -> " ++ indent 2 inner
indented :: String -> [Char]
indented x = intercalate "\n" (map (" "++) (lines x))
indent :: Int -> String -> [Char]
indent n = intercalate ("\n" ++ replicate n ' ') . lines
lined :: [[Char]] -> [Char]
lined = intercalate "\n "
printPat :: (Printable i, PrintableType t) => Print i l -> Pattern t i l -> String
printPat printer=
\case
BangPattern p -> "!" ++ printPat printer p
VariablePattern _ i -> printit printer i
ConstructorPattern _ i ps ->
printit printer i ++
(if null ps
then ""
else " " ++ unwords (map inner ps))
WildcardPattern{} -> "_"
AsPattern _ ident p -> printit printer ident ++ "@" ++ printPat printer p
LiteralPattern _ l -> printLiteral l
where
inner =
\case
BangPattern p -> "!" ++ inner p
VariablePattern _ i -> printit printer i
WildcardPattern _ s -> s
ConstructorPattern _ i ps
| null ps -> printit printer i
| otherwise ->
"(" ++ printit printer i ++ " " ++ unwords (map inner ps) ++ ")"
AsPattern _ ident p -> printit printer ident ++ "@" ++ printPat printer p
LiteralPattern _ l -> printLiteral l
printExpressionAppArg :: (Printable i, PrintableType t) => Print i l ->(Expression t i l) -> String
printExpressionAppArg printer =
\case
e@(ApplicationExpression {})
| nodict e -> paren (printExpression printer e)
e@(IfExpression {}) -> paren (printExpression printer e)
e@(InfixExpression {}) -> paren (printExpression printer e)
e@(LambdaExpression {}) -> paren (printExpression printer e)
e@(CaseExpression {}) -> paren (printExpression printer e)
e -> printExpression printer e
where
nodict =
\case
ApplicationExpression _ _ (VariableExpression _ (nonrenamableName -> Just (DictName {})))
| not (printDictionaries printer) -> False
_ -> True
printExpressionIfPred :: (Printable i, PrintableType t) => Print i l -> (Expression t i l) -> String
printExpressionIfPred printer=
\case
e@(IfExpression {}) -> paren (printExpression printer e)
e@(LambdaExpression {}) -> paren (printExpression printer e)
e@(CaseExpression {}) -> paren (printExpression printer e)
e -> printExpression printer e
printExpressionAppOp :: (Printable i, PrintableType t) => Print i l -> (Expression t i l) -> String
printExpressionAppOp printer=
\case
e@(IfExpression {}) -> paren (printExpression printer e)
e@(LambdaExpression {}) -> paren (printExpression printer e)
e@(CaseExpression {}) -> paren (printExpression printer e)
e -> printExpression printer e
paren :: [Char] -> [Char]
paren e = "(" ++ indent 1 e ++ ")"
printLiteral :: Literal -> String
printLiteral (IntegerLiteral i) = show i
printLiteral (RationalLiteral i) = printf "%f" (fromRational i :: Double)
printLiteral (StringLiteral x) = show x
printLiteral (CharacterLiteral x) = show x
printScheme :: (Printable i, PrintableType t, PrintableType t1) => Print i l -> SpecialTypes i -> Scheme t i t1 -> [Char]
printScheme printer specialTypes (Forall kinds qualifiedType') =
(if null kinds
then ""
else "forall " ++
unwords
(zipWith
(\_i k ->
printTypeVariable
(Print
{ printTypes = const Nothing
, printDictionaries = False
, printNameDetails = printNameDetails printer
})
k)
[0 :: Int ..]
kinds) ++
". ") ++
printQualifiedType specialTypes qualifiedType'
where
printQualifiedType specialTypes (Qualified predicates typ) =
case predicates of
[] -> printType printer specialTypes typ
_ ->
"(" ++
intercalate
", "
(map (printPredicate printer specialTypes) predicates) ++
") => " ++ printType printer specialTypes typ
printClass :: Printable i => Print i l -> SpecialTypes i -> Class Type i l -> String
printClass printer specialTypes (Class vars supers instances i methods) =
"class " ++
printSupers printer specialTypes supers ++
printit printer i ++
" " ++
unwords (map (printTypeVariable printer) vars) ++ " where\n " ++
intercalate "\n " (map (printMethod printer specialTypes) (M.toList methods)) ++
"\n" ++ intercalate "\n" (map (printInstance printer specialTypes) instances)
printMethod :: Printable i => Print i l -> SpecialTypes i -> (i, Scheme Type i Type) -> String
printMethod printer specialTypes (i, scheme) =
printit printer i ++ " :: " ++ printScheme printer specialTypes scheme
printInstance :: Printable i => Print i l -> SpecialTypes i -> Instance Type i l -> String
printInstance printer specialTypes (Instance scheme _) =
"instance " ++
printScheme printer specialTypes scheme
printSupers :: Printable i => Print i l -> SpecialTypes i -> [Predicate Type i] -> [Char]
printSupers printer specialTypes supers
| null supers = ""
| otherwise =
"(" ++ intercalate ", " (map (printPredicate printer specialTypes) supers) ++ ") => "
printPredicate :: (Printable i, PrintableType t) => Print i l -> SpecialTypes i -> Predicate t i -> [Char]
printPredicate printer specialTypes (IsIn identifier types) =
printIdentifier printer identifier ++
" " ++ unwords (map (wrap . printType printer specialTypes) types)
where wrap x = if any isSpace x
then "(" ++ x ++ ")"
else x
printKind :: Kind -> [Char]
printKind =
\case
StarKind -> "Type"
FunctionKind x' y -> printKind x' ++ " -> " ++ printKind y
printTypeSansParens :: (Printable i) => Print i l -> SpecialTypes i -> Type i -> [Char]
printTypeSansParens printer specialTypes =
\case
ApplicationType (ApplicationType func x') y'
| func == ConstructorType (specialTypesFunction specialTypes) ->
printType printer specialTypes x' ++
" -> " ++ printTypeSansParens printer specialTypes y'
o -> printType printer specialTypes o
instance PrintableType Type where
printType printer specialTypes =
\case
VariableType v -> printTypeVariable printer v
ConstructorType tyCon -> printTypeConstructor printer tyCon
ApplicationType (ApplicationType func x') y
| func == ConstructorType (specialTypesFunction specialTypes) ->
"(" ++
printType printer specialTypes x' ++
" -> " ++ printTypeSansParens printer specialTypes y ++ ")"
-- ApplicationType list ty | list == specialTypesList specialTypes ->
-- "[" ++ printTypeSansParens specialTypes ty ++ "]"
ApplicationType x' y ->
printType printer specialTypes x' ++ " " ++ printTypeArg y
-- GenericType int -> "g" ++ show int
where
printTypeArg =
\case
x@ApplicationType {} -> "(" ++ printType printer specialTypes x ++ ")"
x -> printType printer specialTypes x
instance PrintableType UnkindedType where
printType printer specialTypes =
\case
UnkindedTypeVariable v -> printIdentifier printer v
UnkindedTypeConstructor tyCon -> printIdentifier printer tyCon
UnkindedTypeApp x' y ->
"(" ++ printType printer specialTypes x' ++ " " ++ printType printer specialTypes y ++ ")"
printTypeConstructor :: Printable j => Print i l -> TypeConstructor j -> String
printTypeConstructor printer (TypeConstructor identifier kind) =
case kind of
StarKind -> printIdentifier printer identifier
FunctionKind {} -> printIdentifier printer identifier
-- _ -> "(" ++ printIdentifier identifier ++ " :: " ++ printKind kind ++ ")"
printTypeVariable :: Printable i => Print i l -> TypeVariable i -> String
printTypeVariable printer (TypeVariable identifier kind) =
case kind of
StarKind -> printIdentifier printer identifier
_ -> "(" ++ printIdentifier printer identifier ++ " :: " ++ printKind kind ++ ")"
curlyQuotes :: [Char] -> [Char]
curlyQuotes t = "‘" <> t <> "’"