indigo-0.6.0: src/Indigo/Backend/Case.hs
-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
-- | Backend machinery for cases.
module Indigo.Backend.Case
( caseRec
, entryCaseRec
, entryCaseSimpleRec
, IndigoCaseClauseL
, IndigoClause (..)
, CaseCommonF
) where
import Data.Vinyl.Core (RMap(..))
import Fmt (pretty)
import Indigo.Backend.Expr.Compilation (compileExpr)
import Indigo.Backend.Prelude
import Indigo.Backend.Scope
import Indigo.Common.Expr (Expr)
import Indigo.Common.SIS (SomeGenCode(SomeGenCode), SomeIndigoState(SomeIndigoState))
import Indigo.Common.State
import Indigo.Common.Var (Var)
import Indigo.Lorentz
import Lorentz.ADT qualified as L
import Lorentz.Entrypoints.Doc qualified as L
import Lorentz.Instr qualified as L
import Morley.Michelson.Typed.Haskell.Instr.Sum
(CaseClauseParam(..), CaseClauses, CtorField(..), InstrCaseC)
import Morley.Util.Type (type (++))
import Morley.Util.TypeLits (AppendSymbol)
-- | This type is analogous to the 'CaseClauseL' type but instead of wrapping a Lorentz
-- instruction, this wraps an Indigo value with the same input/output types.
data IndigoCaseClauseL ret (param :: CaseClauseParam) where
OneFieldIndigoCaseClauseL
:: (forall inp .
MetaData inp
-> CaseClauseL inp (RetOutStack ret ++ inp) ('CaseClauseParam ctor ('OneField x)))
-> IndigoCaseClauseL ret ('CaseClauseParam ctor ('OneField x))
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)
=>
CaseArrow
name
(IndigoClause x ret)
(IndigoCaseClauseL ret ('CaseClauseParam ctor ('OneField x)))
where
(/->) _ (IndigoClause varCase sIndSt (ret :: retBr)) =
OneFieldIndigoCaseClauseL $ \md@MetaData{..} -> case sIndSt of
(SomeIndigoState body :: SomeIndigoState (x : inp)) ->
-- Create a reference to the top of stack
case body (pushRefMd varCase md) of
SomeGenCode bodyCd ->
CaseClauseL $
auxiliaryHook md ("case branch against " <> pretty varCase) $
-- Compute returning expressions and clean up everything
compileScope @retBr (\stk -> MetaData stk mdObjects mdHooks) bodyCd ret #
-- Remove @x@ from the stack too
liftClear @retBr @inp @(x : inp) L.drop
-- | 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
)
-- | 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.
caseRec
:: forall dt inp ret clauses . CaseCommon dt ret clauses
=> Expr dt
-> clauses
-> 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 $ usingIndigoState md (compileExpr g) in
finalizeStatement @ret (mdStack md) vars $ stmtHook md (condStmtPretty @ret vars "caseRec" g) $
exprHook md (pretty g) cdG #
L.case_ (toCaseClauseL md cls)
-- | 'case_' for pattern-matching on parameter.
entryCaseRec
:: forall dt entrypointKind inp ret clauses .
( CaseCommon dt ret clauses
, DocumentEntrypoints entrypointKind dt
)
=> Proxy entrypointKind
-> Expr dt
-> clauses
-> 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 $ usingIndigoState md (compileExpr g) in
finalizeStatement @ret (mdStack md) vars $ stmtHook md (condStmtPretty @ret vars "entryCaseRec" g) $
exprHook md (pretty g) cdG #
L.entryCase_ proxy (toCaseClauseL md cls)
-- | 'entryCase_' for contracts with flat parameter.
entryCaseSimpleRec
:: forall dt inp ret clauses .
( CaseCommon dt ret clauses
, DocumentEntrypoints PlainEntrypointsKind dt
, NiceParameterFull dt
, RequireFlatParamEps dt
)
=> Expr dt
-> clauses
-> 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 $ usingIndigoState md (compileExpr g) in
finalizeStatement @ret (mdStack md) vars $ stmtHook md (condStmtPretty @ret vars "entryCaseSimpleRec" g) $
exprHook md (pretty g) cdG #
L.entryCaseSimple_ (toCaseClauseL md cls)
toCaseClauseL
:: forall inp ret cs .
MetaData inp
-> Rec (IndigoCaseClauseL ret) cs
-> Rec (CaseClauseL inp (RetOutStack ret ++ inp)) cs
toCaseClauseL _ RNil = RNil
toCaseClauseL md (OneFieldIndigoCaseClauseL fn :& rest) = fn md :& toCaseClauseL md rest