----------------------------------------------------------------
-- the Henk Abstract Syntax
-- Copyright 2000, Jan-Willem Roorda and Daan Leijen
----------------------------------------------------------------
module HenkAS where

import Text.PrettyPrint.HughesPJ

----------------------------------------------------------------
-- Abstract Syntax 
----------------------------------------------------------------
data Program        = Program [TypeDecl] [ValueDecl]
                    
data TypeDecl       = Data Var [Var]
                    
data ValueDecl      = Let Bind
                    | LetRec [Bind]
                    
data Bind           = Bind Var Expr
                    
data Expr           = Var Var
                    | Lit Lit
                    | Box
                    | Star
                    | Unknown
                    
                    | App Expr Expr          
                    | Case Expr [Alt] [Expr]
                    | In ValueDecl Expr
                    | Pi Var Expr
                    | Lam Var Expr
                    
data Alt            = Alt Pat Expr

data Pat            = PatVar Var
                    | PatLit Lit

data Var            = TVar Identifier Expr

data Lit            = LitInt Integer

type Identifier     = String    

anonymous           = "_"
isAnonymous s       = (null s || (head s == head anonymous))


----------------------------------------------------------------
-- pretty print abstract syntax
----------------------------------------------------------------
instance Show Program where
  showsPrec d program   = shows (pprogram program)

vsep ds
    = vcat (map ($$ text "") ds)    


-- program
pprogram (Program tdecls vdecls)
    = vsep ((map ptdecl tdecls) ++ (map pvdecl vdecls))
    
ptdecl (Data v vs)
    = (text "data" <+> pbindvar v)
      $$ indent (text "=" <+> braced (map ptvar vs))
  
    
pvdecl vdecl
    = case vdecl of
        Let bind     -> text "let" <+> pbind bind
        LetRec binds -> text "letrec" $$ indent (braced (map pbind binds))
  
pbind (Bind v e)
    = pbindvar v $$ indent (text "=" <+> pexpr e)
  
-- expressions (are parenthesis correct ?)  
parensExpr e
    = case e of
        In _ _      -> parens (pexpr e)
        Pi _ _      -> parens (pexpr e)
        Lam _ _     -> parens (pexpr e)
        Case _ _ _  -> parens (pexpr e)
        App _ _     -> parens (pexpr e)
        Var (TVar i t) -> case t of
                            Unknown -> pexpr e
                            other   -> parens (pexpr e)
        other       -> pexpr e
  
pexpr e
    = case e of
        Var v       -> pboundvar v
        Lit l       -> plit l
        Box         -> text "[]"
        Star        -> text "*"
        Unknown     -> text "?"
                        
        App e1 e2   -> pexpr e1 <+> parensExpr e2
        Case e as ts-> sep $ [text "case" <+> parensExpr e <+> text "of"
                             ,nest 3 (braced (map palt as))
                             ] ++
                             (if (null as) 
                               then []
                               else [text "at"
                                    ,nest 3 (braced (map pexpr ts))
                                    ])
                       
        In v e      -> sep[ pvdecl v, text "in" <+> pexpr e]        
        Pi v e      -> case v of
                         TVar i t    | isAnonymous i -> parensExpr t <+> text "->" <+> pexpr e
                         TVar i Star -> sep[ text "\\/" <> text i <> text ".", pexpr e]
                         other       -> sep[ text "|~|" <> pbindvar v <> text ".", pexpr e]
        Lam v e     -> case v of
                         TVar i Star -> sep[ text "/\\" <> text i <> text ".", pexpr e]
                         other       -> sep[ text "\\" <> pbindvar v <> text ".", pexpr e]
  
  
-- atomic stuff  
palt (Alt p e)
    = ppat p <+> text "=>" <+> pexpr e
    
ppat p
    = case p of PatVar v -> pboundvar v
                PatLit l -> plit l
            
                  
pboundvar v@(TVar i e)
    = case e of Unknown  -> text i
                other    -> ptvar v
  
pbindvar v@(TVar i e)
    = case e of Star     -> text i
                other    -> ptvar v
                
ptvar (TVar i e)
    = text i <> colon <+> pexpr e
            
             
plit l
    = case l of LitInt i -> integer i
    
braced []
    = empty
    
braced ds
    = let prefix = map text $ ["{"] ++ repeat ";"
      in  cat ((zipWith (<+>) prefix ds) ++ [text "}"])
      
indent
    = nest 4
    
  
    