packages feed

KiCS-0.8.4: src/ShowFunctionalProg.hs

{-# OPTIONS -fglasgow-exts #-}
-- uses pattern guards to recognize strings and lists
------------------------------------------------------------------------------
--- A pretty printer for AbstractHaskell, adapted from AbstractCurryPrinter
---
--- This library defines a function "showProg" that shows
--- an AbstractCurry program in standard Curry syntax.
---
--- @author Martin Engelke, Bernd Brassel, Michael Hanus, Sebastian Fischer
--- @version May 2007
-- in November 2004: 
-- - added filter for type variables (to print <var0> as var0, like in Prelude)
-- - prettyprint list patterns
-- in July 2005:
-- - added options to most functions
-- - print qualified symbol when necessary (local functions missing)
-- in May 2007:
-- - prettier representation of Curry and Haskell Strings
------------------------------------------------------------------------------
module ShowFunctionalProg(showProg,showProgOpt,
                            showTypeDecls,
                            showTypeDecl,
                            showTypeExpr,
                            showFuncDecl,
                            showExpr,showPattern,
                            isInfixOpName,isTuple) where

import Curry.ExtendedFlat.Type(QName(..), qnOf)
import FunctionalProg
import Data.List
import Data.Char(ord)
import Monad (ap)
import Brace


-------------------------------------------------------------------------------
-- Functions to print an AbstractCurry program in standard Curry syntax
-------------------------------------------------------------------------------

data Options = PrintOpt { unqual :: Bool,
                          sep :: String}

defaultOptions :: Options
defaultOptions = PrintOpt False ""

--- Shows an AbstractCurry program in standard Curry syntax.
showProg :: Prog -> String
showProg = showProgOpt (unqual defaultOptions)

showProgOpt :: Bool -> Prog -> String
showProgOpt uq (Prog m imps exps typedecls insdecls funcdecls opdecls)
   = "module "++m++showExports m exps ++" where\n\n"
     ++ showImports imps
     ++ showOpDecls opdecls
     ++ showTypeDecls opts typedecls
     ++ showInsDecls opts insdecls
     ++ separate "\n\n" (map (showFuncDeclOpt opts) funcdecls)
     ++ "\n"
  where
    opts = defaultOptions{unqual=uq}

-----------------------------------------
-- export declaration
-----------------------------------------

showExports :: String -> [String] -> String
showExports m exps = brace " (" ")" ", " (("module "++m):exps)

-----------------------------------------
-- import declaration
-----------------------------------------

showImports :: [String] -> String
showImports imps = brace "" "\n\n" "\n" (map ("import "++) imps)
    
-----------------------------------------
-- infix operators
-----------------------------------------

showOpDecls :: [OpDecl] -> String
showOpDecls opdecls = brace "" "\n\n" "\n" (map showOpDecl opdecls)

showOpDecl :: OpDecl -> String
showOpDecl (Op qn fixity precedence)
   = separate " " [showFixity fixity,
                   show precedence,
                   '`':showIdentifier (localName qn) ++"`"]


showFixity :: Fixity -> String
showFixity InfixOp  = "infix"
showFixity InfixlOp = "infixl"
showFixity InfixrOp = "infixr"

--------------------------------------------------
-- type declarations, instances, type classes
--------------------------------------------------

--- Shows a list of AbstractCurry type declarations in standard Curry syntax.
showTypeDecls :: Options -> [TypeDecl] -> String
showTypeDecls opts typedecls =  
  brace "" "\n\n" "\n\n" (map (showTypeDecl opts) typedecls)

--- Shows an AbstractCurry type declaration in standard Curry syntax.
showTypeDecl :: Options -> TypeDecl -> String
showTypeDecl opts t = 
  decl ++ showIdentifier (localName (typeName t)) ++ 
  brace " " "" " " (map (showTypeExpr opts False . TVar) (typeVars t)) ++ " = "++
  (case t of
    TypeSyn{typeExpr=e} -> showTypeExpr opts False e
    Type{consDecls=cs} -> separate "\n  | " (map (showConsDecl opts) cs) ++
                          brace "\n  deriving (" ")" "," (derive t))
  where
    decl = case t of {TypeSyn{} -> "type "; Type{} -> "data "} 

showConsDecl :: Options -> ConsDecl -> String
showConsDecl opts c 
   = separate (if strictArgs c then " !" else " ") 
              (showIdentifier (localName (consName c)) : 
               map (showTypeExpr opts True) (consArgs c))

showInsDecls :: Options -> [InstanceDecl] -> String
showInsDecls opts is = brace "" "\n\n" "\n\n" (map (showInsDecl opts) is)

showInsDecl :: Options -> InstanceDecl -> String
showInsDecl opts (Instance tcs tc fs) 
  = "instance " 
  ++ showTypeConstr opts tcs
  ++ showTypeClass opts tc 
  ++ brace " where\n  " "\n\n" "  " (map (showFuncDeclOpt (opts{sep="  "})) fs)


showTypeConstr :: Options -> [TypeClass] -> String
showTypeConstr opts tcs = brace "(" ") => " "," (map (showTypeClass opts) tcs)


showTypeClass :: Options -> TypeClass -> String
showTypeClass opts (TypeClass qn ts) 
  = localName qn ++ brace " " "" " " (map (showTypeExpr opts True) ts)

--- Shows an AbstractCurry type expression in standard Curry syntax.
--- If the first argument is True, the type expression is enclosed
--- in brackets.
showTypeExpr :: Options -> Bool -> TypeExpr -> String
showTypeExpr _ _ (TVar name) = showIdentifier name
showTypeExpr opts nested (FuncType domain range) =
   (if nested then brace "(" ")" else separate) " -> "
   [showTypeExpr opts (case domain of {FuncType _ _ -> False; _ -> True}) domain,
    showTypeExpr opts False range]
showTypeExpr opts nested (TCons qn typelist) = 
   (if nested && not (null typelist) then brace "(" ")" else separate) ""
   [showTypeCons opts qn typelist]
showTypeExpr opts nested (TConstr tcs t) = 
   (if nested then brace "(" ")" else separate) ""
   [showTypeConstr opts tcs ++ showTypeExpr opts False t]

showTypeCons :: Options -> QName -> [TypeExpr] -> String
showTypeCons opts qn ts = 
  showSymbol opts qn ++ 
  brace " " "" " " (map (showTypeExpr opts True) ts)



------------------------------------------
-- function declarations
------------------------------------------

--- Shows an AbstractCurry function declaration in standard Curry syntax.
showFuncDecl :: FuncDecl -> String
showFuncDecl = showFuncDeclOpt defaultOptions

showFuncDeclOpt :: Options -> FuncDecl -> String
showFuncDeclOpt opts f = 
  maybe "" (\t->fname ++" :: "++ (showTypeExpr opts False t) ++ "\n") 
           (funcType f) ++
  maybe (fname ++ " external") 
        (brace (fname++" ") "\n\n" ("\n"++sep opts++fname++" ") . 
        map (showRule opts)) (funcBody f)
  where
    fname = showIdentifier (localName (funcName f))

showRule :: Options -> Rule -> String
showRule opts (Rule ps r ls) 
  = separate " " (map (showPatternOpt opts) ps) ++
    showRhs opts r ++
    brace "\n   where\n    " "" "\n    " (map (showLocalDecl opts) ls)

showRhs :: Options -> Rhs -> String
showRhs opts (SimpleExpr e) = " = "++showExprOpt opts e
showRhs opts (GuardedExpr gs) = brace "\n  " "" "\n  " (map (showGuard opts) gs)

showGuard :: Options -> (Expr,Expr) -> String
showGuard opts (g,r) = "  | " ++ showExprOpt opts g ++ " = " ++ showExprOpt opts r

showLocalDecl :: Options -> LocalDecl -> String
showLocalDecl opts (LocalFunc funcdecl) = showFuncDeclOpt (opts{sep="    "}) funcdecl
showLocalDecl opts (LocalPat pattern expr ls) =
   showPatternOpt opts pattern ++ " = " ++ showExprOpt opts expr ++
   brace "\n   where\n    " "" "\n    " (map (showLocalDecl opts) ls)

---------------------------------------
-- symbols, expresssions, identifiers
---------------------------------------

-- Remove characters '<' and '>' from identifiers sind these characters
-- are sometimes introduced in new identifiers generated by the front end (for sections)
-- also eliminate non standard characters.

showIdentifier :: String -> String
showIdentifier "[]" = "[]"
showIdentifier "_" = "_"
showIdentifier name 
  | isInfixOpName name = "("++name++")"
  | isTuple name = name
  | otherwise = let newName = normChars name in
     if head newName=='\'' then "c_"++newName else newName
  where
   normChars :: String -> String
   normChars [] = []
   normChars (c@'_':cs) = c:normChars cs
   normChars (c:cs) 
     | (co >= na && co <= nz) = c:normChars cs
     | (co >= nA && co <= nZ) = c:normChars cs
     | (co >= n0 && co <= n9) = c:normChars cs
     | otherwise = '\'':show co++normChars cs
     where
       co = ord c
       na = 97
       nz = 122
       nA = 65
       nZ = 90
       n0 = 48
       n9 = 57

--- Shows an AbstractCurry expression in standard Curry syntax.

showExpr :: Expr -> String
showExpr = showExprOpt defaultOptions

showExprOpt :: Options -> Expr -> String
showExprOpt _ (Var name) = showIdentifier name
showExprOpt _ (Lit lit) = showLiteral lit
showExprOpt opts (Symbol name) = showSymbol opts name
showExprOpt opts e@(Apply func arg)
  | Just cs <- expAsCurryString   e = fromCurryString cs
  | Just cl <- expAsCurryList     e = fromCurryList cl
  | Just hs <- expAsHaskellString e = fromHaskellString hs
  | Just hl <- expAsHaskellList   e = fromHaskellList hl
  | otherwise = showExprOpt opts func ++ brace "(" ")" "" [showExprOpt opts arg]
 where
  -- string or list is non-empty (the empty string is parsed as empty list)
  fromCurryString s = "(fromHaskellString " ++ show s++ ")"

  fromCurryList es
    = "(fromHaskellList ["
   ++ concat (intersperse "," (map (showExprOpt opts) es)) ++ "])"

  fromHaskellString :: String -> String
  fromHaskellString s = show s -- quotation marks and quoted special chars

  fromHaskellList es
    = "[" ++ concat (intersperse "," (map (showExprOpt opts) es)) ++ "]"

showExprOpt opts (Lambda patts expr) = showLambda opts patts expr
showExprOpt opts (LetDecl localdecls expr)
   = brace "let {" "} in " "; " (map (showLocalDecl opts) localdecls) ++
     showExprOpt opts expr
showExprOpt opts (DoExpr stmts)
   = brace "do\n    " "\n  " "\n    " (map (showStatement opts) stmts)
showExprOpt opts (ListComp expr stmts)
   =    brace "[" "]" " | " 
          [showExprOpt opts expr,separate ", " (map (showStatement opts) stmts)]
showExprOpt opts (Case expr branches)
   = brace ("case " ++ showExprOpt opts expr ++ " of\n") "\n" "\n  "
       (map (showBranchExpr opts) branches)
showExprOpt _ (String s) = '"':s++"\"" --"

showSymbol :: Options -> QName -> String
showSymbol _ qn
    | modName qn == ""
    = showIdentifier (localName qn)
showSymbol opts qn
    | isInfixOpName (localName qn)
    = brace "(" ")" "" [modName qn ++ "." ++ localName qn]
    | not (unqual opts) || "External" `isPrefixOf` modName qn
    = modName qn++"."++showIdentifier (localName qn)
    | otherwise
    = showIdentifier (localName qn)


showLambda :: Options -> [Pattern] -> Expr -> String
showLambda opts patts expr = 
  brace "\\ " " -> " " " (map (showPatternOpt opts) patts) ++
  showExprOpt opts expr


showStatement :: Options -> Statement -> String
showStatement opts (SExpr expr) = showExprOpt opts expr
showStatement opts (SPat pattern expr)
   = showPatternOpt opts pattern ++ " <- " ++ showExprOpt opts expr
showStatement opts (SLet localdecls)
   =  brace "let " " in \n  " "\n    " (map (showLocalDecl opts) localdecls)

-- try to transform expression into a non-empty Curry string
expAsCurryString :: Expr -> Maybe String
expAsCurryString (Symbol qn) 
    | qnOf qn == ("CurryPrelude","List")
    = Just ""
expAsCurryString (Apply (Apply (Symbol qn1)
                          (Apply (Symbol qn2)
                                 (Lit (Charc c))))
                   cs)
    | qnOf qn1 == ("CurryPrelude",":<") && qnOf qn2 == ("CurryPrelude","C_Char")
    = Just (c:) `ap` expAsCurryString cs
expAsCurryString _ = Nothing

-- try to transform expression into a Curry list
expAsCurryList :: Expr -> Maybe [Expr]
expAsCurryList (Symbol qn) 
    | qnOf qn == ("CurryPrelude","List")
        = Just []
expAsCurryList (Apply (Apply (Symbol qn) x) xs)
    | qnOf qn == ("CurryPrelude",":<")
    = Just (x:) `ap` expAsCurryList xs
expAsCurryList _ = Nothing

-- try to transform expression into a non-empty Haskell string
expAsHaskellString :: Expr -> Maybe String
expAsHaskellString (Symbol qn)
    | qnOf qn == ("","[]")
    = Just ""
expAsHaskellString (Apply (Apply (Symbol qn) (Lit (Charc c))) cs)
    | qnOf qn == ("",":")
    = Just (c:) `ap` expAsHaskellString cs
expAsHaskellString _ = Nothing

-- try to transform expression into a Haskell list
expAsHaskellList :: Expr -> Maybe [Expr]
expAsHaskellList (Symbol qn)
    | qnOf qn ==  ("","[]") = Just []
expAsHaskellList (Apply (Apply (Symbol qn) x) xs)
    | qnOf qn == ("",":")
    = Just (x:) `ap` expAsHaskellList xs
expAsHaskellList _ = Nothing

-------------------------------------------------------
-- patterns
-------------------------------------------------------

showPattern :: Pattern -> String
showPattern = showPatternOpt defaultOptions

showPatternOpt :: Options -> Pattern -> String
showPatternOpt _ (PVar name) = showIdentifier name
showPatternOpt _ (PLit lit) = showLiteral lit
showPatternOpt opts (PComb name []) = showSymbol opts name 
showPatternOpt opts (PComb sym ps)
   = brace "(" ")" " " (showSymbol opts sym:map (showPatternOpt opts) ps)
showPatternOpt opts (AsPat v p) = 
  showPatternOpt opts (PVar v)++"@"++showPatternOpt opts p

showBranchExpr :: Options -> BranchExpr -> String
showBranchExpr opts (Branch pattern expr)
   = showPatternOpt opts pattern ++ " -> " ++ showExprOpt opts expr

showLiteral :: Literal -> String
showLiteral (HasIntc i) = '(':show i++"::Int)"
showLiteral (Intc i) = '(':show i++"::C_Int)"
showLiteral (Floatc f) = '(':show f++"::Float)"
showLiteral (Charc c) = "'"++showCharc c++"'"

showCharc :: Char -> String
showCharc c = case c of 
   '\n' -> "\\n"
   '\t' -> "\\t"
   '\r' -> "\\r"
   '\\' -> "\\\\"
   '\"' -> "\\\""
   '\'' -> "\\'"
   _ -> [c]

-------------------------------------------------------------------------------
--- tests for various properties of AbstractCurry constructs
-------------------------------------------------------------------------------

isInfixOpName :: String -> Bool
isInfixOpName = all (`elem` infixIDs)

isTuple :: String -> Bool
isTuple "" = False
isTuple (c:cs) = c=='(' && dropWhile (==',') cs == ")"

------------------------------------------------------------------------------
--- constants used by AbstractCurryPrinter
------------------------------------------------------------------------------

infixIDs :: String
infixIDs =  "~!@#$%^&*+-=<>?./|\\:"