packages feed

hat-2.7.0.0: trans/TraceDerive.hs

module TraceDerive where

import Syntax
import TraceId 
  (TraceId,mkLambdaBound,tokenId,getUnqualified,dropModule,modArity
  ,tPriority,tFixity,Fixity(..)
  ,tTokenTrue,tTokenFalse,tTokenEqualEqual,tTokenAndAnd
  ,tTokenEQ,tTokenCompare,tTokenLocalFromEnum,tTokenInt
  ,tTokenMinBound,tTokenMaxBound,tTokenFromEnum,tTokenToEnum
  ,tTokenError,tTokenEnumFrom,tTokenEnumFromThen,tTokenEnumFromTo
  ,tTokenEnumFromThenTo,tTokenGreater,tTokenGreaterEqual,tTokenCompose
  ,tTokenShowsPrec,tTokenShowParen,tTokenShowString,tTokenShowChar
  ,tTokenReadsPrec,tTokenReadParen,tTokenYield,tTokenAlt,tTokenThenAp
  ,tTokenThenLex,tTokenRange,tTokenIndex,tTokenInRange,tTokenMap,tTokenMinus
  ,tTokenLocalToEnum,tTokenTuple2,tTokenFun,tTokenRangeSize
  ,tTokenGtGtEq,tTokenPlus,tTokenTimes,tTokenReturn)
import TokenId (mkUnqualifiedTokenId,tEq)
import AuxTypes (defaultAuxInfo)

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

-- derive instances for all given classes for a data/newtype
derive :: [Context TraceId] -> Simple TraceId -> [Constr TraceId] 
       -> [(Pos,TraceId)] -> [Decl TraceId]
derive tyContexts sTy constrs = 
  map (deriveClass tyContexts (simpleToType sTy) pTyVars constrs)
  where
  Simple _ _ pTyVars = sTy

-- derive instance for a given class for a data/newtype
deriveClass :: [Context TraceId] -> Instance TraceId -> [(Pos,TraceId)] 
            -> [Constr TraceId] -> (Pos,TraceId) -> Decl TraceId
deriveClass tyContexts instTy pTyVars constrs (pos,cls) 
  | getUnqualified cls == "Eq" = deriveEq pos usualContexts cls instTy constrs
  | getUnqualified cls == "Ord" = 
    deriveOrd pos usualContexts cls instTy constrs
  | getUnqualified cls == "Bounded" =
    deriveBounded pos usualContexts cls instTy constrs
  | getUnqualified cls == "Enum" = 
    deriveEnum pos usualContexts cls instTy constrs
  | getUnqualified cls == "Read" = 
    deriveRead pos usualContexts cls instTy constrs
  | getUnqualified cls == "Show" = 
    deriveShow pos usualContexts cls instTy constrs
  | getUnqualified cls == "Ix" = 
    deriveIx pos usualContexts cls instTy constrs
  | otherwise = error ("deriveClass: unknown class " ++ show (tokenId cls))
  where
  -- this is a HACK that covers only the common cases
  -- for correct result would need to implement full context reduction
  -- and take the least fixpoint
  usualContexts = tyContexts ++ map (\pt-> Context pos cls [pt]) pTyVars

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

deriveEq :: Pos 
         -> [Context TraceId] -> TraceId -> Instance TraceId 
         -> [Constr TraceId] 
         -> Decl TraceId
deriveEq pos contexts cls ty constrs =
  DeclInstance pos contexts cls [ty]
    (DeclsParse 
      [DeclFun pos (modArity (dropModule tTokenEqualEqual) 2)
        (map funEqConstr constrs ++ 
          [Fun [PatWildcard pos,PatWildcard pos] 
            (Unguarded (ExpCon pos tTokenFalse)) noDecls])])
  where
  vars = traceVars pos
  funEqConstr :: Constr TraceId -> Fun TraceId
  funEqConstr constr = 
    if arity == 0 
      then
        Fun [ExpCon pos conId,ExpCon pos conId] 
          (Unguarded (ExpCon pos tTokenTrue)) noDecls
      else
        Fun 
          [ExpApplication pos (ExpCon pos conId : argsL)
          , ExpApplication pos  (ExpCon pos conId : argsR)]
          (Unguarded (foldr1 andExp (zipWith equalExp argsL argsR)))
          noDecls
    where
    andExp :: Exp TraceId -> Exp TraceId -> Exp TraceId
    andExp e1 e2 = ExpApplication pos [ExpVar pos tTokenAndAnd,e1,e2]
    equalExp :: Exp TraceId -> Exp TraceId -> Exp TraceId
    equalExp e1 e2 = ExpApplication pos [ExpVar pos tTokenEqualEqual,e1,e2]
    (argsL,restVars) = splitAt arity vars
    argsR = take arity restVars
    conId = getConstrId constr
    arity = constrArity constr

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

deriveOrd :: Pos 
          -> [Context TraceId] -> TraceId -> Instance TraceId 
          -> [Constr TraceId] 
          -> Decl TraceId
deriveOrd pos contexts cls ty constrs =
  DeclInstance pos contexts cls [ty]
    (DeclsParse 
      [DeclFun pos (modArity (dropModule tTokenCompare) 2)
        (concatMap funCompareEqConstr constrs ++ 
          [Fun [var1,var2] 
            (Unguarded 
              (ExpApplication pos 
                [ExpVar pos tTokenCompare
                ,ExpType pos (ExpApplication pos [localFromEnumVar,var1])
                   [] (TypeCons pos tTokenInt [])
                ,ExpApplication pos [localFromEnumVar,var2]]))
            (DeclsParse 
              [DeclFun pos tTokenLocalFromEnum
                (zipWith funLocalFromEnum constrs [0..])])])])
  where
  var1:var2:vars = traceVars pos
  localFromEnumVar = ExpVar pos tTokenLocalFromEnum
  funCompareEqConstr constr = 
    if arity == 0 then []
      else
        [Fun
          [ExpApplication pos (ExpCon pos conId : argsL)
          ,ExpApplication pos (ExpCon pos conId : argsR)]
          (Unguarded (foldr1 caseExp (zipWith compareExp argsL argsR)))
          noDecls]
    where
    caseExp :: Exp TraceId -> Exp TraceId -> Exp TraceId
    caseExp e1 e2 = 
      ExpCase pos e1 
        [Alt (ExpCon pos tTokenEQ) (Unguarded e2) noDecls
        ,Alt var1 (Unguarded var1) noDecls]
    compareExp :: Exp TraceId -> Exp TraceId -> Exp TraceId
    compareExp e1 e2 = ExpApplication pos [ExpVar pos tTokenCompare,e1,e2]
    (argsL,restVars) = splitAt arity vars
    argsR = take arity restVars
    conId = getConstrId constr
    arity = constrArity constr
  funLocalFromEnum constr num =
    Fun [ExpApplication pos (ExpCon pos conId : args)]
      (Unguarded (ExpLit pos (LitInteger Boxed num)))
      noDecls
    where
    args = replicate (constrArity constr) (PatWildcard pos)
    conId = getConstrId constr


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

deriveBounded :: Pos 
              -> [Context TraceId] -> TraceId -> Instance TraceId 
              -> [Constr TraceId] 
              -> Decl TraceId
deriveBounded pos contexts cls ty constrs =
  DeclInstance pos contexts cls [ty]
    (if all (== 0) (map constrArity constrs) 
      then
        (DeclsParse
          [DeclFun pos (modArity (dropModule tTokenMinBound) 0)
            [Fun [] (Unguarded (ExpCon pos (getConstrId (head constrs)))) 
              noDecls]
          ,DeclFun pos (modArity (dropModule tTokenMaxBound) 0)
            [Fun [] (Unguarded (ExpCon pos (getConstrId (last constrs)))) 
              noDecls]])
      else {- exactly one constructor -}
        let [constr] = constrs in
          (DeclsParse
            [DeclFun pos (modArity (dropModule tTokenMinBound) 0)
              [Fun [] (Unguarded 
                  (ExpApplication pos 
                    (ExpCon pos (getConstrId (head constrs)) 
                    : replicate (constrArity constr) 
                        (ExpVar pos tTokenMinBound)) )) 
              noDecls]
            ,DeclFun pos (modArity (dropModule tTokenMaxBound) 0)
              [Fun [] (Unguarded 
                  (ExpApplication pos 
                    (ExpCon pos (getConstrId (head constrs)) 
                    : replicate (constrArity constr) 
                        (ExpVar pos tTokenMaxBound)) )) 
              noDecls]]))

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

deriveEnum :: Pos 
           -> [Context TraceId] -> TraceId -> Instance TraceId 
           -> [Constr TraceId] 
           -> Decl TraceId
deriveEnum pos contexts cls ty constrs =
  -- assert: all (== 0) (map constrArity constrs) 
  DeclInstance pos contexts cls [ty]
    (DeclsParse
      [DeclFun pos (modArity (dropModule tTokenFromEnum) 1)
        (zipWith funFromEnum constrs [0..])
      ,DeclFun pos (modArity (dropModule tTokenToEnum) 1)
        (zipWith funToEnum constrs [0..] ++ 
          [Fun [PatWildcard pos] (Unguarded 
            (ExpApplication pos 
              [ExpVar pos tTokenError
              ,ExpLit pos (LitString Boxed "toEnum: argument out of bounds")]))
            noDecls])
      ,DeclFun pos (modArity (dropModule tTokenEnumFrom) 1)
        [Fun [var1] (Unguarded 
          (ExpApplication pos
            [ExpVar pos tTokenEnumFromTo,var1
            ,ExpCon pos (getConstrId (last constrs))]))
          noDecls]
      ,DeclFun pos (modArity (dropModule tTokenEnumFromThen) 2)
        [Fun [var1,var2] (Unguarded
          (ExpApplication pos
            [ExpVar pos tTokenEnumFromThenTo,var1,var2
            ,ExpIf pos
              (ExpApplication pos 
                [ExpVar pos tTokenGreaterEqual
                ,ExpApplication pos [ExpVar pos tTokenFromEnum,var1]
                ,ExpApplication pos [ExpVar pos tTokenFromEnum,var2]])
              (ExpCon pos (getConstrId (last constrs)))
              (ExpCon pos (getConstrId (head constrs)))]))
          noDecls]
      ])
  where
  var1:var2:_ = traceVars pos
  funFromEnum constr num =
    Fun [ExpCon pos (getConstrId constr)] 
      (Unguarded (ExpLit pos (LitInteger Boxed num)))
      noDecls
  funToEnum constr num =
    Fun [ExpLit pos (LitInteger Boxed num)] 
      (Unguarded (ExpCon pos (getConstrId constr)))
      noDecls

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

deriveShow :: Pos 
           -> [Context TraceId] -> TraceId -> Instance TraceId 
           -> [Constr TraceId] 
           -> Decl TraceId
deriveShow pos contexts cls ty constrs =
  DeclInstance pos contexts cls [ty]
    (DeclsParse
      [DeclFun pos (modArity (dropModule tTokenShowsPrec) 2)
        (map funShowsPrec constrs)])
  where
  precVar:vars = traceVars pos
  funShowsPrec constr =
    Fun [precVar,ExpApplication pos (ExpCon pos conId:args)] (Unguarded
      (if arity == 0 
         then showStringExp (getUnqualified conId)
         else
           case getUnqualified conId of
             ':':_ -> -- is defined in infix style (and arity == 2)
               ExpApplication pos 
                 [ExpVar pos tTokenShowParen
                 ,ExpApplication pos 
                   [ExpVar pos tTokenGreater,precVar
                   ,ExpLit pos 
                     (LitInteger Boxed priority)]
                 ,  showPrec priorityPlus1 (head args) 
                  `compose`
                    showStringExp (' ' : getUnqualified conId ++ " ")
                  `compose`
                    showPrec priorityPlus1 (args!!1)
                 ]
             _ ->
               let labels = map snd . getConstrLabels $ constr in
               if null labels 
                 then
                   ExpApplication pos 
                     [ExpVar pos tTokenShowParen
                     ,ExpApplication pos 
                       [ExpVar pos tTokenGreater,precVar
                       ,ExpLit pos (LitInteger Boxed priority)]
                     ,showStringExp (getUnqualified conId ++ " ")
                        `compose`
                       foldr1 composeSpace (map (showPrec priorityPlus1) args)
                     ]
                 else
                   showStringExp (getUnqualified conId ++ "{")
                     `compose`
                     foldr1 composeComma (zipWith showField labels args)
                     `compose`
                     showCharExp '}'
      ))
      noDecls
    where
    showStringExp s = 
      ExpApplication pos 
        [ExpVar pos tTokenShowString
        ,ExpLit pos (LitString Boxed s)]
    showCharExp c = 
      ExpApplication pos 
        [ExpVar pos tTokenShowChar
        ,ExpLit pos (LitChar Boxed c)]
    e1 `compose` e2 = ExpApplication pos [ExpVar pos tTokenCompose,e1,e2]
    e1 `composeSpace` e2 = e1 `compose` showCharExp ' ' `compose` e2
    e1 `composeComma` e2 = e1 `compose` showCharExp ',' `compose` e2
    showField label e = 
      showStringExp (getUnqualified label) `compose` showCharExp '=' 
        `compose` showPrec 0 e 
    showPrec d e = 
      ExpApplication pos 
        [ExpVar pos tTokenShowsPrec,ExpLit pos (LitInteger Boxed d),e]
    args = take arity vars
    conId = getConstrId constr
    arity = constrArity constr
    priority = toInteger (tPriority conId)
    priorityPlus1 = priority+1 

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

deriveRead :: Pos 
           -> [Context TraceId] -> TraceId -> Instance TraceId 
           -> [Constr TraceId] 
           -> Decl TraceId
deriveRead pos contexts cls ty constrs =
  DeclInstance pos contexts cls [ty]
    (DeclsParse
      [DeclFun pos (modArity (dropModule tTokenReadsPrec) 1)
        [Fun [precVar] (Unguarded (foldr1 alt . map expReadsPrec $ constrs)) 
          noDecls]])
  where
  precVar:_ = traceVars pos
  e1 `alt` e2 = ExpApplication pos [ExpVar pos tTokenAlt,e1,e2]
  expReadsPrec constr =
    if arity == 0 
      then readParen (ExpCon pos tTokenFalse) 
             (yield (ExpCon pos conId) `thenLex` getUnqualified conId)
      else
        case getUnqualified conId of
          ':':_ -> -- is defined in infix style (and arity == 2)
            readParen precGreaterPriority
              (yield (ExpCon pos conId) `thenAp`
                readsArg `thenLex` getUnqualified conId `thenAp` readsArg)
          _ ->
           let labels = map snd . getConstrLabels $ constr in
           if null labels 
             then
               readParen precGreaterPriority
                 (foldl thenAp 
                    (yield (ExpCon pos conId) `thenLex` getUnqualified conId)
                    (replicate arity readsArg))
             else
               (foldl thenCommaField
                 (yield (ExpCon pos conId) `thenLex` getUnqualified conId 
                   `thenLex` "{" `thenField` (head labels))
                 (tail labels))
                 `thenLex` "}" 
    where
    infixl 6 `thenAp`,`thenLex`, `thenField`
    readParen c e = ExpApplication pos [ExpVar pos tTokenReadParen,c,e]
    yield e = ExpApplication pos [ExpVar pos tTokenYield,e]
    e1 `thenLex` s = ExpApplication pos [ExpVar pos tTokenThenLex,e1,string s]
    e1 `thenAp` e2 = ExpApplication pos [ExpVar pos tTokenThenAp,e1,e2]
    precGreaterPriority = 
      ExpApplication pos 
        [ExpVar pos tTokenGreater,precVar
        ,ExpLit pos (LitInteger Boxed priority)]
    string s = ExpLit pos (LitString Boxed s)
    readsArg = 
      ExpApplication pos 
        [ExpVar pos tTokenReadsPrec
        ,ExpLit pos (LitInteger Boxed priorityPlus1)]
    readsArg0 = 
      ExpApplication pos 
        [ExpVar pos tTokenReadsPrec
        ,ExpLit pos (LitInteger Boxed 0)]
    p `thenField` label = 
      p `thenLex` getUnqualified label `thenLex` "=" 
        `thenAp` readsArg0 
    p `thenCommaField` label = p `thenLex` "," `thenField` label
    conId = getConstrId constr
    arity = constrArity constr
    priority = toInteger (tPriority conId)
    priorityPlus1 = priority+1 

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

deriveIx :: Pos 
         -> [Context TraceId] -> TraceId -> Instance TraceId 
         -> [Constr TraceId] 
         -> Decl TraceId
deriveIx pos contexts cls ty constrs =
  DeclInstance pos contexts cls [ty]
    (if all (== 0) (map constrArity constrs) 
      then
        (DeclsParse
          [DeclFun pos (modArity (dropModule tTokenRange) 1)
            [Fun [ExpApplication pos [ExpCon pos tTokenTuple2,lvar,uvar]] 
              (Unguarded 
                (ExpApplication pos 
                  [ExpVar pos tTokenMap,toEnumVar
                  ,ExpApplication pos 
                    [ExpVar pos tTokenEnumFromTo
                    ,ExpApplication pos [fromEnumVar,lvar]
                    ,ExpApplication pos [fromEnumVar,uvar]]]))
              (DeclsParse (declsToEnum ++ declsFromEnum))]
          ,DeclFun pos (modArity (dropModule tTokenIndex) 2)
            [Fun [ExpApplication pos [ExpCon pos tTokenTuple2,lvar,uvar],ivar] 
              (Unguarded 
                (ExpApplication pos 
                  [ExpVar pos tTokenMinus
                  ,ExpApplication pos [fromEnumVar,ivar]
                  ,ExpApplication pos [fromEnumVar,lvar]]))
              (DeclsParse declsFromEnum)]
          ,DeclFun pos (modArity (dropModule tTokenInRange) 2)
            [Fun [ExpApplication pos [ExpCon pos tTokenTuple2,lvar,uvar],ivar] 
              (Unguarded 
                (ExpApplication pos 
                  [ExpVar pos tTokenInRange
                  ,ExpApplication pos 
                    [ExpCon pos tTokenTuple2
                    ,ExpApplication pos [fromEnumVar,lvar]
                    ,ExpApplication pos [fromEnumVar,uvar]]
                  ,ExpApplication pos [fromEnumVar,ivar]]))
              (DeclsParse declsFromEnum)]
          ])
      else {- exactly one constructor -}
        (DeclsParse
          [DeclFun pos (modArity (dropModule tTokenRange) 1)
            [Fun 
              [ExpApplication pos [ExpCon pos tTokenTuple2,conLvars,conUvars]]
              (Unguarded 
                (foldr ($) 
                  (ExpApplication pos [ExpVar pos tTokenReturn,conIvars]) 
                  (zipWith3 rangeComb lvars uvars ivars)))
              noDecls]
          ,DeclFun pos (modArity (dropModule tTokenIndex) 2)
            [Fun 
              [ExpApplication pos [ExpCon pos tTokenTuple2,conLvars,conUvars]
              ,conIvars] 
              (Unguarded
                (foldl (flip ($))
                  (indexExp (head lvars) (head uvars) (head ivars)) 
                  (tail (zipWith3 indexComb lvars uvars ivars))))
              noDecls]
          ,DeclFun pos (modArity (dropModule tTokenInRange) 2)
            [Fun 
              [ExpApplication pos [ExpCon pos tTokenTuple2,conLvars,conUvars]
              ,conIvars] 
              (Unguarded
                (foldr1 andExp (zipWith3 inRangeExp lvars uvars ivars))) 
              noDecls]
          ]))
  where
  -- for enumeration type
  lvar:uvar:ivar:_ = traceVars pos
  fromEnumVar = ExpVar pos tTokenLocalFromEnum
  toEnumVar = ExpVar pos tTokenLocalToEnum
  declsFromEnum :: [Decl TraceId]
  declsFromEnum = 
    [DeclVarsType [(pos,tTokenLocalFromEnum)] [] 
      (TypeCons pos tTokenFun [ty,TypeCons pos tTokenInt []])
    ,DeclFun pos tTokenLocalFromEnum (zipWith funFromEnum constrs [0..])] 
  declsToEnum = 
    [DeclVarsType [(pos,tTokenLocalToEnum)] [] 
      (TypeCons pos tTokenFun [TypeCons pos tTokenInt [],ty])
    ,DeclFun pos tTokenLocalToEnum (zipWith funToEnum constrs [0..])]
  funFromEnum constr num =
    Fun [ExpCon pos (getConstrId constr)] 
      (Unguarded (ExpLit pos (LitInteger Boxed num))) noDecls
  funToEnum constr num =
    Fun [ExpLit pos (LitInteger Boxed num)] 
      (Unguarded (ExpCon pos (getConstrId constr))) noDecls
  -- for single constructor type
  [constr] = constrs
  conId = getConstrId constr
  arity = constrArity constr
  (lvars,vars1) = splitAt arity (traceVars pos)
  (uvars,vars2) = splitAt arity vars1
  ivars = take arity vars2
  conLvars = ExpApplication pos (ExpCon pos conId:lvars)
  conUvars = ExpApplication pos (ExpCon pos conId:uvars)
  conIvars = ExpApplication pos (ExpCon pos conId:ivars)
  rangeComb :: Exp TraceId -> Exp TraceId -> Exp TraceId -> Exp TraceId 
            -> Exp TraceId
  rangeComb l u i cont = 
    ExpApplication pos 
      [ExpVar pos tTokenGtGtEq
      ,ExpApplication pos 
        [ExpVar pos tTokenRange
        ,ExpApplication pos [ExpCon pos tTokenTuple2,l,u]]
      ,ExpLambda pos [i] cont]
  indexExp l u i =
    ExpApplication pos 
      [ExpVar pos tTokenIndex
      ,ExpApplication pos [ExpCon pos tTokenTuple2,l,u]
      ,i]
  indexComb :: Exp TraceId -> Exp TraceId -> Exp TraceId -> Exp TraceId 
            -> Exp TraceId
  indexComb l u i e =
    ExpApplication pos 
      [ExpVar pos tTokenPlus
      ,indexExp l u i
      ,ExpApplication pos 
        [ExpVar pos tTokenTimes
        ,ExpApplication pos 
          [ExpVar pos tTokenRangeSize
          ,ExpApplication pos [ExpCon pos tTokenTuple2,l,u]]
        ,e]]
  inRangeExp l u i =
    ExpApplication pos 
      [ExpVar pos tTokenInRange
      ,ExpApplication pos [ExpCon pos tTokenTuple2,l,u]
      ,i]
  andExp :: Exp TraceId -> Exp TraceId -> Exp TraceId
  andExp e1 e2 = ExpApplication pos [ExpVar pos tTokenAndAnd,e1,e2]

-- ----------------------------------------------------------------------------
-- helper functions

-- infinite list of variables
-- only need not conflict with names of standard class methods
traceVars :: Pos -> [Exp TraceId]
traceVars pos = 
  map (ExpVar pos . mkLambdaBound defaultAuxInfo . mkUnqualifiedTokenId . 
        ('y':) . show) [1..]