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

module Drasil.Shared.InterfaceCommon (
  -- Types
  Label, Library, Body, Block, Variable, Value, NamedArgs, MixedCall,
  MixedCtorCall, PosCall, PosCtorCall, InOutCall, InOutFunc, DocInOutFunc,
  -- Typeclasses
  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(..), ListStatement(..),
  Set(..), NativeVector(..), InternalList(..), listSlice, listIndexExists, at,
  EmptyStatement(..), MultiStatement(..), ValueStatement(..),
  AssignStatement(..), (&=), DeclStatement(..), PrintConsole(..),
  ReadConsole(..), FileHandling(..), PrintFile(..), ReadFile(..),
  StringStatement(..), 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(..), 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

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

type Body = Doc

-- | Class for representing a `Body`, which is basically a lexical scope of code.
-- Examples include a function body, the branch(es) of an `if`-statement, etc.
class BodySym r  bod block | r -> bod block where
  -- | Given a list of `block`s, create a `Body` of them.
  body           :: [MS (r block)] -> MS (r bod)
  -- | Given a comment and a body, add the comment as a header for the body.
  addComments :: Label -> MS (r bod) -> MS (r bod)

bodyStatements
  :: (BlockSym r block stmt, BodySym r bod block) => [MS (r stmt)] -> MS (r bod)
bodyStatements :: forall {k} (r :: k -> *) (block :: k) (stmt :: k) (bod :: k).
(BlockSym r block stmt, BodySym r bod block) =>
[MS (r stmt)] -> MS (r bod)
bodyStatements [MS (r stmt)]
sts = [MS (r block)] -> MS (r bod)
forall {k} (r :: k -> *) (bod :: k) (block :: k).
BodySym r bod block =>
[MS (r block)] -> MS (r bod)
body [[MS (r stmt)] -> MS (r block)
forall {k} (r :: k -> *) (block :: k) (stmt :: k).
BlockSym r block stmt =>
[MS (r stmt)] -> MS (r block)
block [MS (r stmt)]
sts]

oneLiner
  :: (BlockSym r block stmt, BodySym r bod block) => MS (r stmt) -> MS (r bod)
oneLiner :: forall {k} (r :: k -> *) (block :: k) (stmt :: k) (bod :: k).
(BlockSym r block stmt, BodySym r bod block) =>
MS (r stmt) -> MS (r bod)
oneLiner MS (r stmt)
tp = [MS (r stmt)] -> MS (r bod)
forall {k} (r :: k -> *) (block :: k) (stmt :: k) (bod :: k).
(BlockSym r block stmt, BodySym r bod block) =>
[MS (r stmt)] -> MS (r bod)
bodyStatements [MS (r stmt)
tp]

type Block = Doc

-- | Class for representing a `block` of code.
-- A `block` is a series of statements grouped together,
-- not for use by the compiler/interpreter
-- but to improve readability of the generated code.
-- See the bottom of page 2 of Brook's GOOL paper from 2020 for more details.
class BlockSym r block stmt | r -> block stmt where
  block   :: [MS (r stmt)] -> MS (r block)

-- | Class for representing a type.
class TypeSym r typ | r -> typ where
  bool          :: VS (r typ)
  int           :: VS (r typ) -- 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 typ)
  double        :: VS (r typ)
  char          :: VS (r typ)
  string        :: VS (r typ)
  infile        :: VS (r typ)
  outfile       :: VS (r typ)
  referenceType :: VS (r typ) -> VS (r typ)
  listType      :: VS (r typ) -> VS (r typ)
  setType       :: VS (r typ) -> VS (r typ)
  arrayType     :: VS (r typ) -> VS (r typ)
  innerType     :: VS (r typ) -> VS (r typ)
  funcType      :: [VS (r typ)] -> VS (r typ) -> VS (r typ)
  void          :: VS (r typ)

-- 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 typ`
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

-- TODO [Brandon Bosman, 07/22/2026]: rework this so that GOOL handles scopes automatically
-- | Class for representing the lexical scope of a variable.
-- Currently only differentiates `global` and `local`,
-- allowing individual renderers to define which of them the main function is.
class ScopeSym r scope | r -> scope where
  global :: r scope -- Definite global scope
  mainFn :: r scope -- Main program - either main function or global scope
  local  :: r scope -- Definite local scope

type Variable = VarData

-- | Class for representing variables.
class VariableSym r var typ | r -> var typ where
  -- | An instance- or function-level variable, separate from its instance (i.e. `v`, not `o.v`)
  var       :: Label -> VS (r typ) -> VS (r var)
  -- | An instance- or function-level constant, separate from its instance (i.e. `v`, not `o.v`)
  constant  :: Label -> VS (r typ) -> VS (r var)
  -- | 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 typ) -> VS (r var)

class VariableElim r var typ | r -> var typ where
  variableName :: r var -> String
  variableType :: r var -> r typ

listVar
  :: (TypeSym r typ, VariableSym r var typ)
  => Label -> VS (r typ) -> VS (r var)
listVar :: forall {k} (r :: k -> *) (typ :: k) (var :: k).
(TypeSym r typ, VariableSym r var typ) =>
String -> VS (r typ) -> VS (r var)
listVar String
n VS (r typ)
t = String -> VS (r typ) -> VS (r var)
forall {k} (r :: k -> *) (var :: k) (typ :: k).
VariableSym r var typ =>
String -> VS (r typ) -> VS (r var)
var String
n (VS (r typ) -> VS (r typ)
forall {k} (r :: k -> *) (typ :: k).
TypeSym r typ =>
VS (r typ) -> VS (r typ)
listType VS (r typ)
t)

listOf
  :: (TypeSym r typ, VariableSym r var typ)
  => Label -> VS (r typ) -> VS (r var)
listOf :: forall {k} (r :: k -> *) (typ :: k) (var :: k).
(TypeSym r typ, VariableSym r var typ) =>
String -> VS (r typ) -> VS (r var)
listOf = String -> VS (r typ) -> VS (r var)
forall {k} (r :: k -> *) (typ :: k) (var :: k).
(TypeSym r typ, VariableSym r var typ) =>
String -> VS (r typ) -> VS (r var)
listVar

type Value = ValData

-- | Class for representing a value.
class ValueSym r val typ | r -> val typ where
  valueType :: r val -> r typ

class TypeElim r typ | r -> typ where
  getCodeType :: r typ -> CodeType

class Argument r val | r -> val where
  pointerArg :: VS (r val) -> VS (r val)

class Literal r val typ | r -> val typ where
  litTrue   :: VS (r val)
  litFalse  :: VS (r val)
  litChar   :: Char -> VS (r val)
  litDouble :: Double -> VS (r val)
  litFloat  :: Float -> VS (r val)
  litInt    :: Integer -> VS (r val)
  litString :: String -> VS (r val)
  litArray  :: VS (r typ) -> [VS (r val)] -> VS (r val)
  litList   :: VS (r typ) -> [VS (r val)] -> VS (r val)
  litSet    :: VS (r typ) -> [VS (r val)] -> VS (r val)

litZero :: (Literal r val typ, TypeElim r typ) => VS (r typ) -> VS (r val)
litZero :: forall {k} (r :: k -> *) (val :: k) (typ :: k).
(Literal r val typ, TypeElim r typ) =>
VS (r typ) -> VS (r val)
litZero VS (r typ)
t = do
  t' <- VS (r typ)
t
  case getCodeType t' of
    CodeType
Integer -> Integer -> StateT ValueState Identity (r val)
forall {k} (r :: k -> *) (val :: k) (typ :: k).
Literal r val typ =>
Integer -> VS (r val)
litInt Integer
0
    CodeType
Float -> Float -> StateT ValueState Identity (r val)
forall {k} (r :: k -> *) (val :: k) (typ :: k).
Literal r val typ =>
Float -> VS (r val)
litFloat Float
0
    CodeType
Double -> Double -> StateT ValueState Identity (r val)
forall {k} (r :: k -> *) (val :: k) (typ :: k).
Literal r val typ =>
Double -> VS (r val)
litDouble Double
0
    CodeType
_ -> String -> StateT ValueState Identity (r val)
forall a. HasCallStack => String -> a
error String
"litZero expects a numeric type"

class MathConstant r val | r -> val where
  pi :: VS (r val)

class VariableValue r var val | r -> var val where
  valueOf       :: VS (r var) -> VS (r val)

class CommandLineArgs r val | r -> val where
  arg          :: Integer -> VS (r val)
  argsList     :: VS (r val)
  argExists    :: Integer -> VS (r val)

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

  log    :: VS (r val) -> VS (r val)
  ln     :: VS (r val) -> VS (r val)
  exp    :: VS (r val) -> VS (r val)
  sin    :: VS (r val) -> VS (r val)
  cos    :: VS (r val) -> VS (r val)
  tan    :: VS (r val) -> VS (r val)
  csc    :: VS (r val) -> VS (r val)
  sec    :: VS (r val) -> VS (r val)
  cot    :: VS (r val) -> VS (r val)
  arcsin :: VS (r val) -> VS (r val)
  arccos :: VS (r val) -> VS (r val)
  arctan :: VS (r val) -> VS (r val)
  floor  :: VS (r val) -> VS (r val)
  ceil   :: VS (r val) -> VS (r val)

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

class Comparison r val | r -> val where
  (?<)  :: VS (r val) -> VS (r val) -> VS (r val)
  infixl 4 ?<
  (?<=) :: VS (r val) -> VS (r val) -> VS (r val)
  infixl 4 ?<=
  (?>)  :: VS (r val) -> VS (r val) -> VS (r val)
  infixl 4 ?>
  (?>=) :: VS (r val) -> VS (r val) -> VS (r val)
  infixl 4 ?>=
  (?==) :: VS (r val) -> VS (r val) -> VS (r val)
  infixl 3 ?==
  (?!=) :: VS (r val) -> VS (r val) -> VS (r val)
  infixl 3 ?!=

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

-- | A class for representing a binder, i.e. the binding of a variable name
-- to a type, scope, etc.
-- As of July 2026, integration of this typeclass is still WIP, blocked
-- by issues with our variable map.
class BinderSym r binder typ | r -> binder typ where
  binder :: Label -> VS (r typ) -> VS (r binder)

class BinderElim r binder typ | r -> binder typ where
  binderName :: r binder -> String
  binderType :: r binder -> r typ

-- | A class for representing values that can include expressions
class ValueExpression r var val binder typ | r -> var val binder typ where
  -- An inline if-statement, aka the ternary operator.  Inputs:
  -- Condition, True-value, False-value
  inlineIf     :: VS (r val) -> VS (r val) -> VS (r val) -> VS (r val)

  funcAppMixedArgs     ::            MixedCall r var val typ
  extFuncAppMixedArgs  :: Library -> MixedCall r var val typ
  libFuncAppMixedArgs  :: Library -> MixedCall r var val typ

  lambda :: [VS (r binder)] -> VS (r val) -> VS (r val)

  notNull :: VS (r val) -> VS (r val)

funcApp          :: (ValueExpression r var val binder typ) => PosCall r val typ
funcApp :: forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
PosCall r val typ
funcApp String
n VS (r typ)
t [VS (r val)]
vs = MixedCall r var val typ
forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
MixedCall r var val typ
funcAppMixedArgs String
n VS (r typ)
t [VS (r val)]
vs []

funcAppNamedArgs
  :: (ValueExpression r var val binder typ)
  => Label -> VS (r typ) -> NamedArgs r var val -> VS (r val)
funcAppNamedArgs :: forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
String -> VS (r typ) -> NamedArgs r var val -> VS (r val)
funcAppNamedArgs String
n VS (r typ)
t = MixedCall r var val typ
forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
MixedCall r var val typ
funcAppMixedArgs String
n VS (r typ)
t []

extFuncApp :: (ValueExpression r var val binder typ) => Library -> PosCall r val typ
extFuncApp :: forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
String -> PosCall r val typ
extFuncApp String
l String
n VS (r typ)
t [VS (r val)]
vs = String -> MixedCall r var val typ
forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
String -> MixedCall r var val typ
extFuncAppMixedArgs String
l String
n VS (r typ)
t [VS (r val)]
vs []

libFuncApp :: (ValueExpression r var val binder typ) => Library -> PosCall r val typ
libFuncApp :: forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
String -> PosCall r val typ
libFuncApp String
l String
n VS (r typ)
t [VS (r val)]
vs = String -> MixedCall r var val typ
forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
String -> MixedCall r var val typ
libFuncAppMixedArgs String
l String
n VS (r typ)
t [VS (r val)]
vs []

exists :: (ValueExpression r var val binder typ) => VS (r val) -> VS (r val)
exists :: forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
VS (r val) -> VS (r val)
exists = VS (r val) -> VS (r val)
forall {k} (r :: k -> *) (var :: k) (val :: k) (binder :: k)
       (typ :: k).
ValueExpression r var val binder typ =>
VS (r val) -> VS (r val)
notNull

-- | Helper class for representing the conversion between integers and array indices.
-- GOOL is 0-indexed, so languages like Julia that are not 0-indexed
-- need to convert between integers and indices.
class IndexTranslator r val | r -> val where
  -- | Does any necessary conversions from GOOL's zero-indexed assumptions to
  --   the target language's assumptions
  intToIndex :: VS (r val) -> VS (r val)
  -- | Does any necessary conversions from the target language's indexing
  --   assumptions assumptions to GOOL's zero-indexed assumptions
  indexToInt :: VS (r val) -> VS (r val)

-- | A class for representing references.
-- By "reference" we basically mean "C++ pointer" or "OCaml reference".
class Reference r val | r -> val where
  -- | Given a value, convert it to a reference to that value
  makeRef :: VS (r val) -> VS (r val)
  -- | Given a value that may be a reference type,
  -- apply any necessary dereference operation.
  maybeDeref :: VS (r val) -> VS (r val)

class Array r var val | r -> var val where
  -- TODO [Brandon Bosman, 05/19/2026]: Change return type to VS val
  -- | Given array `a` and index `i`, creates `a[i]`
  arrayElem :: VS (r val) -> VS (r val) -> VS (r var)
  -- TODO [Brandon Bosman, 06/03/2026]: Consider switching to a polymorphic `length`
  -- for Array, List, and Set
  -- | Given an array, return its length
  arrayLength :: VS (r val) -> VS (r val)
  -- 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 :: VS (r val) -> VS (r val)

class List r val | r -> val where
  -- | Finds the size of a list.
  --   Arguments are: List
  listSize   :: VS (r val) -> VS (r val)
  -- | Gets the value of an index of a list.
  --   Arguments are: List, Index
  listAccess :: VS (r val) -> VS (r val) -> VS (r val)
  -- | Finds the index of the first occurrence of a value in a list.
  --   Arguments are: List, val
  indexOf :: VS (r val) -> VS (r val) -> VS (r val)

class ListStatement r stmt val | r -> stmt val where
  -- | Inserts a value into a list.
  --   Arguments are: List, Index, val
  listAdd    :: VS (r val) -> VS (r val) -> VS (r val) -> MS (r stmt)
  -- | Appens a value to a list.
  --   Arguments are: List, val
  listAppend :: VS (r val) -> VS (r val) -> MS (r stmt)
  -- | Sets the value of an index of a list.
  --   Arguments are: List, Index, val
  listSet    :: VS (r val) -> VS (r val) -> VS (r val) -> MS (r stmt)

class Set r val | r -> val where
  -- | Checks membership
  -- Arguments are: Set, val
  contains :: VS (r val) -> VS (r val) -> VS (r val)
  -- | Inserts a value into a set
  -- Arguments are: Set, val
  setAdd :: VS (r val) -> VS (r val) -> VS (r val) -- TODO [Brandon Bosman, 06/24/2026]: Make this a Statement
  -- | Removes a value from a set
  -- Arguments are: Set, val
  setRemove :: VS (r val) -> VS (r val) -> VS (r val) -- TODO [Brandon Bosman, 06/24/2026]: Make this a SStatement
  -- | Removes a value from a set
  -- Arguments are: Set, Set
  setUnion :: VS (r val) -> VS (r val) -> VS (r val) -- 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 'VS val'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 NativeVector r val typ | r -> val typ where
  -- | The type of a vector with the given element type.
  --   For most languages it will be 'listType'
  vecType :: VS (r typ) -> VS (r typ)
  -- | A vector literal with the given element type and elements.
  --   For most languages it will be 'litList'.
  litVec :: VS (r typ) -> [VS (r val)] -> VS (r val)
  -- | Scales a vector by a scalar.
  --   Arguments are: Scalar, Vector
  vecScale :: VS (r val) -> VS (r val) -> VS (r val)
  -- | Adds two vectors elementwise.
  --   Arguments are: Vector, Vector
  vecAdd :: VS (r val) -> VS (r val) -> VS (r val)
  -- | Gets the element of a vector at an index.
  --   Arguments are: Vector, Index
  vecIndex :: VS (r val) -> VS (r val) -> VS (r val)
  -- | Dot product of two vectors (returns a scalar).
  --   Arguments are: Vector, Vector
  vecDot :: VS (r val) -> VS (r val) -> VS (r val)
  -- | Euclidean norm (magnitude) of a vector (returns a scalar).
  --   Argument is: Vector
  vecMag :: VS (r val) -> VS (r val)
  -- | Unit vector in the direction of a vector (returns a vector).
  --   Argument is: Vector
  vecUnit :: VS (r val) -> VS (r val)

class InternalList r block var val | r -> block var val where
  listSlice'      :: Maybe (VS (r val)) -> Maybe (VS (r val)) -> Maybe (VS (r val))
    -> VS (r var) -> VS (r val) -> 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 block var val)
  => VS (r var)
  -> VS (r val)
  -> Maybe (VS (r val))
  -> Maybe (VS (r val))
  -> Maybe (VS (r val))
  -> MS (r block)
listSlice :: forall {k} (r :: k -> *) (block :: k) (var :: k) (val :: k).
InternalList r block var val =>
VS (r var)
-> VS (r val)
-> Maybe (VS (r val))
-> Maybe (VS (r val))
-> Maybe (VS (r val))
-> MS (r block)
listSlice VS (r var)
vnew VS (r val)
vold Maybe (VS (r val))
b Maybe (VS (r val))
e Maybe (VS (r val))
tp = Maybe (VS (r val))
-> Maybe (VS (r val))
-> Maybe (VS (r val))
-> VS (r var)
-> VS (r val)
-> MS (r block)
forall {k} (r :: k -> *) (block :: k) (var :: k) (val :: k).
InternalList r block var val =>
Maybe (VS (r val))
-> Maybe (VS (r val))
-> Maybe (VS (r val))
-> VS (r var)
-> VS (r val)
-> MS (r block)
listSlice' Maybe (VS (r val))
b Maybe (VS (r val))
e Maybe (VS (r val))
tp VS (r var)
vnew VS (r val)
vold

listIndexExists
  :: (List r val, Comparison r val)
  => VS (r val) -> VS (r val) -> VS (r val)
listIndexExists :: forall {k} (r :: k -> *) (val :: k).
(List r val, Comparison r val) =>
VS (r val) -> VS (r val) -> VS (r val)
listIndexExists VS (r val)
lst VS (r val)
index = VS (r val) -> VS (r val)
forall {k} (r :: k -> *) (val :: k).
List r val =>
VS (r val) -> VS (r val)
listSize VS (r val)
lst VS (r val) -> VS (r val) -> VS (r val)
forall {k} (r :: k -> *) (val :: k).
Comparison r val =>
VS (r val) -> VS (r val) -> VS (r val)
?> VS (r val)
index

at :: (List r val) => VS (r val) -> VS (r val) -> VS (r val)
at :: forall {k} (r :: k -> *) (val :: k).
List r val =>
VS (r val) -> VS (r val) -> VS (r val)
at = VS (r val) -> VS (r val) -> VS (r val)
forall {k} (r :: k -> *) (val :: k).
List r val =>
VS (r val) -> VS (r val) -> VS (r val)
listAccess

class EmptyStatement r stmt | r -> stmt where
  -- | Empty statement
  emptyStmt :: MS (r stmt)

class MultiStatement r stmt | r -> stmt where
  -- | Consolidates a list of statements into a single statement
  multi     :: [MS (r stmt)] -> MS (r stmt)

class ValueStatement r stmt val | r -> stmt val where
  -- | Converts a value to statement
  valStmt :: VS (r val) -> MS (r stmt)

class AssignStatement r stmt var val | r -> stmt var val where
  (&-=)  :: VS (r var) -> VS (r val) -> MS (r stmt)
  infixl 1 &-=
  (&+=)  :: VS (r var) -> VS (r val) -> MS (r stmt)
  infixl 1 &+=
  (&++)  :: VS (r var) -> MS (r stmt)
  infixl 8 &++
  (&--)  :: VS (r var) -> MS (r stmt)
  infixl 8 &--

  assign :: VS (r var) -> VS (r val) -> MS (r stmt)

(&=)
  :: (AssignStatement r stmt var val)
  => VS (r var) -> VS (r val) -> MS (r stmt)
infixr 1 &=
&= :: forall {k} (r :: k -> *) (stmt :: k) (var :: k) (val :: k).
AssignStatement r stmt var val =>
VS (r var) -> VS (r val) -> MS (r stmt)
(&=) = VS (r var) -> VS (r val) -> MS (r stmt)
forall {k} (r :: k -> *) (stmt :: k) (var :: k) (val :: k).
AssignStatement r stmt var val =>
VS (r var) -> VS (r val) -> MS (r stmt)
assign

class DeclStatement r bod stmt var scope val | r -> bod stmt var scope val where
  -- | Declare a variable without giving it a value.
  -- Not for use with arrays; use `arrayDec` instead.
  varDec       :: VS (r var) -> r scope -> MS (r stmt)
  -- | Declare a variable and give it a value.
  -- Not for use with arrays; use `arrayDecDef` instead.
  varDecDef    :: VS (r var) -> r scope -> VS (r val) -> MS (r stmt)
  -- | Given the size of the list, the variable to store the list in,
  -- and the scope of the variable, declare a list of the given size.
  listDec      :: Integer -> VS (r var) -> r scope -> MS (r stmt)
  listDecDef   :: VS (r var) -> r scope -> [VS (r val)] -> MS (r stmt)
  setDec       :: VS (r var) -> r scope -> MS (r stmt)
  setDecDef    :: VS (r var) -> r scope -> VS (r val) -> MS (r stmt)
  -- | Given 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,
  -- declare an array of the given size.
  arrayDec     :: Integer -> VS (r val) -> VS (r var) -> r scope -> MS (r stmt)
  arrayDecDef  :: VS (r var) -> r scope -> [VS (r val)] -> MS (r stmt)
  constDecDef  :: VS (r var) -> r scope -> VS (r val) -> MS (r stmt)
  funcDecDef   :: VS (r var) -> r scope -> [VS (r var)] -> MS (r bod) -> MS (r stmt)

class PrintConsole r stmt val | r -> stmt val where
  print      :: VS (r val) -> MS (r stmt)
  printLn    :: VS (r val) -> MS (r stmt)
  -- TODO [Brandon Bosman, 07/23/2026]: Could these be helpers?
  printStr   :: String -> MS (r stmt)
  printStrLn :: String -> MS (r stmt)

class ReadConsole r stmt var | r -> stmt var where
  getInput         :: VS (r var) -> MS (r stmt)
  discardInput     :: MS (r stmt)

class FileHandling r stmt var val | r -> stmt var val where
  openFileR :: VS (r var) -> VS (r val) -> MS (r stmt)
  openFileW :: VS (r var) -> VS (r val) -> MS (r stmt)
  openFileA :: VS (r var) -> VS (r val) -> MS (r stmt)
  closeFile :: VS (r val) -> MS (r stmt)

class PrintFile r stmt val | r -> stmt val where
  -- | Given the file handle and value to print, print the value to the file.
  printFile      :: VS (r val) -> VS (r val) -> MS (r stmt)
  printFileLn    :: VS (r val) -> VS (r val) -> MS (r stmt)
  printFileStr   :: VS (r val) -> String -> MS (r stmt)
  printFileStrLn :: VS (r val) -> String -> MS (r stmt)

class ReadFile r stmt var val | r -> stmt var val where
  getFileInput     :: VS (r val) -> VS (r var) -> MS (r stmt)
  discardFileInput :: VS (r val) -> MS (r stmt)
  getFileInputLine :: VS (r val) -> VS (r var) -> MS (r stmt)
  discardFileLine  :: VS (r val) -> MS (r stmt)
  getFileInputAll  :: VS (r val) -> VS (r var) -> MS (r stmt)

class StringStatement r stmt var val | r -> stmt var val where
  -- | Given a char to split on, variable to store result in, and string to split,
  -- generates a statement splitting the string into a list of strings
  -- delimited by the char.
  stringSplit :: Char -> VS (r var) -> VS (r val) -> MS (r stmt)
  stringListVals  :: [VS (r var)] -> VS (r val) -> MS (r stmt)
  -- | Given a list of variables and a value containing a list of strings,
  -- assign the ith element of the list of strings into the ith variable
  stringListLists :: [VS (r var)] -> VS (r val) -> MS (r stmt)

-- The three lists are inputs, outputs, and both, respectively
type InOutCall r stmt var val =
     Label
  -> [VS (r val)]
  -> [VS (r var)]
  -> [VS (r var)]
  -> MS (r stmt)

class FuncAppStatement r stmt var val | r -> stmt var val where
  inOutCall    ::            InOutCall r stmt var val
  extInOutCall :: Library -> InOutCall r stmt var val

class CommentStatement r stmt | r -> stmt where
  comment :: String -> MS (r stmt)

class ControlStatement r bod stmt var val | r -> bod stmt var val where
  break :: MS (r stmt)
  continue :: MS (r stmt)

  returnStmt :: VS (r val) -> MS (r stmt)

  throw :: Label -> MS (r stmt)

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

  ifExists :: VS (r val) -> MS (r bod) -> MS (r bod) -> MS (r stmt)

  for      :: MS (r stmt) -> VS (r val) -> MS (r stmt) -> MS (r bod) ->
    MS (r stmt)
  -- Iterator variable, start value, end value, step value, loop body
  forRange :: VS (r var) -> VS (r val) -> VS (r val) -> VS (r val) -> MS (r bod) ->
    MS (r stmt)
  forEach  :: VS (r var) -> VS (r val) -> MS (r bod) -> MS (r stmt)
  while    :: VS (r val) -> MS (r bod) -> MS (r stmt)

  tryCatch :: MS (r bod) -> MS (r bod) -> MS (r stmt)

  assert :: VS (r val) -> VS (r val) -> MS (r stmt)

ifNoElse
  :: (BodySym r bod block, ControlStatement r bod stmt var val)
  => [(VS (r val), MS (r bod))] -> MS (r stmt)
ifNoElse :: forall {k} (r :: k -> *) (bod :: k) (block :: k) (stmt :: k)
       (var :: k) (val :: k).
(BodySym r bod block, ControlStatement r bod stmt var val) =>
[(VS (r val), MS (r bod))] -> MS (r stmt)
ifNoElse [(VS (r val), MS (r bod))]
bs = [(VS (r val), MS (r bod))] -> MS (r bod) -> MS (r stmt)
forall {k} (r :: k -> *) (bod :: k) (stmt :: k) (var :: k)
       (val :: k).
ControlStatement r bod stmt var val =>
[(VS (r val), MS (r bod))] -> MS (r bod) -> MS (r stmt)
ifCond [(VS (r val), MS (r bod))]
bs (MS (r bod) -> MS (r stmt)) -> MS (r bod) -> MS (r stmt)
forall a b. (a -> b) -> a -> b
$ [MS (r block)] -> MS (r bod)
forall {k} (r :: k -> *) (bod :: k) (block :: k).
BodySym r bod block =>
[MS (r block)] -> MS (r bod)
body []

switchAsIf
  :: (ControlStatement r bod stmt var val, Comparison r val)
  => VS (r val) -> [(VS (r val), MS (r bod))] -> MS (r bod) -> MS (r stmt)
switchAsIf :: forall {k} (r :: k -> *) (bod :: k) (stmt :: k) (var :: k)
       (val :: k).
(ControlStatement r bod stmt var val, Comparison r val) =>
VS (r val)
-> [(VS (r val), MS (r bod))] -> MS (r bod) -> MS (r stmt)
switchAsIf VS (r val)
v = [(VS (r val), MS (r bod))] -> MS (r bod) -> MS (r stmt)
forall {k} (r :: k -> *) (bod :: k) (stmt :: k) (var :: k)
       (val :: k).
ControlStatement r bod stmt var val =>
[(VS (r val), MS (r bod))] -> MS (r bod) -> MS (r stmt)
ifCond ([(VS (r val), MS (r bod))] -> MS (r bod) -> MS (r stmt))
-> ([(VS (r val), MS (r bod))] -> [(VS (r val), MS (r bod))])
-> [(VS (r val), MS (r bod))]
-> MS (r bod)
-> MS (r stmt)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((VS (r val), MS (r bod)) -> (VS (r val), MS (r bod)))
-> [(VS (r val), MS (r bod))] -> [(VS (r val), MS (r bod))]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((VS (r val) -> VS (r val))
-> (VS (r val), MS (r bod)) -> (VS (r val), MS (r bod))
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 (VS (r val)
v VS (r val) -> VS (r val) -> VS (r val)
forall {k} (r :: k -> *) (val :: k).
Comparison r val =>
VS (r val) -> VS (r val) -> VS (r val)
?==))

-- TODO [Brandon Bosman, 07/22/2026]: move this to InterfaceGOOL
-- | A class for representing "Visibility", of a class member,
-- i.e. whether it is public or private.
class VisibilitySym r vis | r -> vis where
  private :: r vis
  public  :: r vis

-- | A class for representing function/method parameters.
class ParameterSym r param var | r -> param var where
  param :: VS (r var) -> MS (r param)
  -- | A parameter that is an "alias" type, e.g. a C++ reference.
  -- This is a minor hack, to get around us not having/wanting
  -- "alias types" in GOOL.
  pointerParam :: VS (r var) -> MS (r param)

-- The three lists are inputs, outputs, and both, respectively
type InOutFunc r mthd bod var = [VS (r var)] -> [VS (r var)] -> [VS (r var)] ->
  MS (r bod) -> MS (r mthd)
-- 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 mthd bod var = String -> [(String, VS (r var))] ->
  [(String, VS (r var))] -> [(String, VS (r var))] -> MS (r bod) -> MS (r mthd)

-- | A class for representing functions/methods.
-- Usually 'MethodData' is used for the representation.
class MethodSym r mthd vis param bod var typ | r -> mthd vis param bod var typ where
  docMain :: MS (r bod) -> MS (r mthd)

  function :: Label -> r vis -> VS (r typ) -> [MS (r param)] ->
    MS (r bod) -> MS (r mthd)
  mainFunction  :: MS (r bod) -> MS (r mthd)
  -- Parameters are: function description, parameter descriptions,
  --   return value description if applicable, function
  docFunc :: String -> [String] -> Maybe String -> MS (r mthd) -> MS (r mthd)

  inOutFunc :: Label -> r vis -> InOutFunc r mthd bod var
  docInOutFunc :: Label -> r vis -> DocInOutFunc r mthd bod var

-- Utility

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

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