packages feed

egison-5.0.0: hs-src/Language/Egison/Pretty.hs

{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances  #-}
{-# OPTIONS_GHC -Wno-orphans        #-}

{- |
Module      : Language.Egison.PrettyPrint
Licence     : MIT

This module contains pretty printing for Egison syntax
-}

module Language.Egison.Pretty
    ( prettyTopExprs
    , prettyStr
    , showTSV
    ) where

import           Data.Foldable                  (toList)
import           Data.List                      (intercalate)
import           Prettyprinter
import           Prettyprinter.Render.String    (renderString)
import           Text.Show.Unicode              (ushow)

import           Language.Egison.AST
import           Language.Egison.Data
import           Language.Egison.IExpr hiding (TIPatternNode(..))
import           Language.Egison.IExpr (TIPatternNode(..))
import qualified Language.Egison.Type.Types as Types
import           Language.Egison.Type.Pretty (prettyTypeScheme)

--
-- Pretty printing for Non-S syntax
--

prettyTopExprs :: [TopExpr] -> Doc [TopExpr]
prettyTopExprs exprs = vsep $ punctuate line (map pretty exprs)

instance Pretty TopExpr where
  pretty (Define x (LambdaExpr args body)) =
    hsep (pretty "def" : pretty x : map pretty' args) <+> indentBlock (pretty ":=") [pretty body]
  pretty (Define x expr) =
    pretty "def" <+> pretty x <+> indentBlock (pretty ":=") [pretty expr]
  pretty (Test expr) = pretty expr
  pretty (LoadFile file) = pretty "loadFile" <+> pretty (show file)
  pretty (Load lib) = pretty "load" <+> pretty (show lib)
  pretty (PatternInductiveDecl typeName typeParams constructors) =
    let typeParamsDoc = if null typeParams then emptyDoc else hsep (map pretty typeParams)
        constructorsDoc = vsep $ map (\(PatternConstructor name args) ->
          pretty "|" <+> pretty name <+> hsep (map pretty args)) constructors
    in pretty "inductive" <+> pretty "pattern" <+> pretty typeName <+> typeParamsDoc <+> 
       pretty ":=" <+> constructorsDoc
  pretty (PatternFunctionDecl name typeParams params retType body) =
    let typeParamsDoc = if null typeParams then emptyDoc 
                        else braces (hsep $ punctuate (pretty ",") (map pretty typeParams))
        paramsDoc = hsep $ map (\(pname, ptype) -> 
          parens (pretty pname <+> pretty ":" <+> pretty ptype)) params
    in pretty "def" <+> pretty "pattern" <+> pretty name <+> typeParamsDoc <+> 
       paramsDoc <+> pretty ":" <+> pretty retType <+> pretty ":=" <+> pretty body
  pretty (DeclareSymbol names mTypeExpr) =
    let namesDoc = hsep $ punctuate (pretty ",") (map pretty names)
        typeDoc = case mTypeExpr of
                    Just typeExpr -> pretty ":" <+> pretty typeExpr
                    Nothing -> emptyDoc
    in pretty "declare" <+> pretty "symbol" <+> namesDoc <+> typeDoc
  pretty _ = error "Unsupported topexpr"

instance Pretty ConstantExpr where
  pretty (CharExpr x)    = viaShow x
  pretty (StringExpr x)  = pretty (ushow x)
  pretty (BoolExpr x)    = pretty x
  pretty (IntegerExpr x) = pretty x
  pretty (FloatExpr x)   = pretty x
  pretty SomethingExpr   = pretty "something"
  pretty UndefinedExpr   = pretty "undefined"

instance Pretty Expr where
  pretty (ConstantExpr c) = pretty c
  -- Use |viaShow| to correctly handle escaped characters
  pretty (VarExpr x)     = pretty x
  pretty FreshVarExpr    = pretty "#"
  pretty (IndexedExpr True e indices) = pretty' e <> cat (map pretty indices)
  pretty (IndexedExpr False e indices) = pretty' e <> pretty "..." <> cat (map pretty indices)
  pretty (SubrefsExpr b e1 e2) =
    applyLike [pretty "subrefs" <> (if b then pretty "!" else emptyDoc),
               pretty' e1, pretty' e2]
  pretty (SuprefsExpr b e1 e2) =
    applyLike [pretty "suprefs" <> (if b then pretty "!" else emptyDoc),
               pretty' e1, pretty' e2]
  pretty (UserrefsExpr b e1 e2) =
    applyLike [pretty "userRefs" <> (if b then pretty "!" else emptyDoc),
               pretty' e1, pretty' e2]

  pretty (TupleExpr xs) = tupled (map pretty xs)
  pretty (CollectionExpr xs)
    | length xs < 20 = list (map pretty xs)
    | otherwise      =
      pretty "[" <> align (fillSepAtom (punctuate comma (map pretty xs))) <> pretty "]"
  pretty (HashExpr xs)   = listoid "{|" "|}" (map (\(x, y) -> tupled [pretty x, pretty y]) xs)
  pretty (VectorExpr xs) = listoid "[|" "|]" (map pretty xs)

  pretty (LambdaExpr xs e) =
    lambdaLike (pretty "\\") (map pretty xs) (pretty "->") (pretty e)
  pretty (MemoizedLambdaExpr xs e)  =
    lambdaLike (pretty "memoizedLambda ") (map pretty xs) (pretty "->") (pretty e)
  pretty (TypedMemoizedLambdaExpr params retType e) =
    lambdaLike (pretty "memoizedLambda ") (map pretty params) (pretty ":" <+> pretty retType <+> pretty "->") (pretty e)
  pretty (CambdaExpr x e) =
    indentBlock (pretty "cambda" <+> pretty x <+> pretty "->") [pretty e]
  pretty (PatternFunctionExpr xs p) =
    lambdaLike (pretty "\\") (map pretty xs) (pretty "=>") (pretty p)

  pretty (IfExpr x y z) =
    indentBlock (pretty "if" <+> pretty x)
      [pretty "then" <+> pretty y, pretty "else" <+> pretty z]
  pretty (LetRecExpr bindings body) =
    hang 1 (pretty "let" <+> align (vsep (map pretty bindings)) <> hardline <> pretty "in" <+> align (pretty body))
  pretty (WithSymbolsExpr xs e) =
    indentBlock (pretty "withSymbols" <+> list (map pretty xs)) [pretty e]

  pretty (MatchExpr BFSMode tgt matcher clauses) =
    nest 2 (pretty "match"       <+> pretty tgt <+> prettyMatch matcher clauses)
  pretty (MatchExpr DFSMode tgt matcher clauses) =
    nest 2 (pretty "matchDFS"    <+> pretty tgt <+> prettyMatch matcher clauses)
  pretty (MatchAllExpr BFSMode tgt matcher clauses) =
    nest 2 (pretty "matchAll"    <+> pretty tgt <+> prettyMatch matcher clauses)
  pretty (MatchAllExpr DFSMode tgt matcher clauses) =
    nest 2 (pretty "matchAllDFS" <+> pretty tgt <+> prettyMatch matcher clauses)
  pretty (MatchLambdaExpr matcher clauses) =
    nest 2 (pretty "\\match"     <+> prettyMatch matcher clauses)
  pretty (MatchAllLambdaExpr matcher clauses) =
    nest 2 (pretty "\\matchAll"  <+> prettyMatch matcher clauses)
  pretty (MatcherExpr patDefs) =
    nest 2 (pretty "matcher" <> hardline <> align (vsep (map prettyPatDef patDefs)))
      where
        prettyPatDef (PatternDef pppat expr body) =
          nest 2 (pipe <+> pretty pppat <+> pretty "as" <+>
            group (pretty expr) <+> pretty "with" <> hardline <>
              align (vsep (map prettyPatBody body)))
        prettyPatBody (pdpat, expr) =
          indentBlock (pipe <+> align (pretty pdpat) <+> pretty "->") [pretty expr]

  pretty (AlgebraicDataMatcherExpr patDefs) =
    nest 2 (pretty "algebraicDataMatcher" <> hardline <> align (vsep (map prettyPatDef patDefs)))
      where
        prettyPatDef (name, exprs) = pipe <+> hsep (pretty name : map pretty exprs)

  pretty (QuoteExpr e) = squote <> pretty' e
  pretty (QuoteSymbolExpr e) = pretty '`' <> pretty' e

  pretty (PrefixExpr op x@(ConstantExpr (IntegerExpr _))) = pretty op <> pretty x
  pretty (PrefixExpr op x)
    | isAtomOrApp x = pretty op <+> pretty x
    | otherwise     = pretty op <+> parens (pretty x)
  -- (x1 op' x2) op y
  pretty (InfixExpr op x@(InfixExpr op' _ _) y) =
    if priority op > priority op' || priority op == priority op' && assoc op == InfixR
       then parens (pretty x) <+> pretty op <> infixRight (pretty'' y)
       else pretty x          <+> pretty op <> infixRight (pretty'' y)
  -- x op (y1 op' y2)
  pretty (InfixExpr op x y@(InfixExpr op' _ _)) =
    if priority op > priority op' || priority op == priority op' && assoc op == InfixL
       then pretty'' x <+> pretty op <> infixRight (parens (pretty y))
       else pretty'' x <+> pretty op <> infixRight (pretty y)
  pretty (InfixExpr op x y) =
    pretty'' x <+> pretty op <> infixRight (pretty'' y)
  pretty (SectionExpr op Nothing Nothing) = parens (pretty op)
  pretty (SectionExpr op (Just x) Nothing) = parens (pretty x <+> pretty op)
  pretty (SectionExpr op Nothing (Just x)) = parens (pretty op <+> pretty x)

  pretty (DoExpr [] y) = pretty "do" <+> pretty y
  pretty (DoExpr xs (ApplyExpr (VarExpr "return") [])) =
    pretty "do" <+> align (hsepHard (map prettyDoBinds xs))
  pretty (DoExpr xs y) = pretty "do" <+> align (hsepHard (map prettyDoBinds xs ++ [pretty y]))

  pretty (SeqExpr e1 e2) = applyLike [pretty "seq", pretty' e1, pretty' e2]
  pretty (ApplyExpr x ys) = applyLike (map pretty' (x : ys))
  pretty (AnonParamFuncExpr n e) = pretty n <> pretty '#' <> pretty' e
  pretty (AnonParamExpr n) = pretty '$' <> pretty n

  pretty (GenerateTensorExpr gen shape) =
    applyLike [pretty "generateTensor", pretty' gen, pretty' shape]
  pretty (TensorExpr e1 e2) =
    applyLike [pretty "tensor", pretty' e1, pretty' e2]
  pretty (TensorContractExpr e1) =
    applyLike [pretty "contract", pretty' e1]
  pretty (TensorMapExpr e1 e2) =
    applyLike [pretty "tensorMap", pretty' e1, pretty' e2]
  pretty (TensorMap2Expr e1 e2 e3) =
    applyLike [pretty "tensorMap2", pretty' e1, pretty' e2, pretty' e3]
  pretty (TransposeExpr e1 e2) =
    applyLike [pretty "transpose", pretty' e1, pretty' e2]
  pretty (FlipIndicesExpr _) = error "unreachable"

  pretty (FunctionExpr xs) = pretty "function" <+> tupled (map pretty xs)

  pretty p = pretty (show p)

instance (Pretty a, Complex a) => Pretty (Arg a) where
  pretty (Arg x)         = pretty x
  pretty (InvertedArg x) = pretty "!" <> pretty' x

instance Pretty ArgPattern where
  pretty APWildCard              = pretty "_"
  pretty (APPatVar x)            = pretty x
  pretty (APInductivePat x args) = applyLike (pretty x : map pretty' args)
  pretty (APTuplePat args)       = tupled (map pretty args)
  pretty APEmptyPat              = pretty "[]"
  pretty (APConsPat arg1 arg2)   = pretty'' arg1 <+> pretty "::" <+> pretty'' arg2
  pretty (APSnocPat arg1 arg2)   = pretty'' arg1 <+> pretty "*:" <+> pretty' arg2

instance Pretty VarWithIndices where
  pretty (VarWithIndices xs is) = pretty xs <> hcat (map pretty is)

instance Pretty VarIndex where
  pretty (VSubscript x)        = pretty ('_' : x)
  pretty (VSuperscript x)      = pretty ('~' : x)
  pretty (VSymmScripts xs)     = pretty '[' <> hcat (map pretty xs) <> pretty ']'
  pretty (VAntiSymmScripts xs) = pretty '{' <> hcat (map pretty xs) <> pretty '}'

instance Pretty BindingExpr where
  pretty (Bind (PDPatVar f) (LambdaExpr args body)) =
    hsep (pretty f : map pretty' args) <+> indentBlock (pretty ":=") [pretty body]
  pretty (Bind pat expr) = pretty pat <+> pretty ":=" <+> align (pretty expr)
  pretty (BindWithIndices var expr) = pretty var <+> pretty ":=" <+> align (pretty expr)
  pretty (BindWithType typedVar expr) =
    let constraints = typedVarConstraints typedVar
        constraintsDoc = if null constraints
                         then mempty
                         else pretty "{" <> hsep (punctuate (pretty ",") (map pretty constraints)) <> pretty "}" <> space
    in hsep (pretty (typedVarName typedVar) : [constraintsDoc | not (null constraints)] ++ map pretty (typedVarParams typedVar)) <+>
       pretty ":" <+> pretty (typedVarRetType typedVar) <+> pretty ":=" <+> align (pretty expr)

instance Pretty TypedParam where
  pretty (TPVar name ty) = parens (pretty name <+> pretty ":" <+> pretty ty)
  pretty (TPInvertedVar name ty) = parens (pretty "!" <+> pretty name <+> pretty ":" <+> pretty ty)
  pretty (TPTuple elems) = parens (hsep (punctuate comma (map pretty elems)))
  pretty (TPWildcard ty) = parens (pretty "_" <+> pretty ":" <+> pretty ty)
  pretty (TPUntypedVar name) = pretty name
  pretty TPUntypedWildcard = pretty "_"

instance Pretty TypeExpr where
  pretty TEInt = pretty "Integer"
  pretty TEMathExpr = pretty "MathExpr"
  pretty TEFloat = pretty "Float"
  pretty TEBool = pretty "Bool"
  pretty TEChar = pretty "Char"
  pretty TEString = pretty "String"
  pretty (TEVar v) = pretty v
  pretty (TEList t) = brackets (pretty t)
  pretty (TETuple []) = pretty "()"
  pretty (TETuple ts) = parens (hsep (punctuate comma (map pretty ts)))
  pretty (TEFun t1 t2) = pretty t1 <+> pretty "->" <+> pretty t2
  pretty (TEMatcher t) = pretty "Matcher" <+> pretty t
  pretty (TEPattern t) = pretty "Pattern" <+> pretty t
  pretty (TEIO t) = pretty "IO" <+> pretty t
  pretty (TETensor t) = pretty "Tensor" <+> pretty t
  pretty (TEApp t args) = hsep (pretty t : map pretty args)

instance Pretty ConstraintExpr where
  pretty (ConstraintExpr cls types) = hsep (pretty cls : map pretty types)

instance {-# OVERLAPPING #-} Pretty MatchClause where
  pretty (pat, expr) =
    pipe <+> align (pretty pat) <+> indentBlock (pretty "->") [pretty expr]

instance {-# OVERLAPPING #-} Pretty (IndexExpr String) where -- for 'VarWithIndices'
  pretty (Superscript s)  = pretty ("~" ++ s)
  pretty (Subscript s)    = pretty ("_" ++ s)
  pretty (SupSubscript s) = pretty ("~_" ++ s)
  pretty (Userscript i)   = pretty ("|" ++ show i)
  pretty _                = undefined

instance (Pretty a, Complex a) => Pretty (IndexExpr a) where
  pretty (Subscript i)          = pretty '_' <> pretty' i
  pretty (Superscript i)        = pretty '~' <> pretty' i
  pretty (SupSubscript i)       = pretty "~_" <> pretty' i
  pretty (MultiSubscript i j)   = pretty '_' <> pretty' i <> pretty "..._" <> pretty' j
  pretty (MultiSuperscript i j) = pretty '~' <> pretty' i <> pretty "...~" <> pretty' j
  pretty (Userscript i)         = pretty '|' <> pretty' i

instance Pretty Pattern where
  pretty WildCard     = pretty "_"
  pretty (PatVar x)   = pretty "$" <> pretty x
  pretty (ValuePat v) = pretty "#" <> pretty' v
  pretty (PredPat v)  = pretty "?" <> pretty' v
  pretty (IndexedPat p indices) =
    pretty p <> hcat (map (\i -> pretty '_' <> pretty' i) indices)
  pretty (LetPat binds pat) =
    pretty "let" <+> align (vsep (map pretty binds)) <+> pretty "in" <+> pretty pat
  -- (p11 op' p12) op p2
  pretty (InfixPat op p1@(InfixPat op' _ _) p2) =
    if priority op > priority op' || priority op == priority op' && assoc op == InfixR
       then parens (pretty p1) <+> pretty (repr op) <+> pretty'' p2
       else pretty p1          <+> pretty (repr op) <+> pretty'' p2
  -- p1 op (p21 op' p22)
  pretty (InfixPat op p1 p2@(InfixPat op' _ _)) =
    if priority op > priority op' || priority op == priority op' && assoc op == InfixL
       then pretty'' p1 <+> pretty (repr op) <+> parens (pretty p2)
       else pretty'' p1 <+> pretty (repr op) <+> pretty p2
  pretty (InfixPat op p1 p2) = pretty'' p1 <+> pretty (repr op) <+> pretty'' p2
  pretty (NotPat pat) = pretty "!" <> pretty' pat
  pretty (TuplePat pats) = tupled $ map pretty pats
  pretty (InductivePat "nil" []) = pretty "[]"
  pretty (InductivePat "::" [p, InductivePat "nil" []]) = pretty "[" <> pretty p <> pretty "]"
  pretty (InductivePat ctor xs) = hsep (pretty ctor : map pretty' xs)
  pretty (LoopPat i range p1 p2) =
    hang 2 (pretty "loop" <+> pretty '$' <> pretty i <+> pretty range <>
      flatAlt (hardline <> group (pretty' p1) <> hardline <> group (pretty' p2))
              (space <> pretty' p1 <+> pretty' p2))
  pretty ContPat = pretty "..."
  pretty (PApplyPat fn ps) = applyLike (pretty' fn : map pretty' ps)
  pretty (VarPat x) = pretty ('~' : x)
  pretty SeqNilPat = pretty "{}"
  pretty (SeqConsPat p1 p2) = listoid "{" "}" (f p1 p2)
    where
      f p1 SeqNilPat          = [pretty p1]
      f p1 (SeqConsPat p2 p3) = pretty p1 : f p2 p3
      f p1 p2                 = [pretty p1, pretty p2]
  pretty LaterPatVar = pretty "@"
  pretty (DApplyPat p ps) = applyLike (map pretty' (p : ps))
  pretty e            = pretty (show e)

instance {-# OVERLAPPING #-} Pretty LoopRange where
  pretty (LoopRange from (ApplyExpr (VarExpr "from")
                                    [ApplyExpr (VarExpr "i.-") [_, ConstantExpr (IntegerExpr 1)]]) pat) =
    tupled [pretty from, pretty pat]
  pretty (LoopRange from to pat) = tupled [pretty from, pretty to, pretty pat]

instance Pretty PrimitivePatPattern where
  pretty PPWildCard                = pretty "_"
  pretty PPPatVar                  = pretty "$"
  pretty (PPValuePat x)          = pretty ('#' : '$' : x)
  pretty (PPInductivePat x pppats) = hsep (pretty x : map pretty pppats)
  pretty (PPTuplePat pppats)       = tupled (map pretty pppats)

instance Pretty PrimitiveDataPattern where
  pretty PDWildCard                = pretty "_"
  pretty (PDPatVar x)              = pretty x
  pretty (PDInductivePat x pdpats) = applyLike (pretty x : map pretty' pdpats)
  pretty (PDTuplePat pdpats)       = tupled (map pretty pdpats)
  pretty PDEmptyPat                = pretty "[]"
  pretty (PDConsPat pdp1 pdp2)     = pretty'' pdp1 <+> pretty "::" <+> pretty'' pdp2
  pretty (PDSnocPat pdp1 pdp2)     = pretty'' pdp1 <+> pretty "*:" <+> pretty' pdp2
  pretty (PDConstantPat expr)      = pretty expr

instance Pretty Op where
  pretty op | isWedge op = pretty ("!" ++ repr op)
            | otherwise  = pretty (repr op)

instance Pretty IExpr where
  pretty (IConstantExpr c) = pretty c
  pretty (IVarExpr name) = pretty name
  
  pretty (IIndexedExpr override expr indices) =
    pretty' expr <> (if override then pretty "..." else emptyDoc) <> hcat (map prettyIndex indices)
    where
      prettyIndex (Sub e) = pretty "_" <> prettyIndexExpr e
      prettyIndex (Sup e) = pretty "~" <> prettyIndexExpr e
      prettyIndex (SupSub e) = pretty "~_" <> prettyIndexExpr e
      prettyIndex (User e) = pretty "|" <> prettyIndexExpr e
      prettyIndex (DF _ _) = emptyDoc
      prettyIndex (MultiSub _ _ _) = pretty "_..."
      prettyIndex (MultiSup _ _ _) = pretty "~..."
      prettyIndexExpr e = if isAtom e then pretty e else parens (pretty e)
  
  pretty (ISubrefsExpr override expr subExpr) =
    pretty' expr <> (if override then pretty "..." else emptyDoc) <> pretty "._" <> prettyRefExpr subExpr
  pretty (ISuprefsExpr override expr supExpr) =
    pretty' expr <> (if override then pretty "..." else emptyDoc) <> pretty ".~" <> prettyRefExpr supExpr
  pretty (IUserrefsExpr override expr userExpr) =
    pretty' expr <> (if override then pretty "..." else emptyDoc) <> pretty ".|" <> prettyRefExpr userExpr
  
  pretty (IInductiveDataExpr name args)
    | null args = pretty name
    | otherwise = applyLike (pretty name : map pretty' args)
  
  pretty (ITupleExpr []) = pretty "(" <> pretty ")"
  pretty (ITupleExpr xs) = tupled (map pretty xs)
  
  pretty (ICollectionExpr xs) = list (map pretty xs)
  
  pretty (IConsExpr x xs) = pretty'' x <+> pretty "::" <+> pretty'' xs
  pretty (IJoinExpr x xs) = pretty'' x <+> pretty "++" <+> pretty'' xs
  
  pretty (IHashExpr pairs) = 
    pretty "{|" <+> hsep (punctuate comma (map prettyPair pairs)) <+> pretty "|}"
    where prettyPair (k, v) = parens (pretty k <> comma <+> pretty v)
  
  pretty (IVectorExpr xs) = 
    pretty "[|" <+> hsep (punctuate comma (map pretty xs)) <+> pretty "|]"
  
  pretty (ILambdaExpr _mVar params body) =
    lambdaLike (pretty "\\") (map prettyVar params) (pretty "->") (pretty body)
    where prettyVar (Var name []) = pretty name
          prettyVar v = pretty (show v)  -- fallback for complex vars
  
  pretty (IMemoizedLambdaExpr xs e) =
    lambdaLike (pretty "memoizedLambda") (map pretty xs) (pretty "->") (pretty e)
  
  pretty (ICambdaExpr x e) =
    indentBlock (pretty "cambda" <+> pretty x <+> pretty "->") [pretty e]
  
  pretty (IIfExpr cond thenE elseE) =
    indentBlock (pretty "if" <+> pretty cond)
      [pretty "then" <+> pretty thenE, pretty "else" <+> pretty elseE]
  
  pretty (ILetRecExpr bindings body) =
    hang 1 (pretty "let" <+> align (vsep (map prettyIBinding bindings)) <> hardline <> 
            pretty "in" <+> align (pretty body))
  
  pretty (ILetExpr bindings body) =
    hang 1 (pretty "let" <+> align (vsep (map prettyIBinding bindings)) <> hardline <> 
            pretty "in" <+> align (pretty body))
  
  pretty (IWithSymbolsExpr xs e) =
    indentBlock (pretty "withSymbols" <+> list (map pretty xs)) [pretty e]
  
  pretty (IMatchExpr BFSMode tgt matcher clauses) =
    nest 2 (pretty "match" <+> pretty tgt <+> prettyIMatch matcher clauses)
  pretty (IMatchExpr DFSMode tgt matcher clauses) =
    nest 2 (pretty "matchDFS" <+> pretty tgt <+> prettyIMatch matcher clauses)
  
  pretty (IMatchAllExpr BFSMode tgt matcher clauses) =
    nest 2 (pretty "matchAll" <+> pretty tgt <+> prettyIMatch matcher clauses)
  pretty (IMatchAllExpr DFSMode tgt matcher clauses) =
    nest 2 (pretty "matchAllDFS" <+> pretty tgt <+> prettyIMatch matcher clauses)
  
  pretty (IMatcherExpr patDefs) =
    nest 2 (pretty "matcher" <> hardline <> align (vsep (map prettyIPatDef patDefs)))
    where
      prettyIPatDef (pppat, expr, body) =
        nest 2 (pipe <+> pretty pppat <+> pretty "as" <+>
          group (pretty expr) <+> pretty "with" <> hardline <>
            align (vsep (map prettyIPatBody body)))
      prettyIPatBody (pdpat, expr) =
        indentBlock (pipe <+> align (pretty pdpat) <+> pretty "->") [pretty expr]
  
  pretty (IQuoteExpr e) = squote <> pretty' e
  pretty (IQuoteSymbolExpr e) = pretty '`' <> pretty' e
  
  pretty (IWedgeApplyExpr op args) = applyLike (pretty' op : map pretty' args)
  
  pretty (IDoExpr [] y) = pretty "do" <+> pretty y
  pretty (IDoExpr xs y) = pretty "do" <+> align (hsepHard (map prettyIDoBinds xs ++ [pretty y]))
  
  pretty (ISeqExpr e1 e2) = applyLike [pretty "seq", pretty' e1, pretty' e2]
  
  pretty (IApplyExpr fn args) = applyLike (map pretty' (fn : args))
  
  pretty (IGenerateTensorExpr gen shape) =
    applyLike [pretty "generateTensor", pretty' gen, pretty' shape]
  
  pretty (ITensorExpr e1 e2) =
    applyLike [pretty "tensor", pretty' e1, pretty' e2]
  
  pretty (ITensorContractExpr e1) =
    applyLike [pretty "contract", pretty' e1]
  
  pretty (ITensorMapExpr e1 e2) =
    applyLike [pretty "tensorMap", pretty' e1, pretty' e2]
  
  pretty (ITensorMap2Expr e1 e2 e3) =
    applyLike [pretty "tensorMap2", pretty' e1, pretty' e2, pretty' e3]

  pretty (ITensorMap2WedgeExpr e1 e2 e3) =
    applyLike [pretty "tensorMap2Wedge", pretty' e1, pretty' e2, pretty' e3]

  pretty (ITransposeExpr e1 e2) =
    applyLike [pretty "transpose", pretty' e1, pretty' e2]
  
  pretty (IFlipIndicesExpr e) =
    applyLike [pretty "flipIndices", pretty' e]
  
  pretty (IFunctionExpr xs) = pretty "function" <+> tupled (map pretty xs)

prettyRefExpr :: IExpr -> Doc ann
prettyRefExpr e = if isAtom e then pretty e else parens (pretty e)

prettyIBinding :: IBindingExpr -> Doc ann
prettyIBinding (pdpat, expr) = pretty pdpat <+> pretty ":=" <+> align (pretty expr)

prettyIDoBinds :: IBindingExpr -> Doc ann
prettyIDoBinds (pdpat, expr) = pretty pdpat <+> pretty "<-" <+> pretty expr

prettyIMatch :: IExpr -> [IMatchClause] -> Doc ann
prettyIMatch matcher clauses =
  pretty "as" <+> pretty matcher <+> pretty "with" <> hardline <>
    indent 2 (vsep (map prettyIClause clauses))
  where
    prettyIClause (pat, body) =
      indentBlock (pipe <+> pretty pat <+> pretty "->") [pretty body]

instance Complex IExpr where
  isAtom (IConstantExpr (IntegerExpr i)) | i < 0 = False
  isAtom (IConstantExpr _) = True
  isAtom (IVarExpr _) = True
  isAtom ITupleExpr{} = True
  isAtom ICollectionExpr{} = True
  isAtom IHashExpr{} = True
  isAtom IVectorExpr{} = True
  isAtom IMatcherExpr{} = True
  isAtom (IIndexedExpr _ e _) = isAtom e
  isAtom (IInductiveDataExpr _ []) = True
  isAtom _ = False
  
  isAtomOrApp (IApplyExpr _ _) = True
  isAtomOrApp (IInductiveDataExpr _ (_:_)) = True
  isAtomOrApp e = isAtom e
  
  isInfix _ = False  -- IExpr doesn't have infix expressions (they're desugared)

instance Pretty IPrimitiveDataPattern where
  pretty (PDPatVar (Var name [])) = pretty name
  pretty (PDPatVar var) = pretty (show var)
  pretty PDWildCard = pretty "_"
  pretty (PDInductivePat name []) = pretty name
  pretty (PDInductivePat name pats) = applyLike (pretty name : map pretty pats)
  pretty (PDTuplePat pats) = tupled (map pretty pats)
  pretty PDEmptyPat = pretty "[]"
  pretty (PDConsPat pat1 pat2) = pretty pat1 <+> pretty "::" <+> pretty pat2
  pretty (PDSnocPat pat1 pat2) = pretty pat1 <+> pretty "*:" <+> pretty pat2
  pretty (PDConstantPat c) = pretty c
  -- MathExpr primitive patterns
  pretty (PDDivPat p1 p2) = applyLike [pretty "Div", pretty p1, pretty p2]
  pretty (PDPlusPat p) = applyLike [pretty "Plus", pretty p]
  pretty (PDTermPat p1 p2) = applyLike [pretty "Term", pretty p1, pretty p2]
  pretty (PDSymbolPat p1 p2) = applyLike [pretty "Symbol", pretty p1, pretty p2]
  pretty (PDApply1Pat p1 p2) = applyLike [pretty "Apply1", pretty p1, pretty p2]
  pretty (PDApply2Pat p1 p2 p3) = applyLike [pretty "Apply2", pretty p1, pretty p2, pretty p3]
  pretty (PDApply3Pat p1 p2 p3 p4) = applyLike [pretty "Apply3", pretty p1, pretty p2, pretty p3, pretty p4]
  pretty (PDApply4Pat p1 p2 p3 p4 p5) = applyLike [pretty "Apply4", pretty p1, pretty p2, pretty p3, pretty p4, pretty p5]
  pretty (PDQuotePat p) = applyLike [pretty "Quote", pretty p]
  pretty (PDFunctionPat p1 p2) = applyLike [pretty "Function", pretty p1, pretty p2]
  pretty (PDSubPat p) = applyLike [pretty "Sub", pretty p]
  pretty (PDSupPat p) = applyLike [pretty "Sup", pretty p]
  pretty (PDUserPat p) = applyLike [pretty "User", pretty p]

instance Pretty IPattern where
  pretty IWildCard = pretty "_"
  pretty (IPatVar name) = pretty "~" <> pretty name  -- IPatVar is VarPat (~x, pattern variable reference)
  pretty (IValuePat expr) = pretty "#" <> pretty' expr
  pretty (IPredPat expr) = pretty "?" <> pretty' expr
  pretty (IIndexedPat pat indices) =
    pretty' pat <> hcat (map prettyIndex indices)
    where
      prettyIndex e = if isAtom e then pretty e else parens (pretty e)
  pretty (ILetPat bindings pat) =
    pretty "let" <+> align (vsep (map prettyIBinding bindings)) <+> pretty "in" <+> pretty pat
  pretty (INotPat pat) = pretty "!" <> pretty' pat
  pretty (IAndPat pat1 pat2) = pretty' pat1 <+> pretty "&" <+> pretty' pat2
  pretty (IOrPat pat1 pat2) = pretty' pat1 <+> pretty "|" <+> pretty' pat2
  pretty (IForallPat var pat) = pretty "forall" <+> pretty var <+> pretty pat
  pretty (ITuplePat pats) = tupled (map pretty pats)
  pretty (IInductivePat name []) = pretty name
  pretty (IInductivePat name pats) = applyLike (pretty name : map pretty' pats)
  pretty (ILoopPat var (ILoopRange start end pat) bodyPat restPat) =
    pretty "loop" <+> pretty "$" <> pretty var <+>
    tupled [pretty start, pretty end, pretty pat] <+>
    pretty' bodyPat <+> pretty' restPat
  pretty IContPat = pretty "..."
  pretty (IPApplyPat expr pats) = applyLike (pretty' expr : map pretty' pats)
  pretty (IVarPat name) = pretty "$" <> pretty name  -- IVarPat is PatVar ($x, new binding)
  pretty (IInductiveOrPApplyPat name pats)
    | null pats = pretty name
    | otherwise = applyLike (pretty name : map pretty' pats)
  pretty ISeqNilPat = pretty "{}"
  pretty (ISeqConsPat p1 p2) = listoid "{" "}" (f p1 p2)
    where
      f p1 ISeqNilPat          = [pretty p1]
      f p1 (ISeqConsPat p2 p3) = pretty p1 : f p2 p3
      f p1 p2                  = [pretty p1, pretty p2]
  pretty ILaterPatVar = pretty "@"
  pretty (IDApplyPat pat pats) = applyLike (pretty' pat : map pretty' pats)

instance Complex IPattern where
  isAtom IWildCard = True
  isAtom (IPatVar _) = True
  isAtom (ITuplePat _) = True
  isAtom (IInductivePat _ []) = True
  isAtom ISeqNilPat = True
  isAtom _ = False
  
  isAtomOrApp (IPApplyPat _ _) = True
  isAtomOrApp (IInductiveOrPApplyPat _ (_:_)) = True
  isAtomOrApp (IInductivePat _ (_:_)) = True
  isAtomOrApp pat = isAtom pat
  
  isInfix _ = False

-- Pretty print for TIPattern (use existing prettyPatternWithType)
instance Pretty TIPattern where
  pretty = prettyPatternWithType

instance Complex TIPattern where
  isAtom (TIPattern _ TIWildCard) = True
  isAtom (TIPattern _ (TIVarPat _)) = True
  isAtom (TIPattern _ (TITuplePat _)) = True
  isAtom (TIPattern _ (TIInductivePat _ [])) = True
  isAtom (TIPattern _ TISeqNilPat) = True
  isAtom _ = False
  
  isAtomOrApp (TIPattern _ (TIPApplyPat _ _)) = True
  isAtomOrApp (TIPattern _ (TIInductiveOrPApplyPat _ (_:_))) = True
  isAtomOrApp (TIPattern _ (TIInductivePat _ (_:_))) = True
  isAtomOrApp pat = isAtom pat
  
  isInfix _ = False

-- Pretty print for ITopExpr
instance Pretty ITopExpr where
  pretty (IDefine var iexpr) =
    pretty "def" <+> prettyVar var <+> indentBlock (pretty ":=") [pretty iexpr]
  pretty (IDefineMany bindings) =
    vsep (map prettyDefineMany bindings)
    where
      prettyDefineMany (var, iexpr) =
        pretty "def" <+> prettyVar var <+> pretty ":=" <+> pretty iexpr
  pretty (ITest iexpr) = 
    pretty iexpr
  pretty (IExecute iexpr) =
    pretty "execute" <+> pretty iexpr
  pretty (ILoadFile path) =
    pretty "loadFile" <+> pretty (show path)
  pretty (ILoad lib) =
    pretty "load" <+> pretty (show lib)
  pretty (IDeclareSymbol names mType) =
    let namesDoc = hsep $ punctuate (pretty ",") (map pretty names)
        typeDoc = case mType of
                    Just ty -> pretty ":" <+> prettyTypeDoc ty
                    Nothing -> emptyDoc
    in pretty "declare" <+> pretty "symbol" <+> namesDoc <+> typeDoc
  pretty (IPatternFunctionDecl name tyVars params retType body) =
    let tyVarsDoc = if null tyVars
                      then emptyDoc
                      else pretty "{" <> hsep (punctuate (pretty ",") (map prettyTyVar tyVars)) <> pretty "}"
        paramsDoc = hsep (map prettyParam params)
        retTypeDoc = prettyTypeDoc retType
    in pretty "def" <+> pretty "pattern" <+> pretty name <+> tyVarsDoc <+> paramsDoc <+> 
       pretty ":" <+> retTypeDoc <+> indentBlock (pretty ":=") [pretty body]
    where
      prettyTyVar (Types.TyVar v) = pretty v
      prettyParam (pname, pty) = pretty "(" <> pretty pname <+> pretty ":" <+> prettyTypeDoc pty <> pretty ")"

-- Pretty print for TIExpr and TITopExpr
instance Pretty TIExpr where
  pretty tiexpr = prettyTIExprWithType tiexpr

-- Pretty print TIExpr with type annotations for all subexpressions
prettyTIExprWithType :: TIExpr -> Doc ann
prettyTIExprWithType tiexpr =
  let (Types.Forall _ constraints ty) = tiScheme tiexpr
      constraintDoc = prettyConstraintsDoc constraints
  in parens (prettyTIExprNode (tiExprNode tiexpr) <+> pretty ":" <+> constraintDoc <> prettyTypeDoc ty)

-- Pretty print pattern with type annotations (recursive)
prettyPatternWithType :: TIPattern -> Doc ann
prettyPatternWithType (TIPattern (Types.Forall _ constraints ty) node) =
  let constraintDoc = prettyConstraintsDoc constraints
  in parens (prettyTIPatternNode node <+> pretty ":" <+> constraintDoc <> prettyTypeDoc ty)

-- Pretty print pattern node recursively
prettyTIPatternNode :: TIPatternNode -> Doc ann
prettyTIPatternNode node = case node of
  TIWildCard -> pretty "_"
  TIPatVar name -> pretty "~" <> pretty name  -- TIPatVar is VarPat (~x, pattern variable reference)
  TIValuePat expr -> pretty "#" <> prettyTIExprWithType expr
  TIPredPat expr -> pretty "?" <> prettyTIExprWithType expr
  TIIndexedPat pat exprs -> prettyPatternWithType pat <> hcat (map prettyIndexExpr exprs)
    where prettyIndexExpr e = pretty "_" <> prettyTIExprWithType e
  TILetPat bindings pat -> pretty "let" <+> hsep (map prettyBinding bindings) <+> pretty "in" <+> prettyPatternWithType pat
    where prettyBinding (p, e) = pretty p <+> pretty ":=" <+> prettyTIExprWithType e
  TINotPat pat -> pretty "!" <> prettyPatternWithType pat
  TIAndPat p1 p2 -> prettyPatternWithType p1 <+> pretty "&" <+> prettyPatternWithType p2
  TIOrPat p1 p2 -> prettyPatternWithType p1 <+> pretty "|" <+> prettyPatternWithType p2
  TIForallPat p1 p2 -> pretty "forall" <+> prettyPatternWithType p1 <+> prettyPatternWithType p2
  TITuplePat pats -> tupled (map prettyPatternWithType pats)
  TIInductivePat name pats -> pretty name <+> hsep (map prettyPatternWithType pats)
  TILoopPat var (TILoopRange start end pat) p1 p2 ->
    pretty "loop" <+> pretty "$" <> pretty var <+>
    tupled [prettyTIExprWithType start, prettyTIExprWithType end, prettyPatternWithType pat] <+>
    prettyPatternWithType p1 <+> prettyPatternWithType p2
  TIContPat -> pretty "..."
  TIPApplyPat func pats -> prettyTIExprWithType func <+> hsep (map prettyPatternWithType pats)
  TIVarPat name -> pretty "$" <> pretty name  -- TIVarPat is PatVar ($x, new binding)
  TIInductiveOrPApplyPat name pats -> pretty name <+> hsep (map prettyPatternWithType pats)
  TISeqNilPat -> pretty "{}"
  TISeqConsPat p1 p2 -> listoid "{" "}" (f p1 p2)
    where
      f p1 (TIPattern _ TISeqNilPat)          = [prettyPatternWithType p1]
      f p1 (TIPattern _ (TISeqConsPat p2 p3)) = prettyPatternWithType p1 : f p2 p3
      f p1 p2                                  = [prettyPatternWithType p1, prettyPatternWithType p2]
  TILaterPatVar -> pretty "@"
  TIDApplyPat pat pats -> prettyPatternWithType pat <+> hsep (map prettyPatternWithType pats)

-- Pretty print TIExprNode recursively
prettyTIExprNode :: TIExprNode -> Doc ann
prettyTIExprNode node = case node of
  TIConstantExpr c -> pretty c
  TIVarExpr name -> pretty name
  
  TILambdaExpr _ params body ->
    pretty "\\" <> hsep (map prettyVar params) <+> pretty "->" <+> prettyTIExprWithType body
  
  TIApplyExpr func args ->
    prettyTIExprWithType func <+> hsep (map prettyTIExprWithType args)
  
  TITupleExpr exprs ->
    tupled (map prettyTIExprWithType exprs)
  
  TICollectionExpr exprs ->
    brackets (hsep $ punctuate comma (map prettyTIExprWithType exprs))
  
  TIConsExpr h t ->
    prettyTIExprWithType h <+> pretty "::" <+> prettyTIExprWithType t
  
  TIJoinExpr l r ->
    prettyTIExprWithType l <+> pretty "++" <+> prettyTIExprWithType r
  
  TIIfExpr cond thenE elseE ->
    pretty "if" <+> prettyTIExprWithType cond <+>
    pretty "then" <+> prettyTIExprWithType thenE <+>
    pretty "else" <+> prettyTIExprWithType elseE
  
  TILetExpr bindings body ->
    pretty "let" <+> vsep (map prettyBinding bindings) <+> pretty "in" <+> prettyTIExprWithType body
    where prettyBinding (pat, expr) = pretty pat <+> pretty ":=" <+> prettyTIExprWithType expr
  
  TITensorMapExpr func tensor ->
    pretty "tensorMap" <+> prettyTIExprWithType func <+> prettyTIExprWithType tensor
  
  TITensorMap2Expr func t1 t2 ->
    pretty "tensorMap2" <+> prettyTIExprWithType func <+> prettyTIExprWithType t1 <+> prettyTIExprWithType t2

  TITensorMap2WedgeExpr func t1 t2 ->
    pretty "tensorMap2Wedge" <+> prettyTIExprWithType func <+> prettyTIExprWithType t1 <+> prettyTIExprWithType t2

  TITensorContractExpr tensor ->
    pretty "contract" <+> prettyTIExprWithType tensor
  
  TITensorExpr shape elems ->
    pretty "tensor" <+> prettyTIExprWithType shape <+> prettyTIExprWithType elems
  
  TIGenerateTensorExpr func shape ->
    pretty "generateTensor" <+> prettyTIExprWithType func <+> prettyTIExprWithType shape
  
  TITransposeExpr perm tensor ->
    pretty "transpose" <+> prettyTIExprWithType perm <+> prettyTIExprWithType tensor
  
  TIFlipIndicesExpr tensor ->
    pretty "flipIndices" <+> prettyTIExprWithType tensor
  
  TIVectorExpr exprs ->
    pretty "[|" <+> hsep (punctuate comma (map prettyTIExprWithType exprs)) <+> pretty "|]"
  
  TIHashExpr pairs ->
    pretty "{|" <+> hsep (punctuate comma (map prettyPair pairs)) <+> pretty "|}"
    where prettyPair (k, v) = parens (prettyTIExprWithType k <> comma <+> prettyTIExprWithType v)
  
  TISeqExpr e1 e2 ->
    prettyTIExprWithType e1 <> pretty ";" <+> prettyTIExprWithType e2
  
  TIMemoizedLambdaExpr params body ->
    pretty "memoizedLambda" <+> hsep (map pretty params) <+> pretty "->" <+> prettyTIExprWithType body
  
  TICambdaExpr param body ->
    pretty "cambda" <+> pretty param <+> pretty "->" <+> prettyTIExprWithType body
  
  TIWithSymbolsExpr syms body ->
    pretty "withSymbols" <+> list (map pretty syms) <+> prettyTIExprWithType body
  
  TIDoExpr bindings body ->
    pretty "do" <+> vsep (map prettyBinding bindings) <+> prettyTIExprWithType body
    where prettyBinding (pat, expr) = pretty pat <+> pretty "<-" <+> prettyTIExprWithType expr
  
  TIMatchExpr _mode target matcher clauses ->
    pretty "match" <+> prettyTIExprWithType target <+> pretty "as" <+> prettyTIExprWithType matcher <+>
    pretty "with" <+> vsep (map prettyClause clauses)
    where prettyClause (tipat, body) = 
            pretty "|" <+> prettyPatternWithType tipat <+> pretty "->" <+> prettyTIExprWithType body

  TIMatchAllExpr _mode target matcher clauses ->
    pretty "matchAll" <+> prettyTIExprWithType target <+> pretty "as" <+> prettyTIExprWithType matcher <+>
    pretty "with" <+> vsep (map prettyClause clauses)
    where prettyClause (tipat, body) = 
            pretty "|" <+> prettyPatternWithType tipat <+> pretty "->" <+> prettyTIExprWithType body
  
  TIInductiveDataExpr name exprs ->
    pretty name <+> hsep (map prettyTIExprWithType exprs)
  
  TIQuoteExpr e ->
    squote <> prettyTIExprWithType e
  
  TIQuoteSymbolExpr e ->
    pretty '`' <> prettyTIExprWithType e
  
  TISubrefsExpr _ base ref ->
    pretty "subrefs" <+> prettyTIExprWithType base <+> prettyTIExprWithType ref
  
  TISuprefsExpr _ base ref ->
    pretty "suprefs" <+> prettyTIExprWithType base <+> prettyTIExprWithType ref
  
  TIUserrefsExpr _ base ref ->
    pretty "userrefs" <+> prettyTIExprWithType base <+> prettyTIExprWithType ref
  
  TIWedgeApplyExpr func args ->
    pretty "!" <+> prettyTIExprWithType func <+> hsep (map prettyTIExprWithType args)
  
  TIIndexedExpr _ base indices ->
    prettyTIExprWithType base <> hcat (map prettyIndex indices)
    where
      prettyIndex (Sub e) = pretty "_" <> pretty e
      prettyIndex (Sup e) = pretty "~" <> pretty e
      prettyIndex (SupSub e) = pretty "~_" <> pretty e
      prettyIndex (User e) = pretty "|" <> pretty e
      prettyIndex (MultiSub e1 n e2) = pretty "_..." <> pretty e1 <> pretty n <> pretty e2
      prettyIndex (MultiSup e1 n e2) = pretty "~..." <> pretty e1 <> pretty n <> pretty e2
      prettyIndex (DF _i1 _i2) = emptyDoc
  
  TIFunctionExpr names ->
    pretty "function" <+> hsep (map pretty names)
  
  TILetRecExpr bindings body ->
    pretty "let" <+> vsep (map prettyBinding bindings) <+> pretty "in" <+> prettyTIExprWithType body
    where prettyBinding (pat, expr) = pretty pat <+> pretty ":=" <+> prettyTIExprWithType expr
  
  TIMatcherExpr patDefs ->
    pretty "matcher" <+> vsep (map prettyPatDef patDefs)
    where prettyPatDef (pat, expr, _bindings) = pretty pat <+> pretty "->" <+> prettyTIExprWithType expr

instance Pretty TITopExpr where
  pretty (TIDefine scheme var tiexpr) =
    let typeStr = prettyTypeScheme scheme
    in pretty "def" <+> prettyVar var <+> pretty ":" <+> pretty typeStr <+> 
       indentBlock (pretty ":=") [pretty tiexpr]
  pretty (TITest tiexpr) = 
    pretty tiexpr
  pretty (TIExecute tiexpr) =
    pretty "execute" <+> pretty tiexpr
  pretty (TILoadFile path) =
    pretty "loadFile" <+> pretty (show path)
  pretty (TILoad lib) =
    pretty "load" <+> pretty (show lib)
  pretty (TIDefineMany bindings) =
    vsep (map prettyBinding bindings)
    where
      prettyBinding (var, tiexpr) =
        prettyVar var <+> pretty ":=" <+> pretty tiexpr
  pretty (TIDeclareSymbol names ty) =
    let namesDoc = hsep $ punctuate (pretty ",") (map pretty names)
    in pretty "declare" <+> pretty "symbol" <+> namesDoc <+> pretty ":" <+> prettyTypeDoc ty
  pretty (TIPatternFunctionDecl name typeScheme params retType body) =
    let typeStr = prettyTypeScheme typeScheme
        paramsDoc = hsep (map prettyParam params)
        retTypeDoc = prettyTypeDoc retType
    in pretty "def" <+> pretty "pattern" <+> pretty name <+> pretty ":" <+> pretty typeStr <+>
       paramsDoc <+> pretty ":" <+> retTypeDoc <+> indentBlock (pretty ":=") [pretty body]
    where
      prettyParam (pname, pty) = pretty "(" <> pretty pname <+> pretty ":" <+> prettyTypeDoc pty <> pretty ")"

-- Helper function to pretty print Var
prettyVar :: Var -> Doc ann
prettyVar (Var name []) = pretty name
prettyVar (Var name indices) = pretty name <> hcat (map prettyVarIndex indices)
  where
    prettyVarIndex (Sub Nothing) = pretty "_"
    prettyVarIndex (Sub (Just v)) = pretty "_" <> prettyVar v
    prettyVarIndex (Sup Nothing) = pretty "~"
    prettyVarIndex (Sup (Just v)) = pretty "~" <> prettyVar v
    prettyVarIndex (SupSub Nothing) = pretty "~_"
    prettyVarIndex (SupSub (Just v)) = pretty "~_" <> prettyVar v
    prettyVarIndex (User Nothing) = pretty "|"
    prettyVarIndex (User (Just v)) = pretty "|" <> prettyVar v
    prettyVarIndex (MultiSub _ _ _) = pretty "_..."
    prettyVarIndex (MultiSup _ _ _) = pretty "~..."
    prettyVarIndex (DF _ _) = emptyDoc

-- Helper function to pretty print constraints as Doc
prettyConstraintsDoc :: [Types.Constraint] -> Doc ann
prettyConstraintsDoc [] = emptyDoc
prettyConstraintsDoc [c] = pretty "{" <> prettyConstraintDoc c <> pretty "}" <> space
prettyConstraintsDoc cs =
  pretty "{" <> hsep (punctuate comma (map prettyConstraintDoc cs)) <> pretty "}" <> space

-- Helper function to pretty print a single constraint as Doc
prettyConstraintDoc :: Types.Constraint -> Doc ann
prettyConstraintDoc (Types.Constraint className tyArg) = 
  pretty className <+> prettyTypeDoc tyArg

-- Helper function to pretty print Type as Doc
prettyTypeDoc :: Types.Type -> Doc ann
prettyTypeDoc Types.TInt = pretty "Integer"
prettyTypeDoc Types.TFloat = pretty "Float"
prettyTypeDoc Types.TBool = pretty "Bool"
prettyTypeDoc Types.TChar = pretty "Char"
prettyTypeDoc Types.TString = pretty "String"
prettyTypeDoc (Types.TTuple []) = pretty "()"
prettyTypeDoc (Types.TVar (Types.TyVar v)) = pretty v
prettyTypeDoc (Types.TFun t1 t2) = prettyTypeArg t1 <+> pretty "->" <+> prettyTypeDoc t2
  where
    prettyTypeArg t@(Types.TFun _ _) = parens (prettyTypeDoc t)
    prettyTypeArg t = prettyTypeDoc t
prettyTypeDoc (Types.TTuple ts) = tupled (map prettyTypeDoc ts)
prettyTypeDoc (Types.TCollection t) = brackets (prettyTypeDoc t)
prettyTypeDoc (Types.THash k v) =
  pretty "Hash" <+> prettyTypeDoc k <+> prettyHashValueTypeDoc v
  where
    -- Hash value types need parentheses if they are function types
    prettyHashValueTypeDoc t@(Types.TFun _ _) = parens (prettyTypeDoc t)
    prettyHashValueTypeDoc t = prettyTypeDoc t
prettyTypeDoc (Types.TMatcher t) = pretty "Matcher" <+> prettyTypeDoc t
prettyTypeDoc (Types.TIO t) = pretty "IO" <+> prettyTypeDoc t
prettyTypeDoc (Types.TIORef t) = pretty "IORef" <+> prettyTypeDoc t
prettyTypeDoc (Types.TTensor t) = pretty "Tensor" <+> prettyTypeDoc t
prettyTypeDoc (Types.TInductive name []) = pretty name
prettyTypeDoc (Types.TInductive name ts) = hsep (pretty name : map prettyTypeDoc ts)
prettyTypeDoc Types.TAny = pretty "_"
prettyTypeDoc Types.TPort = pretty "Port"
prettyTypeDoc Types.TMathExpr = pretty "MathExpr"
prettyTypeDoc Types.TPolyExpr = pretty "PolyExpr"
prettyTypeDoc Types.TTermExpr = pretty "TermExpr"
prettyTypeDoc Types.TSymbolExpr = pretty "SymbolExpr"
prettyTypeDoc Types.TIndexExpr = pretty "IndexExpr"

class Complex a where
  isAtom :: a -> Bool
  isAtomOrApp :: a -> Bool
  isInfix :: a -> Bool

instance Complex Expr where
  isAtom (ConstantExpr (IntegerExpr i)) | i < 0  = False
  isAtom PrefixExpr{}             = False
  isAtom InfixExpr{}              = False
  isAtom (ApplyExpr _ [])         = True
  isAtom ApplyExpr{}              = False
  isAtom LambdaExpr{}             = False
  isAtom MemoizedLambdaExpr{}     = False
  isAtom TypedMemoizedLambdaExpr{} = False
  isAtom CambdaExpr{}             = False
  isAtom PatternFunctionExpr{}    = False
  isAtom IfExpr{}                 = False
  isAtom LetRecExpr{}             = False
  isAtom SubrefsExpr{}            = False
  isAtom SuprefsExpr{}            = False
  isAtom UserrefsExpr{}           = False
  isAtom WithSymbolsExpr{}        = False
  isAtom MatchExpr{}              = False
  isAtom MatchAllExpr{}           = False
  isAtom MatchLambdaExpr{}        = False
  isAtom MatchAllLambdaExpr{}     = False
  isAtom MatcherExpr{}            = False
  isAtom AlgebraicDataMatcherExpr{} = False
  isAtom GenerateTensorExpr{}     = False
  isAtom TensorExpr{}             = False
  isAtom FunctionExpr{}           = False
  isAtom TensorContractExpr{}     = False
  isAtom TensorMapExpr{}          = False
  isAtom TensorMap2Expr{}         = False
  isAtom TransposeExpr{}          = False
  isAtom _                        = True

  isAtomOrApp ApplyExpr{} = True
  isAtomOrApp e           = isAtom e

  isInfix InfixExpr{} = True
  isInfix _           = False

instance Complex a => Complex (Arg a) where
  isAtom (Arg x) = isAtom x
  isAtom _       = True

  isAtomOrApp = isAtom

  isInfix _ = False

instance Complex ArgPattern where
  isAtom (APInductivePat _ []) = True
  isAtom APInductivePat{}      = False
  isAtom APConsPat{}           = False
  isAtom APSnocPat{}           = False
  isAtom _                     = True

  isAtomOrApp = isAtom
  isInfix _ = False

instance Complex Pattern where
  isAtom LetPat{}            = False
  isAtom (InductivePat _ []) = True
  isAtom (InductivePat _ _)  = False
  isAtom InfixPat{}          = False
  isAtom LoopPat{}           = False
  isAtom (PApplyPat _ [])    = True
  isAtom (PApplyPat _ _)     = False
  isAtom _                   = True

  isAtomOrApp PApplyPat{}    = True
  isAtomOrApp InductivePat{} = True
  isAtomOrApp e              = isAtom e

  isInfix InfixPat{} = True
  isInfix _          = False

instance Complex PrimitiveDataPattern where
  isAtom (PDInductivePat _ []) = True
  isAtom (PDInductivePat _ _)  = False
  isAtom PDConsPat{}           = False
  isAtom PDSnocPat{}           = False
  isAtom _                     = True

  isAtomOrApp PDInductivePat{} = True
  isAtomOrApp PDSnocPat{}      = True
  isAtomOrApp e                = isAtom e

  isInfix PDConsPat{} = True
  isInfix _           = False

pretty' :: (Pretty a, Complex a) => a -> Doc ann
pretty' x | isAtom x  = pretty x
          | otherwise = parens $ pretty x

pretty'' :: (Pretty a, Complex a) => a -> Doc ann
pretty'' x | isAtomOrApp x || isInfix x = pretty x
           | otherwise                  = parens $ pretty x

-- Display "hoge" instead of "() := hoge"
prettyDoBinds :: BindingExpr -> Doc ann
prettyDoBinds (Bind (PDTuplePat []) expr) = pretty expr
prettyDoBinds bind                        = pretty "let" <+> pretty bind

prettyMatch :: Expr -> [MatchClause] -> Doc ann
prettyMatch matcher clauses =
  pretty "as" <> group (flatAlt (hardline <> pretty matcher) (space <> pretty matcher) <+> pretty "with") <> hardline <>
    align (vsep (map pretty clauses))

listoid :: String -> String -> [Doc ann] -> Doc ann
listoid lp rp elems =
  encloseSep (pretty lp) (pretty rp) (comma <> space) elems

-- Just like |fillSep|, but does not break the atomicity of grouped Docs
fillSepAtom :: [Doc ann] -> Doc ann
fillSepAtom [] = emptyDoc
fillSepAtom [x] = x
fillSepAtom (x:xs) = x <> fillSepAtom' xs
  where
    fillSepAtom' [] = emptyDoc
    fillSepAtom' (x:xs) =
      group (flatAlt (hardline <> x) (space <> x)) <> fillSepAtom' xs

indentBlock :: Doc ann -> [Doc ann] -> Doc ann
indentBlock header bodies =
  group (nest 2 (header <> flatAlt (hardline <> hsepHard bodies) (space <> hsep bodies)))

hsepHard :: [Doc ann] -> Doc ann
hsepHard = concatWith (\x y -> x <> hardline <> y)

lambdaLike :: Doc ann -> [Doc ann] -> Doc ann -> Doc ann -> Doc ann
lambdaLike start [] arrow body =
  indentBlock (start <> pretty "()" <+> arrow) [body]
lambdaLike start args arrow body =
  indentBlock (start <> hsep args <+> arrow) [body]

applyLike :: [Doc ann] -> Doc ann
applyLike = hang 2 . sep . map group

-- Tests if the argument can be printed in a single line, and if not,
-- inserts a line break before printing it.
-- This is useful for nicely printing infix expressions.
infixRight :: Doc ann -> Doc ann
infixRight p = group (flatAlt (hardline <> p) (space <> p))

showTSV :: EgisonValue -> String
showTSV (Tuple (val:vals)) = foldl (\r x -> r ++ "\t" ++ x) (show val) (map show vals)
showTSV (Collection vals)  = intercalate "\t" (map show (toList vals))
showTSV val                = show val

--
-- Pretty printer for error messages
--

prettyStr :: Pretty a => a -> String
prettyStr = renderString . layoutPretty (LayoutOptions Unbounded) . pretty