indigo-0.2.1: src/Indigo/Backend/Conditional.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
-- | Conditional statements of Indigo language.
module Indigo.Backend.Conditional
( if_
, ifSome
, ifRight
, ifCons
, IfConstraint
) where
import qualified Data.Kind as Kind
import qualified GHC.TypeLits as Lit
import Util.Type (type (++))
import Indigo.Backend.Prelude
import Indigo.Backend.Scope
import Indigo.Internal
import Indigo.Lorentz
import qualified Lorentz.Instr as L
import qualified Lorentz.Macro as L
type family CompareBranchesResults (a :: Kind.Type) (b :: Kind.Type) :: Constraint where
CompareBranchesResults x x = ()
CompareBranchesResults x y = Lit.TypeError
('Lit.Text " Result types of if branches diverged: "
'Lit.:<>: 'Lit.ShowType x 'Lit.:<>: ('Lit.Text " against ") 'Lit.:<>: 'Lit.ShowType y
)
type IfConstraint a b =
( ScopeCodeGen a
, ScopeCodeGen b
, CompareBranchesResults (RetExprs a) (RetExprs b)
-- These constraints below are implied by the one above, but GHC needs a proof
, RetVars a ~ RetVars b
, RetOutStack a ~ RetOutStack b
)
-- | If statement. All variables created inside its branches will be released
-- after the execution leaves the scope in which they were created.
if_
:: forall inp xs ys a b . IfConstraint a b
=> Expr Bool
-> IndigoState inp xs a
-> IndigoState inp ys b
-> IndigoState inp (RetOutStack a ++ inp) (RetVars a)
if_ e t f = IndigoState $ \md ->
let cde = gcCode $ runIndigoState (compileExpr e) md in
let gc1 = runIndigoState t md in
let gc2 = runIndigoState f md in
finalizeStatement @a md (cde # L.if_ (compileScope gc1) (compileScope gc2))
-- | If which works like case for Maybe.
ifSome
:: forall inp xs ys x a b . (IfConstraint a b, KnownValue x)
=> Expr (Maybe x)
-> (Var x -> IndigoState (x & inp) xs a)
-> IndigoState inp ys b
-> IndigoState inp (RetOutStack a ++ inp) (RetVars a)
ifSome e t f = IndigoState $ \md ->
let cde = gcCode $ runIndigoState (compileExpr e) md in
let (v, mdJust) = pushRefMd md in
let gc1 = runIndigoState (t v) mdJust in
let gc2 = runIndigoState f md in
finalizeStatement @a md $
cde #
L.ifSome
( compileScope gc1 #
-- after this we have stack (e1 & e2 .. & ek & x & inp)
liftClear' @(ClassifyReturnValue a) @a @(x & inp) @inp L.drop
-- this can be lifted together with glClear code, but let's leave it like this for now
)
(compileScope gc2)
-- | If which works like case for Either.
ifRight
:: forall inp xs ys x y a b . (IfConstraint a b, KnownValue x, KnownValue y)
=> Expr (Either y x)
-> (Var x -> IndigoState (x & inp) xs a)
-> (Var y -> IndigoState (y & inp) ys b)
-> IndigoState inp (RetOutStack a ++ inp) (RetVars a)
ifRight e r l = IndigoState $ \md ->
let
cde = gcCode $ runIndigoState (compileExpr e) md
(v, mdRight) = pushRefMd md
(w, mdLeft) = pushRefMd md
gc1 = runIndigoState (r v) mdRight
gc2 = runIndigoState (l w) mdLeft
in
finalizeStatement @a md $
cde #
L.ifRight
( compileScope gc1 #
-- after this we have stack (e1 & e2 .. & ek & x & inp)
liftClear' @(ClassifyReturnValue a) @a @(x & inp) @inp L.drop
-- this can be lifted together with glClear code, but let's leave it like this for now
)
( compileScope gc2 #
-- after this we have stack (e1 & e2 .. & ek & x & inp)
liftClear' @(ClassifyReturnValue b) @b @(y & inp) @inp L.drop
-- this can be lifted together with glClear code, but let's leave it like this for now
)
ifCons
:: forall inp xs ys x a b . (IfConstraint a b, KnownValue x)
=> Expr (List x)
-> (Var x -> Var (List x) -> IndigoState (x & List x & inp) xs a)
-> IndigoState inp ys b
-> IndigoState inp (RetOutStack a ++ inp) (RetVars a)
ifCons e t f = IndigoState $ \md ->
let
cde = gcCode $ runIndigoState (compileExpr e) md
(l, mdList) = pushRefMd md
(v, mdVal) = pushRefMd mdList
gc1 = runIndigoState (t v l) mdVal
gc2 = runIndigoState f md
in
finalizeStatement @a md $
cde #
L.ifCons
( compileScope gc1 #
liftClear' @(ClassifyReturnValue a) @a @(x & List x & inp) @inp (L.drop # L.drop)
)
(compileScope gc2)