-- SPDX-FileCopyrightText: 2020 Tocqueville Group -- -- SPDX-License-Identifier: LicenseRef-MIT-TQ -- | All the basic 'Expr'essions used in Indigo code. -- -- Note: infix operators acting on structure follow a naming convention: -- -- 1. the last character identifies the structure type: -- -- - @:@ for containers ('Map', 'BigMap', 'Set', 'List') -- - @\@@ for 'UStore' -- - @!@ for 'HasField' -- - @~@ fot 'Util.Named' -- -- 2. the preceding characters identify the action: -- -- - @#@ for get, lookup or from -- - @!@ for set, update or to -- - @+@ for insert -- - @++@ for insertNew -- - @-@ for remove -- - @?@ for mem or elem -- -- The only exception to this convention is '(.:)' (for 'cons') module Indigo.Internal.Expr.Symbolic ( -- * Basic constExpr, varExpr, cast -- * Math , add, sub, mul, div, mod, neg, abs , (+), (-), (*), (/), (%) -- * Comparison , eq, neq, lt, gt, le, ge , (==), (/=), (<), (>), (<=), (>=) -- * Conversion , isNat, toInt, nonZero, coerce, forcedCoerce -- * Bits and boolean , lsl, lsr, and, or, xor, not , (<<<), (>>>), (&&), (||), (^) -- * Serialization , pack, unpack -- * Pairs , pair, car, cdr, fst, snd -- * Maybe , some, none -- * Either , right, left -- * Bytes and string , slice, concat, (<>) -- * List , concatAll, nil, cons, (.:) -- * Containers , get, update, insert, remove, mem, size , (#:), (!:), (+:), (-:), (?:) , empty, emptyBigMap, emptyMap, emptySet -- * UStore , uGet, uUpdate, uInsert, uInsertNew, uDelete, uMem , (#@), (!@), (+@), (++@), (-@), (?@) -- * Sum types , wrap, unwrap -- * HasField , (!!), (#!) -- * Record and Named , name, unName, (!~), (#~) , construct, constructRec -- * Contract , contract , self , contractAddress , contractCallingUnsafe , contractCallingString , runFutureContract , implicitAccount , convertEpAddressToContract , makeView , makeVoid -- * Auxiliary , now , amount , sender , blake2b , sha256 , sha512 , hashKey , chainId , balance , checkSignature ) where import Data.Vinyl.Core (RMap(..)) import Indigo.Internal.Expr.Types import Indigo.Internal.Field import Indigo.Internal.Object (Var) import Indigo.Lorentz hiding (forcedCoerce) import Indigo.Prelude import qualified Michelson.Typed.Arith as M import Util.TypeTuple import Michelson.Text (unMText) import Michelson.Typed.Haskell.Instr.Sum (CtorOnlyField, InstrUnwrapC, InstrWrapOneC) import Michelson.Untyped.Entrypoints (unsafeBuildEpName) ---------------------------------------------------------------------------- -- Basic ---------------------------------------------------------------------------- constExpr :: NiceConstant a => a -> Expr a constExpr = C -- | Create an expression holding a variable. varExpr :: KnownValue a => Var a -> Expr a varExpr = V cast :: (ex :~> a) => ex -> Expr a cast = Cast . toExpr ---------------------------------------------------------------------------- -- Math ---------------------------------------------------------------------------- infixl 6 + add, (+) :: IsArithExpr exN exM M.Add n m => exN -> exM -> Expr (ArithResHs M.Add n m) add n m = Add (toExpr n) (toExpr m) (+) = add infixl 6 - sub, (-) :: IsArithExpr exN exM M.Sub n m => exN -> exM -> Expr (ArithResHs M.Sub n m) sub n m = Sub (toExpr n) (toExpr m) (-) = sub infixl 7 * mul, (*) :: IsArithExpr exN exM M.Mul n m => exN -> exM -> Expr (ArithResHs M.Mul n m) mul n m = Mul (toExpr n) (toExpr m) (*) = mul infixl 7 / div, (/) :: IsDivExpr exN exM n m => exN -> exM -> Expr (EDivOpResHs n m) div n m = Div (toExpr n) (toExpr m) (/) = div infixl 7 % mod, (%) :: IsModExpr exN exM n m => exN -> exM -> Expr (EModOpResHs n m) mod n m = Mod (toExpr n) (toExpr m) (%) = mod abs :: IsUnaryArithExpr exN M.Abs n => exN -> Expr (UnaryArithResHs M.Abs n) abs = Abs . toExpr neg :: IsUnaryArithExpr exN M.Neg n => exN -> Expr (UnaryArithResHs M.Neg n) neg = Neg . toExpr ---------------------------------------------------------------------------- -- Comparison ---------------------------------------------------------------------------- infix 4 == eq, (==) :: (NiceComparable n, c :~> n, c1 :~> n) => c -> c1 -> Expr Bool eq a b = Eq' (toExpr a) (toExpr b) (==) = eq infix 4 /= neq, (/=) :: (NiceComparable n, c :~> n, c1 :~> n) => c -> c1 -> Expr Bool neq a b = Neq (toExpr a) (toExpr b) (/=) = neq infix 4 < lt, (<) :: (NiceComparable n, c :~> n, c1 :~> n) => c -> c1 -> Expr Bool lt a b = Lt (toExpr a) (toExpr b) (<) = lt infix 4 > gt, (>) :: (NiceComparable n, c :~> n, c1 :~> n) => c -> c1 -> Expr Bool gt a b = Gt (toExpr a) (toExpr b) (>) = gt infix 4 <= le, (<=) :: (NiceComparable n, c :~> n, c1 :~> n) => c -> c1 -> Expr Bool le a b = Le (toExpr a) (toExpr b) (<=) = le infix 4 >= ge, (>=) :: (NiceComparable n, c :~> n, c1 :~> n) => c -> c1 -> Expr Bool ge a b = Ge (toExpr a) (toExpr b) (>=) = ge ---------------------------------------------------------------------------- -- Conversion ---------------------------------------------------------------------------- isNat :: (ex :~> Integer) => ex -> Expr (Maybe Natural) isNat = IsNat . toExpr toInt :: (ex :~> Natural) => ex -> Expr Integer toInt = Int' . toExpr nonZero :: (ex :~> n, NonZero n, KnownValue (Maybe n)) => ex -> Expr (Maybe n) nonZero = NonZero . toExpr -- | Convert between types that have the same Michelson representation and an -- explicit permission for that in the face of 'CanCastTo' constraint. coerce :: forall b a ex. (Castable_ a b, KnownValue b, ex :~> a) => ex -> Expr b coerce = Coerce . toExpr -- | Convert between expressions of types that have the same Michelson -- representation. forcedCoerce :: forall b a ex. (MichelsonCoercible a b, KnownValue b, ex :~> a) => ex -> Expr b forcedCoerce = ForcedCoerce . toExpr ---------------------------------------------------------------------------- -- Bits and boolean ---------------------------------------------------------------------------- infixl 8 <<< lsl, (<<<) :: IsArithExpr exN exM M.Lsl n m => exN -> exM -> Expr (ArithResHs M.Lsl n m) lsl a b = Lsl (toExpr a) (toExpr b) (<<<) = lsl infixl 8 >>> lsr, (>>>) :: IsArithExpr exN exM M.Lsr n m => exN -> exM -> Expr (ArithResHs M.Lsr n m) lsr a b = Lsr (toExpr a) (toExpr b) (>>>) = lsr infixr 2 || or, (||) :: IsArithExpr exN exM M.Or n m => exN -> exM -> Expr (ArithResHs M.Or n m) or a b = Or (toExpr a) (toExpr b) (||) = or infixr 3 && and, (&&) :: IsArithExpr exN exM M.And n m => exN -> exM -> Expr (ArithResHs M.And n m) and a b = And (toExpr a) (toExpr b) (&&) = and infixr 2 ^ xor, (^) :: IsArithExpr exN exM M.Xor n m => exN -> exM -> Expr (ArithResHs M.Xor n m) xor a b = Xor (toExpr a) (toExpr b) (^) = xor not :: IsUnaryArithExpr exN M.Not n => exN -> Expr (UnaryArithResHs M.Not n) not = Not . toExpr ---------------------------------------------------------------------------- -- Serialization ---------------------------------------------------------------------------- pack :: (ex :~> a, NicePackedValue a) => ex -> Expr ByteString pack = Pack . toExpr unpack :: (NiceUnpackedValue a, exb :~> ByteString) => exb -> Expr (Maybe a) unpack = Unpack . toExpr ---------------------------------------------------------------------------- -- Pairs ---------------------------------------------------------------------------- pair :: (ex1 :~> n, ex2 :~> m, KnownValue (n, m)) => ex1 -> ex2 -> Expr (n, m) pair a b = Pair (toExpr a) (toExpr b) car, fst :: (op :~> (n, m), KnownValue n) => op -> Expr n car = fst fst = Fst . toExpr cdr, snd :: (op :~> (n, m), KnownValue m) => op -> Expr m cdr = snd snd = Snd . toExpr ---------------------------------------------------------------------------- -- Maybe ---------------------------------------------------------------------------- some :: (ex :~> t, KnownValue (Maybe t)) => ex -> Expr (Maybe t) some = Some . toExpr none :: KnownValue t => Expr (Maybe t) none = None ---------------------------------------------------------------------------- -- Either ---------------------------------------------------------------------------- right :: (ex :~> x, KnownValue y, KnownValue (Either y x)) => ex -> Expr (Either y x) right = Right' . toExpr left :: (ex :~> y, KnownValue x, KnownValue (Either y x)) => ex -> Expr (Either y x) left = Left' . toExpr ---------------------------------------------------------------------------- -- Bytes and string ---------------------------------------------------------------------------- slice :: ( an :~> Natural , bn :~> Natural , IsSliceExpr ex c ) => (an, bn) -> ex -> Expr (Maybe c) slice (a, b) ex = Slice (toExpr a) (toExpr b) (toExpr ex) infixr 6 <> concat, (<>) :: IsConcatExpr exN1 exN2 n => exN1 -> exN2 -> Expr n concat a b = Concat (toExpr a) (toExpr b) (<>) = concat ---------------------------------------------------------------------------- -- List ---------------------------------------------------------------------------- infixr 5 .: cons, (.:) :: (ex1 :~> a, ex2 :~> List a) => ex1 -> ex2 -> Expr (List a) cons el lst = Cons (toExpr el) (toExpr lst) (.:) = cons concatAll :: IsConcatListExpr exN n => exN -> Expr n concatAll = Concat' . toExpr nil :: KnownValue a => Expr (List a) nil = Nil ---------------------------------------------------------------------------- -- Containers ---------------------------------------------------------------------------- class ExprMagma c where empty :: (NiceComparable (UpdOpKeyHs c), KnownValue c) => Expr c instance KnownValue v => ExprMagma (BigMap k v) where empty = EmptyBigMap instance KnownValue v => ExprMagma (Map k v) where empty = EmptyMap instance ExprMagma (Set k) where empty = EmptySet -- | Expression class to insert an element into a data structure. -- -- Note that while this is based on 'update' and 'UpdOpHs', it is necessary to -- have different instances to allow for different 'update' parameter types, -- ('Set' uses a 'Bool' instead of a 'Maybe'), just like 'ExprRemovable'. -- -- Moreover, this class is parameterized with an @insParam@ as well in order to -- have both key-value pairs ('Map' and 'BigMap') as well as key only ('Set'). class UpdOpHs c => ExprInsertable c insParam where insert :: ex :~> c => insParam -> ex -> Expr c instance (NiceComparable k, exKey :~> k, exValue :~> v) => ExprInsertable (BigMap k v) (exKey, exValue) where insert (k, v) c = update (k, some v) c instance (NiceComparable k, exKey :~> k, exValue :~> v) => ExprInsertable (Map k v) (exKey, exValue) where insert (k, v) c = update (k, some v) c instance (NiceComparable a, exKey :~> a) => ExprInsertable (Set a) exKey where insert k c = update (k, True) c -- | Expression class to remove an element from a data structure. -- -- Note that while this is based on 'update' and 'UpdOpHs', it is necessary to -- have different instances to allow for different 'update' parameter types, -- ('Set' uses a 'Bool' instead of a 'Maybe'). class UpdOpHs c => ExprRemovable c where remove :: (exStruct :~> c, exKey :~> UpdOpKeyHs c) => exKey -> exStruct -> Expr c instance (NiceComparable k, KnownValue v) => ExprRemovable (BigMap k v) where remove k c = update (k, none) c instance (NiceComparable k, KnownValue v) => ExprRemovable (Map k v) where remove k c = update (k, none) c instance NiceComparable a => ExprRemovable (Set a) where remove k c = update (k, False) c get :: IsGetExpr exKey exMap map => exKey -> exMap -> Expr (Maybe (GetOpValHs map)) get k m = Get (toExpr k) (toExpr m) update :: IsUpdExpr exKey exVal exMap map => (exKey, exVal) -> exMap -> Expr map update (k, v) s = Update (toExpr s) (toExpr k) (toExpr v) mem :: IsMemExpr exKey exN n => exKey -> exN -> Expr Bool mem key n = Mem (toExpr key) (toExpr n) size :: IsSizeExpr exN n => exN -> Expr Natural size = Size . toExpr infixl 8 #: (#:) :: IsGetExpr exKey exMap map => exMap -> exKey -> Expr (Maybe (GetOpValHs map)) (#:) = flip get infixl 8 !: (!:) :: IsUpdExpr exKey exVal exMap map => exMap -> (exKey, exVal) -> Expr map (!:) = flip update infixl 8 +: (+:) :: ( ExprInsertable c exParam , exStructure :~> c ) => exStructure -> exParam -> Expr c (+:) = flip insert infixl 8 -: (-:) :: ( ExprRemovable c , exStruct :~> c , exKey :~> UpdOpKeyHs c ) => exStruct -> exKey -> Expr c (-:) = flip remove infixl 8 ?: (?:) :: IsMemExpr exKey exN n => exN -> exKey -> Expr Bool (?:) = flip mem emptyBigMap :: (KnownValue value, NiceComparable key, KnownValue (BigMap key value)) => Expr (BigMap key value) emptyBigMap = empty emptyMap :: (KnownValue value, NiceComparable key, KnownValue (Map key value)) => Expr (Map key value) emptyMap = empty emptySet :: (NiceComparable key, KnownValue (Set key)) => Expr (Set key) emptySet = empty ---------------------------------------------------------------------------- -- UStore ---------------------------------------------------------------------------- infixr 8 #@ uGet, (#@) :: ( HasUStore name key value store , exKey :~> key , exStore :~> (UStore store) ) => exStore -> (Label name, exKey) -> Expr (Maybe value) uGet store (uName, key) = UGet uName (toExpr key) (toExpr store) (#@) = uGet infixl 8 !@ uUpdate, (!@) :: ( HasUStore name key value store , exKey :~> key , exVal :~> Maybe value , exStore :~> UStore store ) => exStore -> (Label name, exKey, exVal) -> Expr (UStore store) uUpdate store (uName, key, val) = UUpdate uName (toExpr key) (toExpr val) (toExpr store) (!@) = uUpdate infixr 8 +@ uInsert, (+@) :: ( HasUStore name key value store , exKey :~> key , exVal :~> value , exStore :~> UStore store ) => exStore -> (Label name, exKey, exVal) -> Expr (UStore store) uInsert store (uName, key, val) = UInsert uName (toExpr key) (toExpr val) (toExpr store) (+@) = uInsert infixr 8 ++@ uInsertNew, (++@) :: ( HasUStore name key value store , IsError err , exKey :~> key , exVal :~> value , exStore :~> UStore store ) => exStore -> (Label name, err, exKey, exVal) -> Expr (UStore store) uInsertNew store (uName, err, key, val) = UInsertNew uName err (toExpr key) (toExpr val) (toExpr store) (++@) = uInsertNew infixl 8 -@ uDelete, (-@) :: ( HasUStore name key value store , exKey :~> key , exStore :~> (UStore store) ) => exStore -> (Label name, exKey) -> Expr (UStore store) uDelete store (uName, key) = UDelete uName (toExpr key) (toExpr store) (-@) = uDelete infixl 8 ?@ uMem, (?@) :: ( HasUStore name key value store , exKey :~> key , exStore :~> (UStore store) ) => exStore -> (Label name, exKey) -> Expr Bool uMem store (uName, key) = UMem uName (toExpr key) (toExpr store) (?@) = uMem ---------------------------------------------------------------------------- -- Sum types ---------------------------------------------------------------------------- wrap :: ( InstrWrapOneC dt name , exField :~> CtorOnlyField name dt , KnownValue dt ) => Label name -> exField -> Expr dt wrap l = Wrap l . toExpr unwrap :: ( InstrUnwrapC dt name , exDt :~> dt , KnownValue (CtorOnlyField name dt) ) => Label name -> exDt -> Expr (CtorOnlyField name dt) unwrap l = Unwrap l . toExpr ---------------------------------------------------------------------------- -- HasField ---------------------------------------------------------------------------- infixl 8 #! (#!) :: (HasField dt name ftype, exDt :~> dt) => exDt -> Label name -> Expr ftype (#!) (toExpr -> (ObjMan fa)) fName = ObjMan (ToField fa fName) (#!) exDt fName = ObjMan (ToField (Object $ toExpr exDt) fName) infixl 8 !! (!!) :: ( HasField dt name ftype , exDt :~> dt , exFld :~> ftype ) => exDt -> (Label name, exFld) -> Expr dt (!!) (toExpr -> (ObjMan fa)) (fName, eFld) = ObjMan (SetField fa fName (toExpr eFld)) dt !! (fName, eFld) = ObjMan (SetField (Object $ toExpr dt) fName (toExpr eFld)) ---------------------------------------------------------------------------- -- Record and Named ---------------------------------------------------------------------------- name :: (ex :~> t, KnownValue (name :! t)) => Label name -> ex -> Expr (name :! t) name lName = Name lName . toExpr unName :: (ex :~> (name :! t), KnownValue t) => Label name -> ex -> Expr t unName lName = UnName lName . toExpr infixl 8 !~ (!~) :: (ex :~> t, KnownValue (name :! t)) => ex -> Label name -> Expr (name :! t) (!~) = flip name infixl 8 #~ (#~) :: (ex :~> (name :! t), KnownValue t) => ex -> Label name -> Expr t (#~) = flip unName -- TODO: we should try to make this have a set of 'IsExpr' as input instead of 'Expr' construct :: ( InstrConstructC dt, KnownValue dt , RMap (ConstructorFieldTypes dt) , fields ~ Rec Expr (ConstructorFieldTypes dt) , RecFromTuple fields ) => IsoRecTuple fields -> Expr dt construct = Construct . recFromTuple constructRec :: ( InstrConstructC dt , RMap (ConstructorFieldTypes dt) , KnownValue dt ) => Rec Expr (ConstructorFieldTypes dt) -> Expr dt constructRec = Construct ---------------------------------------------------------------------------- -- Contract ---------------------------------------------------------------------------- contract :: ( NiceParameterFull p , NoExplicitDefaultEntrypoint p , ToTAddress p addr , ToT addr ~ ToT Address , exAddr :~> addr ) => exAddr -> Expr (Maybe (ContractRef p)) contract = Contract . toExpr self :: ( NiceParameterFull p , NoExplicitDefaultEntrypoint p ) => Expr (ContractRef p) self = Self contractAddress :: (exc :~> ContractRef p) => exc -> Expr Address contractAddress = ContractAddress . toExpr contractCallingUnsafe :: ( NiceParameter arg , exAddr :~> Address ) => EpName -> exAddr -> Expr (Maybe (ContractRef arg)) contractCallingUnsafe epName = ContractCallingUnsafe epName . toExpr contractCallingString :: ( NiceParameter arg , exAddr :~> Address ) => MText -> exAddr -> Expr (Maybe (ContractRef arg)) contractCallingString = contractCallingUnsafe . unsafeBuildEpName . unMText runFutureContract :: ( NiceParameter p , conExpr :~> FutureContract p ) => conExpr -> Expr (Maybe (ContractRef p)) runFutureContract = RunFutureContract . toExpr implicitAccount :: (exkh :~> KeyHash) => exkh -> Expr (ContractRef ()) implicitAccount = ImplicitAccount . toExpr convertEpAddressToContract :: ( NiceParameter p , epExpr :~> EpAddress ) => epExpr -> Expr (Maybe (ContractRef p)) convertEpAddressToContract = ConvertEpAddressToContract . toExpr makeView :: ( KnownValue (View a r) , exa :~> a , exCRef :~> ContractRef r ) => exa -> exCRef -> Expr (View a r) makeView a cRef = MakeView (toExpr a) (toExpr cRef) makeVoid :: ( KnownValue (Void_ a b) , exa :~> a , exCRef :~> Lambda b b ) => exa -> exCRef -> Expr (Void_ a b) makeVoid a cRef = MakeVoid (toExpr a) (toExpr cRef) ---------------------------------------------------------------------------- -- Auxiliary ---------------------------------------------------------------------------- now :: Expr Timestamp now = Now amount :: Expr Mutez amount = Amount sender :: Expr Address sender = Sender checkSignature :: ( pkExpr :~> PublicKey , sigExpr :~> Signature , hashExpr :~> ByteString ) => pkExpr -> sigExpr -> hashExpr -> Expr Bool checkSignature pk sig hash = CheckSignature (toExpr pk) (toExpr sig) (toExpr hash) sha256 :: (hashExpr :~> ByteString) => hashExpr -> Expr ByteString sha256 = Sha256 . toExpr sha512 :: (hashExpr :~> ByteString) => hashExpr -> Expr ByteString sha512 = Sha512 . toExpr blake2b :: (hashExpr :~> ByteString) => hashExpr -> Expr ByteString blake2b = Blake2b . toExpr hashKey :: (keyExpr :~> PublicKey) => keyExpr -> Expr KeyHash hashKey = HashKey . toExpr chainId :: Expr ChainId chainId = ChainId balance :: Expr Mutez balance = Balance