1
1
mirror of https://github.com/anoma/juvix.git synced 2025-01-05 22:46:08 +03:00

Add builtin ByteArray type (#2933)

This PR adds support for a builtin `ByteArray` type and associated
functions for constructing a `ByteArray` from a list of bytes and a
function to query the size of the `ByteArray`. It is only available in
the Anoma backend.

In Core / Tree, ByteArray constant is stored using a Haskell ByteString.

In Anoma the ByteArray is stored as a cell where the head is the length
of the ByteArray and the tail is an integer is an integer formed by
concatenating the bytes in the array using little-endian byte ordering.

The Nock for constructing a `ByteArray` uses the `length`, `add`,
`folder` and `lsh` functions from the Anoma hoon stdlib. See the [code
comment](fa068a30e7/src/Juvix/Compiler/Nockma/StdlibFunction.hs (L37))
for more details.

Example:

```
module test082;

import Stdlib.Prelude open;
import Stdlib.Debug.Trace open;

builtin bytearray
axiom ByteArray : Type;

builtin bytearray-from-list-byte
axiom mkByteArray : List Byte -> ByteArray;

builtin bytearray-size
axiom size : ByteArray -> Nat;

bs0 : ByteArray := mkByteArray [];

bs1 : ByteArray := mkByteArray [0x0; 0x0; 0x0];

bs2 : ByteArray := mkByteArray [0x1; 0x0; 0x0; 0x0];

bs3 : ByteArray := mkByteArray [0x2; 0x1];

bs4 : ByteArray := mkByteArray [0x100];

main : ByteArray :=
  trace (size bs0)
   >-> trace bs0
   >-> trace (size bs1)
    >-> trace bs1
    >-> trace (size bs2)
    >-> trace bs2
    >-> trace (size bs3)
    >-> trace bs3
    >-> trace (size bs4)
    >-> bs4;
```

Output using `tests/Anoma/Compilation/positive/test082.juvix`

```
$ juvix compile anoma -g test082.juvix
$ juvix dev nockma run test082.pretty.nockma
0
[0 0]
3
[3 0]
4
[4 1]
2
[2 258]
1
[1 0]
```
This commit is contained in:
Paul Cadman 2024-08-13 11:13:27 +01:00 committed by GitHub
parent 2b5ece7b28
commit ce5c2c5c55
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
72 changed files with 723 additions and 46 deletions

View File

@ -110,6 +110,7 @@ getConstantType = \case
ConstUnit -> TyUnit
ConstVoid -> TyVoid
ConstUInt8 {} -> mkTypeUInt8
ConstByteArray {} -> TyByteArray
getValueType' :: (Member (Error AsmError) r) => Maybe Location -> InfoTable -> Memory -> Value -> Sem r Type
getValueType' loc tab mem = \case

View File

@ -39,6 +39,7 @@ genCode fi =
Tree.Binop x -> goBinop isTail x
Tree.Unop x -> goUnop isTail x
Tree.Cairo x -> goCairo isTail x
Tree.ByteArray {} -> error "ByteArray instructions are not supported in the Asm backend"
Tree.Anoma {} -> error "Anoma instructions are not supported in the Asm backend"
Tree.Constant x -> goConstant isTail x
Tree.MemRef x -> goMemRef isTail x

View File

@ -350,6 +350,7 @@ fromRegInstr bNoStack info = \case
Reg.ConstUnit -> macroVar "OBJ_UNIT"
Reg.ConstVoid -> macroVar "OBJ_VOID"
Reg.ConstUInt8 x -> macroCall "make_smallint" [integer x]
Reg.ConstByteArray {} -> impossible
fromPrealloc :: Reg.InstrPrealloc -> Statement
fromPrealloc Reg.InstrPrealloc {..} =

View File

@ -251,6 +251,7 @@ fromRegInstr info = \case
Reg.ConstUnit -> mkVar "OBJ_UNIT"
Reg.ConstVoid -> mkVar "OBJ_VOID"
Reg.ConstUInt8 x -> mkCall "make_smallint" [mkInteger x]
Reg.ConstByteArray {} -> impossible
fromAlloc :: Reg.InstrAlloc -> [Statement]
fromAlloc Reg.InstrAlloc {..} =

View File

@ -13,12 +13,14 @@ module Juvix.Compiler.Builtins
module Juvix.Compiler.Builtins.Anoma,
module Juvix.Compiler.Builtins.Cairo,
module Juvix.Compiler.Builtins.Byte,
module Juvix.Compiler.Builtins.ByteArray,
)
where
import Juvix.Compiler.Builtins.Anoma
import Juvix.Compiler.Builtins.Bool
import Juvix.Compiler.Builtins.Byte
import Juvix.Compiler.Builtins.ByteArray
import Juvix.Compiler.Builtins.Cairo
import Juvix.Compiler.Builtins.Control
import Juvix.Compiler.Builtins.Debug

View File

@ -0,0 +1,27 @@
module Juvix.Compiler.Builtins.ByteArray where
import Juvix.Compiler.Builtins.Effect
import Juvix.Compiler.Internal.Extra
import Juvix.Prelude
registerByteArray :: (Member Builtins r) => AxiomDef -> Sem r ()
registerByteArray d = do
unless (isSmallUniverse' (d ^. axiomType)) (error "ByteArray should be in the small universe")
registerBuiltin BuiltinByteArray (d ^. axiomName)
registerByteArrayFromListByte :: (Member Builtins r) => AxiomDef -> Sem r ()
registerByteArrayFromListByte d = do
let loc = getLoc d
byte_ <- getBuiltinName loc BuiltinByte
list_ <- getBuiltinName loc BuiltinList
byteArray <- getBuiltinName loc BuiltinByteArray
unless (d ^. axiomType == (list_ @@ byte_ --> byteArray)) (error "bytearray-from-list-byte has the wrong type")
registerBuiltin BuiltinByteArrayFromListByte (d ^. axiomName)
registerByteArrayLength :: (Member Builtins r) => AxiomDef -> Sem r ()
registerByteArrayLength d = do
let loc = getLoc d
byteArray <- getBuiltinName loc BuiltinByteArray
nat_ <- getBuiltinName loc BuiltinNat
unless (d ^. axiomType == (byteArray --> nat_)) (error "bytearray-length has the wrong type")
registerBuiltin BuiltinByteArrayLength (d ^. axiomName)

View File

@ -250,6 +250,7 @@ fromReg tab = mkResult $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolI
Reg.ConstVoid -> 0
Reg.ConstString {} -> unsupported "strings"
Reg.ConstUInt8 {} -> unsupported "uint8"
Reg.ConstByteArray {} -> unsupported "bytearray"
mkLoad :: Reg.ConstrField -> Sem r RValue
mkLoad Reg.ConstrField {..} = do

View File

@ -222,6 +222,9 @@ data BuiltinAxiom
| BuiltinByteEq
| BuiltinByteToNat
| BuiltinByteFromNat
| BuiltinByteArray
| BuiltinByteArrayFromListByte
| BuiltinByteArrayLength
deriving stock (Show, Eq, Ord, Enum, Bounded, Generic, Data)
instance HasNameKind BuiltinAxiom where
@ -263,7 +266,9 @@ instance HasNameKind BuiltinAxiom where
BuiltinByteEq -> KNameFunction
BuiltinByteToNat -> KNameFunction
BuiltinByteFromNat -> KNameFunction
BuiltinByteArray -> KNameInductive
BuiltinByteArrayFromListByte -> KNameFunction
BuiltinByteArrayLength -> KNameFunction
getNameKindPretty :: BuiltinAxiom -> NameKind
getNameKindPretty = getNameKind
@ -312,6 +317,9 @@ instance Pretty BuiltinAxiom where
BuiltinByteEq -> Str.byteEq
BuiltinByteToNat -> Str.byteToNat
BuiltinByteFromNat -> Str.byteFromNat
BuiltinByteArray -> Str.byteArray
BuiltinByteArrayFromListByte -> Str.byteArrayFromListByte
BuiltinByteArrayLength -> Str.byteArrayLength
data BuiltinType
= BuiltinTypeInductive BuiltinInductive

View File

@ -288,6 +288,17 @@ declareMaybeBuiltins = do
(tagJust, "just", mkPi' mkDynamic', Just BuiltinMaybeJust)
]
declareListBuiltins :: (Member InfoTableBuilder r) => Sem r ()
declareListBuiltins = do
tagNil <- freshTag
tagCons <- freshTag
declareInductiveBuiltins
"BuiltinList"
(Just (BuiltinTypeInductive BuiltinList))
[ (tagNil, "builtinListNil", mkPis' [mkSmallUniv], Just BuiltinListNil),
(tagCons, "builtinListCons", \x -> mkPis' [mkSmallUniv, mkDynamic', x] x, Just BuiltinListCons)
]
reserveLiteralIntToNatSymbol :: (Member InfoTableBuilder r) => Sem r ()
reserveLiteralIntToNatSymbol = do
sym <- freshSymbol

View File

@ -2,6 +2,7 @@ module Juvix.Compiler.Core.Evaluator where
import Control.Exception qualified as Exception
import Crypto.Sign.Ed25519 qualified as E
import Data.ByteString qualified as BS
import Data.HashMap.Strict qualified as HashMap
import Data.Serialize qualified as S
import GHC.Base (seq)
@ -215,6 +216,8 @@ geval opts herr tab env0 = eval' env0
OpRandomEcPoint -> randomEcPointOp
OpUInt8ToInt -> uint8ToIntOp
OpUInt8FromInt -> uint8FromIntOp
OpByteArrayFromListByte -> byteArrayFromListByteOp
OpByteArrayLength -> byteArrayLengthOp
where
err :: Text -> a
err msg = evalError msg n
@ -533,6 +536,30 @@ geval opts herr tab env0 = eval' env0
. uint8FromNode
$ v
{-# INLINE uint8ToIntOp #-}
byteArrayFromListByteOp :: [Node] -> Node
byteArrayFromListByteOp =
unary $ \node ->
let !v = eval' env node
in nodeFromByteString
. BS.pack
. fromMaybe (evalError "expected list byte" v)
. listUInt8FromNode
$ v
{-# INLINE byteArrayFromListByteOp #-}
byteArrayLengthOp :: [Node] -> Node
byteArrayLengthOp =
unary $ \node ->
let !v = eval' env node
in nodeFromInteger
. fromIntegral
. BS.length
. fromMaybe (evalError "expected bytearray" v)
. byteArrayFromNode
$ v
{-# INLINE byteArrayLengthOp #-}
{-# INLINE applyBuiltin #-}
-- secretKey, publicKey are not encoded with their length as
@ -558,6 +585,10 @@ geval opts herr tab env0 = eval' env0
nodeFromUInt8 !w = mkConstant' (ConstUInt8 w)
{-# INLINE nodeFromUInt8 #-}
nodeFromByteString :: ByteString -> Node
nodeFromByteString !b = mkConstant' (ConstByteArray b)
{-# INLINE nodeFromByteString #-}
nodeFromBool :: Bool -> Node
nodeFromBool b = mkConstr' (BuiltinTag tag) []
where
@ -567,10 +598,10 @@ geval opts herr tab env0 = eval' env0
{-# INLINE nodeFromBool #-}
mkBuiltinConstructor :: BuiltinConstructor -> [Node] -> Maybe Node
mkBuiltinConstructor ctor args =
(\tag -> mkConstr' tag args)
. (^. constructorTag)
<$> lookupTabBuiltinConstructor tab ctor
mkBuiltinConstructor ctor args = (\tag -> mkConstr' tag args) <$> builtinConstructorTag ctor
builtinConstructorTag :: BuiltinConstructor -> Maybe Tag
builtinConstructorTag ctor = (^. constructorTag) <$> lookupTabBuiltinConstructor tab ctor
nodeMaybeNothing :: Node
nodeMaybeNothing =
@ -611,6 +642,29 @@ geval opts herr tab env0 = eval' env0
_ -> Nothing
{-# INLINE uint8FromNode #-}
byteArrayFromNode :: Node -> Maybe ByteString
byteArrayFromNode = \case
NCst (Constant _ (ConstByteArray b)) -> Just b
_ -> Nothing
{-# INLINE byteArrayFromNode #-}
listUInt8FromNode :: Node -> Maybe [Word8]
listUInt8FromNode = \case
NCtr (Constr _ t xs) -> do
consTag <- builtinConstructorTag BuiltinListCons
nilTag <- builtinConstructorTag BuiltinListNil
if
| t == nilTag -> return []
| t == consTag -> case (filter (not . isType') xs) of
(hd : tl) -> do
uint8Hd <- uint8FromNode hd
uint8Tl <- concatMapM listUInt8FromNode tl
return (uint8Hd : uint8Tl)
_ -> Nothing
| otherwise -> Nothing
_ -> Nothing
{-# INLINE listUInt8FromNode #-}
printNode :: Node -> Text
printNode = \case
NCst (Constant _ (ConstString s)) -> s

View File

@ -200,6 +200,12 @@ mkTypeUInt8 i = mkTypePrim i primitiveUInt8
mkTypeUInt8' :: Type
mkTypeUInt8' = mkTypeUInt8 Info.empty
mkTypeByteArray :: Info -> Type
mkTypeByteArray i = mkTypePrim i PrimByteArray
mkTypeByteArray' :: Type
mkTypeByteArray' = mkTypeByteArray Info.empty
mkDynamic :: Info -> Type
mkDynamic i = NDyn (DynamicTy i)

View File

@ -435,6 +435,8 @@ builtinOpArgTypes = \case
OpRandomEcPoint -> []
OpUInt8ToInt -> [mkTypeUInt8']
OpUInt8FromInt -> [mkTypeInteger']
OpByteArrayFromListByte -> [mkDynamic']
OpByteArrayLength -> [mkTypeByteArray']
translateCase :: (Node -> Node -> Node -> a) -> a -> Case -> a
translateCase translateIfFun dflt Case {..} = case _caseBranches of

View File

@ -19,6 +19,8 @@ import Juvix.Data.Keyword.All
kwBind,
kwBottom,
kwBuiltin,
kwByteArrayFromListByte,
kwByteArrayLength,
kwCase,
kwColon,
kwComma,

View File

@ -38,6 +38,8 @@ data BuiltinOp
| OpRandomEcPoint
| OpUInt8ToInt
| OpUInt8FromInt
| OpByteArrayFromListByte
| OpByteArrayLength
deriving stock (Eq, Generic)
instance Serialize BuiltinOp
@ -94,6 +96,8 @@ builtinOpArgsNum = \case
OpRandomEcPoint -> 0
OpUInt8ToInt -> 1
OpUInt8FromInt -> 1
OpByteArrayFromListByte -> 1
OpByteArrayLength -> 1
builtinConstrArgsNum :: BuiltinDataTag -> Int
builtinConstrArgsNum = \case
@ -139,6 +143,8 @@ builtinIsFoldable = \case
OpRandomEcPoint -> False
OpUInt8ToInt -> True
OpUInt8FromInt -> True
OpByteArrayFromListByte -> False
OpByteArrayLength -> False
builtinIsCairo :: BuiltinOp -> Bool
builtinIsCairo op = op `elem` builtinsCairo
@ -146,6 +152,9 @@ builtinIsCairo op = op `elem` builtinsCairo
builtinIsAnoma :: BuiltinOp -> Bool
builtinIsAnoma op = op `elem` builtinsAnoma
builtinIsByteArray :: BuiltinOp -> Bool
builtinIsByteArray op = op `elem` builtinsByteArray
builtinsString :: [BuiltinOp]
builtinsString = [OpStrConcat, OpStrToInt, OpShow]
@ -165,3 +174,6 @@ builtinsAnoma =
builtinsUInt8 :: [BuiltinOp]
builtinsUInt8 = [OpUInt8FromInt, OpUInt8ToInt]
builtinsByteArray :: [BuiltinOp]
builtinsByteArray = [OpByteArrayFromListByte, OpByteArrayLength]

View File

@ -39,6 +39,7 @@ data ConstantValue
| ConstField !FField
| ConstString !Text
| ConstUInt8 !Word8
| ConstByteArray !ByteString
deriving stock (Eq, Generic)
-- | Info about a single binder. Associated with Lambda, Pi, Let, Case or Match.

View File

@ -15,6 +15,7 @@ data Primitive
| PrimBool PrimBoolInfo
| PrimString
| PrimField
| PrimByteArray
deriving stock (Eq, Generic)
primitiveUInt8 :: Primitive

View File

@ -5,6 +5,7 @@ module Juvix.Compiler.Core.Pretty.Base
)
where
import Data.ByteString qualified as BS
import Data.HashMap.Strict qualified as HashMap
import Data.Map.Strict qualified as Map
import Juvix.Compiler.Core.Data.BinderList qualified as BL
@ -64,6 +65,8 @@ instance PrettyCode BuiltinOp where
OpRandomEcPoint -> return primRandomEcPoint
OpUInt8ToInt -> return primUInt8ToInt
OpUInt8FromInt -> return primFieldFromInt
OpByteArrayFromListByte -> return primByteArrayFromListByte
OpByteArrayLength -> return primByteArrayLength
instance PrettyCode BuiltinDataTag where
ppCode = \case
@ -81,10 +84,12 @@ instance PrettyCode Tag where
instance PrettyCode Primitive where
ppCode = \case
p@(PrimInteger _) | p == primitiveUInt8 -> return $ annotate (AnnKind KNameInductive) (pretty ("UInt8" :: String))
PrimInteger _ -> return $ annotate (AnnKind KNameInductive) (pretty ("Int" :: String))
PrimField -> return $ annotate (AnnKind KNameInductive) (pretty ("Field" :: String))
PrimBool _ -> return $ annotate (AnnKind KNameInductive) (pretty ("Bool" :: String))
PrimString -> return $ annotate (AnnKind KNameInductive) (pretty ("String" :: String))
PrimByteArray -> return $ annotate (AnnKind KNameInductive) (pretty ("ByteArray" :: String))
ppName :: NameKind -> Text -> Sem r (Doc Ann)
ppName kind name = return $ annotate (AnnKind kind) (pretty name)
@ -104,6 +109,7 @@ ppCodeVar' name v = do
else return name'
instance PrettyCode ConstantValue where
ppCode :: forall r. (Member (Reader Options) r) => ConstantValue -> Sem r (Doc Ann)
ppCode = \case
ConstInteger int ->
return $ annotate AnnLiteralInteger (pretty int)
@ -113,13 +119,31 @@ instance PrettyCode ConstantValue where
return $ annotate AnnLiteralInteger (pretty i)
ConstString txt ->
return $ annotate AnnLiteralString (pretty (show txt :: String))
ConstByteArray bs -> do
let bytes = ConstUInt8 <$> BS.unpack bs
codeBs <- mapM ppCode bytes
bytesList <- go codeBs
op <- ppCode OpByteArrayFromListByte
return (op <+> bytesList)
where
go :: [Doc Ann] -> Sem r (Doc Ann)
go xs = do
uint8Ty <- ppCode mkTypeUInt8'
case xs of
[] -> return (parens (kwBuiltinNil <+> uint8Ty))
(d : ds) -> do
next <- go ds
return (parens (kwBuiltinCons <+> uint8Ty <+> d <+> next))
instance PrettyCode Word8 where
ppCode i = return (pretty i <> "u8")
instance PrettyCode (Constant' i) where
ppCode Constant {..} = case _constantValue of
ConstField fld ->
return $ annotate AnnLiteralInteger (pretty fld <> "F")
ConstUInt8 i ->
return $ annotate AnnLiteralInteger (pretty i <> "u8")
annotate AnnLiteralInteger <$> ppCode i
_ -> ppCode _constantValue
instance (PrettyCode a, HasAtomicity a) => PrettyCode (App' i a) where
@ -548,7 +572,7 @@ instance PrettyCode InfoTable where
shouldPrintInductive :: Maybe BuiltinType -> Bool
shouldPrintInductive = \case
Just (BuiltinTypeInductive i) -> case i of
BuiltinList -> True
BuiltinList -> False
BuiltinMaybe -> False
BuiltinPair -> True
BuiltinPoseidonState -> True
@ -761,6 +785,12 @@ primUInt8FromInt = primitive Str.itou8
primFieldToInt :: Doc Ann
primFieldToInt = primitive Str.ftoi
primByteArrayFromListByte :: Doc Ann
primByteArrayFromListByte = primitive Str.byteArrayFromListByte
primByteArrayLength :: Doc Ann
primByteArrayLength = primitive Str.byteArrayLength
primLess :: Doc Ann
primLess = primitive Str.less
@ -868,3 +898,9 @@ kwBottomAscii = keyword Str.bottomAscii
kwBottom :: Doc Ann
kwBottom = keyword Str.bottom
kwBuiltinCons :: Doc Ann
kwBuiltinCons = constructor Str.builtinListCons
kwBuiltinNil :: Doc Ann
kwBuiltinNil = constructor Str.builtinListNil

View File

@ -71,6 +71,8 @@ checkBuiltins allowUntypedFail = dmapRM go
throw $ unsupportedError "cairo" node (getInfoLocation _builtinAppInfo)
| _builtinAppOp `elem` builtinsAnoma ->
throw $ unsupportedError "anoma" node (getInfoLocation _builtinAppInfo)
| _builtinAppOp `elem` builtinsByteArray ->
throw $ unsupportedError "bytearray" node (getInfoLocation _builtinAppInfo)
| otherwise ->
return $ Recur node
_ -> return $ Recur node

View File

@ -12,7 +12,7 @@ checkCairo md = do
checkMainType
checkNoAxioms md
mapAllNodesM checkNoIO md
mapAllNodesM (checkBuiltins' (builtinsString ++ builtinsUInt8) [PrimString, primitiveUInt8]) md
mapAllNodesM (checkBuiltins' (builtinsString ++ builtinsUInt8 ++ builtinsByteArray) [PrimString, primitiveUInt8, PrimByteArray]) md
where
checkMainType :: Sem r ()
checkMainType =
@ -60,6 +60,7 @@ checkCairo md = do
PrimBool {} -> True
PrimField {} -> True
PrimString {} -> False
PrimByteArray {} -> False
isRecordOrList :: TypeConstr -> Bool
isRecordOrList TypeConstr {..} = case ii ^. inductiveBuiltin of

View File

@ -10,4 +10,4 @@ checkExec md = do
checkNoAxioms md
checkMainExists md
checkMainTypeExec md
mapAllNodesM (checkBuiltins' (builtinsCairo ++ builtinsAnoma) []) md
mapAllNodesM (checkBuiltins' (builtinsCairo ++ builtinsAnoma ++ builtinsByteArray) [PrimByteArray]) md

View File

@ -35,6 +35,7 @@ computeNodeTypeInfo md = umapL go
ConstField {} -> mkTypeField'
ConstString {} -> mkTypeString'
ConstUInt8 {} -> mkTypeUInt8'
ConstByteArray {} -> mkDynamic'
NApp {} ->
let (fn, args) = unfoldApps' node
fty = Info.getNodeType fn
@ -85,6 +86,8 @@ computeNodeTypeInfo md = umapL go
_ -> error "incorrect random_ec_point builtin application"
OpUInt8ToInt -> mkTypeInteger'
OpUInt8FromInt -> mkTypeUInt8'
OpByteArrayFromListByte -> mkDynamic'
OpByteArrayLength -> mkTypeInteger'
NCtr Constr {..} ->
let ci = lookupConstructorInfo md _constrTag
ii = lookupInductiveInfo md (ci ^. constructorInductive)

View File

@ -20,6 +20,7 @@ convertNode md = umap go
Just (BuiltinTypeAxiom BuiltinString) -> mkTypeString'
Just (BuiltinTypeAxiom BuiltinField) -> mkTypeField'
Just (BuiltinTypeAxiom BuiltinByte) -> mkTypeUInt8'
Just (BuiltinTypeAxiom BuiltinByteArray) -> mkTypeByteArray'
_ -> node
where
ii = lookupInductiveInfo md _typeConstrSymbol

View File

@ -618,6 +618,9 @@ goAxiomInductive a = whenJust (a ^. Internal.axiomBuiltin) builtinInductive
Internal.BuiltinByteEq -> return ()
Internal.BuiltinByteToNat -> return ()
Internal.BuiltinByteFromNat -> return ()
Internal.BuiltinByteArray -> registerInductiveAxiom (Just BuiltinByteArray) []
Internal.BuiltinByteArrayFromListByte -> return ()
Internal.BuiltinByteArrayLength -> return ()
registerInductiveAxiom :: Maybe BuiltinAxiom -> [(Tag, Text, Type -> Type, Maybe BuiltinConstructor)] -> Sem r ()
registerInductiveAxiom ax ctrs = do
@ -826,6 +829,11 @@ goAxiomDef a = maybe goAxiomNotBuiltin builtinBody (a ^. Internal.axiomBuiltin)
registerAxiomDef (mkLambda' mkTypeUInt8' (mkBuiltinApp' OpUInt8ToInt [mkVar' 0]))
Internal.BuiltinByteFromNat ->
registerAxiomDef (mkLambda' mkTypeInteger' (mkBuiltinApp' OpUInt8FromInt [mkVar' 0]))
Internal.BuiltinByteArray -> return ()
Internal.BuiltinByteArrayFromListByte ->
registerAxiomDef (mkLambda' mkDynamic' (mkBuiltinApp' OpByteArrayFromListByte [mkVar' 0]))
Internal.BuiltinByteArrayLength ->
registerAxiomDef (mkLambda' mkTypeInteger' (mkBuiltinApp' OpByteArrayLength [mkVar' 0]))
axiomType' :: Sem r Type
axiomType' = fromTopIndex (goType (a ^. Internal.axiomType))
@ -1227,6 +1235,9 @@ goApplication a = do
Just Internal.BuiltinByteEq -> app
Just Internal.BuiltinByteToNat -> app
Just Internal.BuiltinByteFromNat -> app
Just Internal.BuiltinByteArray -> app
Just Internal.BuiltinByteArrayFromListByte -> app
Just Internal.BuiltinByteArrayLength -> app
Nothing -> app
Internal.ExpressionIden (Internal.IdenFunction n) -> do
funInfoBuiltin <- Internal.getFunctionBuiltinInfo n

View File

@ -79,6 +79,7 @@ parseToplevel = do
lift declareBoolBuiltins
lift declareNatBuiltins
lift declareMaybeBuiltins
lift declareListBuiltins
space
P.endBy statement (kw delimSemicolon)
r <- optional expression
@ -580,6 +581,8 @@ builtinAppExpr varsNum vars = do
<|> (kw kwAnomaVerifyWithMessage $> OpAnomaVerifyWithMessage)
<|> (kw kwAnomaSignDetached $> OpAnomaSignDetached)
<|> (kw kwAnomaVerifyDetached $> OpAnomaVerifyDetached)
<|> (kw kwByteArrayFromListByte $> OpByteArrayFromListByte)
<|> (kw kwByteArrayLength $> OpByteArrayLength)
args <- P.many (atom varsNum vars)
return $ mkBuiltinApp' op args
@ -1119,6 +1122,8 @@ exprNamed varsNum vars = do
"Int" -> return mkTypeInteger'
"Field" -> return mkTypeField'
"String" -> return mkTypeString'
"UInt8" -> return mkTypeUInt8'
"ByteArray" -> return mkDynamic'
_ ->
case HashMap.lookup txt vars of
Just k -> do

View File

@ -112,6 +112,9 @@ fromCore fsize tab =
BuiltinByteEq -> False
BuiltinByteToNat -> False
BuiltinByteFromNat -> False
BuiltinByteArray -> False
BuiltinByteArrayFromListByte -> False
BuiltinByteArrayLength -> False
BuiltinTypeInductive i -> case i of
BuiltinList -> True
BuiltinMaybe -> True

View File

@ -591,6 +591,9 @@ registerBuiltinAxiom d = \case
BuiltinByteEq -> registerByteEq d
BuiltinByteToNat -> registerByteToNat d
BuiltinByteFromNat -> registerByteFromNat d
BuiltinByteArray -> registerByteArray d
BuiltinByteArrayFromListByte -> registerByteArrayFromListByte d
BuiltinByteArrayLength -> registerByteArrayLength d
goInductive ::
(Members '[Reader EntryPoint, Reader DefaultArgsStack, NameIdGen, Reader Pragmas, Builtins, Error ScoperError, State ConstructorInfos, Reader S.InfoTable] r) =>

View File

@ -7,6 +7,7 @@ module Juvix.Compiler.Nockma.Evaluator
where
import Crypto.Sign.Ed25519
import Data.ByteString qualified as BS
import Data.HashMap.Strict qualified as HashMap
import Juvix.Compiler.Nockma.Encoding
import Juvix.Compiler.Nockma.Encoding qualified as Encoding
@ -253,10 +254,36 @@ evalProfile inistack initerm =
StdlibCatBytes -> case args' of
TCell (TermAtom arg1) (TermAtom arg2) -> goCat arg1 arg2
_ -> error "expected a term with two atoms"
StdlibFoldBytes -> TermAtom <$> goFoldBytes args'
StdlibLengthList -> do
let xs = checkTermToList args'
let len = integerToNatural (toInteger (length xs))
TermAtom . mkEmptyAtom <$> fromNatural len
where
goCat :: Atom a -> Atom a -> Sem r (Term a)
goCat arg1 arg2 = TermAtom . setAtomHint AtomHintString <$> atomConcatenateBytes arg1 arg2
goFoldBytes :: Term a -> Sem r (Atom a)
goFoldBytes c = do
bs <- mapM nockNatural (checkTermToListAtom c)
byteStringToAtom (BS.pack (fromIntegral <$> bs))
checkTermToList :: Term a -> [Term a]
checkTermToList = \case
TermAtom x ->
if
| x `nockmaEq` nockNil -> []
| otherwise -> error "expected a list to be terminated by nil"
TermCell c -> c ^. cellLeft : checkTermToList (c ^. cellRight)
checkTermToListAtom :: Term a -> [Atom a]
checkTermToListAtom = map check . checkTermToList
where
check :: Term a -> Atom a
check = \case
TermAtom x -> x
TermCell {} -> error "expect list element to be an atom"
signatureLength :: Int
signatureLength = 64

View File

@ -26,6 +26,7 @@ stdlibPath = \case
StdlibSign -> [nock| [9 10 0 1] |]
StdlibSignDetached -> [nock| [9 23 0 1] |]
StdlibVerify -> [nock| [9 4 0 1] |]
StdlibLengthList -> [nock| [9 1.406 0 31] |]
-- Obtained from the urbit dojo using:
--
-- => anoma !=(~(cat block 3))
@ -33,3 +34,44 @@ stdlibPath = \case
-- The `3` here is because we want to treat each atom as sequences of 2^3
-- bits, i.e bytes.
StdlibCatBytes -> [nock| [8 [9 10 0 7] 9 4 10 [6 7 [0 3] 1 3] 0 2] |]
-- Obtained from the urbit dojo using:
--
-- =>(anoma !=(|=([l=(list @)] (foldr l |=([fst=@ snd=@] (add (~(lsh block 3) 1 snd) fst))))))
--
-- The `3` here is because we want to shift left in byte = 2^3 bit steps.
StdlibFoldBytes ->
[nock|
[ 8
[1 0]
[ 1
8
[9 46 0 127]
9
2
10
[ 6
[0 14]
7
[0 3]
8
[1 0 0]
[ 1
8
[9 20 0 1.023]
9
2
10
[6 [7 [0 3] 8 [8 [9 10 0 127] 9 90 10 [6 7 [0 3] 1 3] 0 2] 9 2 10 [6 [7 [0 3] 1 1] 0 29] 0 2] 0 28]
0
2
]
0
1
]
0
2
]
0
1
]
|]

View File

@ -21,6 +21,8 @@ instance Pretty StdlibFunction where
StdlibSignDetached -> "sign-detached"
StdlibVerify -> "verify"
StdlibCatBytes -> "cat"
StdlibFoldBytes -> "fold-bytes"
StdlibLengthList -> "length-list"
data StdlibFunction
= StdlibDec
@ -39,6 +41,8 @@ data StdlibFunction
| StdlibSignDetached
| StdlibVerify
| StdlibCatBytes
| StdlibFoldBytes
| StdlibLengthList
deriving stock (Show, Lift, Eq, Bounded, Enum, Generic)
instance Hashable StdlibFunction

View File

@ -33,6 +33,7 @@ module Juvix.Compiler.Nockma.Translation.FromTree
)
where
import Data.ByteString qualified as BS
import Juvix.Compiler.Nockma.Encoding
import Juvix.Compiler.Nockma.Language.Path
import Juvix.Compiler.Nockma.Pretty
@ -360,6 +361,7 @@ compile :: forall r. (Members '[Reader FunctionCtx, Reader CompilerCtx] r) => Tr
compile = \case
Tree.Binop b -> goBinop b
Tree.Unop b -> goUnop b
Tree.ByteArray b -> goByteArrayOp b
Tree.Cairo {} -> cairoErr
Tree.Anoma b -> goAnomaOp b
Tree.Constant c -> return (goConstant (c ^. Tree.nodeConstant))
@ -441,6 +443,7 @@ compile = \case
Tree.ConstVoid -> OpQuote # constVoid
Tree.ConstField {} -> fieldErr
Tree.ConstUInt8 i -> nockIntegralLiteral i
Tree.ConstByteArray bs -> OpQuote # (toNock @Natural (fromIntegral (BS.length bs)) # toNock (byteStringToNatural bs))
goConstString :: Text -> Term Natural
goConstString t =
@ -492,6 +495,18 @@ compile = \case
Tree.OpAnomaVerifyWithMessage -> return (goAnomaVerifyWithMessage args)
Tree.OpAnomaSignDetached -> return (goAnomaSignDetached args)
goByteArrayOp :: Tree.NodeByteArray -> Sem r (Term Natural)
goByteArrayOp Tree.NodeByteArray {..} = do
args <- mapM compile _nodeByteArrayArgs
return $ case _nodeByteArrayOpcode of
Tree.OpByteArrayLength -> goByteArrayLength args
Tree.OpByteArrayFromListUInt8 -> callStdlib StdlibLengthList args # callStdlib StdlibFoldBytes args
where
goByteArrayLength :: [Term Natural] -> Term Natural
goByteArrayLength = \case
[ba] -> ba >># opAddress "head-of-the-bytestring" [L]
_ -> impossible
goUnop :: Tree.NodeUnop -> Sem r (Term Natural)
goUnop Tree.NodeUnop {..} = do
arg <- compile _nodeUnopArg

View File

@ -13,6 +13,7 @@ data TransformationId
| FilterUnreachable
| Validate
| CheckNoAnoma
| CheckNoByteArray
deriving stock (Data, Bounded, Enum, Show)
data PipelineId
@ -27,7 +28,7 @@ toNockmaTransformations :: [TransformationId]
toNockmaTransformations = [Validate, Apply, FilterUnreachable, TempHeight]
toAsmTransformations :: [TransformationId]
toAsmTransformations = [Validate, CheckNoAnoma]
toAsmTransformations = [Validate, CheckNoAnoma, CheckNoByteArray]
toCairoAsmTransformations :: [TransformationId]
toCairoAsmTransformations = [Validate, Apply, FilterUnreachable]
@ -43,6 +44,7 @@ instance TransformationId' TransformationId where
FilterUnreachable -> strFilterUnreachable
Validate -> strValidate
CheckNoAnoma -> strCheckNoAnoma
CheckNoByteArray -> strCheckNoByteArray
instance PipelineId' TransformationId PipelineId where
pipelineText :: PipelineId -> Text

View File

@ -34,3 +34,6 @@ strValidate = "validate"
strCheckNoAnoma :: Text
strCheckNoAnoma = "check-no-anoma"
strCheckNoByteArray :: Text
strCheckNoByteArray = "check-no-bytearray"

View File

@ -1,6 +1,7 @@
module Juvix.Compiler.Tree.Evaluator where
import Control.Exception qualified as Exception
import Data.ByteString qualified as BS
import GHC.IO (unsafePerformIO)
import GHC.Show qualified as S
import Juvix.Compiler.Core.Data.BinderList qualified as BL
@ -37,6 +38,7 @@ hEval hout tab = eval' [] mempty
eval' args temps node = case node of
Binop x -> goBinop x
Unop x -> goUnop x
ByteArray x -> goByteArrayOp x
Anoma {} -> evalError "unsupported: Anoma builtin"
Cairo {} -> evalError "unsupported: Cairo builtin"
Constant c -> goConstant c
@ -76,6 +78,33 @@ hEval hout tab = eval' [] mempty
OpTrace -> goTrace v
OpFail -> goFail v
goByteArrayOp :: NodeByteArray -> Value
goByteArrayOp NodeByteArray {..} =
case _nodeByteArrayOpcode of
OpByteArrayLength -> case _nodeByteArrayArgs of
[nodeArg] ->
let !arg = eval' args temps nodeArg
in case arg of
(ValByteArray bs) -> ValInteger (fromIntegral (BS.length bs))
_ -> evalError "expected argument to be a ByteString"
_ -> evalError "expected exactly one argument"
OpByteArrayFromListUInt8 -> case _nodeByteArrayArgs of
[nodeArg] ->
let !arg = eval' args temps nodeArg
!listUInt8 :: [Word8] = checkListUInt8 arg
in ValByteArray (BS.pack listUInt8)
_ -> evalError "expected exactly one argument"
where
checkListUInt8 :: Value -> [Word8]
checkListUInt8 = \case
ValConstr c -> case c ^. constrArgs of
-- is nil
[] -> []
-- is cons
[ValUInt8 w, t] -> w : checkListUInt8 t
_ -> evalError "expected either a nullary or a binary constructor"
_ -> evalError "expected a constructor"
goFail :: Value -> Value
goFail v = evalError ("failure: " <> printValue tab v)
@ -232,6 +261,7 @@ valueToNode = \case
_nodeAllocClosureArgs = map valueToNode _closureArgs
}
ValUInt8 i -> mkConst $ ConstUInt8 i
ValByteArray b -> mkConst $ ConstByteArray b
hEvalIO :: (MonadIO m) => Handle -> Handle -> InfoTable -> FunctionInfo -> m Value
hEvalIO hin hout infoTable funInfo = do

View File

@ -130,6 +130,7 @@ constantToValue = \case
ConstUnit -> ValUnit
ConstVoid -> ValVoid
ConstUInt8 i -> ValUInt8 i
ConstByteArray b -> ValByteArray b
valueToConstant :: Value -> Constant
valueToConstant = \case

View File

@ -1,6 +1,7 @@
module Juvix.Compiler.Tree.EvaluatorEff (eval, hEvalIOEither) where
import Control.Exception qualified as Exception
import Data.ByteString qualified as BS
import Juvix.Compiler.Core.Data.BinderList qualified as BL
import Juvix.Compiler.Tree.Data.InfoTable
import Juvix.Compiler.Tree.Error
@ -33,6 +34,7 @@ eval tab = runReader emptyEvalCtx . eval'
eval' node = case node of
Binop x -> goBinop x
Unop x -> goUnop x
ByteArray x -> goByteArrayOp x
Anoma {} -> evalError "unsupported: Anoma builtins"
Cairo {} -> evalError "unsupported: Cairo builtins"
Constant c -> return (goConstant c)
@ -71,6 +73,33 @@ eval tab = runReader emptyEvalCtx . eval'
OpTrace -> goTrace v
OpFail -> goFail v
goByteArrayOp :: NodeByteArray -> Sem r' Value
goByteArrayOp NodeByteArray {..} =
case _nodeByteArrayOpcode of
OpByteArrayLength -> case _nodeByteArrayArgs of
[nodeArg] -> do
arg <- eval' nodeArg
case arg of
(ValByteArray bs) -> return $ ValInteger (fromIntegral (BS.length bs))
_ -> evalError "expected argument to be a ByteString"
_ -> evalError "expected exactly one argument"
OpByteArrayFromListUInt8 -> case _nodeByteArrayArgs of
[nodeArg] -> do
arg <- eval' nodeArg
listUInt8 :: [Word8] <- checkListUInt8 arg
return $ ValByteArray (BS.pack listUInt8)
_ -> evalError "expected exactly one argument"
where
checkListUInt8 :: Value -> Sem r' [Word8]
checkListUInt8 = \case
ValConstr c -> case c ^. constrArgs of
-- is nil
[] -> return []
-- is cons
[ValUInt8 w, t] -> (w :) <$> checkListUInt8 t
_ -> evalError "expected either a nullary or a binary constructor"
_ -> evalError "expected a constructor"
goFail :: Value -> Sem r' Value
goFail v = evalError ("failure: " <> printValue tab v)

View File

@ -18,6 +18,7 @@ getNodeInfo :: Node -> NodeInfo
getNodeInfo = \case
Binop NodeBinop {..} -> _nodeBinopInfo
Unop NodeUnop {..} -> _nodeUnopInfo
ByteArray NodeByteArray {..} -> _nodeByteArrayInfo
Cairo NodeCairo {..} -> _nodeCairoInfo
Anoma NodeAnoma {..} -> _nodeAnomaInfo
Constant NodeConstant {..} -> _nodeConstantInfo
@ -142,6 +143,17 @@ destruct = \case
_nodeUnopInfo
}
}
ByteArray NodeByteArray {..} ->
NodeDetails
{ _nodeChildren = map noTempVar _nodeByteArrayArgs,
_nodeReassemble = manyChildren $ \args ->
ByteArray
NodeByteArray
{ _nodeByteArrayArgs = args,
_nodeByteArrayOpcode,
_nodeByteArrayInfo
}
}
Cairo NodeCairo {..} ->
NodeDetails
{ _nodeChildren = map noTempVar _nodeCairoArgs,

View File

@ -4,33 +4,18 @@
{-# HLINT ignore "Avoid restricted extensions" #-}
{-# HLINT ignore "Avoid restricted flags" #-}
module Juvix.Compiler.Tree.Extra.Type where
module Juvix.Compiler.Tree.Extra.Type
( module Juvix.Compiler.Tree.Extra.Type,
module Juvix.Compiler.Tree.Extra.Type.Base,
)
where
import Juvix.Compiler.Tree.Data.InfoTable.Base
import Juvix.Compiler.Tree.Error
import Juvix.Compiler.Tree.Extra.Type.Base
import Juvix.Compiler.Tree.Language.Base
import Juvix.Compiler.Tree.Pretty
mkTypeInteger :: Type
mkTypeInteger = TyInteger (TypeInteger Nothing Nothing)
mkTypeUInt8 :: Type
mkTypeUInt8 = TyInteger (TypeInteger (Just 0) (Just 255))
mkTypeBool :: Type
mkTypeBool = TyBool (TypeBool (BuiltinTag TagTrue) (BuiltinTag TagFalse))
mkTypeConstr :: Symbol -> Tag -> [Type] -> Type
mkTypeConstr ind tag argTypes = TyConstr (TypeConstr ind tag argTypes)
mkTypeInductive :: Symbol -> Type
mkTypeInductive ind = TyInductive (TypeInductive ind)
mkTypeFun :: [Type] -> Type -> Type
mkTypeFun args tgt = case args of
[] -> tgt
a : args' -> TyFun (TypeFun (a :| args') tgt)
unfoldType :: Type -> ([Type], Type)
unfoldType ty = (typeArgs ty, typeTarget ty)
@ -80,6 +65,7 @@ isSubtype ty1 ty2 = case (ty1, ty2) of
(TyBool {}, TyBool {}) -> True
(TyString, TyString) -> True
(TyField, TyField) -> True
(TyByteArray, TyByteArray) -> True
(TyUnit, TyUnit) -> True
(TyVoid, TyVoid) -> True
(TyInductive {}, TyInductive {}) -> ty1 == ty2
@ -93,6 +79,8 @@ isSubtype ty1 ty2 = case (ty1, ty2) of
(_, TyString) -> False
(TyField, _) -> False
(_, TyField) -> False
(TyByteArray, _) -> False
(_, TyByteArray) -> False
(TyBool {}, _) -> False
(_, TyBool {}) -> False
(TyFun {}, _) -> False
@ -149,6 +137,7 @@ unifyTypes ty1 ty2 = case (ty1, ty2) of
| ty1 == ty2 -> return ty1
(TyString, TyString) -> return TyString
(TyField, TyField) -> return TyField
(TyByteArray, TyByteArray) -> return TyByteArray
(TyUnit, TyUnit) -> return TyUnit
(TyVoid, TyVoid) -> return TyVoid
(TyInductive {}, TyInductive {})
@ -163,6 +152,8 @@ unifyTypes ty1 ty2 = case (ty1, ty2) of
(_, TyString) -> err
(TyField, _) -> err
(_, TyField) -> err
(TyByteArray, _) -> err
(_, TyByteArray) -> err
(TyBool {}, _) -> err
(_, TyBool {}) -> err
(TyFun {}, _) -> err

View File

@ -0,0 +1,24 @@
module Juvix.Compiler.Tree.Extra.Type.Base where
import Juvix.Compiler.Tree.Language.Base
import Juvix.Compiler.Tree.Language.Type
mkTypeInteger :: Type
mkTypeInteger = TyInteger (TypeInteger Nothing Nothing)
mkTypeUInt8 :: Type
mkTypeUInt8 = TyInteger (TypeInteger (Just 0) (Just 255))
mkTypeBool :: Type
mkTypeBool = TyBool (TypeBool (BuiltinTag TagTrue) (BuiltinTag TagFalse))
mkTypeConstr :: Symbol -> Tag -> [Type] -> Type
mkTypeConstr ind tag argTypes = TyConstr (TypeConstr ind tag argTypes)
mkTypeInductive :: Symbol -> Type
mkTypeInductive ind = TyInductive (TypeInductive ind)
mkTypeFun :: [Type] -> Type -> Type
mkTypeFun args tgt = case args of
[] -> tgt
a : args' -> TyFun (TypeFun (a :| args') tgt)

View File

@ -19,6 +19,8 @@ import Juvix.Data.Keyword.All
kwArgsNum,
kwAtoi,
kwBr,
kwByteArrayFromListUInt8,
kwByteArrayLength,
kwCAlloc,
kwCCall,
kwCExtend,
@ -88,5 +90,7 @@ allKeywords =
kwAnomaVerifyWithMessage,
kwPoseidon,
kwEcOp,
kwRandomEcPoint
kwRandomEcPoint,
kwByteArrayLength,
kwByteArrayFromListUInt8
]

View File

@ -18,6 +18,7 @@ data Node
| Unop NodeUnop
| Cairo NodeCairo
| Anoma NodeAnoma
| ByteArray NodeByteArray
| -- | A constant value.
Constant NodeConstant
| -- | A memory reference.
@ -82,6 +83,12 @@ data NodeUnop = NodeUnop
_nodeUnopArg :: Node
}
data NodeByteArray = NodeByteArray
{ _nodeByteArrayInfo :: NodeInfo,
_nodeByteArrayOpcode :: ByteArrayOp,
_nodeByteArrayArgs :: [Node]
}
data NodeCairo = NodeCairo
{ _nodeCairoInfo :: NodeInfo,
_nodeCairoOpcode :: CairoOp,

View File

@ -19,6 +19,7 @@ data Constant
| ConstUnit
| ConstVoid
| ConstUInt8 Word8
| ConstByteArray ByteString
deriving stock (Eq, Generic)
instance (Hashable Constant)

View File

@ -66,6 +66,14 @@ data CairoOp
OpCairoRandomEcPoint
deriving stock (Eq)
-- | Builtin ByteArray operations
data ByteArrayOp
= -- | Convert a list of UInt8 to a ByteArray
OpByteArrayFromListUInt8
| -- | Get the size of a ByteArray
OpByteArrayLength
deriving stock (Eq)
cairoOpArgsNum :: CairoOp -> Int
cairoOpArgsNum = \case
OpCairoPoseidon -> 1

View File

@ -8,6 +8,7 @@ data Type
| TyBool TypeBool
| TyString
| TyField
| TyByteArray
| TyUnit
| TyVoid
| TyInductive TypeInductive
@ -81,6 +82,7 @@ instance HasAtomicity Type where
TyField -> Atom
TyUnit -> Atom
TyVoid -> Atom
TyByteArray -> Atom
TyInductive x -> atomicity x
TyConstr x -> atomicity x
TyFun x -> atomicity x

View File

@ -24,6 +24,7 @@ data Value
| ValConstr Constr
| ValClosure Closure
| ValUInt8 Word8
| ValByteArray ByteString
deriving stock (Eq)
data Constr = Constr
@ -62,3 +63,4 @@ instance HasAtomicity Value where
ValConstr c -> atomicity c
ValClosure cl -> atomicity cl
ValUInt8 {} -> Atom
ValByteArray {} -> Atom

View File

@ -4,12 +4,14 @@ module Juvix.Compiler.Tree.Pretty.Base
)
where
import Data.ByteString qualified as BS
import Data.Foldable
import Data.HashMap.Strict qualified as HashMap
import Data.List.NonEmpty qualified as NonEmpty
import Juvix.Compiler.Core.Pretty.Base qualified as Core
import Juvix.Compiler.Internal.Data.Name
import Juvix.Compiler.Tree.Data.InfoTable
import Juvix.Compiler.Tree.Extra.Type.Base
import Juvix.Compiler.Tree.Language
import Juvix.Compiler.Tree.Language.Value
import Juvix.Compiler.Tree.Pretty.Extra
@ -101,6 +103,7 @@ instance PrettyCode Value where
ppCode cl
ValUInt8 i ->
return $ integer i
ValByteArray bs -> ppCode bs
instance PrettyCode TypeInductive where
ppCode :: (Member (Reader Options) r) => TypeInductive -> Sem r (Doc Ann)
@ -139,10 +142,14 @@ instance PrettyCode Type where
ppCode = \case
TyDynamic ->
return $ annotate (AnnKind KNameInductive) Str.mul
TyInteger {} ->
return $ annotate (AnnKind KNameInductive) Str.integer
t@(TyInteger {})
| t == mkTypeUInt8 ->
return $ annotate (AnnKind KNameInductive) Str.uint8
TyInteger {} -> return $ annotate (AnnKind KNameInductive) Str.integer
TyField {} ->
return $ annotate (AnnKind KNameInductive) Str.field
TyByteArray {} ->
return $ annotate (AnnKind KNameInductive) Str.byteArray
TyBool {} ->
return $ annotate (AnnKind KNameInductive) Str.bool
TyString ->
@ -200,7 +207,24 @@ instance PrettyCode Constant where
ConstVoid {} ->
return $ annotate (AnnKind KNameConstructor) Str.void
ConstUInt8 v ->
return $ annotate AnnLiteralInteger (pretty v)
return $ annotate AnnLiteralInteger (pretty v <> "u8")
ConstByteArray v -> do
ctorOp <- ppCode OpByteArrayFromListUInt8
bs <- ppCode v
return (ctorOp <> parens bs)
instance PrettyCode ByteString where
ppCode bs = do
ppBytes <- mapM ppCode (ConstUInt8 <$> BS.unpack bs)
return (toListCtors ppBytes)
where
toListCtors :: [Doc Ann] -> Doc Ann
toListCtors = \case
[] -> nodeAllocCtor Str.nil []
(x : xs) -> nodeAllocCtor Str.cons [x, toListCtors xs]
nodeAllocCtor :: Text -> [Doc Ann] -> Doc Ann
nodeAllocCtor n args = primitive Str.instrAlloc <> brackets (pretty n) <> parens (ppCodeArgs' args)
instance PrettyCode BoolOp where
ppCode op = return $ primitive $ case op of
@ -246,6 +270,12 @@ instance PrettyCode UnaryOp where
OpIntToUInt8 -> Str.instrIntToUInt8
OpUInt8ToInt -> Str.instrUInt8ToInt
instance PrettyCode ByteArrayOp where
ppCode =
return . \case
OpByteArrayFromListUInt8 -> Str.instrByteArrayFromListUInt8
OpByteArrayLength -> Str.instrByteArrayLength
instance PrettyCode CairoOp where
ppCode op = return $ primitive $ case op of
OpCairoPoseidon -> Str.instrPoseidon
@ -286,6 +316,12 @@ instance PrettyCode NodeAnoma where
args <- ppCodeArgs _nodeAnomaArgs
return (op <> parens args)
instance PrettyCode NodeByteArray where
ppCode NodeByteArray {..} = do
op <- ppCode _nodeByteArrayOpcode
args <- ppCodeArgs _nodeByteArrayArgs
return (op <> parens args)
instance PrettyCode NodeConstant where
ppCode NodeConstant {..} = ppCode _nodeConstant
@ -295,7 +331,10 @@ instance PrettyCode NodeMemRef where
ppCodeArgs :: (Member (Reader Options) r) => [Node] -> Sem r (Doc Ann)
ppCodeArgs args = do
args' <- mapM ppCode args
return $ hsep $ punctuate comma args'
return $ ppCodeArgs' args'
ppCodeArgs' :: [Doc Ann] -> Doc Ann
ppCodeArgs' args = hsep $ punctuate comma args
instance PrettyCode NodeAllocConstr where
ppCode NodeAllocConstr {..} = do
@ -378,6 +417,7 @@ instance PrettyCode Node where
ppCode = \case
Binop x -> ppCode x
Unop x -> ppCode x
ByteArray x -> ppCode x
Anoma x -> ppCode x
Cairo x -> ppCode x
Constant x -> ppCode x

View File

@ -11,6 +11,7 @@ import Juvix.Compiler.Tree.Error
import Juvix.Compiler.Tree.Transformation.Apply
import Juvix.Compiler.Tree.Transformation.Base
import Juvix.Compiler.Tree.Transformation.CheckNoAnoma
import Juvix.Compiler.Tree.Transformation.CheckNoByteArray
import Juvix.Compiler.Tree.Transformation.FilterUnreachable
import Juvix.Compiler.Tree.Transformation.IdentityTrans
import Juvix.Compiler.Tree.Transformation.TempHeight
@ -29,3 +30,4 @@ applyTransformations ts tbl = foldM (flip appTrans) tbl ts
FilterUnreachable -> return . filterUnreachable
Validate -> mapError (JuvixError @TreeError) . validate
CheckNoAnoma -> \tbl' -> mapError (JuvixError @TreeError) (checkNoAnoma tbl') $> tbl'
CheckNoByteArray -> \tbl' -> mapError (JuvixError @TreeError) (checkNoByteArray tbl') $> tbl'

View File

@ -0,0 +1,24 @@
module Juvix.Compiler.Tree.Transformation.CheckNoByteArray where
import Juvix.Compiler.Tree.Data.InfoTable
import Juvix.Compiler.Tree.Error
import Juvix.Compiler.Tree.Extra.Recursors
import Juvix.Compiler.Tree.Transformation.Base
checkNoByteArray :: forall r. (Member (Error TreeError) r) => InfoTable -> Sem r ()
checkNoByteArray = walkT checkNode
where
checkNode :: Symbol -> Node -> Sem r ()
checkNode _ = \case
ByteArray NodeByteArray {..} -> case _nodeByteArrayOpcode of
OpByteArrayLength -> unsupportedErr "OpByteArrayLength"
OpByteArrayFromListUInt8 -> unsupportedErr "OpByteArrayFromListUInt8"
where
unsupportedErr :: Text -> Sem r ()
unsupportedErr opName =
throw
TreeError
{ _treeErrorMsg = opName <> " is unsupported",
_treeErrorLoc = _nodeByteArrayInfo ^. nodeInfoLocation
}
_ -> return ()

View File

@ -15,6 +15,7 @@ inferType tab funInfo = goInfer mempty
goInfer bl = \case
Binop x -> goBinop bl x
Unop x -> goUnop bl x
ByteArray x -> goByteArray bl x
Cairo x -> goCairo bl x
Anoma x -> goAnoma bl x
Constant x -> goConst bl x
@ -85,6 +86,11 @@ inferType tab funInfo = goInfer mempty
OpUInt8ToInt -> checkUnop mkTypeUInt8 mkTypeInteger
OpIntToUInt8 -> checkUnop mkTypeInteger mkTypeUInt8
goByteArray :: BinderList Type -> NodeByteArray -> Sem r Type
goByteArray bl NodeByteArray {..} = do
mapM_ (\arg -> checkType bl arg TyDynamic) _nodeByteArrayArgs
return TyDynamic
goCairo :: BinderList Type -> NodeCairo -> Sem r Type
goCairo bl NodeCairo {..} = do
mapM_ (\arg -> checkType bl arg TyDynamic) _nodeCairoArgs
@ -104,6 +110,7 @@ inferType tab funInfo = goInfer mempty
ConstUnit {} -> return TyUnit
ConstVoid {} -> return TyVoid
ConstUInt8 {} -> return mkTypeUInt8
ConstByteArray {} -> return TyByteArray
goMemRef :: BinderList Type -> NodeMemRef -> Sem r Type
goMemRef bl NodeMemRef {..} = case _nodeMemRef of

View File

@ -87,6 +87,8 @@ genCode infoTable fi =
mkConst (ConstField fld)
Core.Constant _ (Core.ConstUInt8 i) ->
mkConst (ConstUInt8 i)
Core.Constant _ (Core.ConstByteArray bs) ->
mkConst (ConstByteArray bs)
goApps :: Int -> BinderList MemRef -> Core.Apps -> Node
goApps tempSize refs Core.Apps {..} =
@ -140,6 +142,13 @@ genCode infoTable fi =
goBuiltinApp :: Int -> BinderList MemRef -> Core.BuiltinApp -> Node
goBuiltinApp tempSize refs Core.BuiltinApp {..}
| Core.builtinIsByteArray _builtinAppOp =
ByteArray $
NodeByteArray
{ _nodeByteArrayInfo = mempty,
_nodeByteArrayOpcode = genByteArrayOp _builtinAppOp,
_nodeByteArrayArgs = args
}
| Core.builtinIsCairo _builtinAppOp =
Cairo $
NodeCairo
@ -308,6 +317,12 @@ genCode infoTable fi =
Core.OpUInt8ToInt -> PrimUnop OpUInt8ToInt
_ -> impossible
genByteArrayOp :: Core.BuiltinOp -> ByteArrayOp
genByteArrayOp = \case
Core.OpByteArrayFromListByte -> OpByteArrayFromListUInt8
Core.OpByteArrayLength -> OpByteArrayLength
_ -> impossible
genCairoOp :: Core.BuiltinOp -> CairoOp
genCairoOp = \case
Core.OpPoseidonHash -> OpCairoPoseidon
@ -361,6 +376,8 @@ convertPrimitiveType = \case
TyString
Core.PrimField ->
TyField
Core.PrimByteArray ->
TyByteArray
-- | `convertNestedType` ensures that the conversion of a type with Dynamic in the
-- target is curried. The result of `convertType 0 ty` is always uncurried.

View File

@ -53,6 +53,7 @@ parseNode ::
parseNode =
(Binop <$> parseBinop)
<|> (Unop <$> parseUnop)
<|> (ByteArray <$> parseByteArray)
<|> (Anoma <$> parseAnoma)
<|> (Cairo <$> parseCairo)
<|> (Constant <$> parseConst)
@ -119,6 +120,23 @@ parseUnaryOp kwd op = do
arg <- parens parseNode
return $ NodeUnop (NodeInfo (Just loc)) op arg
parseByteArray ::
(Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) =>
ParsecS r NodeByteArray
parseByteArray =
parseByteArrayOp kwByteArrayFromListUInt8 OpByteArrayFromListUInt8
<|> parseByteArrayOp kwByteArrayLength OpByteArrayLength
parseByteArrayOp ::
(Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) =>
Keyword ->
ByteArrayOp ->
ParsecS r NodeByteArray
parseByteArrayOp kwd op = do
loc <- onlyInterval (kw kwd)
args <- parseArgs
return $ NodeByteArray (NodeInfo (Just loc)) op args
parseAnoma ::
(Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) =>
ParsecS r NodeAnoma

View File

@ -309,6 +309,7 @@ typeNamed = do
"bool" -> return mkTypeBool
"string" -> return TyString
"unit" -> return TyUnit
"uint8" -> return mkTypeUInt8
_ -> do
idt <- lift $ getIdent' @t @e txt
case idt of
@ -316,13 +317,18 @@ typeNamed = do
_ -> parseFailure off ("not a type: " ++ fromText txt)
constant :: ParsecS r Constant
constant = fieldValue <|> integerValue <|> boolValue <|> stringValue <|> unitValue <|> voidValue
constant = fieldValue <|> uint8Value <|> integerValue <|> boolValue <|> stringValue <|> unitValue <|> voidValue
fieldValue :: ParsecS r Constant
fieldValue = P.try $ do
(i, _) <- field
return $ ConstField (fieldFromInteger defaultFieldSize i)
uint8Value :: ParsecS r Constant
uint8Value = P.try $ do
(i, _) <- uint8
return $ ConstUInt8 (fromInteger i)
integerValue :: ParsecS r Constant
integerValue = do
i <- (^. withLocParam) <$> integer

View File

@ -31,6 +31,9 @@ number = number' integer
field :: ParsecS r (Integer, Interval)
field = lexemeInterval field'
uint8 :: ParsecS r (Integer, Interval)
uint8 = lexemeInterval uint8'
string :: ParsecS r (Text, Interval)
string = lexemeInterval string'

View File

@ -79,6 +79,9 @@ primitive = annotate (AnnKind KNameAxiom) . pretty
keyword :: Text -> Doc Ann
keyword = annotate AnnKeyword . pretty
constructor :: Text -> Doc Ann
constructor = annotate (AnnKind KNameConstructor) . pretty
kwNotMutual :: Doc Ann
kwNotMutual = keyword Str.notMutual

View File

@ -277,6 +277,9 @@ kwPrealloc = asciiKw Str.prealloc
kwArgsNum :: Keyword
kwArgsNum = asciiKw Str.instrArgsNum
kwByteArrayFromListUInt8 :: Keyword
kwByteArrayFromListUInt8 = asciiKw Str.instrByteArrayFromListUInt8
kwPoseidon :: Keyword
kwPoseidon = asciiKw Str.instrPoseidon
@ -463,6 +466,12 @@ kwAnomaSignDetached = asciiKw Str.anomaSignDetached
kwAnomaVerifyWithMessage :: Keyword
kwAnomaVerifyWithMessage = asciiKw Str.anomaVerifyWithMessage
kwByteArrayFromListByte :: Keyword
kwByteArrayFromListByte = asciiKw Str.byteArrayFromListByte
kwByteArrayLength :: Keyword
kwByteArrayLength = asciiKw Str.byteArrayLength
delimBraceL :: Keyword
delimBraceL = mkDelim Str.braceL

View File

@ -185,6 +185,15 @@ byteToNat = "byte-to-nat"
byteFromNat :: (IsString s) => s
byteFromNat = "byte-from-nat"
byteArray :: (IsString s) => s
byteArray = "bytearray"
byteArrayFromListByte :: (IsString s) => s
byteArrayFromListByte = "bytearray-from-list-byte"
byteArrayLength :: (IsString s) => s
byteArrayLength = "bytearray-length"
nat :: (IsString s) => s
nat = "nat"
@ -728,6 +737,9 @@ fun_ = "function"
integer :: (IsString s) => s
integer = "integer"
uint8 :: (IsString s) => s
uint8 = "uint8"
bool :: (IsString s) => s
bool = "bool"
@ -794,6 +806,12 @@ instrUInt8ToInt = "u8toi"
instrIntToUInt8 :: (IsString s) => s
instrIntToUInt8 = "itou8"
instrByteArrayFromListUInt8 :: (IsString s) => s
instrByteArrayFromListUInt8 = "bytearray-from-list-uint8"
instrByteArrayLength :: (IsString s) => s
instrByteArrayLength = "bytearray-length"
instrShow :: (IsString s) => s
instrShow = "show"
@ -923,6 +941,12 @@ nil = "nil"
cons :: (IsString s) => s
cons = "cons"
builtinListCons :: (IsString s) => s
builtinListCons = "builtinListCons"
builtinListNil :: (IsString s) => s
builtinListNil = "builtinListNil"
nothing :: (IsString s) => s
nothing = "nothing"

View File

@ -612,5 +612,22 @@ allTests =
[nock| 3 |],
[nock| 240 |],
[nock| [1 238 3 2 nil] |]
],
mkAnomaCallTest
"Test082: ByteArray"
$(mkRelDir ".")
$(mkRelFile "test082.juvix")
[]
$ checkOutput
[ [nock| 0 |],
[nock| [0 0] |],
[nock| 3 |],
[nock| [3 0] |],
[nock| 4 |],
[nock| [4 1] |],
[nock| 2 |],
[nock| [2 258] |],
[nock| 1 |],
[nock| [1 0] |]
]
]

View File

@ -8,7 +8,10 @@ allTests :: TestTree
allTests = testGroup "JuvixCore to JuvixAsm positive tests" (map liftTest (Eval.filterOutTests ignoredTests Eval.compilableTests))
ignoredTests :: [String]
ignoredTests = ["Test062: Anoma"]
ignoredTests =
[ "Test062: Anoma",
"Test064: ByteArray"
]
liftTest :: Eval.PosTest -> TestTree
liftTest _testEval =

View File

@ -7,7 +7,7 @@ import Core.Eval.Positive qualified as Eval
allTests :: TestTree
allTests = testGroup "JuvixCore compilation tests" (map liftTest (Eval.filterOutTests ignoredTests Eval.compilableTests))
-- Arbitrary precision integers and fields not yet supported
-- Arbitrary precision integers, fields, Anoma ops, and bytearrayss not yet supported
ignoredTests :: [String]
ignoredTests =
[ "Test011: Tail recursion: Fibonacci numbers in linear time",
@ -17,7 +17,8 @@ ignoredTests =
"Test036: Big numbers",
"Test040: LetRec - fib, fact",
"Test061: Fields",
"Test062: Anoma"
"Test062: Anoma",
"Test064: ByteArray"
]
liftTest :: Eval.PosTest -> TestTree

View File

@ -352,5 +352,15 @@ tests =
"Test062: Anoma"
$(mkRelDir ".")
$(mkRelFile "test062.jvc")
$(mkRelFile "out/test062.out")
$(mkRelFile "out/test062.out"),
PosTest
"Test063: UInt8"
$(mkRelDir ".")
$(mkRelFile "test063.jvc")
$(mkRelFile "out/test063.out"),
PosTest
"Test064: ByteArray"
$(mkRelDir ".")
$(mkRelFile "test064.jvc")
$(mkRelFile "out/test064.out")
]

View File

@ -49,7 +49,7 @@ testDescr Tree.PosTest {..} =
-- | Tests which require Nockma-specific expected output files
testsConstr :: [Int]
testsConstr = [9, 28, 35]
testsConstr = [9, 28, 35, 40]
testsNegativeInteger :: [Int]
testsNegativeInteger = [16]

View File

@ -269,7 +269,16 @@ juvixCallingConventionTests =
lenR :: Term Natural = nockIntegralLiteral (length r)
tupR = OpQuote # foldTerms (toNock <$> r)
in compilerTest "appendToTuple (left empty, right-nonempty)" (appendToTuple (OpQuote # nockNilTagged "test-appendtotuple") (nockNatLiteral 0) tupR lenR) (eqNock res),
compilerTest "stdlib cat" (callStdlib StdlibCatBytes [nockNatLiteral 2, nockNatLiteral 1]) (eqNock [nock| 258 |])
compilerTest "stdlib cat" (callStdlib StdlibCatBytes [nockNatLiteral 2, nockNatLiteral 1]) (eqNock [nock| 258 |]),
compilerTest "fold bytes empty" (callStdlib StdlibFoldBytes [OpQuote # makeList []]) (eqNock [nock| 0 |]),
compilerTest "fold bytes [1, 0, 0] == 1" (callStdlib StdlibFoldBytes [OpQuote # makeList (toNock @Natural <$> [1, 0, 0])]) (eqNock [nock| 1 |]),
compilerTest "fold bytes single byte" (callStdlib StdlibFoldBytes [OpQuote # makeList (toNock @Natural <$> [123])]) (eqNock [nock| 123 |]),
compilerTest "fold bytes [0, 1] == 256" (callStdlib StdlibFoldBytes [OpQuote # makeList (toNock @Natural <$> [0, 1])]) (eqNock [nock| 256 |]),
compilerTest "fold bytes [5, 1] == 261" (callStdlib StdlibFoldBytes [OpQuote # makeList (toNock @Natural <$> [5, 1])]) (eqNock [nock| 261 |]),
compilerTest "fold bytes [0, 1, 0] == 256" (callStdlib StdlibFoldBytes [OpQuote # makeList (toNock @Natural <$> [0, 1, 0])]) (eqNock [nock| 256 |]),
compilerTest "length [] == 0" (callStdlib StdlibLengthList [OpQuote # makeList []]) (eqNock [nock| 0 |]),
compilerTest "length [10] == 1" (callStdlib StdlibLengthList [OpQuote # makeList [[nock| 10 |]]]) (eqNock [nock| 1 |]),
compilerTest "length [[1 2, 3], 0] == 2" (callStdlib StdlibLengthList [OpQuote # makeList [[nock| [1 2 3] |], [nock| 0 |]]]) (eqNock [nock| 2 |])
]
unitTests :: [Test]

View File

@ -15,8 +15,11 @@ testDescr Eval.PosTest {..} =
_testAssertion = Steps $ treeAsmAssertion file' expected'
}
ignoredTests :: [String]
ignoredTests = ["Test040: ByteArray"]
allTests :: TestTree
allTests =
testGroup
"JuvixTree to JuvixAsm positive tests"
(map (mkTest . testDescr) Eval.tests)
(map (mkTest . testDescr) (Eval.filterOutTests ignoredTests Eval.tests))

View File

@ -229,5 +229,15 @@ tests =
"Test038: Apply & argsnum"
$(mkRelDir ".")
$(mkRelFile "test038.jvt")
$(mkRelFile "out/test038.out")
$(mkRelFile "out/test038.out"),
PosTest
"Test039: UInt8"
$(mkRelDir ".")
$(mkRelFile "test039.jvt")
$(mkRelFile "out/test039.out"),
PosTest
"Test040: ByteArray"
$(mkRelDir ".")
$(mkRelFile "test040.jvt")
$(mkRelFile "out/test040.out")
]

View File

@ -0,0 +1,35 @@
module test082;
import Stdlib.Prelude open;
import Stdlib.Debug.Trace open;
builtin bytearray
axiom ByteArray : Type;
builtin bytearray-from-list-byte
axiom mkByteArray : List Byte -> ByteArray;
builtin bytearray-length
axiom size : ByteArray -> Nat;
bs0 : ByteArray := mkByteArray [];
bs1 : ByteArray := mkByteArray [0x0; 0x0; 0x0];
bs2 : ByteArray := mkByteArray [0x1; 0x0; 0x0; 0x0];
bs3 : ByteArray := mkByteArray [0x2; 0x1];
bs4 : ByteArray := mkByteArray [0x100];
main : ByteArray :=
trace (size bs0)
>-> trace bs0
>-> trace (size bs1)
>-> trace bs1
>-> trace (size bs2)
>-> trace bs2
>-> trace (size bs3)
>-> trace bs3
>-> trace (size bs4)
>-> bs4;

View File

@ -0,0 +1,2 @@
bytearray-from-list-byte (builtinListCons UInt8 1 (builtinListCons UInt8 2 (builtinListNil UInt8)))
2

View File

@ -1,5 +1,5 @@
-- UInt8
def f := \x x;
def f : UInt8 -> UInt8 := \x x;
f 257u8

View File

@ -0,0 +1,6 @@
def writeLn := \x write x >> write "\n";
def bs : ByteArray := bytearray-from-list-byte (builtinListCons UInt8 257u8 (builtinListCons UInt8 2u8 (builtinListNil UInt8)));
writeLn bs >> writeLn (bytearray-length bs)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
2
[2 258]

View File

@ -0,0 +1,2 @@
2
alloc[cons](2u8, alloc[cons](1u8, alloc[nil]()))

View File

@ -0,0 +1,11 @@
function id(uint8) : uint8;
function main() : *
function id(uint8) : uint8 {
arg[0]
}
function main() : * {
call[id](257u8)
}

View File

@ -0,0 +1,12 @@
-- ByteArray
type list {
nil : list;
cons : * -> list -> list;
}
function main() : * {
save(bytearray-from-list-uint8(alloc[cons](258u8, alloc[cons](1u8, alloc[nil]())))) {
seq(trace(bytearray-length(tmp[0])), tmp[0])
}
}