> {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses,
> FunctionalDependencies #-}
> module Epic.Language where
> import Control.Monad
> import System.IO
> import System.Directory
> import System.Environment
> import Debug.Trace
> import Data.Char
> -- | (Debugging) options to give to compiler
> data CompileOptions = KeepC -- ^ Keep intermediate C file
>                     | Trace -- ^ Generate trace at run-time (debug)
>                     | ShowBytecode -- ^ Show generated code
>                     | ShowParseTree -- ^ Show parse tree
>                     | MakeHeader FilePath -- ^ Output a .h file too
>                     | GCCOpt String -- ^ Extra GCC option
>                     | Debug -- ^ Generate debug info
>                     | Checking Int -- ^ Checking level (0 none)
>                     | ExternalMain -- ^ main is defined externally (in C)
>                     | MainInc FilePath -- ^ File to #include in main program
>                     | LinkObj FilePath -- ^ .o file to link with
>   deriving Eq
Raw data types. Int, Char, Bool are unboxed.
> data Type = TyInt
>           | TyChar
>           | TyBool
>           | TyFloat
>           | TyBigInt
>           | TyBigFloat
>           | TyString
>           | TyPtr
>           | TyUnit
>           | TyAny -- unchecked, polymorphic
>           | TyData -- generic data type
>           | TyCType String -- Exported, C typedef
>           | TyFun -- any function
>           | TyLin Type -- guarantee at most one instance
>           | TyEval Type -- guarantee evaluated
>   deriving Eq
> instance Show Type where
>     show TyInt = "Int"
>     show TyChar = "Char"
>     show TyBool = "Bool"
>     show TyFloat = "Float"
>     show TyBigInt = "BigInt"
>     show TyBigFloat = "BigFloat"
>     show TyString = "String"
>     show TyPtr = "Ptr"
>     show TyUnit = "Unit"
>     show TyAny = "Any"
>     show TyData = "Data"
>     show (TyCType s) = "CType " ++ s
>     show TyFun = "Fun"
>     show (TyLin s) = "Linear(" ++ show s ++ ")"
>     show (TyEval s) = "Eval(" ++ show s ++ ")"
> data Const = MkInt Int
>            | MkBigInt Integer
>            | MkChar Char
>            | MkFloat Double
>            | MkBigFloat Double
>            | MkString String
>            | MkBool Bool
>            | MkUnit
>            | MkUnused
>   deriving (Show, Eq)
> data Name = UN String  -- user name
>           | MN String Int -- machine generated name
>   deriving Eq
> instance Show Name where
>     show (UN str) = "_U_"++str
>     show (MN str i) = "_M_"++show i++"_"++str
> showuser (UN str) = str
> showuser (MN str i) = "["++str++"_"++show i++"]"
> quotename [] = ""
> quotename ('_':cs) = "__"++quotename cs
> quotename ('\'':cs) = "_PR_"++quotename cs
> quotename ('?':cs) = "_QU_"++quotename cs
> quotename ('$':cs) = "_DO_"++quotename cs
> quotename ('#':cs) = "_HA_"++quotename cs
> quotename ('@':cs) = "_AT_"++quotename cs
> quotename (c:cs) | isAlphaNum c = c:(quotename cs)
>                  | otherwise = "_" ++ show (fromEnum c) ++ "_" ++ quotename cs
> showC n = quotename (show n)
> type Context = [(Name,([Type],Type))] -- Name, arg types, return type
Get the arity of a definition in the context
> arity x ctxt = case lookup x ctxt of
>                   Nothing -> error $ "No such function " ++ show x
>                   (Just (args,ret)) -> length args
> type Tag = Int
> type HFun = Expr -> Expr
> data Expr = V Int -- Locally bound name
>           | R Name -- Global reference
>           | App Expr [Expr] -- Function application
>           | Lazy Expr -- Lazy function application
>           | Effect Expr -- Expression with side effects (i.e. don't update when EVALing)
>           | Par Expr -- evaluate an expression in parallel
>           | Con Tag [Expr] -- Constructor, tags, arguments (fully applied)
>           | Const Const -- a constant
>           | Proj Expr Int -- Project argument
>           | Case Expr [CaseAlt]
>           | If Expr Expr Expr
>           | While Expr Expr
>           | WhileAcc Expr Expr Expr
>           | Op Op Expr Expr -- Infix operator
>           | Let Name Type Expr Expr -- Let binding
>           | LetM Name Expr Expr -- Update a variable
>           | HLet Name Type Expr HFun -- HOAS let, for evaluation
>           | Update Int Expr Expr -- Update a variable (scope-checked)
>           | Lam Name Type Expr -- inner lambda
>           | HLam Name Type HFun -- HOAS lambda, for evaluation
>           | Error String -- Exit with error message
>           | Impossible -- Claimed impossible to reach code
>           | WithMem Allocator Expr Expr -- evaluate with manual allocation
>           | ForeignCall Type String [(Expr, Type)] -- Foreign function call
>           | LazyForeignCall Type String [(Expr, Type)] -- Foreign function call
>   deriving Eq
> data CaseAlt = Alt { alt_tag :: Tag,
>                      alt_args :: [(Name, Type)], -- bound arguments
>                      alt_expr :: Expr -- what to do
>                    }
>              | HAlt { alt_tag :: Tag,
>                       alt_numargs :: Int,
>                       alt_binds :: HRHS }
>              | ConstAlt { alt_const :: Int,
>                           alt_expr :: Expr }
>              | DefaultCase { alt_expr :: Expr }
>   deriving Eq
> data HRHS = HExp Expr
>           | HBind Name Type (Expr -> HRHS)
>   deriving Eq
> instance Eq (Expr -> Expr) where -- can't compare HOAS for equality
>     _ == _ = False
> instance Eq (Expr -> HRHS) where -- can't compare HOAS for equality
>     _ == _ = False
> instance Ord CaseAlt where -- only the tag matters
>    compare (Alt t1 _ _) (Alt t2 _ _) = compare t1 t2
>    compare (Alt _ _ _) (DefaultCase _) = LT
>    compare (DefaultCase _) (Alt _ _ _) = GT
>    compare _ _ = EQ
> data Allocator = FixedPool | GrowablePool
>   deriving Eq
> data Op = Plus | Minus | Times | Divide | Modulo
>         | OpEQ | OpLT | OpLE | OpGT | OpGE
>         | FPlus | FMinus | FTimes | FDivide
>         | OpFEQ | OpFLT | OpFLE | OpFGT | OpFGE
>         | ShL  | ShR
>   deriving (Show, Eq)
> instance Show CaseAlt where
>     show (DefaultCase e) = "default -> " ++ show e
>     show (ConstAlt i e) = show i ++ " -> " ++ show e
>     show (Alt t args e) = "Con " ++ show t ++ show args ++ " -> " ++ show e
> instance Show Expr where
>     show (V i) = "var" ++ show i
>     show (R n) = show n
>     show (App f as) = show f ++ show as
>     show (Lazy e) = "%lazy(" ++ show e ++ ")"
>     show (Par e) = "%par(" ++ show e ++ ")"
>     show (Effect e) = "%effect(" ++ show e ++ ")"
>     show (Con t es) = "Con " ++ show t ++ show es
>     show (Const c) = show c
>     show (Proj e i) = show e ++ "!" ++ show i
>     show (Case e alts) = "case " ++ show e ++ " of " ++ show alts
>     show (If x t e) = "if " ++ show x ++ " then " ++ show t ++ " else " ++ show e
>     show (While e b) = "%while(" ++ show e ++ "," ++ show b ++ ")"
>     show (WhileAcc e b a) = "%while(" ++ show e ++ ", " ++ show b ++ 
>                             ", " ++ show a ++ ")"
>     show (Op o l r) = "(" ++ show l ++ " " ++ show o ++ " " ++ show r ++")"
>     show (Let n t v e) = "let " ++ show n ++ ":" ++ show t ++ " = " ++
>                          show v ++ " in " ++ show e
>     show (LetM n v e) = "let! " ++ show n ++ " = " ++
>                          show v ++ " in " ++ show e
>     show (Update n v e) = "let! var" ++ show n ++ " = " ++
>                          show v ++ " in " ++ show e
>     show (Lam n t e) = "\\ " ++ show n ++ ":" ++ show t ++ " . " ++
>                          show e
>     show (Error e) = "error(" ++ show e ++ ")"
>     show Impossible = "Impossible"
>     show (WithMem a m e) = "%memory(" ++ show a ++ "," ++ show m ++ ", " ++ show e ++ ")"
>     show (ForeignCall t s as) = "foreign " ++ show t ++ " " ++
>                                 show s ++ show as
>     show (LazyForeignCall t s as) = "lazy foreign " ++ show t ++ " " ++
>                                     show s ++ show as
> instance Show Allocator where
>     show FixedPool = "%fixed"
>     show GrowablePool = "%growable"
Supercombinator definitions
> data Func = Bind { fun_args :: [(Name, Type)],
>                    locals :: Int, -- total number of locals
>                    defn :: Expr,
>                    flags :: [CGFlag]}
>   deriving Show
Programs
> data Decl = Decl { fname :: Name,
>                    frettype :: Type,
>                    fdef :: Func,
>                    fexport :: Maybe String,  -- C name
>                    fcompflags :: [CGFlag]
>                  }
>           | Extern { fname :: Name, 
>                      frettype :: Type,
>                      fargs :: [Type] }
>           | Include String
>           | Link String
>           | CType String
>   deriving Show
> data EvalDecl = EDecl { ename :: Name,
>                         edef :: Expr -- as HOAS
>                       }
>               | EDirective
> class SubstV x where
>     subst :: Int -> Expr -> x -> x
> instance SubstV a => SubstV [a] where
>     subst v rep xs = map (subst v rep) xs
> instance SubstV (Expr, Type) where
>     subst v rep (x, t) = (subst v rep x, t)
> instance SubstV Expr where
>     subst v rep (V x) | v == x = rep
>     subst v rep (App x xs) = App (subst v rep x) (subst v rep xs)
>     subst v rep (Lazy x) = Lazy (subst v rep x)
>     subst v rep (Par x) = Par (subst v rep x)
>     subst v rep (Effect x) = Effect (subst v rep x)
>     subst v rep (Con t xs) = Con t (subst v rep xs)
>     subst v rep (Proj x i) = Proj (subst v rep x) i
>     subst v rep (Case x alts) = Case (subst v rep x) (subst v rep alts)
>     subst v rep (If a t e) 
>         = If (subst v rep a) (subst v rep t) (subst v rep e)
>     subst v rep (While a e) = While (subst v rep a) (subst v rep e)
>     subst v rep (WhileAcc a t e) 
>         = WhileAcc (subst v rep a) (subst v rep t) (subst v rep e)
>     subst v rep (Op o x y) = Op o (subst v rep x) (subst v rep y)
>     subst v rep (Let n ty val sc) 
>         = Let n ty (subst v rep val) (subst v rep sc)
>     subst v rep (LetM n val sc)
>         = LetM n (subst v rep val) (subst v rep sc)
>     subst v rep (Lam n t e) = Lam n t (subst v rep e)
>     subst v rep (WithMem a x y) = WithMem a (subst v rep x) (subst v rep y)
>     subst v rep (ForeignCall t s xs) = ForeignCall t s (subst v rep xs)
>     subst v rep (LazyForeignCall t s xs) 
>         = LazyForeignCall t s (subst v rep xs)
>     subst v rep x = x
> instance SubstV CaseAlt where
>     subst v rep (Alt t as e) = Alt t as (subst v rep e)
>     subst v rep (ConstAlt i e) = ConstAlt i (subst v rep e)
>     subst v rep (DefaultCase e) = DefaultCase (subst v rep e)
> class HOAS a b | a -> b where
>     hoas :: Int -> a -> b
>     mkHOAS :: a -> b
>     mkHOAS = hoas 0
> instance HOAS a a => HOAS [a] [a] where
>     hoas v xs = map (hoas v) xs
> instance HOAS a a => HOAS (a, Type) (a, Type) where
>     hoas v (x, t) = (hoas v x, t)
> instance HOAS Func Expr where
>     hoas v (Bind args locs def flags) = hargs v args def where
>        hargs v [] def = hoas v def
>        hargs v ((x,t):xs) def 
>           = HLam x t (\var -> hargs (v+1) xs (subst v var def))
> instance HOAS Expr Expr where
>     hoas v (App f xs) = App (hoas v f) (hoas v xs)
>     hoas v (Lazy x) = Lazy (hoas v x)
>     hoas v (Par x) = Par (hoas v x)
>     hoas v (Effect x) = Effect (hoas v x)
>     hoas v (Con t xs) = Con t (hoas v xs)
>     hoas v (Proj x i) = Proj (hoas v x) i
>     hoas v (Case x xs) = Case (hoas v x) (hoas v xs)
>     hoas v (If x t e) = If (hoas v x) (hoas v t) (hoas v e)
>     hoas v (While x y) = While (hoas v x) (hoas v y)
>     hoas v (WhileAcc x y z) = WhileAcc (hoas v x) (hoas v y) (hoas v z)
>     hoas v (Op o x y) = Op o (hoas v x) (hoas v y)
>     hoas v (Let n t val sc) 
>         = HLet n t val (\var -> subst v var (hoas (v+1) sc))
>     hoas v (Lam n ty sc)
>         = HLam n ty (\var -> subst v var (hoas (v+1) sc))
>     hoas v (WithMem a x y) = WithMem a (hoas v x) (hoas v y)
>     hoas v (ForeignCall t n xs) = ForeignCall t n (hoas v xs)
>     hoas v (LazyForeignCall t n xs) = LazyForeignCall t n (hoas v xs)
>     hoas v x = x
> instance HOAS CaseAlt CaseAlt where
>     hoas v (Alt t args rhs) = HAlt t (length args) (hbind v args rhs) where
>        hbind v [] rhs = HExp (hoas v rhs)
>        hbind v ((x,t):xs) rhs 
>             = HBind x t (\var -> hbind (v+1) xs (subst v var rhs))
>     hoas v (ConstAlt i e) = ConstAlt i (hoas v e)
>     hoas v (DefaultCase e) = DefaultCase (hoas v e)
> data CGFlag = Inline | Strict
>   deriving (Show, Eq)
> data Result r = Success r
>               | Failure String String Int
>     deriving (Show, Eq)
> 
> instance Monad Result where
>     (Success r)   >>= k = k r
>     (Failure err fn line) >>= k = Failure err fn line
>     return              = Success
>     fail s              = Failure s "(no file)" 0
> 
> instance MonadPlus Result where
>     mzero = Failure "Error" "(no file)" 0
>     mplus (Success x) _ = (Success x)
>     mplus (Failure _ _ _) y = y
> 
> appForm :: Expr -> Bool
appForm (App _ _) = True appForm (V _) = True
> appForm (R _) = True
appForm (Con _ _) = True appForm (Const _) = True appForm (LazyForeignCall _ _ _) = True
> appForm _ = False
> checkLevel :: [CompileOptions] -> Int
> checkLevel [] = 1
> checkLevel (Checking i:_) = i
> checkLevel (_:xs) = checkLevel xs
Temp files for compiler output
> tempfile :: IO (FilePath, Handle)
> tempfile = do env <- environment "TMPDIR"
>               let dir = case env of
>                               Nothing -> "/tmp"
>                               (Just d) -> d
>               openTempFile dir "esc"
> environment :: String -> IO (Maybe String)
> environment x = catch (do e <- getEnv x
>                           return (Just e))
>                       (\_ -> return Nothing)
Some tests foo x = let y = case x of c1 a b -> a b c2 c -> bar (c+2) in y+3 testctxt = [((UN "foo"),([TyData], TyInt)), ((UN "bar"),([TyInt], TyInt))] testprog = Bind [TyData] 3 $ Let (UN "y") TyInt (Case (V 0) [Alt 0 [TyFun,TyInt] (App (V 1) [V 2]), Alt 1 [TyInt] (App (R (UN "bar")) [Op Plus (V 1) (Const (MkInt 2))])]) (Op Plus (V 1) (Const (MkInt 3)))