shithub: MicroHs

Download patch

ref: c99d3092588f9a19f9ac71a98293bf64059e048e
parent: 7b827493e7bb7992068a2f9ded548b6bee83d5e6
author: Lennart Augustsson <lennart.augustsson@epicgames.com>
date: Thu Oct 19 10:41:00 EDT 2023

Add pretty printing.

--- a/src/MicroHs/Expr.hs
+++ b/src/MicroHs/Expr.hs
@@ -29,8 +29,7 @@
   errorMessage,
   Assoc(..), eqAssoc, Fixity
   ) where
-import Prelude --Xhiding (Monad(..), Applicative(..), MonadFail(..), Functor(..), (<$>), showString, showChar, showList)
-import Data.List
+import Prelude --Xhiding (Monad(..), Applicative(..), MonadFail(..), Functor(..), (<$>), showString, showChar, showList, (<>))
 import Data.Maybe
 import MicroHs.Ident
 import qualified Data.Double as D
@@ -38,6 +37,7 @@
 --Ximport GHC.Stack
 --Ximport Control.DeepSeq
 --Yimport Primitives(NFData(..))
+import MicroHs.Pretty
 
 type IdentModule = Ident
 
@@ -351,96 +351,107 @@
     ExpValue i -> i
 -}
 
-showImportItem :: ImportItem -> String
-showImportItem ae =
+showExpr :: Expr -> String
+showExpr = render . ppExpr
+
+showEDefs :: [EDef] -> String
+showEDefs = render . ppEDefs
+
+showEBind :: EBind -> String
+showEBind = render . ppEBind
+
+showEType :: EType -> String
+showEType = render . ppEType
+
+ppImportItem :: ImportItem -> Doc
+ppImportItem ae =
   case ae of
-    ImpTypeCon i -> showIdent i ++ "(..)"
-    ImpType i -> showIdent i
-    ImpValue i -> showIdent i
+    ImpTypeCon i -> ppIdent i <> text "(..)"
+    ImpType i -> ppIdent i
+    ImpValue i -> ppIdent i
 
-showEDef :: EDef -> String
-showEDef def =
+ppEDef :: EDef -> Doc
+ppEDef def =
   case def of
-    Data lhs cs -> "data " ++ showLHS lhs ++ " = " ++ intercalate " | " (map showConstr cs)
-    Newtype lhs c -> "newtype " ++ showLHS lhs ++ " = " ++ showConstr c
-    Type lhs t -> "type " ++ showLHS lhs ++ " = " ++ showEType t
-    Fcn i eqns -> unlines (map (\ (Eqn ps alts) -> showIdent i ++ " " ++ unwords (map showEPat ps) ++ showAlts "=" alts) eqns)
-    Sign i t -> showIdent i ++ " :: " ++ showEType t
-    Import (ImportSpec q m mm mis) -> "import " ++ (if q then "qualified " else "") ++ showIdent m ++ maybe "" ((" as " ++) . unIdent) mm ++
+    Data lhs [] -> text "data" <+> ppLHS lhs
+    Data lhs cs -> text "data" <+> ppLHS lhs <+> text "=" <+> hsep (punctuate (text " |") (map ppConstr cs))
+    Newtype lhs c -> text "newtype" <+> ppLHS lhs <+> text "=" <+> ppConstr c
+    Type lhs t -> text "type" <+> ppLHS lhs <+> text "=" <+> ppEType t
+    Fcn i eqns -> vcat (map (\ (Eqn ps alts) -> sep [ppIdent i <+> hsep (map ppEPat ps), ppAlts (text "=") alts]) eqns)
+    Sign i t -> ppIdent i <+> text "::" <+> ppEType t
+    Import (ImportSpec q m mm mis) -> text "import" <+> (if q then text "qualified" else empty) <+> ppIdent m <> text (maybe "" ((" as " ++) . unIdent) mm) <>
       case mis of
-        Nothing -> ""
-        Just (h, is) -> (if h then " hiding" else "") ++ "(" ++ intercalate ", " (map showImportItem is) ++ ")"
-    ForImp ie i t -> "foreign import ccall " ++ showString ie ++ " " ++ showIdent i ++ " :: " ++ showEType t
-    Infix (a, p) is -> "infix" ++ f a ++ " " ++ showInt p ++ " " ++ intercalate ", " (map showIdent is)
+        Nothing -> empty
+        Just (h, is) -> text (if h then " hiding" else "") <> parens (hsep $ punctuate (text ", ") (map ppImportItem is))
+    ForImp ie i t -> text ("foreign import ccall " ++ showString ie) <+> ppIdent i <+> text "::" <+> ppEType t
+    Infix (a, p) is -> text ("infix" ++ f a) <+> text (showInt p) <+> hsep (punctuate (text ", ") (map ppIdent is))
       where f AssocLeft = "l"; f AssocRight = "r"; f AssocNone = ""
 
-showConstr :: Constr -> String
-showConstr (Constr c (Left  ts)) = unwords (showIdent c : map showEType ts)
-showConstr (Constr c (Right fs)) = unwords (showIdent c : "{" : map f fs ++ ["}"])
-  where f (i, t) = showIdent i ++ " :: " ++ showEType t ++ ","
+ppConstr :: Constr -> Doc
+ppConstr (Constr c (Left  ts)) = hsep (ppIdent c : map ppEType ts)
+ppConstr (Constr c (Right fs)) = ppIdent c <> braces (hsep $ map f fs)
+  where f (i, t) = ppIdent i <+> text "::" <+> ppEType t <> text ","
 
-showLHS :: LHS -> String
-showLHS lhs =
-  case lhs of
-    (f, vs) -> unwords (showIdent f : map showIdKind vs)
+ppLHS :: LHS -> Doc
+ppLHS (f, vs) = hsep (ppIdent f : map ppIdKind vs)
 
-showIdKind :: IdKind -> String
-showIdKind (IdKind i k) = "(" ++ showIdent i ++ "::" ++ showEKind k ++ ")"
+ppIdKind :: IdKind -> Doc
+ppIdKind (IdKind i k) = parens $ ppIdent i <> text "::" <> ppEKind k
 
-showEDefs :: [EDef] -> String
-showEDefs ds = unlines (map showEDef ds)
+ppEDefs :: [EDef] -> Doc
+ppEDefs ds = vcat (map ppEDef ds)
 
-showAlts :: String -> EAlts -> String
-showAlts sep (EAlts alts bs) = showAltsL sep alts ++ showWhere bs
+ppAlts :: Doc -> EAlts -> Doc
+ppAlts asep (EAlts alts bs) = ppAltsL asep alts <> ppWhere bs
 
-showWhere :: [EBind] -> String
-showWhere [] = ""
-showWhere bs = "where\n" ++ unlines (map showEBind bs)
+ppWhere :: [EBind] -> Doc
+ppWhere [] = empty
+ppWhere bs = text "where" $+$ nest 2 (vcat (map ppEBind bs))
 
-showAltsL :: String -> [EAlt] -> String
-showAltsL sep [([], e)] = " " ++ sep ++ " " ++ showExpr e
-showAltsL sep alts = unlines (map (showAlt sep) alts)
+ppAltsL :: Doc -> [EAlt] -> Doc
+ppAltsL asep [([], e)] = text "" <+> asep <+> ppExpr e
+ppAltsL asep alts = vcat (map (ppAlt asep) alts)
 
-showAlt :: String -> EAlt -> String
-showAlt sep (ss, e) = " | " ++ concat (intersperse ", " (map showEStmt ss)) ++ " " ++ sep ++ " " ++ showExpr e
+ppAlt :: Doc -> EAlt -> Doc
+ppAlt asep (ss, e) = text " |" <+> hsep (punctuate (text ",") (map ppEStmt ss)) <+> asep <+> ppExpr e
 
-showExpr :: Expr -> String
-showExpr ae =
+ppExpr :: Expr -> Doc
+ppExpr ae =
   case ae of
-    EVar v -> showIdent v
-    EApp _ _ -> showApp [] ae
-    EOper e ies -> showExpr (foldl (\ e1 (i, e2) -> EApp (EApp (EVar i) e1) e2) e ies)
-    ELam ps e -> "(\\" ++ unwords (map showExpr ps) ++ " -> " ++ showExpr e ++ ")"
-    ELit _ i -> showLit i
-    ECase e as -> "case " ++ showExpr e ++ " of {\n" ++ unlines (map showCaseArm as) ++ "}"
-    ELet bs e -> "let\n" ++ unlines (map showEBind bs) ++ "in " ++ showExpr e
-    ETuple es -> "(" ++ intercalate "," (map showExpr es) ++ ")"
-    EListish (LList es) -> showList showExpr es
-    EDo mn ss -> maybe "do" (\ n -> showIdent n ++ ".do\n") mn ++ unlines (map showEStmt ss)
-    ESectL e i -> "(" ++ showExpr e ++ " " ++ showIdent i ++ ")"
-    ESectR i e -> "(" ++ showIdent i ++ " " ++ showExpr e ++ ")"
-    EIf e1 e2 e3 -> "if " ++ showExpr e1 ++ " then " ++ showExpr e2 ++ " else " ++ showExpr e3
-    EListish l -> showListish l
-    ESign e t -> showExpr e ++ " :: " ++ showEType t
-    EAt i e -> showIdent i ++ "@" ++ showExpr e
-    EUVar i -> "a" ++ showInt i
-    ECon c -> showCon c
-    EForall iks e -> "forall " ++ unwords (map showIdKind iks) ++ " . " ++ showEType e
+    EVar v -> ppIdent v
+    EApp _ _ -> ppApp [] ae
+    EOper e ies -> ppExpr (foldl (\ e1 (i, e2) -> EApp (EApp (EVar i) e1) e2) e ies)
+    ELam ps e -> parens $ text "\\" <> hsep (map ppExpr ps) <+> text "->" <+> ppExpr e
+    ELit _ i -> text (showLit i)
+    ECase e as -> text "case" <+> ppExpr e <+> text "of" $$ nest 2 (vcat (map ppCaseArm as))
+    ELet bs e -> text "let" $$ nest 2 (vcat (map ppEBind bs)) $$ text "in" <+> ppExpr e
+    ETuple es -> parens $ hsep $ punctuate (text ",") (map ppExpr es)
+    EListish (LList es) -> ppList ppExpr es
+    EDo mn ss -> maybe (text "do") (\ n -> ppIdent n <> text ".do") mn $$ nest 2 (vcat (map ppEStmt ss))
+    ESectL e i -> parens $ ppExpr e <+> ppIdent i
+    ESectR i e -> parens $ ppIdent i <+> ppExpr e
+    EIf e1 e2 e3 -> parens $ sep [text "if" <+> ppExpr e1, text "then" <+> ppExpr e2, text "else" <+> ppExpr e3]
+    EListish l -> ppListish l
+    ESign e t -> ppExpr e <+> text "::" <+> ppEType t
+    EAt i e -> ppIdent i <> text "@" <> ppExpr e
+    EUVar i -> text ("a" ++ showInt i)
+    ECon c -> ppCon c
+    EForall iks e -> text "forall" <+> hsep (map ppIdKind iks) <+> text "." <+> ppEType e
   where
-    showApp as (EApp f a) = showApp (a:as) f
-    showApp as (EVar i) | eqString op "->", [a, b] <- as = "(" ++ showExpr a ++ " -> " ++ showExpr b ++ ")"
-                        | eqChar (head op) ',' = showExpr (ETuple as)
-                        | eqString op "[]", length as == 1 = showExpr (EListish (LList as))
+    ppApp as (EApp f a) = ppApp (a:as) f
+    ppApp as (EVar i) | eqString op "->", [a, b] <- as = parens $ ppExpr a <+> text "->" <+> ppExpr b
+                      | eqChar (head op) ',' = ppExpr (ETuple as)
+                      | eqString op "[]", length as == 1 = ppExpr (EListish (LList as))
                         where op = unQualString (unIdent i)
-    showApp as f = "(" ++ unwords (map showExpr (f:as)) ++ ")"
+    ppApp as f = parens $ hsep (map ppExpr (f:as))
 
-showListish :: Listish -> String
-showListish _ = "<<Listish>>"
+ppListish :: Listish -> Doc
+ppListish _ = text "<<Listish>>"
 
-showCon :: Con -> String
-showCon (ConData _ s) = showIdent s
-showCon (ConNew s) = showIdent s
-showCon (ConLit l) = showLit l
+ppCon :: Con -> Doc
+ppCon (ConData _ s) = ppIdent s
+ppCon (ConNew s) = ppIdent s
+ppCon (ConLit l) = text (showLit l)
 
 -- Literals are tagged the way they appear in the combinator file:
 --  #   Int
@@ -459,30 +470,33 @@
     LPrim s   -> s
     LForImp s -> '^' : s
 
-showEStmt :: EStmt -> String
-showEStmt as =
+ppEStmt :: EStmt -> Doc
+ppEStmt as =
   case as of
-    SBind p e -> showEPat p ++ " <- " ++ showExpr e
-    SThen e -> showExpr e
-    SLet bs -> "let\n" ++ unlines (map showEBind bs)
+    SBind p e -> ppEPat p <+> text "<-" <+> ppExpr e
+    SThen e -> ppExpr e
+    SLet bs -> text "let" $$ nest 2 (vcat (map ppEBind bs))
 
-showEBind :: EBind -> String
-showEBind ab =
+ppEBind :: EBind -> Doc
+ppEBind ab =
   case ab of
-    BFcn i eqns -> showEDef (Fcn i eqns)
-    BPat p e -> showEPat p ++ " = " ++ showExpr e
-    BSign i t -> showIdent i ++ " :: " ++ showEType t
+    BFcn i eqns -> ppEDef (Fcn i eqns)
+    BPat p e -> ppEPat p <+> text "=" <+> ppExpr e
+    BSign i t -> ppIdent i <+> text "::" <+> ppEType t
 
-showCaseArm :: ECaseArm -> String
-showCaseArm arm =
+ppCaseArm :: ECaseArm -> Doc
+ppCaseArm arm =
   case arm of
-    (p, alts) -> showEPat p ++ showAlts "->" alts
+    (p, alts) -> ppEPat p <> ppAlts (text "->") alts
 
-showEPat :: EPat -> String
-showEPat = showExpr
+ppEPat :: EPat -> Doc
+ppEPat = ppExpr
 
-showEType :: EType -> String
-showEType = showExpr
+ppEType :: EType -> Doc
+ppEType = ppExpr
 
-showEKind :: EKind -> String
-showEKind = showEType
+ppEKind :: EKind -> Doc
+ppEKind = ppEType
+
+ppList :: (a -> Doc) -> [a] -> Doc
+ppList pp xs = brackets $ hsep $ punctuate (text ",") (map pp xs)
--- a/src/MicroHs/Ident.hs
+++ b/src/MicroHs/Ident.hs
@@ -4,6 +4,7 @@
   Line, Col, Loc,
   Ident(..),
   mkIdent, mkIdentLoc, unIdent, eqIdent, leIdent, qualIdent, showIdent, getSLocIdent, setSLocIdent,
+  ppIdent,
   mkIdentSLoc,
   isLower_, isIdentChar, isOperChar, isConIdent,
   isDummyIdent,
@@ -18,6 +19,7 @@
 --Yimport Primitives(NFData(..))
 import Data.Char
 --Ximport Compat
+import MicroHs.Pretty
 
 type Line = Int
 type Col  = Int
@@ -57,6 +59,9 @@
 
 showIdent :: Ident -> String
 showIdent (Ident _ i) = i
+
+ppIdent :: Ident -> Doc
+ppIdent (Ident _ i) = text i
 
 eqIdent :: Ident -> Ident -> Bool
 eqIdent (Ident _ i) (Ident _ j) = eqString i j
--- a/src/MicroHs/TypeCheck.hs
+++ b/src/MicroHs/TypeCheck.hs
@@ -1366,7 +1366,7 @@
 showTModule :: forall a . (a -> String) -> TModule a -> String
 showTModule sh amdl =
   case amdl of
-    TModule mn _ _ _ _ a -> "Tmodule " ++ showIdent mn ++ "\n" ++ sh a
+    TModule mn _ _ _ _ a -> "Tmodule " ++ showIdent mn ++ "\n" ++ sh a ++ "\n"
 
 {-
 showValueTable :: ValueTable -> String
--