{-# LANGUAGE GADTs #-}
-- | Defines an AST for defining Modules.
module Language.Drasil.Mod (Class(..), StateVariable(..), Func(..),
  FuncData(..), FuncDef(..), FuncStmt(..), Initializer, Mod(..), Name,
  Version, Description, Import, ($:=), pubStateVar, privStateVar, classDef,
  classImplements, ctorDef, ffor, fforRange, fDecDef, fname, fstdecl, funcData, funcDef,
  funcDefParams, packmod, packmodRequires
) where

import Language.Drasil (Space, MayHaveUnit, Quantity, CodeExpr, LiteralC(..))
import Database.Drasil (ChunkDB)
import GOOL.Drasil (ScopeTag(..))

import Language.Drasil.Chunk.Code (CodeVarChunk, CodeFuncChunk, codevars,
  codevars', quantvar)
import Language.Drasil.Chunk.Parameter (ParameterChunk, pcAuto)
import Language.Drasil.Code.DataDesc (DataDesc)

import Utils.Drasil (toPlainName)

import Data.List ((\\), nub)

-- | Type synonym for clarity.
type Name = String
-- | Type synonym for clarity.
type Description = String
-- | Type synonym for clarity.
type Import = String
-- | Type synonym for clarity.
type Version = String

-- | Holds module information.
data Mod = Mod Name Description [Import] [Class] [Func]

-- | Define a 'Mod' with the given 'Name', 'Description', 'Classes', and 'Functions'.
packmod :: Name -> Description -> [Class] -> [Func] -> Mod
packmod :: Name -> Name -> [Class] -> [Func] -> Mod
packmod Name
n Name
d = Name -> Name -> [Name] -> [Class] -> [Func] -> Mod
packmodRequires Name
n Name
d []

-- | Define a 'Mod' that requires some library imports, with the given Name,
-- Description, Classes, and Functions.
packmodRequires :: Name -> Description -> [Import] -> [Class] -> [Func] -> Mod
packmodRequires :: Name -> Name -> [Name] -> [Class] -> [Func] -> Mod
packmodRequires Name
n = Name -> Name -> [Name] -> [Class] -> [Func] -> Mod
Mod (Name -> Name
toPlainName Name
n)

-- | Holds information needed to define a class.
data Class = ClassDef {
  Class -> Name
className :: Name,
  Class -> Maybe Name
implements :: Maybe Name,
  Class -> Name
classDesc :: Description,
  Class -> [StateVariable]
stateVars :: [StateVariable],
  Class -> [Func]
methods :: [Func]}

-- | State variables hold attach a 'ScopeTag' to a 'CodeVarChunk'.
data StateVariable = SV {
  StateVariable -> ScopeTag
svScope :: ScopeTag,
  StateVariable -> CodeVarChunk
stVar :: CodeVarChunk}

-- | Define a public state variable based on the given 'CodeVarChunk'.
pubStateVar :: CodeVarChunk -> StateVariable
pubStateVar :: CodeVarChunk -> StateVariable
pubStateVar = ScopeTag -> CodeVarChunk -> StateVariable
SV ScopeTag
Pub

-- | Define a private state variable based on the given 'CodeVarChunk'.
privStateVar :: CodeVarChunk -> StateVariable
privStateVar :: CodeVarChunk -> StateVariable
privStateVar = ScopeTag -> CodeVarChunk -> StateVariable
SV ScopeTag
Priv

-- | Define a class with the given 'Name', 'Description', state variables, and
-- methods.
classDef :: Name -> Description -> [StateVariable] -> [Func] -> Class
classDef :: Name -> Name -> [StateVariable] -> [Func] -> Class
classDef Name
n = Name -> Maybe Name -> Name -> [StateVariable] -> [Func] -> Class
ClassDef Name
n forall a. Maybe a
Nothing

-- | Define a class that implements an interface. 1st 'Name' is class name, 2nd is
-- interface name.
classImplements :: Name -> Name -> Description -> [StateVariable] -> [Func] ->
  Class
classImplements :: Name -> Name -> Name -> [StateVariable] -> [Func] -> Class
classImplements Name
n Name
i = Name -> Maybe Name -> Name -> [StateVariable] -> [Func] -> Class
ClassDef Name
n (forall a. a -> Maybe a
Just Name
i)

-- | Holds a function definition or function data.
data Func = FDef FuncDef
          | FData FuncData

-- | Define a function that reads data from a file, according to the given
-- 'DataDesc'.
funcData :: Name -> Description -> DataDesc -> Func
funcData :: Name -> Name -> DataDesc -> Func
funcData Name
n Name
desc DataDesc
d = FuncData -> Func
FData forall a b. (a -> b) -> a -> b
$ Name -> Name -> DataDesc -> FuncData
FuncData (Name -> Name
toPlainName Name
n) Name
desc DataDesc
d

-- | Define a function by providing the 'FuncStmt's for its body. Other
-- parameters are function name, description, list of parameters, space of the
-- returned value, and description of the returned value.
funcDef :: (Quantity c, MayHaveUnit c) => Name -> Description -> [c] ->
  Space -> Maybe Description -> [FuncStmt] -> Func
funcDef :: forall c.
(Quantity c, MayHaveUnit c) =>
Name -> Name -> [c] -> Space -> Maybe Name -> [FuncStmt] -> Func
funcDef Name
s Name
desc [c]
i Space
t Maybe Name
returnDesc [FuncStmt]
fs = FuncDef -> Func
FDef forall a b. (a -> b) -> a -> b
$ Name
-> Name
-> [ParameterChunk]
-> Space
-> Maybe Name
-> [FuncStmt]
-> FuncDef
FuncDef (Name -> Name
toPlainName Name
s) Name
desc
  (forall a b. (a -> b) -> [a] -> [b]
map (forall c. CodeIdea c => c -> ParameterChunk
pcAuto forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar) [c]
i) Space
t Maybe Name
returnDesc [FuncStmt]
fs

-- | Like 'funcDef' but uses 'ParameterChunk's to represent the parameters.
funcDefParams :: Name -> Description -> [ParameterChunk] -> Space ->
  Maybe Description -> [FuncStmt] -> Func
funcDefParams :: Name
-> Name
-> [ParameterChunk]
-> Space
-> Maybe Name
-> [FuncStmt]
-> Func
funcDefParams Name
s Name
desc [ParameterChunk]
ps Space
t Maybe Name
returnDesc [FuncStmt]
fs = FuncDef -> Func
FDef forall a b. (a -> b) -> a -> b
$ Name
-> Name
-> [ParameterChunk]
-> Space
-> Maybe Name
-> [FuncStmt]
-> FuncDef
FuncDef (Name -> Name
toPlainName Name
s) Name
desc
  [ParameterChunk]
ps Space
t Maybe Name
returnDesc [FuncStmt]
fs

-- | Define a constructor, with the given name, description, parameters,
-- initializers (variable-value pairs), and 'FuncStmt's for the body.
ctorDef :: Name -> Description -> [ParameterChunk] -> [Initializer] ->
  [FuncStmt] -> Func
ctorDef :: Name
-> Name -> [ParameterChunk] -> [Initializer] -> [FuncStmt] -> Func
ctorDef Name
n Name
desc [ParameterChunk]
ps [Initializer]
is [FuncStmt]
fs = FuncDef -> Func
FDef forall a b. (a -> b) -> a -> b
$ Name
-> Name
-> [ParameterChunk]
-> [Initializer]
-> [FuncStmt]
-> FuncDef
CtorDef Name
n Name
desc [ParameterChunk]
ps [Initializer]
is [FuncStmt]
fs

-- | Function data. Holds a name, description, and pieces of data with its own description.
data FuncData where
  FuncData :: Name -> Description -> DataDesc -> FuncData

-- | Defines a function.
data FuncDef where
  -- | Parameters are: Name, description, parameters, return type, return description, statements.
  FuncDef :: Name -> Description -> [ParameterChunk] -> Space ->
    Maybe Description -> [FuncStmt] -> FuncDef
  CtorDef :: Name -> Description -> [ParameterChunk] -> [Initializer] ->
    [FuncStmt] -> FuncDef

-- | Variable-value pair.
type Initializer = (CodeVarChunk, CodeExpr)

data FuncStmt where
  FAsg      :: CodeVarChunk -> CodeExpr -> FuncStmt
  FAsgIndex :: CodeVarChunk -> Integer -> CodeExpr -> FuncStmt
  -- | For-loop; Variable, Start, Stop, Step, Body.
  FFor      :: CodeVarChunk -> CodeExpr -> CodeExpr -> CodeExpr
                -> [FuncStmt] -> FuncStmt
  FForEach  :: CodeVarChunk -> CodeExpr -> [FuncStmt] -> FuncStmt
  FWhile    :: CodeExpr -> [FuncStmt] -> FuncStmt
  FCond     :: CodeExpr -> [FuncStmt] -> [FuncStmt] -> FuncStmt
  FRet      :: CodeExpr -> FuncStmt
  FThrow    :: String -> FuncStmt
  FTry      :: [FuncStmt] -> [FuncStmt] -> FuncStmt
  FContinue :: FuncStmt
  FDecDef   :: CodeVarChunk -> CodeExpr -> FuncStmt
  FFuncDef  :: CodeFuncChunk -> [ParameterChunk] -> [FuncStmt] -> FuncStmt
  FVal      :: CodeExpr -> FuncStmt
  FMulti    :: [FuncStmt] -> FuncStmt
  -- slight hack, for now
  FAppend   :: CodeExpr -> CodeExpr -> FuncStmt

-- | Define an assignment statement.
($:=) :: (Quantity c, MayHaveUnit c) => c -> CodeExpr -> FuncStmt
c
v $:= :: forall c. (Quantity c, MayHaveUnit c) => c -> CodeExpr -> FuncStmt
$:= CodeExpr
e = CodeVarChunk -> CodeExpr -> FuncStmt
FAsg (forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar c
v) CodeExpr
e

-- | Define a for-loop. 'Quantity' is for the iteration variable, 'CodeExpr' is the
-- upper bound at that variable (the variable will start with a value of 0).
-- ['FuncStmt'] is for the loop body.
ffor :: (Quantity c, MayHaveUnit c) => c -> CodeExpr -> [FuncStmt] -> FuncStmt
ffor :: forall c.
(Quantity c, MayHaveUnit c) =>
c -> CodeExpr -> [FuncStmt] -> FuncStmt
ffor c
v CodeExpr
end = forall c.
(Quantity c, MayHaveUnit c) =>
c -> CodeExpr -> CodeExpr -> CodeExpr -> [FuncStmt] -> FuncStmt
fforRange c
v (forall r. LiteralC r => Integer -> r
int Integer
0) CodeExpr
end (forall r. LiteralC r => Integer -> r
int Integer
1)

-- | Define a for-loop. 'Quantity' is for the iteration variable, and 3 'CodeExpr's
-- for the start, stop, step numbers.
-- ['FuncStmt'] is for the loop body.
fforRange :: (Quantity c, MayHaveUnit c) => c -> CodeExpr -> CodeExpr
  -> CodeExpr -> [FuncStmt] -> FuncStmt
fforRange :: forall c.
(Quantity c, MayHaveUnit c) =>
c -> CodeExpr -> CodeExpr -> CodeExpr -> [FuncStmt] -> FuncStmt
fforRange c
v = CodeVarChunk
-> CodeExpr -> CodeExpr -> CodeExpr -> [FuncStmt] -> FuncStmt
FFor (forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar c
v)

-- | Define a declare-define statement.
fDecDef :: (Quantity c, MayHaveUnit c) => c -> CodeExpr -> FuncStmt
fDecDef :: forall c. (Quantity c, MayHaveUnit c) => c -> CodeExpr -> FuncStmt
fDecDef c
v  = CodeVarChunk -> CodeExpr -> FuncStmt
FDecDef (forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar c
v)

-- | Returns the list of 'CodeVarChunk's that are used in the list of 'FuncStmt's
-- but are not declared in any of the 'FuncStmt's.
fstdecl :: ChunkDB -> [FuncStmt] -> [CodeVarChunk]
fstdecl :: ChunkDB -> [FuncStmt] -> [CodeVarChunk]
fstdecl ChunkDB
ctx [FuncStmt]
fsts = forall a. Eq a => [a] -> [a]
nub (forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
ctx) [FuncStmt]
fsts) forall a. Eq a => [a] -> [a] -> [a]
\\ forall a. Eq a => [a] -> [a]
nub (forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
ctx) [FuncStmt]
fsts)
  where
    fstvars :: ChunkDB -> FuncStmt -> [CodeVarChunk]
    fstvars :: ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm (FDecDef CodeVarChunk
cch CodeExpr
e) = CodeVarChunk
cchforall a. a -> [a] -> [a]
:CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm
    fstvars ChunkDB
sm (FFuncDef CodeFuncChunk
cch [ParameterChunk]
ps [FuncStmt]
sts) = forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar CodeFuncChunk
cch forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar [ParameterChunk]
ps
      forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
sts
    fstvars ChunkDB
sm (FAsg CodeVarChunk
cch CodeExpr
e) = CodeVarChunk
cchforall a. a -> [a] -> [a]
:CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm
    fstvars ChunkDB
sm (FAsgIndex CodeVarChunk
cch Integer
_ CodeExpr
e) = CodeVarChunk
cchforall a. a -> [a] -> [a]
:CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm
    fstvars ChunkDB
sm (FFor CodeVarChunk
cch CodeExpr
s CodeExpr
e CodeExpr
st [FuncStmt]
fs) = forall a. Eq a => [a] -> [a]
nub forall a b. (a -> b) -> a -> b
$ CodeVarChunk
cch forall a. a -> [a] -> [a]
: CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm forall a. [a] -> [a] -> [a]
++ CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
s ChunkDB
sm
       forall a. [a] -> [a] -> [a]
++ CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
st ChunkDB
sm forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
fs
    fstvars ChunkDB
sm (FForEach CodeVarChunk
cch CodeExpr
e [FuncStmt]
fs) = forall a. Eq a => [a] -> [a]
nub (CodeVarChunk
cch forall a. a -> [a] -> [a]
: CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
fs)
    fstvars ChunkDB
sm (FWhile CodeExpr
e [FuncStmt]
fs) = CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
fs
    fstvars ChunkDB
sm (FCond CodeExpr
e [FuncStmt]
tfs [FuncStmt]
efs) = CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
tfs forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
efs
    fstvars ChunkDB
sm (FRet CodeExpr
e) = CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
e ChunkDB
sm
    fstvars ChunkDB
sm (FTry [FuncStmt]
tfs [FuncStmt]
cfs) = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
tfs forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm ) [FuncStmt]
cfs
    fstvars ChunkDB
_  (FThrow Name
_) = [] -- is this right?
    fstvars ChunkDB
_  FuncStmt
FContinue = []
    fstvars ChunkDB
sm (FVal CodeExpr
v) = CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars' CodeExpr
v ChunkDB
sm
    fstvars ChunkDB
sm (FMulti [FuncStmt]
ss) = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
fstvars ChunkDB
sm) [FuncStmt]
ss
    fstvars ChunkDB
sm (FAppend CodeExpr
a CodeExpr
b) = forall a. Eq a => [a] -> [a]
nub (CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars CodeExpr
a ChunkDB
sm forall a. [a] -> [a] -> [a]
++ CodeExpr -> ChunkDB -> [CodeVarChunk]
codevars CodeExpr
b ChunkDB
sm)

    declared :: ChunkDB -> FuncStmt -> [CodeVarChunk]
    declared :: ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
_  (FDecDef CodeVarChunk
cch CodeExpr
_) = [CodeVarChunk
cch]
    declared ChunkDB
sm (FFuncDef CodeFuncChunk
cch [ParameterChunk]
ps [FuncStmt]
sts) = forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar CodeFuncChunk
cch forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map forall c. (Quantity c, MayHaveUnit c) => c -> CodeVarChunk
quantvar [ParameterChunk]
ps
      forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
sts
    declared ChunkDB
_  (FAsg CodeVarChunk
_ CodeExpr
_) = []
    declared ChunkDB
_  FAsgIndex {} = []
    declared ChunkDB
sm (FFor CodeVarChunk
cch CodeExpr
_ CodeExpr
_ CodeExpr
_ [FuncStmt]
fs) = CodeVarChunk
cch forall a. a -> [a] -> [a]
: forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
fs
    declared ChunkDB
sm (FForEach CodeVarChunk
cch CodeExpr
_ [FuncStmt]
fs) = CodeVarChunk
cch forall a. a -> [a] -> [a]
: forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
fs
    declared ChunkDB
sm (FWhile CodeExpr
_ [FuncStmt]
fs) = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
fs
    declared ChunkDB
sm (FCond CodeExpr
_ [FuncStmt]
tfs [FuncStmt]
efs) = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
tfs forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
efs
    declared ChunkDB
_  (FRet CodeExpr
_) = []
    declared ChunkDB
sm (FTry [FuncStmt]
tfs [FuncStmt]
cfs) = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
tfs forall a. [a] -> [a] -> [a]
++ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
cfs
    declared ChunkDB
_  (FThrow Name
_) = [] -- is this right?
    declared ChunkDB
_  FuncStmt
FContinue = []
    declared ChunkDB
_  (FVal CodeExpr
_) = []
    declared ChunkDB
sm (FMulti [FuncStmt]
ss) = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (ChunkDB -> FuncStmt -> [CodeVarChunk]
declared ChunkDB
sm) [FuncStmt]
ss
    declared ChunkDB
_  (FAppend CodeExpr
_ CodeExpr
_) = []

-- | Gets the name of a function.
fname :: Func -> Name
fname :: Func -> Name
fname (FDef (FuncDef Name
n Name
_ [ParameterChunk]
_ Space
_ Maybe Name
_ [FuncStmt]
_)) = Name
n
fname (FDef (CtorDef Name
n Name
_ [ParameterChunk]
_ [Initializer]
_ [FuncStmt]
_)) = Name
n
fname (FData (FuncData Name
n Name
_ DataDesc
_)) = Name
n