{- |
    Module      :  $Header$
    Description :  Abstract syntax for conditional compiling
    Copyright   :  (c) 2017        Kai-Oliver Prott
                       2017        Finn Teegen
    License     :  BSD-3-clause

    Maintainer  :  fte@informatik.uni-kiel.de
    Stability   :  experimental
    Portability :  portable

    TODO
-}
{-# LANGUAGE CPP #-}
module Curry.CondCompile.Type
  ( Program, Stmt (..), Else (..), Elif (..), Cond (..), Op (..)
  ) where

#if __GLASGOW_HASKELL__ >= 804
import Prelude hiding ((<>))
#endif

import Curry.Base.Pretty

type Program = [Stmt]

data Stmt = If Cond [Stmt] [Elif] Else
          | IfDef String [Stmt] [Elif] Else
          | IfNDef String [Stmt] [Elif] Else
          | Define String Int
          | Undef String
          | Line String
  deriving Int -> Stmt -> ShowS
[Stmt] -> ShowS
Stmt -> String
(Int -> Stmt -> ShowS)
-> (Stmt -> String) -> ([Stmt] -> ShowS) -> Show Stmt
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Stmt] -> ShowS
$cshowList :: [Stmt] -> ShowS
show :: Stmt -> String
$cshow :: Stmt -> String
showsPrec :: Int -> Stmt -> ShowS
$cshowsPrec :: Int -> Stmt -> ShowS
Show

newtype Else = Else (Maybe [Stmt])
  deriving Int -> Else -> ShowS
[Else] -> ShowS
Else -> String
(Int -> Else -> ShowS)
-> (Else -> String) -> ([Else] -> ShowS) -> Show Else
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Else] -> ShowS
$cshowList :: [Else] -> ShowS
show :: Else -> String
$cshow :: Else -> String
showsPrec :: Int -> Else -> ShowS
$cshowsPrec :: Int -> Else -> ShowS
Show

newtype Elif = Elif (Cond, [Stmt])
  deriving Int -> Elif -> ShowS
[Elif] -> ShowS
Elif -> String
(Int -> Elif -> ShowS)
-> (Elif -> String) -> ([Elif] -> ShowS) -> Show Elif
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Elif] -> ShowS
$cshowList :: [Elif] -> ShowS
show :: Elif -> String
$cshow :: Elif -> String
showsPrec :: Int -> Elif -> ShowS
$cshowsPrec :: Int -> Elif -> ShowS
Show

data Cond = Comp String Op Int
          | Defined String
          | NDefined String
  deriving Int -> Cond -> ShowS
[Cond] -> ShowS
Cond -> String
(Int -> Cond -> ShowS)
-> (Cond -> String) -> ([Cond] -> ShowS) -> Show Cond
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Cond] -> ShowS
$cshowList :: [Cond] -> ShowS
show :: Cond -> String
$cshow :: Cond -> String
showsPrec :: Int -> Cond -> ShowS
$cshowsPrec :: Int -> Cond -> ShowS
Show

data Op = Eq
        | Neq
        | Lt
        | Leq
        | Gt
        | Geq
  deriving Int -> Op -> ShowS
[Op] -> ShowS
Op -> String
(Int -> Op -> ShowS)
-> (Op -> String) -> ([Op] -> ShowS) -> Show Op
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Op] -> ShowS
$cshowList :: [Op] -> ShowS
show :: Op -> String
$cshow :: Op -> String
showsPrec :: Int -> Op -> ShowS
$cshowsPrec :: Int -> Op -> ShowS
Show

instance Pretty Stmt where
  pPrint :: Stmt -> Doc
pPrint (If     c :: Cond
c stmts :: [Stmt]
stmts is :: [Elif]
is e :: Else
e) = String -> Doc -> [Stmt] -> [Elif] -> Else -> Doc
prettyIf "#if"     (Cond -> Doc
forall a. Pretty a => a -> Doc
pPrint Cond
c) [Stmt]
stmts [Elif]
is Else
e
  pPrint (IfDef  v :: String
v stmts :: [Stmt]
stmts is :: [Elif]
is e :: Else
e) = String -> Doc -> [Stmt] -> [Elif] -> Else -> Doc
prettyIf "#ifdef"  (String -> Doc
text String
v)   [Stmt]
stmts [Elif]
is Else
e
  pPrint (IfNDef v :: String
v stmts :: [Stmt]
stmts is :: [Elif]
is e :: Else
e) = String -> Doc -> [Stmt] -> [Elif] -> Else -> Doc
prettyIf "#ifndef" (String -> Doc
text String
v)   [Stmt]
stmts [Elif]
is Else
e
  pPrint (Define v :: String
v i :: Int
i         ) = String -> Doc
text "#define" Doc -> Doc -> Doc
<+> String -> Doc
text String
v Doc -> Doc -> Doc
<+> Int -> Doc
int Int
i
  pPrint (Undef  v :: String
v           ) = String -> Doc
text "#undef"  Doc -> Doc -> Doc
<+> String -> Doc
text String
v
  pPrint (Line   s :: String
s           ) = String -> Doc
text String
s

  pPrintList :: [Stmt] -> Doc
pPrintList = (Stmt -> Doc -> Doc) -> Doc -> [Stmt] -> Doc
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (Doc -> Doc -> Doc
($+$) (Doc -> Doc -> Doc) -> (Stmt -> Doc) -> Stmt -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Stmt -> Doc
forall a. Pretty a => a -> Doc
pPrint) Doc
empty

instance Pretty Elif where
  pPrint :: Elif -> Doc
pPrint (Elif (c :: Cond
c, stmts :: [Stmt]
stmts)) = String -> Doc
text "#elif" Doc -> Doc -> Doc
<+> Cond -> Doc
forall a. Pretty a => a -> Doc
pPrint Cond
c Doc -> Doc -> Doc
$+$ [Stmt] -> Doc
forall a. Pretty a => a -> Doc
pPrint [Stmt]
stmts

  pPrintList :: [Elif] -> Doc
pPrintList = (Elif -> Doc -> Doc) -> Doc -> [Elif] -> Doc
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (Doc -> Doc -> Doc
($+$) (Doc -> Doc -> Doc) -> (Elif -> Doc) -> Elif -> Doc -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Elif -> Doc
forall a. Pretty a => a -> Doc
pPrint) Doc
empty

instance Pretty Else where
  pPrint :: Else -> Doc
pPrint (Else (Just stmts :: [Stmt]
stmts)) = String -> Doc
text "#else" Doc -> Doc -> Doc
$+$ [Stmt] -> Doc
forall a. Pretty a => a -> Doc
pPrint [Stmt]
stmts
  pPrint (Else Nothing)      = Doc
empty

prettyIf :: String -> Doc -> [Stmt] -> [Elif] -> Else -> Doc
prettyIf :: String -> Doc -> [Stmt] -> [Elif] -> Else -> Doc
prettyIf k :: String
k doc :: Doc
doc stmts :: [Stmt]
stmts is :: [Elif]
is e :: Else
e = (Doc -> Doc -> Doc) -> Doc -> [Doc] -> Doc
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Doc -> Doc -> Doc
($+$) Doc
empty
  [String -> Doc
text String
k Doc -> Doc -> Doc
<+> Doc
doc, [Stmt] -> Doc
forall a. Pretty a => a -> Doc
pPrint [Stmt]
stmts, [Elif] -> Doc
forall a. Pretty a => a -> Doc
pPrint [Elif]
is, Else -> Doc
forall a. Pretty a => a -> Doc
pPrint Else
e, String -> Doc
text "#endif"]

instance Pretty Cond where
  pPrint :: Cond -> Doc
pPrint (Comp v :: String
v op :: Op
op i :: Int
i) = String -> Doc
text String
v Doc -> Doc -> Doc
<+> Op -> Doc
forall a. Pretty a => a -> Doc
pPrint Op
op Doc -> Doc -> Doc
<+> Int -> Doc
int Int
i
  pPrint (Defined  v :: String
v ) = String -> Doc
text "defined("  Doc -> Doc -> Doc
<> String -> Doc
text String
v Doc -> Doc -> Doc
<> Char -> Doc
char ')'
  pPrint (NDefined v :: String
v ) = String -> Doc
text "!defined(" Doc -> Doc -> Doc
<> String -> Doc
text String
v Doc -> Doc -> Doc
<> Char -> Doc
char ')'

instance Pretty Op where
  pPrint :: Op -> Doc
pPrint Eq  = String -> Doc
text "=="
  pPrint Neq = String -> Doc
text "/="
  pPrint Lt  = String -> Doc
text "<"
  pPrint Leq = String -> Doc
text "<="
  pPrint Gt  = String -> Doc
text ">"
  pPrint Geq = String -> Doc
text ">="