Commit dfd43d66 authored by Pinto Pasquale's avatar Pinto Pasquale Committed by Ilya Peresadin

[#279] add intermediate compilation representation for optimization

Problem: performing analysis and modification of Indigo's code for the
purpose of optimization during compilation is not robust, nor
convenient. The problem resides in the fact that to manipulate the
frontend freer monad before it is compiled to the backend we have to
interpret it and break the bindings.
This was circumvented based on predictability of the `Var`s resulting
from statements, but we need something better to have a wider and more
stable machinery for optimization.

Solution: add an intermediate representation that is not a Monad, but a
data-type that also contains (as "argument") the resulting vars (if
any). The variable are all assigned in the conversion between the
frontend and this new representation. This in turns frees the backend
from generating variables, making its function without a return value
as well (and allowing a conversion to happen).
Additinally, because it was easier to solve these now instead of
updating them and solve them later, this also solves some exising
TODOs.
parent 7fe0beea
......@@ -4,7 +4,7 @@ cabal-version: 2.2
--
-- see: https://github.com/sol/hpack
--
-- hash: 3522be9823fb3d6df6e545ca1192d43f0efbd2b52c673ccef68b3a46be765c18
-- hash: 4809f4d52715f6feb3b8ab2bab5951feea9b427051b197ed15c6e286fef4b6b4
name: indigo
version: 0.2.2
......@@ -39,8 +39,10 @@ library
Indigo.Backend.Scope
Indigo.Backend.Var
Indigo.Compilation
Indigo.Compilation.Field
Indigo.Compilation.Lambda
Indigo.Compilation.Params
Indigo.Compilation.Sequential
Indigo.Frontend
Indigo.Frontend.Language
Indigo.Frontend.Program
......
......@@ -8,7 +8,7 @@ module Indigo
import Indigo.Compilation as Exports
import Indigo.Frontend as Exports
import Indigo.Internal as Exports hiding (return, (=<<), (>>), (>>=))
import Indigo.Internal as Exports hiding ((>>))
import Indigo.Lib as Exports
import Indigo.Lorentz as Exports hiding (forcedCoerce)
import Indigo.Prelude as Exports
......
......@@ -30,6 +30,7 @@ module Indigo.Backend
-- * Side-effects
, transferTokens
, setDelegate
, createContract
-- * Functions, Procedures and Scopes
, scope
......@@ -52,6 +53,7 @@ import qualified Lorentz.Doc as L
import qualified Lorentz.Entrypoints.Doc as L (finalizeParamCallingDoc)
import Lorentz.Entrypoints.Helpers (RequireSumType)
import qualified Lorentz.Instr as L
import qualified Lorentz.Run as L
import qualified Michelson.Typed as MT
import Util.Type (type (++))
......@@ -59,84 +61,100 @@ import Util.Type (type (++))
-- Loop
----------------------------------------------------------------------------
-- | While statement. The same rule about releasing.
while :: Expr Bool -> IndigoState inp xs () -> IndigoState inp inp ()
-- | While statement.
while
:: Expr Bool
-- ^ Expression for the control flow
-> SomeIndigoState inp
-- ^ Block of code to execute, as long as the expression holds 'True'
-> IndigoState inp inp
while e body = IndigoState $ \md ->
let expCd = gcCode $ runIndigoState (compileExpr e) md in
let bodyIndigoState = cleanGenCode $ runIndigoState body md in
GenCode () md (expCd # L.loop (bodyIndigoState # expCd)) L.nop
let expCd = gcCode $ runIndigoState (compileExpr e) md
bodyIndigoState = runSIS body md cleanGenCode
in GenCode md (expCd # L.loop (bodyIndigoState # expCd)) L.nop
-- | While-left statement. Repeats a block of code as long as the control
-- 'Either' is 'Left', returns when it is 'Right'.
whileLeft
:: (KnownValue l, KnownValue r)
=> Expr (Either l r)
-> (Var l -> IndigoState (l & inp) xs ())
-> IndigoState inp (r & inp) (Var r)
whileLeft e body = IndigoState $ \md ->
-- ^ Expression for the control flow value
-> Var l
-- ^ Variable for the 'Left' value (available to the code block)
-> SomeIndigoState (l & inp)
-- ^ Code block to execute while the value is 'Left'
-> Var r
-- ^ Variable that will be assigned to the resulting value
-> IndigoState inp (r & inp)
whileLeft e varL body varR = IndigoState $ \md ->
let
cde = gcCode $ runIndigoState (compileExpr e) md
(l, newMd) = pushRefMd md
gc = cleanGenCode $ runIndigoState (body l) newMd
(r, resMd) = pushRefMd md
in GenCode r resMd (cde # L.loopLeft (gc # L.drop # cde)) L.drop
newMd = pushRefMd varL md
gc = runSIS body newMd cleanGenCode
resMd = pushRefMd varR md
in GenCode resMd (cde # L.loopLeft (gc # L.drop # cde)) L.drop
-- | For statements to iterate over container.
-- | For statements to iterate over a container.
forEach
:: (IterOpHs a, KnownValue (IterOpElHs a))
=> Expr a -> (Var (IterOpElHs a) -> IndigoState ((IterOpElHs a) & inp) xs ())
-> IndigoState inp inp ()
forEach container body = IndigoState $ \md ->
let cde = gcCode $ runIndigoState (compileExpr container) md in
let (var, newMd) = pushRefMd md in
let bodyIndigoState = cleanGenCode $ runIndigoState (body var) newMd in
GenCode () md (cde # L.iter (bodyIndigoState # L.drop)) L.nop
=> Expr a
-- ^ Expression for the container to traverse
-> Var (IterOpElHs a)
-- ^ Variable for the current item (available to the code block)
-> SomeIndigoState ((IterOpElHs a) & inp)
-- ^ Code block to execute over each element of the container
-> IndigoState inp inp
forEach container var body = IndigoState $ \md ->
let cde = gcCode $ runIndigoState (compileExpr container) md
newMd = pushRefMd var md
bodyIndigoState = runSIS body newMd cleanGenCode
in GenCode md (cde # L.iter (bodyIndigoState # L.drop)) L.nop
----------------------------------------------------------------------------
-- Documentation
----------------------------------------------------------------------------
-- | Put a document item.
doc :: DocItem di => di -> IndigoState s s ()
doc di = IndigoState \md -> GenCode () md (L.doc di) L.nop
doc :: DocItem di => di -> IndigoState s s
doc di = IndigoState \md -> GenCode md (L.doc di) L.nop
-- | Group documentation built in the given piece of code
-- into block dedicated to one thing, e.g. to one entrypoint.
docGroup :: DocGrouping -> IndigoState i o () -> IndigoState i o ()
docGroup gr ii = IndigoState $ \md ->
let GenCode _ mdii cd clr = runIndigoState ii md in
GenCode () mdii (L.docGroup gr cd) clr
-- into a block dedicated to one thing, e.g. to one entrypoint.
docGroup :: DocGrouping -> SomeIndigoState i -> SomeIndigoState i
docGroup gr = overSIS $ \(GenCode md cd clr) -> SomeGenCode $
GenCode md (L.docGroup gr cd) clr
-- | Insert documentation of the contract storage type. The type
-- should be passed using type applications.
docStorage :: forall storage s. TypeHasDoc storage => IndigoState s s ()
docStorage = IndigoState \md -> GenCode () md (L.docStorage @storage) L.nop
docStorage :: forall storage s. TypeHasDoc storage => IndigoState s s
docStorage = IndigoState \md -> GenCode md (L.docStorage @storage) L.nop
-- | Give a name to given contract. Apply it to the whole contract code.
contractName :: Text -> IndigoState i o () -> IndigoState i o ()
contractName cName b = IndigoState $ \md ->
let GenCode _ mdb gc clr = runIndigoState b md in
GenCode () mdb (L.contractName cName gc) clr
-- | Give a name to the given contract. Apply it to the whole contract code.
contractName :: Text -> SomeIndigoState i -> SomeIndigoState i
contractName cName = overSIS $ \(GenCode mdb gc clr) ->
SomeGenCode $ GenCode mdb (L.contractName cName gc) clr
-- | Attach general info to given contract.
contractGeneral :: IndigoState i o () -> IndigoState i o ()
contractGeneral b = IndigoState $ \md ->
let GenCode _ mdb gc clr = runIndigoState b md in
GenCode () mdb (L.contractGeneral gc) clr
-- | Attach general info to the given contract.
contractGeneral :: SomeIndigoState i -> SomeIndigoState i
contractGeneral = overSIS $ \(GenCode mdb gc clr) ->
SomeGenCode $ GenCode mdb (L.contractGeneral gc) clr
-- | Attach default general info to the contract documentation.
contractGeneralDefault :: IndigoState s s ()
contractGeneralDefault =
IndigoState \md -> GenCode () md L.contractGeneralDefault L.nop
contractGeneralDefault :: IndigoState s s
contractGeneralDefault = IndigoState \md -> GenCode md L.contractGeneralDefault L.nop
-- | Indigo version for the function of the same name from Lorentz.
finalizeParamCallingDoc
:: (NiceParameterFull cp, RequireSumType cp, HasCallStack)
=> (Var cp -> IndigoState (cp & inp) out x)
-> (Expr cp -> IndigoState inp out x)
finalizeParamCallingDoc act param = IndigoState $ \md ->
let cde = gcCode $ runIndigoState (compileExpr param) md in
let (var, newMd) = pushRefMd md in
let GenCode x md1 cd clr = runIndigoState (act var) newMd in
GenCode x md1 (cde # L.finalizeParamCallingDoc cd) (clr # L.drop)
=> Var cp
-> SomeIndigoState (cp & inp)
-> Expr cp
-> SomeIndigoState inp
finalizeParamCallingDoc vc act param = SomeIndigoState $ \md ->
let cde = gcCode $ runIndigoState (compileExpr param) md
newMd = pushRefMd vc md
in runSIS act newMd $ \(GenCode md1 cd clr) ->
SomeGenCode $ GenCode md1 (cde # L.finalizeParamCallingDoc cd) (clr # L.drop)
----------------------------------------------------------------------------
-- Contract call
......@@ -148,11 +166,12 @@ selfCalling
, KnownValue (GetEntrypointArgCustom p mname)
)
=> EntrypointRef mname
-> Var (ContractRef (GetEntrypointArgCustom p mname))
-- ^ Variable that will be assigned to the resulting 'ContractRef'
-> IndigoState inp (ContractRef (GetEntrypointArgCustom p mname) & inp)
(Var (ContractRef (GetEntrypointArgCustom p mname)))
selfCalling epRef = do
selfCalling epRef var = do
nullaryOp (L.selfCalling @p epRef)
makeTopVar
assignTopVar var
contractCalling
:: forall cp inp epRef epArg addr.
......@@ -161,11 +180,14 @@ contractCalling
, ToT addr ~ ToT Address
, KnownValue epArg
)
=> epRef -> Expr addr
-> IndigoState inp (Maybe (ContractRef epArg) & inp) (Var (Maybe (ContractRef epArg)))
contractCalling epRef addr = do
=> epRef
-> Expr addr
-> Var (Maybe (ContractRef epArg))
-- ^ Variable that will be assigned to the resulting 'ContractRef'
-> IndigoState inp (Maybe (ContractRef epArg) & inp)
contractCalling epRef addr var = do
unaryOp addr (L.contractCalling @cp epRef)
makeTopVar
assignTopVar var
----------------------------------------------------------------------------
-- Side-effects
......@@ -174,16 +196,28 @@ contractCalling epRef addr = do
transferTokens
:: (NiceParameter p, HasSideEffects)
=> Expr p -> Expr Mutez -> Expr (ContractRef p)
-> IndigoState inp inp ()
transferTokens ep em ec = do
MetaData s _ <- iget
-> IndigoState inp inp
transferTokens ep em ec = withMetaData $ \(MetaData s) ->
ternaryOpFlat ep em ec (L.transferTokens # varActionOperation s)
setDelegate :: HasSideEffects => Expr (Maybe KeyHash) -> IndigoState inp inp ()
setDelegate e = do
MetaData s _ <- iget
setDelegate :: HasSideEffects => Expr (Maybe KeyHash) -> IndigoState inp inp
setDelegate e = withMetaData $ \(MetaData s) ->
unaryOpFlat e (L.setDelegate # varActionOperation s)
createContract
:: (HasSideEffects, NiceStorage s, NiceParameterFull p)
=> L.Contract p s
-> Expr (Maybe KeyHash)
-> Expr Mutez
-> Expr s
-> Var Address
-- ^ Variable that will be assigned to the resulting 'Address'
-> IndigoState inp (Address & inp)
createContract lCtr ek em es var = do
withMetaData $ \(MetaData s) ->
ternaryOp ek em es $ L.createContract lCtr # varActionOperation (NoRef :& s)
assignTopVar var
----------------------------------------------------------------------------
-- Functions, Procedures and Scopes
----------------------------------------------------------------------------
......@@ -214,13 +248,17 @@ setDelegate e = do
-- *[s]*
-- @
scope
:: forall a inp out . ScopeCodeGen a
=> IndigoState inp out a
-> IndigoState inp (RetOutStack a ++ inp) (RetVars a)
scope f = IndigoState $ \md ->
let gc = runIndigoState f md in
finalizeStatement @a md (compileScope gc)
:: forall ret inp . ScopeCodeGen ret
=> SomeIndigoState inp
-- ^ Code block to execute inside the scope
-> ret
-- ^ Return value(s) of the scoped code block
-> RetVars ret
-- ^ Variable(s) that will be assigned to the resulting value(s)
-> IndigoState inp (RetOutStack ret ++ inp)
scope f ret retVars = IndigoState $ \md ->
runSIS f md $ \fs -> finalizeStatement @ret md retVars $ compileScope @ret fs ret
-- | Add a comment
comment :: MT.CommentType -> IndigoState i i ()
comment t = IndigoState $ \md -> GenCode () md (L.comment t) L.nop
comment :: MT.CommentType -> IndigoState i i
comment t = IndigoState $ \md -> GenCode md (L.comment t) L.nop
......@@ -4,7 +4,7 @@
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
-- | High level statements of Indigo language.
-- | Backend machinery for cases.
module Indigo.Backend.Case
( caseRec
......@@ -12,9 +12,8 @@ module Indigo.Backend.Case
, entryCaseSimpleRec
, IndigoCaseClauseL
, IndigoClause (..)
, CaseCommonF
, CaseCommon
, IndigoAnyOut (..)
) where
import Data.Vinyl.Core (RMap(..))
......@@ -38,46 +37,51 @@ data IndigoCaseClauseL ret (param :: CaseClauseParam) where
:: (forall inp . MetaData inp -> CaseClauseL inp (RetOutStack ret ++ inp) ('CaseClauseParam ctor ('OneField x)))
-> IndigoCaseClauseL ret ('CaseClauseParam ctor ('OneField x))
data IndigoAnyOut x ret = forall retBranch .
( ScopeCodeGen retBranch
, RetOutStack ret ~ RetOutStack retBranch
) =>
IndigoAnyOut (forall inp . SomeIndigoState (x : inp) retBranch)
data IndigoClause x ret where
IndigoClause
:: ( KnownValue x
, ScopeCodeGen retBr
, ret ~ RetExprs retBr
, RetOutStack ret ~ RetOutStack retBr
)
=> Var x
-- ^ Variable for the clause input value (available to its code block)
-> (forall inp. SomeIndigoState (x : inp))
-- ^ Clause code block
-> retBr
-- ^ Clause return value(s)
-> IndigoClause x ret
instance
( name ~ AppendSymbol "c" ctor
, KnownValue x
)
(name ~ AppendSymbol "c" ctor, KnownValue x)
=>
CaseArrow
name
(Var x -> IndigoAnyOut x ret)
(IndigoClause x ret)
(IndigoCaseClauseL ret ('CaseClauseParam ctor ('OneField x)))
where
(/->) _ ind =
OneFieldIndigoCaseClauseL (\(md :: MetaData inp) ->
-- Create a reference to the top of stack
let (varCase, mdCaseBody) = pushRefMd md in
-- Pass the reference to the case body
case ind varCase of
IndigoAnyOut (SomeIndigoState body :: SomeIndigoState (x : inp) retBr) ->
case body mdCaseBody of
SomeGenCode gc ->
CaseClauseL $
-- Compute returning expressions and clean up everything
compileScope gc #
-- Remove @[email protected] from the stack too
liftClear' @(ClassifyReturnValue retBr) @retBr @(x & inp) @inp L.drop
)
(/->) _ (IndigoClause varCase sIndSt (ret :: retBr)) =
OneFieldIndigoCaseClauseL $ \(md :: MetaData inp) -> case sIndSt of
(SomeIndigoState body :: SomeIndigoState (x : inp)) ->
-- Create a reference to the top of stack
case body $ pushRefMd varCase md of
SomeGenCode gc ->
CaseClauseL $
-- Compute returning expressions and clean up everything
compileScope @retBr gc ret #
-- Remove @[email protected] from the stack too
liftClear @retBr @inp @(x : inp) L.drop
-- This constraint is shared by all @case*@ functions.
-- | This constraint is shared by all @case*@ functions.
-- Including some outside this module.
type CaseCommonF f dt ret clauses =
( InstrCaseC dt
, RMap (CaseClauses dt)
, clauses ~ Rec (f ret) (CaseClauses dt)
, ScopeCodeGen ret
)
( InstrCaseC dt
, RMap (CaseClauses dt)
, clauses ~ Rec (f ret) (CaseClauses dt)
, ScopeCodeGen ret
)
-- | This constraint is shared by all backend @case*@ functions.
type CaseCommon dt ret clauses = CaseCommonF IndigoCaseClauseL dt ret clauses
-- | A case statement for indigo. See examples for a sample usage.
......@@ -85,10 +89,12 @@ caseRec
:: forall dt inp ret clauses . CaseCommon dt ret clauses
=> Expr dt
-> clauses
-> IndigoState inp (RetOutStack ret ++ inp) (RetVars ret)
caseRec g cls = IndigoState $ \md ->
-> RetVars ret
-- ^ Variable(s) that will be assigned to the resulting value(s)
-> IndigoState inp (RetOutStack ret ++ inp)
caseRec g cls vars = IndigoState $ \md ->
let cdG = gcCode $ runIndigoState (compileExpr g) md in
finalizeStatement @ret md (cdG # L.case_ (toCaseClauseL md cls))
finalizeStatement @ret md vars (cdG # L.case_ (toCaseClauseL md cls))
-- | 'case_' for pattern-matching on parameter.
entryCaseRec
......@@ -99,25 +105,29 @@ entryCaseRec
=> Proxy entrypointKind
-> Expr dt
-> clauses
-> IndigoState inp (RetOutStack ret ++ inp) (RetVars ret)
entryCaseRec proxy g cls = IndigoState $ \md ->
-> RetVars ret
-- ^ Variable(s) that will be assigned to the resulting value(s)
-> IndigoState inp (RetOutStack ret ++ inp)
entryCaseRec proxy g cls vars = IndigoState $ \md ->
let cdG = gcCode $ runIndigoState (compileExpr g) md in
finalizeStatement @ret md (cdG # L.entryCase_ proxy (toCaseClauseL md cls))
finalizeStatement @ret md vars(cdG # L.entryCase_ proxy (toCaseClauseL md cls))
-- | 'entryCase_' for contracts with flat parameter.
entryCaseSimpleRec
:: forall cp inp ret clauses .
( CaseCommon cp ret clauses
, DocumentEntrypoints PlainEntrypointsKind cp
, NiceParameterFull cp
, RequireFlatParamEps cp
:: forall dt inp ret clauses .
( CaseCommon dt ret clauses
, DocumentEntrypoints PlainEntrypointsKind dt
, NiceParameterFull dt
, RequireFlatParamEps dt
)
=> Expr cp
=> Expr dt
-> clauses
-> IndigoState inp (RetOutStack ret ++ inp) (RetVars ret)
entryCaseSimpleRec g cls = IndigoState $ \md ->
-> RetVars ret
-- ^ Variable(s) that will be assigned to the resulting value(s)
-> IndigoState inp (RetOutStack ret ++ inp)
entryCaseSimpleRec g cls vars = IndigoState $ \md ->
let cdG = gcCode $ runIndigoState (compileExpr g) md in
finalizeStatement @ret md (cdG # L.entryCaseSimple_ (toCaseClauseL md cls))
finalizeStatement @ret md vars (cdG # L.entryCaseSimple_ (toCaseClauseL md cls))
toCaseClauseL
:: forall inp ret cs .
......
......@@ -4,7 +4,7 @@
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
-- | Conditional statements of Indigo language.
-- | Backend conditional statements of Indigo
module Indigo.Backend.Conditional
( if_
......@@ -44,87 +44,135 @@ type IfConstraint a 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
:: forall inp 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 ->
-- ^ Expression for the control flow
-> SomeIndigoState inp
-- ^ Code block for the positive branch
-> a
-- ^ Return value(s) of the positive branch
-> SomeIndigoState inp
-- ^ Code block for the negative branch
-> b
-- ^ Return value(s) of the negative branch
-> RetVars a
-- ^ Variable(s) that will be assigned to the resulting value(s)
-> IndigoState inp (RetOutStack a ++ inp)
if_ e t retA f retB retVars = 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))
runSIS t md $ \gc1 ->
runSIS f md $ \gc2 ->
finalizeStatement @a md retVars $
cde # L.if_ (compileScope @a gc1 retA) (compileScope @b gc2 retB)
-- | If which works like case for Maybe.
ifSome
:: forall inp xs ys x a b . (IfConstraint a b, KnownValue x)
:: forall inp 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 ->
-- ^ Expression for the control flow
-> Var x
-- ^ Variable for the 'Just' value (available to the next code block)
-> SomeIndigoState (x & inp)
-- ^ Code block for the 'Just' branch
-> a
-- ^ Return value(s) of the 'Just' branch
-> SomeIndigoState inp
-- ^ Code block for the 'Nothing' branch
-> b
-- ^ Return value(s) of the 'Nothing' branch
-> RetVars a
-- ^ Variable(s) that will be assigned to the resulting value(s)
-> IndigoState inp (RetOutStack a ++ inp)
ifSome e varX t retA f retB retVars = 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)
let mdJust = pushRefMd varX md in
runSIS t mdJust $ \gc1 ->
runSIS f md $ \gc2 ->
finalizeStatement @a md retVars $
cde #
L.ifSome
( compileScope @a gc1 retA #
-- 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 @b gc2 retB)
-- | 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 ->
:: forall inp r l a b . (IfConstraint a b, KnownValue r, KnownValue l)
=> Expr (Either l r)
-- ^ Expression for the control flow
-> Var r
-- ^ Variable for the 'Right' value (available to the next code block)
-> SomeIndigoState (r & inp)
-- ^ Code block for the 'Right' branch
-> a
-- ^ Return value(s) of the 'Right' branch
-> Var l
-- ^ Variable for the 'Left' value (available to the next code block)
-> SomeIndigoState (l & inp)
-- ^ Code block for the 'Left' branch
-> b
-- ^ Return value(s) of the 'Left' branch
-> RetVars a
-- ^ Variable(s) that will be assigned to the resulting value(s)
-> IndigoState inp (RetOutStack a ++ inp)
ifRight e varR r retA varL l retB retVars = 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
mdRight = pushRefMd varR