packages feed

KiCS-0.8.5: 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,PrintOptions(..),defaultPrintOptions,
                            showTypeDecls,
                            showTypeDecl,
                            showTypeExpr,
                            showFuncDecl,
                            showExpr,showPattern,
                            isInfixOpName,isTuple) where

import FunctionalProg
import List
import Char(isDigit,ord)
import Maybe (isJust)
import Monad (ap)
import Brace
import Debug.Trace

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

data PrintOptions = PrintOpt { unqual :: Bool,
                          sep :: String,
                          include :: String}

defaultPrintOptions :: PrintOptions
defaultPrintOptions = PrintOpt False "" ""

--- Shows an AbstractCurry program in standard Curry syntax.
showProg :: Prog -> String
showProg = showProgOpt defaultPrintOptions

showProgOpt :: PrintOptions -> Prog -> String
showProgOpt opts p@(Prog m imports exports typedecls insdecls funcdecls opdecls)
   = "{-# OPTIONS -cpp  #-}\n\n"
     ++ "{-# LANGUAGE RankNTypes, ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances #-}\n\n"
     ++ "module "++m++showExports opts m exports ++" where\n\n"
     ++ showImports imports
     ++ "\n\n-- begin included\n\n" 
     ++ include opts 
     ++ "\n\n-- end included\n\n"
     ++ showOpDecls opdecls
     ++ showTypeDecls opts typedecls
     ++ showInsDecls opts insdecls
     ++ separate "\n\n" (map (showFuncDeclOpt opts) funcdecls)
     ++ "\n"


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

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

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

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

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

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

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 :: PrintOptions -> [TypeDecl] -> String
showTypeDecls opts typedecls =  
  brace "" "\n\n" "\n\n" (map (showTypeDecl opts) typedecls)

--- Shows an AbstractCurry type declaration in standard Curry syntax.
showTypeDecl :: PrintOptions -> TypeDecl -> String
showTypeDecl opts t = 
  decl ++ showIdentifier (snd (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 :: PrintOptions -> ConsDecl -> String
showConsDecl opts c 
   = separate (if strictArgs c then " !" else " ") 
              (showIdentifier (snd (consName c)) : 
               map (showTypeExpr opts True) (consArgs c))

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

showInsDecl :: PrintOptions -> 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 opts tcs = brace "(" ") => " "," (map (showTypeClass opts) tcs)

showTypeClass opts (TypeClass qn ts) 
  = snd 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 :: PrintOptions -> 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 (mod,name) typelist) = 
   (if nested && not (null typelist) then brace "(" ")" else separate) ""
   [showTypeCons opts mod name typelist]
showTypeExpr opts nested (TConstr tcs t) = 
   (if nested then brace "(" ")" else separate) ""
   [showTypeConstr opts tcs ++ showTypeExpr opts False t]

showTypeCons :: PrintOptions -> String -> String -> [TypeExpr] -> String
showTypeCons opts mod name ts = 
  showSymbol opts (mod,name) ++ 
  brace " " "" " " (map (showTypeExpr opts True) ts)



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

--- Shows an AbstractCurry function declaration in standard Curry syntax.
showFuncDecl = showFuncDeclOpt defaultPrintOptions

showFuncDeclOpt :: PrintOptions -> 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 (snd (funcName f))

showRule :: PrintOptions -> 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 :: PrintOptions -> Rhs -> String
showRhs opts (SimpleExpr e) = " = "++showExprOpt opts e
showRhs opts (GuardedExpr gs) = brace "\n  " "" "\n  " (map (showGuard opts) gs)

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

showLocalDecl :: PrintOptions -> 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 [] = []
   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 = showExprOpt defaultPrintOptions

showExprOpt :: PrintOptions -> Expr -> String
showExprOpt _ (Var name) = showIdentifier name
showExprOpt _ (Lit lit) = showLiteral lit
showExprOpt opts (Symbol name) = showSymbol opts name
showExprOpt opts exp@(Apply func arg)
  | Just cs <- expAsCurryString   exp = fromCurryString cs
  | Just cl <- expAsCurryList     exp = fromCurryList cl
  | Just hs <- expAsHaskellString exp = fromHaskellString hs
  | Just hl <- expAsHaskellList   exp = 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 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 :: PrintOptions -> QName -> String
showSymbol _ ("",symName) = showIdentifier symName
showSymbol opts (m,symName) 
  | isInfixOpName symName = brace "(" ")" "" [m++"."++symName]
  | not (unqual opts) || isExternalModule = m++"."++showIdentifier symName
  | otherwise = showIdentifier symName
  where
    isExternalModule
      = case m of {('E':'x':'t':'e':'r':'n':'a':'l':_) -> True;_->False}

showLambda opts patts expr = 
  brace "\\ " " -> " " " (map (showPatternOpt opts) patts) ++
  showExprOpt opts expr


showStatement :: PrintOptions -> 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 ("CurryPrelude","List")) = Just ""
expAsCurryString (Apply (Apply (Symbol ("CurryPrelude",":<"))
                          (Apply (Symbol ("CurryPrelude","C_Char"))
                                 (Lit (Charc c))))
                   cs)
  = Just (c:) `ap` expAsCurryString cs
expAsCurryString _ = Nothing

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

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

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

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

showPattern :: Pattern -> String
showPattern = showPatternOpt defaultPrintOptions

showPatternOpt :: PrintOptions -> 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 :: PrintOptions -> 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)

isCFuncType t = case t of
                  FuncType _ _ -> True
                  _ -> False

isTuple [] = False
isTuple (c:cs) = c=='(' && dropWhile (==',') cs == ")"

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

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