-- SPDX-FileCopyrightText: 2020 Tocqueville Group -- -- SPDX-License-Identifier: LicenseRef-MIT-TQ {-# OPTIONS_GHC -Wno-redundant-constraints #-} -- | Strictly typed statements of Indigo language. module Indigo.Backend ( module ReExports -- * Loop , forEach , while , whileLeft -- * Contract call , selfCalling , contractCalling -- * Documentation , doc , docGroup , docStorage , contractName , finalizeParamCallingDoc , contractGeneral , contractGeneralDefault -- * Side-effects , transferTokens , setDelegate -- * Functions, Procedures and Scopes , scope -- * Comments , comment ) where import Indigo.Backend.Case as ReExports import Indigo.Backend.Conditional as ReExports import Indigo.Backend.Error as ReExports import Indigo.Backend.Lambda as ReExports import Indigo.Backend.Scope as ReExports import Indigo.Backend.Var as ReExports import Indigo.Backend.Prelude import Indigo.Internal import Indigo.Lorentz 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 Michelson.Typed as MT import Util.Type (type (++)) ---------------------------------------------------------------------------- -- Loop ---------------------------------------------------------------------------- -- | While statement. The same rule about releasing. while :: Expr Bool -> IndigoState inp xs () -> 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 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 -> 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 -- | For statements to iterate over 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 ---------------------------------------------------------------------------- -- Documentation ---------------------------------------------------------------------------- -- | Put a document item. 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 -- | 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 -- | 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 -- | 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 default general info to the contract documentation. 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) ---------------------------------------------------------------------------- -- Contract call ---------------------------------------------------------------------------- selfCalling :: forall p inp mname. ( NiceParameterFull p , KnownValue (GetEntrypointArgCustom p mname) ) => EntrypointRef mname -> IndigoState inp (ContractRef (GetEntrypointArgCustom p mname) & inp) (Var (ContractRef (GetEntrypointArgCustom p mname))) selfCalling epRef = do nullaryOp (L.selfCalling @p epRef) makeTopVar contractCalling :: forall cp inp epRef epArg addr. ( HasEntrypointArg cp epRef epArg , ToTAddress cp addr , ToT addr ~ ToT Address , KnownValue epArg ) => epRef -> Expr addr -> IndigoState inp (Maybe (ContractRef epArg) & inp) (Var (Maybe (ContractRef epArg))) contractCalling epRef addr = do unaryOp addr (L.contractCalling @cp epRef) makeTopVar ---------------------------------------------------------------------------- -- Side-effects ---------------------------------------------------------------------------- transferTokens :: (NiceParameter p, HasSideEffects) => Expr p -> Expr Mutez -> Expr (ContractRef p) -> IndigoState inp inp () transferTokens ep em ec = do MetaData s _ <- iget ternaryOpFlat ep em ec (L.transferTokens # varActionOperation s) setDelegate :: HasSideEffects => Expr (Maybe KeyHash) -> IndigoState inp inp () setDelegate e = do MetaData s _ <- iget unaryOpFlat e (L.setDelegate # varActionOperation s) ---------------------------------------------------------------------------- -- Functions, Procedures and Scopes ---------------------------------------------------------------------------- -- | Takes an arbitrary 'IndigoM' and wraps it into an 'IndigoFunction' -- producing a local scope for its execution. Once it executed, all -- non-returned variables are cleaned up so that the stack has only -- returned variables at the top. This also can be interpreted as -- @if True then f else nop@. -- -- Note, that by default we do not define scope inside indigo functions, -- meaning that once we want to create a new variable or return it from -- a function we need to do it inside @scope $ instr@ construction, for -- example: -- -- @ -- f :: IndigoFunction s Natural -- f = scope $ do -- *[s]* -- res <- newVar (0 :: Natural) -- *[Natural, s]* -- scope $ do -- _n <- newVar (1 :: Integer) -- *[Integer, Natural, s] -- res += 4 -- *[Natural, s]* -- return res -- *[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) -- | Add a comment comment :: MT.CommentType -> IndigoState i i () comment t = IndigoState $ \md -> GenCode () md (L.comment t) L.nop