{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleContexts #-}

module Drasil.Shared.InterfaceCommon (
  -- Types
  Label, Library, Body, Block, VSBinder, Variable, SVariable, Value, SValue,
  NamedArgs, MixedCall, MixedCtorCall, PosCall, PosCtorCall, InOutCall,
  InOutFunc, DocInOutFunc,
  -- Typeclasses
  SharedProg, SharedStatement, UnRepr(..), BodySym(..), bodyStatements, oneLiner,
  BlockSym(..), TypeSym(..), TypeElim(..), getTypeString, VariableSym(..),
  ScopeSym(..), convScope, VariableElim(..), listOf, listVar, ValueSym(..),
  Argument(..), Literal(..), litZero, MathConstant(..), VariableValue(..),
  CommandLineArgs(..), NumericExpression(..), BooleanExpression(..),
  Comparison(..), ValueExpression(..), funcApp, funcAppNamedArgs, extFuncApp,
  libFuncApp, exists, IndexTranslator(..), Reference(..), Array(..), List(..),
  Set(..), NativeVector(..), InternalList(..), listSlice, listIndexExists, at,
  StatementSym(..), AssignStatement(..), (&=), DeclStatement(..),
  IOStatement(..), StringStatement(..), FunctionSym, FuncAppStatement(..),
  CommentStatement(..), ControlStatement(..), ifNoElse, switchAsIf,
  VisibilitySym(..), ParameterSym(..), MethodSym(..), BinderSym(..),
  BinderElim(..), convType
  ) where

import Data.Bifunctor (first)
import Text.PrettyPrint.HughesPJ (Doc)

import Drasil.Shared.AST (ScopeData(..), ScopeTag(..), TypeData(..), BinderD,
  ParamData, VarData, ValData)
import Drasil.Shared.CodeType (CodeType(..))
import Drasil.Shared.State (MS, VS)

type Label = String
type Library = String

-- In relation to GOOL, the type variable r can be considered as short for "representation"

-- Functions in GOOL's interface beginning with "ext" are to be used to access items from other modules in the same program/project
-- Functions in GOOL's interface beginning with "lib" are to be used to access items from different libraries/projects

-- TODO [Brandon Bosman, 06/09/2026]: UnRepr can be removed from SharedProg
-- if we can root out its use from drasil-code

class (UnRepr r TypeData, SharedStatement r smt, FunctionSym r, InternalList r,
  VariableValue r, IndexTranslator r, TypeElim r,
  VariableElim r, MethodSym r vis smt md, ScopeSym r, BinderSym r
  ) => SharedProg r vis smt md

class (Array r, AssignStatement r smt, Argument r, BooleanExpression r,
  CommandLineArgs r, CommentStatement r smt, Comparison r,
  ControlStatement r smt, DeclStatement r smt, FuncAppStatement r smt,
  IOStatement r smt, List r smt, Literal r, MathConstant r, NumericExpression r,
  ParameterSym r, Reference r, Set r, StringStatement r smt, ValueExpression r,
  VariableValue r
  ) => SharedStatement r smt

-- Shared between OO and Procedural --

class UnRepr repr contents where
  unRepr :: repr contents -> contents

type Body = Doc

class (BlockSym r smt) => BodySym r smt where
  body           :: [MS (r Block)] -> MS (r Body)

  addComments :: Label -> MS (r Body) -> MS (r Body)

bodyStatements :: (BodySym r smt) => [MS (r smt)] -> MS (r Body)
bodyStatements :: forall (r :: * -> *) smt.
BodySym r smt =>
[MS (r smt)] -> MS (r Body)
bodyStatements [MS (r smt)]
sts = [MS (r Body)] -> MS (r Body)
forall (r :: * -> *) smt.
BodySym r smt =>
[MS (r Body)] -> MS (r Body)
body [[MS (r smt)] -> MS (r Body)
forall (r :: * -> *) smt.
BlockSym r smt =>
[MS (r smt)] -> MS (r Body)
block [MS (r smt)]
sts]

oneLiner :: (BodySym r smt) => MS (r smt) -> MS (r Body)
oneLiner :: forall (r :: * -> *) smt.
BodySym r smt =>
MS (r smt) -> MS (r Body)
oneLiner MS (r smt)
tp = [MS (r smt)] -> MS (r Body)
forall (r :: * -> *) smt.
BodySym r smt =>
[MS (r smt)] -> MS (r Body)
bodyStatements [MS (r smt)
tp]

type Block = Doc

class (StatementSym r smt) => BlockSym r smt where
  block   :: [MS (r smt)] -> MS (r Block)

class TypeSym r where
  bool          :: VS (r TypeData)
  int           :: VS (r TypeData) -- This is 32-bit signed ints except in Python,
                            -- which has unlimited precision ints; and Julia,
                            -- Which defaults to 64-bit signed ints
  float         :: VS (r TypeData)
  double        :: VS (r TypeData)
  char          :: VS (r TypeData)
  string        :: VS (r TypeData)
  infile        :: VS (r TypeData)
  outfile       :: VS (r TypeData)
  referenceType :: VS (r TypeData) -> VS (r TypeData)
  listType      :: VS (r TypeData) -> VS (r TypeData)
  setType       :: VS (r TypeData) -> VS (r TypeData)
  arrayType     :: VS (r TypeData) -> VS (r TypeData)
  innerType     :: VS (r TypeData) -> VS (r TypeData)
  funcType      :: [VS (r TypeData)] -> VS (r TypeData) -> VS (r TypeData)
  void          :: VS (r TypeData)

-- TODO [Brandon Bosman, 06/09/2026]: Think about separating GOOL and GProc implementations of this
-- | A helper function for extracting the String representation from an `r TypeData`
getTypeString :: (UnRepr r TypeData) => r TypeData -> String
getTypeString :: forall (r :: * -> *). UnRepr r TypeData => r TypeData -> String
getTypeString = TypeData -> String
typeString (TypeData -> String)
-> (r TypeData -> TypeData) -> r TypeData -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. r TypeData -> TypeData
forall (repr :: * -> *) contents.
UnRepr repr contents =>
repr contents -> contents
unRepr

class ScopeSym r where
  global :: r ScopeData -- Definite global scope
  mainFn :: r ScopeData -- Main program - either main function or global scope
  local  :: r ScopeData -- Definite local scope

type Variable = VarData
type SVariable a = VS (a Variable)

class (TypeSym r) => VariableSym r where
  -- | An instance- or function-level variable, separate from its instance (i.e. `v`, not `o.v`)
  var       :: Label -> VS (r TypeData) -> SVariable r
  -- | An instance- or function-level constant, separate from its instance (i.e. `v`, not `o.v`)
  constant  :: Label -> VS (r TypeData) -> SVariable r
  -- | An instance- or module-level variable from an external library.
  -- Given library `Lib`, variable name `v`, and variable type `t`,
  -- it performs the necessary imports and creates `Lib.v`
  extVar    :: Library -> Label -> VS (r TypeData) -> SVariable r

class (VariableSym r) => VariableElim r where
  variableName :: r Variable -> String
  variableType :: r Variable -> r TypeData

listVar :: (VariableSym r) => Label -> VS (r TypeData) -> SVariable r
listVar :: forall (r :: * -> *).
VariableSym r =>
String -> VS (r TypeData) -> SVariable r
listVar String
n VS (r TypeData)
t = String -> VS (r TypeData) -> SVariable r
forall (r :: * -> *).
VariableSym r =>
String -> VS (r TypeData) -> SVariable r
var String
n (VS (r TypeData) -> VS (r TypeData)
forall (r :: * -> *).
TypeSym r =>
VS (r TypeData) -> VS (r TypeData)
listType VS (r TypeData)
t)

listOf :: (VariableSym r) => Label -> VS (r TypeData) -> SVariable r
listOf :: forall (r :: * -> *).
VariableSym r =>
String -> VS (r TypeData) -> SVariable r
listOf = String -> VS (r TypeData) -> SVariable r
forall (r :: * -> *).
VariableSym r =>
String -> VS (r TypeData) -> SVariable r
listVar

type Value = ValData
type SValue a = VS (a Value)

class (TypeSym r) => ValueSym r where
  valueType :: r Value -> r TypeData

class (TypeSym r) => TypeElim r where
  getCodeType :: r TypeData -> CodeType

class (ValueSym r) => Argument r where
  pointerArg :: SValue r -> SValue r

class (ValueSym r) => Literal r where
  litTrue   :: SValue r
  litFalse  :: SValue r
  litChar   :: Char -> SValue r
  litDouble :: Double -> SValue r
  litFloat  :: Float -> SValue r
  litInt    :: Integer -> SValue r
  litString :: String -> SValue r
  litArray  :: VS (r TypeData) -> [SValue r] -> SValue r
  litList   :: VS (r TypeData) -> [SValue r] -> SValue r
  litSet    :: VS (r TypeData) -> [SValue r] -> SValue r

litZero :: (Literal r, TypeElim r) => VS (r TypeData) -> SValue r
litZero :: forall (r :: * -> *).
(Literal r, TypeElim r) =>
VS (r TypeData) -> SValue r
litZero VS (r TypeData)
t = do
  r TypeData
t' <- VS (r TypeData)
t
  case r TypeData -> CodeType
forall (r :: * -> *). TypeElim r => r TypeData -> CodeType
getCodeType r TypeData
t' of
    CodeType
Integer -> Integer -> SValue r
forall (r :: * -> *). Literal r => Integer -> SValue r
litInt Integer
0
    CodeType
Float -> Float -> SValue r
forall (r :: * -> *). Literal r => Float -> SValue r
litFloat Float
0
    CodeType
Double -> Double -> SValue r
forall (r :: * -> *). Literal r => Double -> SValue r
litDouble Double
0
    CodeType
_ -> String -> SValue r
forall a. HasCallStack => String -> a
error String
"litZero expects a numeric type"

class (ValueSym r) => MathConstant r where
  pi :: SValue r

class (VariableSym r, ValueSym r) => VariableValue r where
  valueOf       :: SVariable r -> SValue r

class (ValueSym r) => CommandLineArgs r where
  arg          :: Integer -> SValue r
  argsList     :: SValue r
  argExists    :: Integer -> SValue r

class (ValueSym r) => NumericExpression r where
  (#~)  :: SValue r -> SValue r
  infixl 8 #~ -- Negation
  (#/^) :: SValue r -> SValue r
  infixl 7 #/^ -- Square root
  (#|)  :: SValue r -> SValue r
  infixl 7 #| -- Absolute value
  (#+)  :: SValue r -> SValue r -> SValue r
  infixl 5 #+
  (#-)  :: SValue r -> SValue r -> SValue r
  infixl 5 #-
  (#*)  :: SValue r -> SValue r -> SValue r
  infixl 6 #*
  (#/)  :: SValue r -> SValue r -> SValue r
  infixl 6 #/
  (#%)  :: SValue r -> SValue r -> SValue r
  infixl 6 #% -- Modulo
  (#^)  :: SValue r -> SValue r -> SValue r
  infixl 7 #^ -- Exponentiation

  log    :: SValue r -> SValue r
  ln     :: SValue r -> SValue r
  exp    :: SValue r -> SValue r
  sin    :: SValue r -> SValue r
  cos    :: SValue r -> SValue r
  tan    :: SValue r -> SValue r
  csc    :: SValue r -> SValue r
  sec    :: SValue r -> SValue r
  cot    :: SValue r -> SValue r
  arcsin :: SValue r -> SValue r
  arccos :: SValue r -> SValue r
  arctan :: SValue r -> SValue r
  floor  :: SValue r -> SValue r
  ceil   :: SValue r -> SValue r

class (ValueSym r) => BooleanExpression r where
  (?!)  :: SValue r -> SValue r
  infixr 6 ?! -- Boolean 'not'
  (?&&) :: SValue r -> SValue r -> SValue r
  infixl 2 ?&&
  (?||) :: SValue r -> SValue r -> SValue r
  infixl 1 ?||

class (ValueSym r) => Comparison r where
  (?<)  :: SValue r -> SValue r -> SValue r
  infixl 4 ?<
  (?<=) :: SValue r -> SValue r -> SValue r
  infixl 4 ?<=
  (?>)  :: SValue r -> SValue r -> SValue r
  infixl 4 ?>
  (?>=) :: SValue r -> SValue r -> SValue r
  infixl 4 ?>=
  (?==) :: SValue r -> SValue r -> SValue r
  infixl 3 ?==
  (?!=) :: SValue r -> SValue r -> SValue r
  infixl 3 ?!=

type NamedArgs r = [(SVariable r, SValue r)]
-- Function call with both positional and named arguments
type MixedCall r = Label -> VS (r TypeData) -> [SValue r] -> NamedArgs r -> SValue r
-- Constructor call with both positional and named arguments
type MixedCtorCall r = VS (r TypeData) -> [SValue r] -> NamedArgs r -> SValue r
-- Function call with only positional arguments
type PosCall r = Label -> VS (r TypeData) -> [SValue r] -> SValue r
-- Constructor call with only positional arguments
type PosCtorCall r = VS (r TypeData) -> [SValue r] -> SValue r

type VSBinder a = VS (a BinderD)

class (TypeSym r) => BinderSym r where
  binder :: Label -> VS (r TypeData) -> VSBinder r

class (BinderSym r) => BinderElim r where
  binderName :: r BinderD -> String
  binderType :: r BinderD -> r TypeData

-- for values that can include expressions
class (VariableSym r, ValueSym r) => ValueExpression r where
  -- An inline if-statement, aka the ternary operator.  Inputs:
  -- Condition, True-value, False-value
  inlineIf     :: SValue r -> SValue r -> SValue r -> SValue r

  funcAppMixedArgs     ::            MixedCall r
  extFuncAppMixedArgs  :: Library -> MixedCall r
  libFuncAppMixedArgs  :: Library -> MixedCall r

  lambda :: [VSBinder r] -> SValue r -> SValue r

  notNull :: SValue r -> SValue r

funcApp          :: (ValueExpression r) => PosCall r
funcApp :: forall (r :: * -> *). ValueExpression r => PosCall r
funcApp String
n VS (r TypeData)
t [SValue r]
vs = MixedCall r
forall (r :: * -> *). ValueExpression r => MixedCall r
funcAppMixedArgs String
n VS (r TypeData)
t [SValue r]
vs []

funcAppNamedArgs :: (ValueExpression r) => Label -> VS (r TypeData) ->
  NamedArgs r -> SValue r
funcAppNamedArgs :: forall (r :: * -> *).
ValueExpression r =>
String -> VS (r TypeData) -> NamedArgs r -> SValue r
funcAppNamedArgs String
n VS (r TypeData)
t = MixedCall r
forall (r :: * -> *). ValueExpression r => MixedCall r
funcAppMixedArgs String
n VS (r TypeData)
t []

extFuncApp       :: (ValueExpression r) => Library -> PosCall r
extFuncApp :: forall (r :: * -> *). ValueExpression r => String -> PosCall r
extFuncApp String
l String
n VS (r TypeData)
t [SValue r]
vs = String -> MixedCall r
forall (r :: * -> *). ValueExpression r => String -> MixedCall r
extFuncAppMixedArgs String
l String
n VS (r TypeData)
t [SValue r]
vs []

libFuncApp       :: (ValueExpression r) => Library -> PosCall r
libFuncApp :: forall (r :: * -> *). ValueExpression r => String -> PosCall r
libFuncApp String
l String
n VS (r TypeData)
t [SValue r]
vs = String -> MixedCall r
forall (r :: * -> *). ValueExpression r => String -> MixedCall r
libFuncAppMixedArgs String
l String
n VS (r TypeData)
t [SValue r]
vs []

exists :: (ValueExpression r) => SValue r -> SValue r
exists :: forall (r :: * -> *). ValueExpression r => SValue r -> SValue r
exists = SValue r -> SValue r
forall (r :: * -> *). ValueExpression r => SValue r -> SValue r
notNull

class (ValueSym r) => IndexTranslator r where
  -- | Does any necessary conversions from GOOL's zero-indexed assumptions to
  --   the target language's assumptions
  intToIndex :: SValue r -> SValue r
  -- | Does any necessary conversions from the target language's indexing
  --   assumptions assumptions to GOOL's zero-indexed assumptions
  indexToInt :: SValue r -> SValue r
  -- | Finds the size of a list.
  --   Arguments are: List

class (TypeSym r, ValueSym r) => Reference r where
  -- | Given a value, convert it to a reference to that value
  makeRef :: SValue r -> SValue r
  -- | Given a value that may be a reference type,
  -- apply any necessary dereference operation.
  maybeDeref :: SValue r -> SValue r

class (IndexTranslator r) => Array r where
  -- TODO [Brandon Bosman, 05/19/2026]: Change return type to SValue
  -- | Given array `a` and index `i`, creates `a[i]`
  arrayElem :: SValue r -> SValue r -> SVariable r
  -- TODO [Brandon Bosman, 06/03/2026]: Consider switching to a polymorphic `length`
  -- for Array, List, and Set
  -- | Given an array, return its length
  arrayLength :: SValue r -> SValue r
  -- TODO [Brandon Bosman, 05/21/2026]: Consider switching this to a polymorphic `copy`,
  -- more like how `print` currently works
  -- | Given a source array, create a (shallow) copy of it
  arrayCopy :: SValue r -> SValue r

class (IndexTranslator r, StatementSym r smt) => List r smt where
  listSize   :: SValue r -> SValue r
  -- | Inserts a value into a list.
  --   Arguments are: List, Index, Value
  listAdd    :: SValue r -> SValue r -> SValue r -> MS (r smt)
  -- | Appens a value to a list.
  --   Arguments are: List, Value
  listAppend :: SValue r -> SValue r -> MS (r smt)
  -- | Gets the value of an index of a list.
  --   Arguments are: List, Index
  listAccess :: SValue r -> SValue r -> SValue r
  -- | Sets the value of an index of a list.
  --   Arguments are: List, Index, Value
  listSet    :: SValue r -> SValue r -> SValue r -> MS (r smt)
  -- | Finds the index of the first occurrence of a value in a list.
  --   Arguments are: List, Value
  indexOf :: SValue r -> SValue r -> SValue r

class (ValueSym r) => Set r where
  -- | Checks membership
  -- Arguments are: Set, Value
  contains :: SValue r -> SValue r -> SValue r
  -- | Inserts a value into a set
  -- Arguments are: Set, Value
  setAdd :: SValue r -> SValue r -> SValue r -- TODO [Brandon Bosman, 06/24/2026]: Make this a Statement
  -- | Removes a value from a set
  -- Arguments are: Set, Value
  setRemove :: SValue r -> SValue r -> SValue r -- TODO [Brandon Bosman, 06/24/2026]: Make this a SStatement
  -- | Removes a value from a set
  -- Arguments are: Set, Set
  setUnion :: SValue r -> SValue r -> SValue r -- TODO [Brandon Bosman, 06/24/2026]: See if we should make this a Statement

-- | Vector operations for languages with native vector support (e.g. MATLAB,
--   Julia). Expression-based: every operation takes and returns 'SValue's, so
--   operations compose like math (e.g. @vecAdd (vecScale s a) b@).
--   Vectors have their own 'vecType' and 'litVec' so callers don't depend on
--   how vectors are represented; these default to 'listType' and 'litList'.
class (IndexTranslator r, Literal r) => NativeVector r where
  -- | The type of a vector with the given element type.
  --   Defaults to 'listType'; a language may override it to use a distinct
  --   vector representation.
  vecType :: VS (r TypeData) -> VS (r TypeData)
  vecType = VS (r TypeData) -> VS (r TypeData)
forall (r :: * -> *).
TypeSym r =>
VS (r TypeData) -> VS (r TypeData)
listType
  -- | A vector literal with the given element type and elements.
  --   Defaults to 'litList'.
  litVec :: VS (r TypeData) -> [SValue r] -> SValue r
  litVec = VS (r TypeData) -> [SValue r] -> SValue r
forall (r :: * -> *).
Literal r =>
VS (r TypeData) -> [SValue r] -> SValue r
litList
  -- | Scales a vector by a scalar.
  --   Arguments are: Scalar, Vector
  vecScale :: SValue r -> SValue r -> SValue r
  -- | Adds two vectors elementwise.
  --   Arguments are: Vector, Vector
  vecAdd :: SValue r -> SValue r -> SValue r
  -- | Gets the element of a vector at an index.
  --   Arguments are: Vector, Index
  vecIndex :: SValue r -> SValue r -> SValue r
  -- | Dot product of two vectors (returns a scalar).
  --   Arguments are: Vector, Vector
  vecDot :: SValue r -> SValue r -> SValue r
  -- | Euclidean norm (magnitude) of a vector (returns a scalar).
  --   Argument is: Vector
  vecMag :: SValue r -> SValue r
  -- | Unit vector in the direction of a vector (returns a vector).
  --   Argument is: Vector
  vecUnit :: SValue r -> SValue r

class (ValueSym r) => InternalList r where
  listSlice'      :: Maybe (SValue r) -> Maybe (SValue r) -> Maybe (SValue r)
    -> SVariable r -> SValue r -> MS (r Block)

-- | Creates a slice of a list and assigns it to a variable.
--   Arguments are:
--   Variable to assign
--   List to read from
--   (optional) Start index inclusive.
--      (if Nothing, then list start if step > 0, list end if step < 0)
--   (optional) End index exclusive.
--      (if Nothing, then list end if step > 0, list start if step > 0)
--   (optional) Step (if Nothing, then defaults to 1)
listSlice :: (InternalList r) => SVariable r -> SValue r ->
  Maybe (SValue r) -> Maybe (SValue r) -> Maybe (SValue r) -> MS (r Block)
listSlice :: forall (r :: * -> *).
InternalList r =>
SVariable r
-> SValue r
-> Maybe (SValue r)
-> Maybe (SValue r)
-> Maybe (SValue r)
-> MS (r Body)
listSlice SVariable r
vnew SValue r
vold Maybe (SValue r)
b Maybe (SValue r)
e Maybe (SValue r)
tp = Maybe (SValue r)
-> Maybe (SValue r)
-> Maybe (SValue r)
-> SVariable r
-> SValue r
-> MS (r Body)
forall (r :: * -> *).
InternalList r =>
Maybe (SValue r)
-> Maybe (SValue r)
-> Maybe (SValue r)
-> SVariable r
-> SValue r
-> MS (r Body)
listSlice' Maybe (SValue r)
b Maybe (SValue r)
e Maybe (SValue r)
tp SVariable r
vnew SValue r
vold

listIndexExists :: (List r smt, Comparison r) => SValue r -> SValue r -> SValue r
listIndexExists :: forall (r :: * -> *) smt.
(List r smt, Comparison r) =>
SValue r -> SValue r -> SValue r
listIndexExists SValue r
lst SValue r
index = SValue r -> SValue r
forall (r :: * -> *) smt. List r smt => SValue r -> SValue r
listSize SValue r
lst SValue r -> SValue r -> SValue r
forall (r :: * -> *).
Comparison r =>
SValue r -> SValue r -> SValue r
?> SValue r
index

at :: (List r smt) => SValue r -> SValue r -> SValue r
at :: forall (r :: * -> *) smt.
List r smt =>
SValue r -> SValue r -> SValue r
at = SValue r -> SValue r -> SValue r
forall (r :: * -> *) smt.
List r smt =>
SValue r -> SValue r -> SValue r
listAccess

class (ValueSym r) => StatementSym r smt | r -> smt where
  valStmt :: SValue r -> MS (r smt) -- converts value to statement
  emptyStmt :: MS (r smt)
  multi     :: [MS (r smt)] -> MS (r smt)

class (VariableSym r, StatementSym r smt) => AssignStatement r smt where
  (&-=)  :: SVariable r -> SValue r -> MS (r smt)
  infixl 1 &-=
  (&+=)  :: SVariable r -> SValue r -> MS (r smt)
  infixl 1 &+=
  (&++)  :: SVariable r -> MS (r smt)
  infixl 8 &++
  (&--)  :: SVariable r -> MS (r smt)
  infixl 8 &--

  assign :: SVariable r -> SValue r -> MS (r smt)

(&=) :: (AssignStatement r smt) => SVariable r -> SValue r -> MS (r smt)
infixr 1 &=
&= :: forall (r :: * -> *) smt.
AssignStatement r smt =>
SVariable r -> SValue r -> MS (r smt)
(&=) = SVariable r -> SValue r -> MS (r smt)
forall (r :: * -> *) smt.
AssignStatement r smt =>
SVariable r -> SValue r -> MS (r smt)
assign

class (VariableSym r, StatementSym r smt, ScopeSym r) => DeclStatement r smt where
  -- | Declare a variable without giving it a value.
  -- Not for use with arrays; use `arrayDec` instead.
  varDec       :: SVariable r -> r ScopeData -> MS (r smt)
  -- | Declare a variable and give it a value.
  -- Not for use with arrays; use `arrayDecDef` instead.
  varDecDef    :: SVariable r -> r ScopeData -> SValue r -> MS (r smt)
  -- First argument is size of the list
  listDec      :: Integer -> SVariable r -> r ScopeData -> MS (r smt)
  listDecDef   :: SVariable r -> r ScopeData -> [SValue r] -> MS (r smt)
  setDec       :: SVariable r -> r ScopeData -> MS (r smt)
  setDecDef    :: SVariable r -> r ScopeData -> SValue r -> MS (r smt)
  -- Takes the size of the aray, the default value to fill the array with,
  -- the variable to store the array in, and the scope of the variable.
  arrayDec     :: Integer -> SValue r -> SVariable r -> r ScopeData -> MS (r smt)
  arrayDecDef  :: SVariable r -> r ScopeData -> [SValue r] -> MS (r smt)
  constDecDef  :: SVariable r -> r ScopeData -> SValue r -> MS (r smt)
  funcDecDef   :: SVariable r -> r ScopeData -> [SVariable r] -> MS (r Body)
    -> MS (r smt)

class (VariableSym r, StatementSym r smt) => IOStatement r smt where
  print      :: SValue r -> MS (r smt)
  printLn    :: SValue r -> MS (r smt)
  printStr   :: String -> MS (r smt)
  printStrLn :: String -> MS (r smt)

  -- First argument is file handle, second argument is value to print
  printFile      :: SValue r -> SValue r -> MS (r smt)
  printFileLn    :: SValue r -> SValue r -> MS (r smt)
  printFileStr   :: SValue r -> String -> MS (r smt)
  printFileStrLn :: SValue r -> String -> MS (r smt)

  getInput         :: SVariable r -> MS (r smt)
  discardInput     :: MS (r smt)
  getFileInput     :: SValue r -> SVariable r -> MS (r smt)
  discardFileInput :: SValue r -> MS (r smt)

  openFileR :: SVariable r -> SValue r -> MS (r smt)
  openFileW :: SVariable r -> SValue r -> MS (r smt)
  openFileA :: SVariable r -> SValue r -> MS (r smt)
  closeFile :: SValue r -> MS (r smt)

  getFileInputLine :: SValue r -> SVariable r -> MS (r smt)
  discardFileLine  :: SValue r -> MS (r smt)
  getFileInputAll  :: SValue r -> SVariable r -> MS (r smt)

class (VariableSym r, StatementSym r smt) => StringStatement r smt where
  -- Parameters are: char to split on, variable to store result in, string to split
  stringSplit :: Char -> SVariable r -> SValue r -> MS (r smt)
  stringListVals  :: [SVariable r] -> SValue r -> MS (r smt)
  -- Given a list of variables and a value containing a list of strings,
  -- assign the ith element of hte list of strings into the ith variable
  stringListLists :: [SVariable r] -> SValue r -> MS (r smt)

class (ValueSym r) => FunctionSym r where

-- The three lists are inputs, outputs, and both, respectively
type InOutCall r smt = Label -> [SValue r] -> [SVariable r] -> [SVariable r] ->
  MS (r smt)

class (VariableSym r, StatementSym r smt) => FuncAppStatement r smt where
  inOutCall    ::            InOutCall r smt
  extInOutCall :: Library -> InOutCall r smt

class (StatementSym r smt) => CommentStatement r smt where
  comment :: String -> MS (r smt)

class (BodySym r smt, VariableSym r) => ControlStatement r smt where
  break :: MS (r smt)
  continue :: MS (r smt)

  returnStmt :: SValue r -> MS (r smt)

  throw :: Label -> MS (r smt)

  -- | String of if-else statements.
  --   Arguments: List of predicates and bodies (if this then that),
  --   Body for else branch
  ifCond     :: [(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)
  switch     :: SValue r -> [(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)

  ifExists :: SValue r -> MS (r Body) -> MS (r Body) -> MS (r smt)

  for      :: MS (r smt) -> SValue r -> MS (r smt) -> MS (r Body) ->
    MS (r smt)
  -- Iterator variable, start value, end value, step value, loop body
  forRange :: SVariable r -> SValue r -> SValue r -> SValue r -> MS (r Body) ->
    MS (r smt)
  forEach  :: SVariable r -> SValue r -> MS (r Body) -> MS (r smt)
  while    :: SValue r -> MS (r Body) -> MS (r smt)

  tryCatch :: MS (r Body) -> MS (r Body) -> MS (r smt)

  assert :: SValue r -> SValue r -> MS (r smt)

ifNoElse :: (ControlStatement r smt) => [(SValue r, MS (r Body))] -> MS (r smt)
ifNoElse :: forall (r :: * -> *) smt.
ControlStatement r smt =>
[(SValue r, MS (r Body))] -> MS (r smt)
ifNoElse [(SValue r, MS (r Body))]
bs = [(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)
forall (r :: * -> *) smt.
ControlStatement r smt =>
[(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)
ifCond [(SValue r, MS (r Body))]
bs (MS (r Body) -> MS (r smt)) -> MS (r Body) -> MS (r smt)
forall a b. (a -> b) -> a -> b
$ [MS (r Body)] -> MS (r Body)
forall (r :: * -> *) smt.
BodySym r smt =>
[MS (r Body)] -> MS (r Body)
body []

switchAsIf :: (ControlStatement r smt, Comparison r) => SValue r ->
  [(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)
switchAsIf :: forall (r :: * -> *) smt.
(ControlStatement r smt, Comparison r) =>
SValue r -> [(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)
switchAsIf SValue r
v = [(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)
forall (r :: * -> *) smt.
ControlStatement r smt =>
[(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt)
ifCond ([(SValue r, MS (r Body))] -> MS (r Body) -> MS (r smt))
-> ([(SValue r, MS (r Body))] -> [(SValue r, MS (r Body))])
-> [(SValue r, MS (r Body))]
-> MS (r Body)
-> MS (r smt)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((SValue r, MS (r Body)) -> (SValue r, MS (r Body)))
-> [(SValue r, MS (r Body))] -> [(SValue r, MS (r Body))]
forall a b. (a -> b) -> [a] -> [b]
map ((SValue r -> SValue r)
-> (SValue r, MS (r Body)) -> (SValue r, MS (r Body))
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (SValue r
v SValue r -> SValue r -> SValue r
forall (r :: * -> *).
Comparison r =>
SValue r -> SValue r -> SValue r
?==))

class VisibilitySym r vis | r -> vis where
  private :: r vis
  public  :: r vis

class (VariableSym r) => ParameterSym r where
  param :: SVariable r -> MS (r ParamData)
  pointerParam :: SVariable r -> MS (r ParamData)

-- The three lists are inputs, outputs, and both, respectively
type InOutFunc r md = [SVariable r] -> [SVariable r] -> [SVariable r] ->
  MS (r Body) -> MS (r md)
-- Parameters are: brief description of function, input descriptions and
-- variables, output descriptions and variables, descriptions and variables
-- for parameters that are both input and output, function body
type DocInOutFunc r md = String -> [(String, SVariable r)] ->
  [(String, SVariable r)] -> [(String, SVariable r)] -> MS (r Body) -> MS (r md)

class (BodySym r smt, ParameterSym r, VisibilitySym r vis) => MethodSym r vis smt md | r -> md
  where
  docMain :: MS (r Body) -> MS (r md)

  function :: Label -> r vis -> VS (r TypeData) -> [MS (r ParamData)] ->
    MS (r Body) -> MS (r md)
  mainFunction  :: MS (r Body) -> MS (r md)
  -- Parameters are: function description, parameter descriptions,
  --   return value description if applicable, function
  docFunc :: String -> [String] -> Maybe String -> MS (r md) -> MS (r md)

  inOutFunc :: Label -> r vis -> InOutFunc r md
  docInOutFunc :: Label -> r vis -> DocInOutFunc r md

-- Utility

convType :: (TypeSym r) => CodeType -> VS (r TypeData)
convType :: forall (r :: * -> *). TypeSym r => CodeType -> VS (r TypeData)
convType CodeType
Boolean = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
bool
convType CodeType
Integer = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
int
convType CodeType
Float = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
float
convType CodeType
Double = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
double
convType CodeType
Char = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
char
convType CodeType
String = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
string
convType (Reference CodeType
t) = VS (r TypeData) -> VS (r TypeData)
forall (r :: * -> *).
TypeSym r =>
VS (r TypeData) -> VS (r TypeData)
referenceType (CodeType -> VS (r TypeData)
forall (r :: * -> *). TypeSym r => CodeType -> VS (r TypeData)
convType CodeType
t)
convType (List CodeType
t) = VS (r TypeData) -> VS (r TypeData)
forall (r :: * -> *).
TypeSym r =>
VS (r TypeData) -> VS (r TypeData)
listType (CodeType -> VS (r TypeData)
forall (r :: * -> *). TypeSym r => CodeType -> VS (r TypeData)
convType CodeType
t)
convType (Set CodeType
t) = VS (r TypeData) -> VS (r TypeData)
forall (r :: * -> *).
TypeSym r =>
VS (r TypeData) -> VS (r TypeData)
setType (CodeType -> VS (r TypeData)
forall (r :: * -> *). TypeSym r => CodeType -> VS (r TypeData)
convType CodeType
t)
convType (Array CodeType
t) = VS (r TypeData) -> VS (r TypeData)
forall (r :: * -> *).
TypeSym r =>
VS (r TypeData) -> VS (r TypeData)
arrayType (CodeType -> VS (r TypeData)
forall (r :: * -> *). TypeSym r => CodeType -> VS (r TypeData)
convType CodeType
t)
convType (Func [CodeType]
ps CodeType
r) = [VS (r TypeData)] -> VS (r TypeData) -> VS (r TypeData)
forall (r :: * -> *).
TypeSym r =>
[VS (r TypeData)] -> VS (r TypeData) -> VS (r TypeData)
funcType ((CodeType -> VS (r TypeData)) -> [CodeType] -> [VS (r TypeData)]
forall a b. (a -> b) -> [a] -> [b]
map CodeType -> VS (r TypeData)
forall (r :: * -> *). TypeSym r => CodeType -> VS (r TypeData)
convType [CodeType]
ps) (CodeType -> VS (r TypeData)
forall (r :: * -> *). TypeSym r => CodeType -> VS (r TypeData)
convType CodeType
r)
convType CodeType
Void = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
void
convType CodeType
InFile = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
infile
convType CodeType
OutFile = VS (r TypeData)
forall (r :: * -> *). TypeSym r => VS (r TypeData)
outfile
convType (Object String
_) = String -> VS (r TypeData)
forall a. HasCallStack => String -> a
error String
"Objects not supported"

convScope :: (ScopeSym r) => ScopeData -> r ScopeData
convScope :: forall (r :: * -> *). ScopeSym r => ScopeData -> r ScopeData
convScope (SD {scopeTag :: ScopeData -> ScopeTag
scopeTag = ScopeTag
Global}) = r ScopeData
forall (r :: * -> *). ScopeSym r => r ScopeData
global
convScope (SD {scopeTag :: ScopeData -> ScopeTag
scopeTag = ScopeTag
Local}) = r ScopeData
forall (r :: * -> *). ScopeSym r => r ScopeData
local