packages feed

indigo-0.2.0: src/Indigo/Internal/Expr/Symbolic.hs

-- 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

  -- * 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
  , (#@), (!@), (+@), (++@), (-@), (?@)

  -- * 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
import Indigo.Prelude
import qualified Michelson.Typed.Arith as M
import Util.TypeTuple
import Michelson.Text (unMText)
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

----------------------------------------------------------------------------
-- Math
----------------------------------------------------------------------------

infixl 6 +
add, (+)
  :: IsArithExpr exN exM M.Add n m
  => exN -> exM
  -> Expr (ArithResHs M.Add n m)
add = Add
(+) = Add

infixl 6 -
sub, (-)
  :: IsArithExpr exN exM M.Sub n m
  => exN -> exM
  -> Expr (ArithResHs M.Sub n m)
sub = Sub
(-) = Sub

infixl 7 *
mul, (*)
  :: IsArithExpr exN exM M.Mul n m
  => exN -> exM
  -> Expr (ArithResHs M.Mul n m)
mul = Mul
(*) = Mul

infixl 7 /
div, (/)
  :: IsDivExpr exN exM n m
  => exN -> exM
  -> Expr (EDivOpResHs n m)
div = Div
(/) = Div

infixl 7 %
mod, (%)
  :: IsModExpr exN exM n m
  => exN -> exM
  -> Expr (EModOpResHs n m)
mod = Mod
(%) = Mod

abs
  :: IsUnaryArithExpr exN M.Abs n
  => exN
  -> Expr (UnaryArithResHs M.Abs n)
abs = Abs

neg
  :: IsUnaryArithExpr exN M.Neg n
  => exN
  -> Expr (UnaryArithResHs M.Neg n)
neg = Neg

----------------------------------------------------------------------------
-- Comparison
----------------------------------------------------------------------------

infix 4 ==
eq, (==)
  :: (NiceComparable n, AreExprs c c1 n n)
  => c -> c1
  -> Expr Bool
eq = Eq'
(==) = Eq'

infix 4 /=
neq, (/=)
  :: (NiceComparable n, AreExprs c c1 n n)
  => c -> c1
  -> Expr Bool
neq = Neq
(/=) = Neq

infix 4 <
lt, (<)
  :: (NiceComparable n, AreExprs c c1 n n)
  => c -> c1
  -> Expr Bool
lt = Lt
(<) = Lt

infix 4 >
gt, (>)
  :: (NiceComparable n, AreExprs c c1 n n)
  => c -> c1
  -> Expr Bool
gt = Gt
(>) = Gt

infix 4 <=
le, (<=)
  :: (NiceComparable n, AreExprs c c1 n n)
  => c -> c1
  -> Expr Bool
le = Le
(<=) = Le

infix 4 >=
ge, (>=)
  :: (NiceComparable n, AreExprs c c1 n n)
  => c -> c1
  -> Expr Bool
ge = Ge
(>=) = Ge

----------------------------------------------------------------------------
-- Conversion
----------------------------------------------------------------------------

isNat :: (ex :~> Integer) => ex -> Expr (Maybe Natural)
isNat = IsNat

toInt :: (ex :~> Natural) => ex -> Expr Integer
toInt = Int'

nonZero :: (ex :~> n, NonZero n, KnownValue (Maybe n)) => ex -> Expr (Maybe n)
nonZero = NonZero

----------------------------------------------------------------------------
-- Bits and boolean
----------------------------------------------------------------------------

infixl 8 <<<
lsl, (<<<)
  :: IsArithExpr exN exM M.Lsl n m
  => exN -> exM
  -> Expr (ArithResHs M.Lsl n m)
lsl = Lsl
(<<<) = Lsl

infixl 8 >>>
lsr, (>>>)
  :: IsArithExpr exN exM M.Lsr n m
  => exN -> exM
  -> Expr (ArithResHs M.Lsr n m)
lsr = Lsr
(>>>) = Lsr

infixr 2 ||
or, (||)
  :: IsArithExpr exN exM M.Or n m
  => exN -> exM
  -> Expr (ArithResHs M.Or n m)
or = Or
(||) = Or

infixr 3 &&
and, (&&)
  :: IsArithExpr exN exM M.And n m
  => exN -> exM
  -> Expr (ArithResHs M.And n m)
and = And
(&&) = And

infixr 2 ^
xor, (^)
  :: IsArithExpr exN exM M.Xor n m
  => exN -> exM
  -> Expr (ArithResHs M.Xor n m)
xor = Xor
(^) = Xor

not
  :: IsUnaryArithExpr exN M.Not n
  => exN
  -> Expr (UnaryArithResHs M.Not n)
not = Not

----------------------------------------------------------------------------
-- Serialization
----------------------------------------------------------------------------

pack :: (IsExpr ex a, NicePackedValue a) => ex -> Expr ByteString
pack = Pack

unpack :: (NiceUnpackedValue a, exb :~> ByteString) => exb -> Expr (Maybe a)
unpack = Unpack

----------------------------------------------------------------------------
-- Pairs
----------------------------------------------------------------------------

pair :: (AreExprs ex1 ex2 n m, KnownValue (n, m)) => ex1 -> ex2 -> Expr (n, m)
pair = Pair

car, fst :: (op :~> (n, m), KnownValue n) => op -> Expr n
car = Fst
fst = Fst

cdr, snd :: (op :~> (n, m), KnownValue m) => op -> Expr m
cdr = Snd
snd = Snd

----------------------------------------------------------------------------
-- Maybe
----------------------------------------------------------------------------

some :: (ex :~> t, KnownValue (Maybe t)) => ex -> Expr (Maybe t)
some = Some

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'

left :: (ex :~> y, KnownValue x, KnownValue (Either y x)) => ex -> Expr (Either y x)
left = Left'

----------------------------------------------------------------------------
-- Bytes and string
----------------------------------------------------------------------------

slice
  :: ( an :~> Natural
     , bn :~> Natural
     , IsSliceExpr ex c
     )
  => (an, bn) -> ex
  -> Expr (Maybe c)
slice (a, b) = Slice a b

infixr 6 <>
concat, (<>)
  :: IsConcatExpr exN1 exN2 n
  => exN1 -> exN2
  -> Expr n
concat = Concat
(<>) = Concat

----------------------------------------------------------------------------
-- List
----------------------------------------------------------------------------

infixr 5 .:
cons, (.:) :: (ex1 :~> a, ex2 :~> List a) => ex1 -> ex2 -> Expr (List a)
cons = Cons
(.:) = Cons

concatAll :: IsConcatListExpr exN n => exN -> Expr n
concatAll = Concat'

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 c k (some v)

instance (NiceComparable k, exKey :~> k, exValue :~> v)
    => ExprInsertable (Map k v) (exKey, exValue) where
  insert (k, v) c = Update c k (some v)

instance (NiceComparable a, exKey :~> a) => ExprInsertable (Set a) exKey where
  insert k c = Update c k True

-- | 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 c k none

instance (NiceComparable k, KnownValue v) => ExprRemovable (Map k v) where
  remove k c = Update c k none

instance NiceComparable a => ExprRemovable (Set a) where
  remove k c = Update c k False

get
  :: IsGetExpr exKey exMap map
  => exKey -> exMap
  -> Expr (Maybe (GetOpValHs map))
get = Get

update
  :: IsUpdExpr exKey exVal exMap map
  => (exKey, exVal) -> exMap
  -> Expr map
update (k, v) s = Update s k v

mem
  :: IsMemExpr exKey exN n
  => exKey -> exN
  -> Expr Bool
mem = Mem

size
  :: IsSizeExpr exN n
  => exN -> Expr Natural
size = Size

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 key 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 key val 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 key val 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 key val 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 key 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 key store
(?@) = uMem

----------------------------------------------------------------------------
-- 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 = Name

unName :: (ex :~> (name :! t), KnownValue t) => Label name -> ex -> Expr t
unName = UnName

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

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

self
  :: ( NiceParameterFull p
     , NoExplicitDefaultEntrypoint p
     )
  => Expr (ContractRef p)
self = Self

contractAddress :: (exc :~> ContractRef p) => exc -> Expr Address
contractAddress = ContractAddress

contractCallingUnsafe
  :: ( NiceParameter arg
     , exAddr :~> Address
     )
  => EpName -> exAddr -> Expr (Maybe (ContractRef arg))
contractCallingUnsafe = ContractCallingUnsafe

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

implicitAccount
  :: (exkh :~> KeyHash)
  => exkh
  -> Expr (ContractRef ())
implicitAccount = ImplicitAccount

convertEpAddressToContract
  :: ( NiceParameter p
     , epExpr :~> EpAddress
     )
  => epExpr -> Expr (Maybe (ContractRef p))
convertEpAddressToContract = ConvertEpAddressToContract

makeView
  :: ( KnownValue (View a r)
     , exa :~> a
     , exCRef :~> ContractRef r
     )
  => exa -> exCRef -> Expr (View a r)
makeView = MakeView

makeVoid
  :: ( KnownValue (Void_ a b)
     , exa :~> a
     , exCRef :~> Lambda b b
     )
  => exa -> exCRef -> Expr (Void_ a b)
makeVoid = MakeVoid

----------------------------------------------------------------------------
-- 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 = CheckSignature

sha256 :: (hashExpr :~> ByteString) => hashExpr -> Expr ByteString
sha256 = Sha256

sha512 :: (hashExpr :~> ByteString) => hashExpr -> Expr ByteString
sha512 = Sha512

blake2b :: (hashExpr :~> ByteString) => hashExpr -> Expr ByteString
blake2b = Blake2b

hashKey :: (keyExpr :~> PublicKey) => keyExpr -> Expr KeyHash
hashKey = HashKey

chainId :: Expr ChainId
chainId = ChainId

balance :: Expr Mutez
balance = Balance