packages feed

hat-2.9.1: trans/SynHelp.hs

-- Useful functions on annotated Haskell syntax
-- All functions are polymorphic in the annotation type.
-- (They may loose annotations or add them rather arbitrarily.)

module SynHelp (module SynHelp, noSpan) where

import Language.Haskell.Exts
import Wired (nameTransModule,tracingModuleNameShort,Arity(..),noSpan)
import Data.Char (isAlpha)
import Data.List (stripPrefix)
import Data.Maybe (fromMaybe)

import Debug.Trace

-- Common features of all identifiers (names)

class Id a where
  -- whether a symbol (operator) or a normal identifier
  isSymbol :: a -> Bool
  getId :: a -> String

instance Id (QName l) where
  isSymbol (Qual _ _ name) = isSymbol name
  isSymbol (UnQual _ name) = isSymbol name
  isSymbol (Special _ _) = True
  getId (Qual _ _ name) = getId name
  getId (UnQual _ name) = getId name
  getId qn@(Special _ spec) = error ("SynHelp.getId: " ++ prettyPrint qn)

instance Id (Name l) where
  isSymbol (Ident _ _) = False
  isSymbol (Symbol _ _) = True
  getId (Ident _ ident) = ident
  getId (Symbol _ ident) = ident

instance Id (QOp l) where
  isSymbol (QVarOp _ _) = False
  isSymbol (QConOp _ _) = True
  getId (QVarOp _ qname) = getId qname
  getId (QConOp _ qname) = getId qname

instance Id (Op l) where
  isSymbol (VarOp _ _) = False
  isSymbol (ConOp _ _) = True
  getId (VarOp _ name) = getId name
  getId (ConOp _ name) = getId name

instance Id (CName l) where
  isSymbol (VarName _ _) = False
  isSymbol (ConName _ _) = True
  getId (VarName _ name) = getId name
  getId (ConName _ name) = getId name

instance Id (ModuleName l) where
  isSymbol _ = False
  getId (ModuleName _ ident) = ident

-- General functions on syntax tree:

conDeclName :: ConDecl l -> Name l
conDeclName (ConDecl _ name _) = name
conDeclName (InfixConDecl _ _ name _) = name
conDeclName (RecDecl _ name _) = name

conDeclArity :: ConDecl l -> Arity
conDeclArity (ConDecl _ _ tys) = length tys
conDeclArity (InfixConDecl _ _ _ _) = 2
conDeclArity (RecDecl _ _ fieldDecls) = 
  sum (map (length . fieldDeclNames) fieldDecls)

fieldDeclNames :: FieldDecl l -> [Name l]
fieldDeclNames (FieldDecl _ names _) = names


-- Build n-ary application
-- pre-condition: list is non-empty
appN :: [Exp l] -> Exp l
appN = foldl1 (\e1 e2 -> App (ann e1) e1 e2)

-- Build n-ary type application
-- pre-condiiton: list is non-empy
tyAppN :: [Type l] -> Type l
tyAppN = foldl1 (\t1 t2 -> TyApp (ann t1) t1 t2)

litInt :: (Show i, Integral i) => l -> i -> Exp l
litInt l i = Lit l (Int l (fromIntegral i) (show i))

litString :: l -> String -> Exp l
litString l str = Lit l (String l str str) 

litChar :: l -> Char -> Exp l
litChar l c = Lit l (Char l c (show c))
  
matchArity :: Match l -> Arity 
matchArity (Match _ _ pats _ _) = length pats
matchArity (InfixMatch _ p _ pats _ _) = 1 + length pats

changeInfixMatch :: Match l -> Match l
changeInfixMatch m@(Match _ _ _ _ _) = m
changeInfixMatch (InfixMatch l p n pats rhs maybeBinds) = 
  Match l n (p:pats) rhs maybeBinds

-- General working on AST:

-- function type constructor
infixr 6 `typeFun`
typeFun :: Type l -> Type l -> Type l
typeFun ty1 ty2 = TyFun (ann ty1) ty1 ty2

-- Compare two names ignoring location
eqName :: Name l -> Name l -> Bool
eqName (Ident _ n1) (Ident _ n2) = n1==n2
eqName (Symbol _ n1) (Symbol _ n2) = n1==n2
eqName _ _ = False 

getModuleNameFromModule :: Module l -> ModuleName l
getModuleNameFromModule (Module l Nothing _ _ _) = ModuleName l "Main"
getModuleNameFromModule (Module l (Just (ModuleHead _ modName _ _)) _ _ _) = modName

getConDeclFromQualConDecl :: QualConDecl l -> ConDecl l
getConDeclFromQualConDecl (QualConDecl _ _ _ conDecl) = conDecl

getFieldNamesFromQualConDecl :: QualConDecl l -> [Name l]
getFieldNamesFromQualConDecl (QualConDecl _ _ _ conDecl) =
  getFieldNamesFromConDecl conDecl

getFieldNamesFromConDecl :: ConDecl l -> [Name l]
getFieldNamesFromConDecl (ConDecl _ _ _) = []
getFieldNamesFromConDecl (InfixConDecl _ _ _ _) = []
getFieldNamesFromConDecl (RecDecl _ _ fieldDecls) = 
  concatMap getFieldNamesFromFieldDecl fieldDecls

getFieldNamesFromFieldDecl :: FieldDecl l -> [Name l]
getFieldNamesFromFieldDecl (FieldDecl _ names _) = names

getConstructorFromConDecl :: ConDecl l -> Name l
getConstructorFromConDecl (ConDecl _ c _) = c
getConstructorFromConDecl (InfixConDecl _ _ c _) = c
getConstructorFromConDecl (RecDecl _ c _) = c

getArityFromConDecl :: ConDecl l -> Int
getArityFromConDecl (ConDecl _ _ bTys) = length bTys
getArityFromConDecl (InfixConDecl _ _ _ _) = 2
getArityFromConDecl (RecDecl _ c fieldDecls) = 
  sum (map getArityFromFieldDecl fieldDecls)

getArityFromFieldDecl :: FieldDecl l -> Int
getArityFromFieldDecl (FieldDecl _ names _) = length names

alt2Match :: Alt l -> Match l
alt2Match (Alt l pat guardedAlts maybeBinds) = 
  Match l undefined [pat] guardedAlts maybeBinds

{- no longer needed for syntax after June 2014 
guardedAlts2Rhs :: GuardedAlts l -> Rhs l
guardedAlts2Rhs (UnGuardedAlt l exp) = UnGuardedRhs l exp
guardedAlts2Rhs (GuardedAlts l gdAlts) = 
  GuardedRhss l (map guardedAlt2GuardedRhs gdAlts)

guardedAlt2GuardedRhs :: GuardedAlt l -> GuardedRhs l
guardedAlt2GuardedRhs (GuardedAlt l stmts exp) = GuardedRhs l stmts exp
-}

qOp2Exp :: QOp l -> Exp l
qOp2Exp (QVarOp l qName) = Var l qName
qOp2Exp (QConOp l qName) = Con l qName

nameFromOp :: Op l -> Name l
nameFromOp (VarOp l name) = name
nameFromOp (ConOp l name) = name

getQualified :: SrcInfo l => QName l -> Name l
getQualified (UnQual _ name) = name
getQualified (Qual _ _ name) = name
getQualified (Special _ specialCon) = 
  error ("SynHelp.getQualified: special QName " ++ specialToId specialCon ++ ".")

mkQual :: ModuleName l -> Name l -> QName l
mkQual modName name = Qual (ann name) modName name

qual ::SrcInfo l => ModuleName l -> QName l -> QName l
qual modName qName = mkQual modName (getQualified qName)

isQual :: QName l -> Bool
isQual (Qual _ _ _) = True
isQual (UnQual _ _) = False
isQual (Special _ _) = False

isUnQual :: QName l -> Bool
isUnQual (UnQual _ _) = True
isUnQual (Qual _ _ _) = False
isUnQual (Special _ _) = False

-- The first name possibly provides a module qualifier
cName2QName :: QName l -> CName l -> QName l
cName2QName (UnQual _ _) (VarName l name) = UnQual l name
cName2QName (UnQual _ _) (ConName l name) = UnQual l name
cName2QName (Qual _ mod _) (VarName l name) = Qual l mod name
cName2QName (Qual _ mod _) (ConName l name) = Qual l mod name
cName2QName (Special _ _) (VarName l name) = UnQual l name
cName2QName (Special _ _) (ConName l name) = UnQual l name

-- Build parts of syntax tree:

-- String may contain . with module name before
-- Assume that this is a normal identifier or symbol, not
-- any special name.
mkQName :: l -> String -> QName l
-- mkQName l "()" = Special l (UnitCon l)
-- mkQName l "[]" = Special l (ListCon l)  -- list type constructor
-- mkQName l "->" = Special l (FunCon l) -- function type constructor
-- mkQName l i@(',':_) = Special l (TupleCon l Boxed (length i))
-- mkQName l ":" = Special l (Cons l) -- list data constructor
-- mkQName l "##" = Special l (UnboxedSingleCon l) -- unboxed singleton tuple constructor
mkQName l str = 
  if length revQual <= 1  -- include function composition "."
    then UnQual l (mkName l str) 
    else Qual l (ModuleName l (reverse (tail revQual))) 
           (mkName l (reverse revName))
  where
  (revName,revQual) = break (== '.') (reverse str)

mkName :: l -> String -> Name l
mkName _ "" = error "SynHelp.mkName: empty String."
mkName l id@(x:_) =
  if isAlpha x || x == '_'
    then Ident l id
    else Symbol l id

-- Test for specific names:

isFunTyCon :: QName l -> Bool
isFunTyCon (Special _ (FunCon _)) = True
isFunTyCon _ = False


combineMaybeContexts :: Maybe (Context l) -> Maybe (Context l) -> 
                        Maybe (Context l)
combineMaybeContexts Nothing mCtx = mCtx
combineMaybeContexts mCtx Nothing = mCtx
combineMaybeContexts (Just ctx1) (Just ctx2) = 
  if null assts then Nothing else Just (CxTuple (ann ctx1) assts)
  where
  assts = contextAssertions ctx1 ++ contextAssertions ctx2

contextAssertions :: Context l -> [Asst l]
contextAssertions (CxSingle _ asst) = [asst]
contextAssertions (CxTuple _ assts) = assts
contextAssertions (CxEmpty _) = []

instRuleQName :: InstRule l -> QName l
instRuleQName (IRule _ _ _ instHead) = instHeadQName instHead
instRuleQName (IParen _ instRule) = instRuleQName instRule

instHeadQName :: InstHead l -> QName l
instHeadQName (IHCon _ qname) = qname
instHeadQName (IHInfix _ _ qname) = qname
instHeadQName (IHParen _ ih) = instHeadQName ih
instHeadQName (IHApp _ ih _) = instHeadQName ih

declHeadName :: DeclHead l -> Name l
declHeadName (DHead _ name) = name
declHeadName (DHInfix _ _ name) = name
declHeadName (DHParen _ dh) = declHeadName dh
declHeadName (DHApp _ dh _) = declHeadName dh

declHeadTyVarBinds :: DeclHead l -> [TyVarBind l]
declHeadTyVarBinds (DHead _ _) = []
declHeadTyVarBinds (DHInfix _ tvbL _) = [tvbL]
declHeadTyVarBinds (DHParen _ dh) = declHeadTyVarBinds dh
declHeadTyVarBinds (DHApp _ dh tvb) = declHeadTyVarBinds dh ++ [tvb]

tyVarBind2Type :: TyVarBind l -> Type l
tyVarBind2Type (KindedVar l name kind) = TyKind l (TyVar l name) kind
tyVarBind2Type (UnkindedVar l name) = TyVar l name

tyVarBind2Name :: TyVarBind l -> Name l
tyVarBind2Name (KindedVar l name kind) = name
tyVarBind2Name (UnkindedVar l name) = name

-- pre-condition: no type synonym appearing in type
decomposeFunType :: Type l -> ([Type l], Type l)
decomposeFunType (TyFun _ tyL tyR) = (tyL:tyArgs, tyRes)
  where
  (tyArgs, tyRes) = decomposeFunType tyR
decomposeFunType ty = ([], ty)

-- ----------------------------------------------------------------------------
-- Updating a name

class Id a => UpdId a where
  -- apply function to unqualified name part 
  -- and prefix module name (if qualified)
  updateId :: (Name l -> Name l) -> a -> a

instance SrcInfo l => UpdId (QName l) where
  updateId f (Qual l moduleName name) = 
    Qual l (nameTransModule moduleName) (updateId f name)
  updateId f (UnQual l name) = UnQual l (updateId f name)
  updateId f (Special l specialCon) =
    Qual l (tracingModuleNameShort l) (updateId f (Ident l (specialToId specialCon))) 

specialToId :: SrcInfo l => SpecialCon l -> String
specialToId (UnitCon _) = "Tuple0"
specialToId (ListCon _) = "List"
specialToId (FunCon _) = "Fun"
specialToId (TupleCon _ Boxed arity) = "Tuple" ++ show arity
specialToId (TupleCon l' Unboxed _) = notSupported l' "Unboxed tuple."
specialToId (Cons l') = "Cons"
specialToId (UnboxedSingleCon l') =
  notSupported l' "Unboxed singleton tuple constructor."

instance UpdId (Name l) where
  -- updateId f = f  is ill-typed
  updateId f (Ident l ident) = Ident l (getId (f (Ident undefined ident)))
  updateId f (Symbol l ident) = Symbol l (getId (f (Symbol undefined ident)))

instance SrcInfo l => UpdId (QOp l) where
  updateId f (QVarOp l qname) = QVarOp l (updateId f qname)
  updateId f (QConOp l qname) = QConOp l (updateId f qname)

instance UpdId (Op l) where
  updateId f (VarOp l name) = VarOp l (updateId f name)
  updateId f (ConOp l name) = ConOp l (updateId f name)

instance UpdId (CName l) where
  updateId f (VarName l name) = VarName l (updateId f name)
  updateId f (ConName l name) = ConName l (updateId f name)

-- ----------------------------------------------------------------------------

dropAnn :: Annotated ast => ast l -> ast ()
dropAnn = fmap (const ())

-- ----------------------------------------------------------------------------
-- Error for non-supported language features

notSupported :: SrcInfo l => l -> String -> a
notSupported l construct = error $
  "hat-trans: unsupported language construct \"" ++ construct ++ "\" at " ++ 
    fileName l ++ ":" ++ show (startLine l) ++ ":" ++ show (startColumn l)