diff --git a/src/Juvix/Compiler/Asm/Extra/Memory.hs b/src/Juvix/Compiler/Asm/Extra/Memory.hs index 729940469f..9b3997301b 100644 --- a/src/Juvix/Compiler/Asm/Extra/Memory.hs +++ b/src/Juvix/Compiler/Asm/Extra/Memory.hs @@ -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 diff --git a/src/Juvix/Compiler/Asm/Translation/FromTree.hs b/src/Juvix/Compiler/Asm/Translation/FromTree.hs index 6974d23d8e..bbe1ab22fe 100644 --- a/src/Juvix/Compiler/Asm/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Asm/Translation/FromTree.hs @@ -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 diff --git a/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs b/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs index d312c80762..3b4424a6e6 100644 --- a/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs +++ b/src/Juvix/Compiler/Backend/C/Translation/FromReg.hs @@ -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 {..} = diff --git a/src/Juvix/Compiler/Backend/Rust/Translation/FromReg.hs b/src/Juvix/Compiler/Backend/Rust/Translation/FromReg.hs index 274ab200dd..a76a4c4f84 100644 --- a/src/Juvix/Compiler/Backend/Rust/Translation/FromReg.hs +++ b/src/Juvix/Compiler/Backend/Rust/Translation/FromReg.hs @@ -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 {..} = diff --git a/src/Juvix/Compiler/Builtins.hs b/src/Juvix/Compiler/Builtins.hs index 2648a6620f..baa336cdf7 100644 --- a/src/Juvix/Compiler/Builtins.hs +++ b/src/Juvix/Compiler/Builtins.hs @@ -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 diff --git a/src/Juvix/Compiler/Builtins/ByteArray.hs b/src/Juvix/Compiler/Builtins/ByteArray.hs new file mode 100644 index 0000000000..722e937d36 --- /dev/null +++ b/src/Juvix/Compiler/Builtins/ByteArray.hs @@ -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) diff --git a/src/Juvix/Compiler/Casm/Translation/FromReg.hs b/src/Juvix/Compiler/Casm/Translation/FromReg.hs index 1cac6232ed..058552dae6 100644 --- a/src/Juvix/Compiler/Casm/Translation/FromReg.hs +++ b/src/Juvix/Compiler/Casm/Translation/FromReg.hs @@ -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 diff --git a/src/Juvix/Compiler/Concrete/Data/Builtins.hs b/src/Juvix/Compiler/Concrete/Data/Builtins.hs index 6adf2536c3..698aab0a47 100644 --- a/src/Juvix/Compiler/Concrete/Data/Builtins.hs +++ b/src/Juvix/Compiler/Concrete/Data/Builtins.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Data/InfoTableBuilder.hs b/src/Juvix/Compiler/Core/Data/InfoTableBuilder.hs index 26f84b00bf..9648fc97d2 100644 --- a/src/Juvix/Compiler/Core/Data/InfoTableBuilder.hs +++ b/src/Juvix/Compiler/Core/Data/InfoTableBuilder.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Evaluator.hs b/src/Juvix/Compiler/Core/Evaluator.hs index b30f90b581..38fcbb0345 100644 --- a/src/Juvix/Compiler/Core/Evaluator.hs +++ b/src/Juvix/Compiler/Core/Evaluator.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Extra/Base.hs b/src/Juvix/Compiler/Core/Extra/Base.hs index d4b1bf6072..b2e0f2c838 100644 --- a/src/Juvix/Compiler/Core/Extra/Base.hs +++ b/src/Juvix/Compiler/Core/Extra/Base.hs @@ -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) diff --git a/src/Juvix/Compiler/Core/Extra/Utils.hs b/src/Juvix/Compiler/Core/Extra/Utils.hs index 5c4c573a05..a57b0c4e26 100644 --- a/src/Juvix/Compiler/Core/Extra/Utils.hs +++ b/src/Juvix/Compiler/Core/Extra/Utils.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Keywords.hs b/src/Juvix/Compiler/Core/Keywords.hs index a2fb2b5e18..6f1d8eac29 100644 --- a/src/Juvix/Compiler/Core/Keywords.hs +++ b/src/Juvix/Compiler/Core/Keywords.hs @@ -19,6 +19,8 @@ import Juvix.Data.Keyword.All kwBind, kwBottom, kwBuiltin, + kwByteArrayFromListByte, + kwByteArrayLength, kwCase, kwColon, kwComma, diff --git a/src/Juvix/Compiler/Core/Language/Builtins.hs b/src/Juvix/Compiler/Core/Language/Builtins.hs index c63684c1cd..e4c1cb3d3b 100644 --- a/src/Juvix/Compiler/Core/Language/Builtins.hs +++ b/src/Juvix/Compiler/Core/Language/Builtins.hs @@ -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] diff --git a/src/Juvix/Compiler/Core/Language/Nodes.hs b/src/Juvix/Compiler/Core/Language/Nodes.hs index c956a73a94..833fdcd285 100644 --- a/src/Juvix/Compiler/Core/Language/Nodes.hs +++ b/src/Juvix/Compiler/Core/Language/Nodes.hs @@ -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. diff --git a/src/Juvix/Compiler/Core/Language/Primitives.hs b/src/Juvix/Compiler/Core/Language/Primitives.hs index ea008e0a08..9c980d2dd8 100644 --- a/src/Juvix/Compiler/Core/Language/Primitives.hs +++ b/src/Juvix/Compiler/Core/Language/Primitives.hs @@ -15,6 +15,7 @@ data Primitive | PrimBool PrimBoolInfo | PrimString | PrimField + | PrimByteArray deriving stock (Eq, Generic) primitiveUInt8 :: Primitive diff --git a/src/Juvix/Compiler/Core/Pretty/Base.hs b/src/Juvix/Compiler/Core/Pretty/Base.hs index f80ee9e32e..987f877be1 100644 --- a/src/Juvix/Compiler/Core/Pretty/Base.hs +++ b/src/Juvix/Compiler/Core/Pretty/Base.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Transformation/Check/Base.hs b/src/Juvix/Compiler/Core/Transformation/Check/Base.hs index 953a57a9be..079662fca4 100644 --- a/src/Juvix/Compiler/Core/Transformation/Check/Base.hs +++ b/src/Juvix/Compiler/Core/Transformation/Check/Base.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Transformation/Check/Cairo.hs b/src/Juvix/Compiler/Core/Transformation/Check/Cairo.hs index 8b7b3fb7e9..59751aaaf3 100644 --- a/src/Juvix/Compiler/Core/Transformation/Check/Cairo.hs +++ b/src/Juvix/Compiler/Core/Transformation/Check/Cairo.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Transformation/Check/Exec.hs b/src/Juvix/Compiler/Core/Transformation/Check/Exec.hs index f11b3265aa..18200f78e1 100644 --- a/src/Juvix/Compiler/Core/Transformation/Check/Exec.hs +++ b/src/Juvix/Compiler/Core/Transformation/Check/Exec.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Transformation/ComputeTypeInfo.hs b/src/Juvix/Compiler/Core/Transformation/ComputeTypeInfo.hs index e79b027f47..e2622cf50a 100644 --- a/src/Juvix/Compiler/Core/Transformation/ComputeTypeInfo.hs +++ b/src/Juvix/Compiler/Core/Transformation/ComputeTypeInfo.hs @@ -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) diff --git a/src/Juvix/Compiler/Core/Transformation/ConvertBuiltinTypes.hs b/src/Juvix/Compiler/Core/Transformation/ConvertBuiltinTypes.hs index 5746972dd2..700f5ab357 100644 --- a/src/Juvix/Compiler/Core/Transformation/ConvertBuiltinTypes.hs +++ b/src/Juvix/Compiler/Core/Transformation/ConvertBuiltinTypes.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Translation/FromInternal.hs b/src/Juvix/Compiler/Core/Translation/FromInternal.hs index 6551eb4b7d..deb2eb6134 100644 --- a/src/Juvix/Compiler/Core/Translation/FromInternal.hs +++ b/src/Juvix/Compiler/Core/Translation/FromInternal.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Translation/FromSource.hs b/src/Juvix/Compiler/Core/Translation/FromSource.hs index 2592087272..26c8633064 100644 --- a/src/Juvix/Compiler/Core/Translation/FromSource.hs +++ b/src/Juvix/Compiler/Core/Translation/FromSource.hs @@ -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 diff --git a/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs b/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs index 34c051b24e..2dd8bea953 100644 --- a/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs +++ b/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs @@ -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 diff --git a/src/Juvix/Compiler/Internal/Translation/FromConcrete.hs b/src/Juvix/Compiler/Internal/Translation/FromConcrete.hs index 677ec97b02..295661c405 100644 --- a/src/Juvix/Compiler/Internal/Translation/FromConcrete.hs +++ b/src/Juvix/Compiler/Internal/Translation/FromConcrete.hs @@ -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) => diff --git a/src/Juvix/Compiler/Nockma/Evaluator.hs b/src/Juvix/Compiler/Nockma/Evaluator.hs index 8ee092432e..a8ef610607 100644 --- a/src/Juvix/Compiler/Nockma/Evaluator.hs +++ b/src/Juvix/Compiler/Nockma/Evaluator.hs @@ -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 diff --git a/src/Juvix/Compiler/Nockma/StdlibFunction.hs b/src/Juvix/Compiler/Nockma/StdlibFunction.hs index fb3ac49fb0..4bb1c144ce 100644 --- a/src/Juvix/Compiler/Nockma/StdlibFunction.hs +++ b/src/Juvix/Compiler/Nockma/StdlibFunction.hs @@ -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 + ] + |] diff --git a/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs b/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs index 38dbc34ba9..de96d7c997 100644 --- a/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs +++ b/src/Juvix/Compiler/Nockma/StdlibFunction/Base.hs @@ -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 diff --git a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs index e8c7d8639a..9b733f0a32 100644 --- a/src/Juvix/Compiler/Nockma/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Nockma/Translation/FromTree.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Data/TransformationId.hs b/src/Juvix/Compiler/Tree/Data/TransformationId.hs index 6b0b5d25ac..8238814ae3 100644 --- a/src/Juvix/Compiler/Tree/Data/TransformationId.hs +++ b/src/Juvix/Compiler/Tree/Data/TransformationId.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs b/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs index ababa4da2f..54c9584530 100644 --- a/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs +++ b/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs @@ -34,3 +34,6 @@ strValidate = "validate" strCheckNoAnoma :: Text strCheckNoAnoma = "check-no-anoma" + +strCheckNoByteArray :: Text +strCheckNoByteArray = "check-no-bytearray" diff --git a/src/Juvix/Compiler/Tree/Evaluator.hs b/src/Juvix/Compiler/Tree/Evaluator.hs index c3c79b2ddf..53f13f1f12 100644 --- a/src/Juvix/Compiler/Tree/Evaluator.hs +++ b/src/Juvix/Compiler/Tree/Evaluator.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Evaluator/Builtins.hs b/src/Juvix/Compiler/Tree/Evaluator/Builtins.hs index 58b8e53772..d9c1fda4f9 100644 --- a/src/Juvix/Compiler/Tree/Evaluator/Builtins.hs +++ b/src/Juvix/Compiler/Tree/Evaluator/Builtins.hs @@ -130,6 +130,7 @@ constantToValue = \case ConstUnit -> ValUnit ConstVoid -> ValVoid ConstUInt8 i -> ValUInt8 i + ConstByteArray b -> ValByteArray b valueToConstant :: Value -> Constant valueToConstant = \case diff --git a/src/Juvix/Compiler/Tree/EvaluatorEff.hs b/src/Juvix/Compiler/Tree/EvaluatorEff.hs index 2acca9d3e4..8ecbbc9782 100644 --- a/src/Juvix/Compiler/Tree/EvaluatorEff.hs +++ b/src/Juvix/Compiler/Tree/EvaluatorEff.hs @@ -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) diff --git a/src/Juvix/Compiler/Tree/Extra/Base.hs b/src/Juvix/Compiler/Tree/Extra/Base.hs index 38b676670f..a2c15021ab 100644 --- a/src/Juvix/Compiler/Tree/Extra/Base.hs +++ b/src/Juvix/Compiler/Tree/Extra/Base.hs @@ -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, diff --git a/src/Juvix/Compiler/Tree/Extra/Type.hs b/src/Juvix/Compiler/Tree/Extra/Type.hs index 1dfc32409a..1903309a10 100644 --- a/src/Juvix/Compiler/Tree/Extra/Type.hs +++ b/src/Juvix/Compiler/Tree/Extra/Type.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Extra/Type/Base.hs b/src/Juvix/Compiler/Tree/Extra/Type/Base.hs new file mode 100644 index 0000000000..6858a2385a --- /dev/null +++ b/src/Juvix/Compiler/Tree/Extra/Type/Base.hs @@ -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) diff --git a/src/Juvix/Compiler/Tree/Keywords.hs b/src/Juvix/Compiler/Tree/Keywords.hs index b878fb50c1..989cdb9137 100644 --- a/src/Juvix/Compiler/Tree/Keywords.hs +++ b/src/Juvix/Compiler/Tree/Keywords.hs @@ -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 ] diff --git a/src/Juvix/Compiler/Tree/Language.hs b/src/Juvix/Compiler/Tree/Language.hs index d4242f74f2..bbb9614910 100644 --- a/src/Juvix/Compiler/Tree/Language.hs +++ b/src/Juvix/Compiler/Tree/Language.hs @@ -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, diff --git a/src/Juvix/Compiler/Tree/Language/Base.hs b/src/Juvix/Compiler/Tree/Language/Base.hs index 99208bd703..10d9f9f52a 100644 --- a/src/Juvix/Compiler/Tree/Language/Base.hs +++ b/src/Juvix/Compiler/Tree/Language/Base.hs @@ -19,6 +19,7 @@ data Constant | ConstUnit | ConstVoid | ConstUInt8 Word8 + | ConstByteArray ByteString deriving stock (Eq, Generic) instance (Hashable Constant) diff --git a/src/Juvix/Compiler/Tree/Language/Builtins.hs b/src/Juvix/Compiler/Tree/Language/Builtins.hs index 94b04cfa2a..3145026a2a 100644 --- a/src/Juvix/Compiler/Tree/Language/Builtins.hs +++ b/src/Juvix/Compiler/Tree/Language/Builtins.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Language/Type.hs b/src/Juvix/Compiler/Tree/Language/Type.hs index 358dd3c265..517576e1d0 100644 --- a/src/Juvix/Compiler/Tree/Language/Type.hs +++ b/src/Juvix/Compiler/Tree/Language/Type.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Language/Value.hs b/src/Juvix/Compiler/Tree/Language/Value.hs index 2e4387a6f0..7df0ef8424 100644 --- a/src/Juvix/Compiler/Tree/Language/Value.hs +++ b/src/Juvix/Compiler/Tree/Language/Value.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Pretty/Base.hs b/src/Juvix/Compiler/Tree/Pretty/Base.hs index bf5e665d0e..abdec2bd81 100644 --- a/src/Juvix/Compiler/Tree/Pretty/Base.hs +++ b/src/Juvix/Compiler/Tree/Pretty/Base.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Transformation.hs b/src/Juvix/Compiler/Tree/Transformation.hs index e78c7ce17d..39120e5dd0 100644 --- a/src/Juvix/Compiler/Tree/Transformation.hs +++ b/src/Juvix/Compiler/Tree/Transformation.hs @@ -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' diff --git a/src/Juvix/Compiler/Tree/Transformation/CheckNoByteArray.hs b/src/Juvix/Compiler/Tree/Transformation/CheckNoByteArray.hs new file mode 100644 index 0000000000..2e2fd76a08 --- /dev/null +++ b/src/Juvix/Compiler/Tree/Transformation/CheckNoByteArray.hs @@ -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 () diff --git a/src/Juvix/Compiler/Tree/Transformation/Validate.hs b/src/Juvix/Compiler/Tree/Transformation/Validate.hs index 2565c76982..c9ba84d41f 100644 --- a/src/Juvix/Compiler/Tree/Transformation/Validate.hs +++ b/src/Juvix/Compiler/Tree/Transformation/Validate.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Translation/FromCore.hs b/src/Juvix/Compiler/Tree/Translation/FromCore.hs index 587b4241df..52e5ead2bf 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromCore.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromCore.hs @@ -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. diff --git a/src/Juvix/Compiler/Tree/Translation/FromSource.hs b/src/Juvix/Compiler/Tree/Translation/FromSource.hs index 77f1d99c15..2a42f354d2 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromSource.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromSource.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Translation/FromSource/Base.hs b/src/Juvix/Compiler/Tree/Translation/FromSource/Base.hs index 603a13ecdd..d3b3fa5882 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromSource/Base.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromSource/Base.hs @@ -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 diff --git a/src/Juvix/Compiler/Tree/Translation/FromSource/Lexer/Base.hs b/src/Juvix/Compiler/Tree/Translation/FromSource/Lexer/Base.hs index 918c82a878..d9869de00b 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromSource/Lexer/Base.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromSource/Lexer/Base.hs @@ -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' diff --git a/src/Juvix/Data/CodeAnn.hs b/src/Juvix/Data/CodeAnn.hs index 49f2ed181e..37cfb4f358 100644 --- a/src/Juvix/Data/CodeAnn.hs +++ b/src/Juvix/Data/CodeAnn.hs @@ -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 diff --git a/src/Juvix/Data/Keyword/All.hs b/src/Juvix/Data/Keyword/All.hs index a48048b923..e8697ff61e 100644 --- a/src/Juvix/Data/Keyword/All.hs +++ b/src/Juvix/Data/Keyword/All.hs @@ -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 diff --git a/src/Juvix/Extra/Strings.hs b/src/Juvix/Extra/Strings.hs index df249563a9..fa064e51b9 100644 --- a/src/Juvix/Extra/Strings.hs +++ b/src/Juvix/Extra/Strings.hs @@ -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" diff --git a/test/Anoma/Compilation/Positive.hs b/test/Anoma/Compilation/Positive.hs index fea8d489cf..9d97cdfcf7 100644 --- a/test/Anoma/Compilation/Positive.hs +++ b/test/Anoma/Compilation/Positive.hs @@ -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] |] ] ] diff --git a/test/Core/Asm/Positive.hs b/test/Core/Asm/Positive.hs index 6d7a081f45..8fee238a47 100644 --- a/test/Core/Asm/Positive.hs +++ b/test/Core/Asm/Positive.hs @@ -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 = diff --git a/test/Core/Compile/Positive.hs b/test/Core/Compile/Positive.hs index f9ee3011dc..85d4276b27 100644 --- a/test/Core/Compile/Positive.hs +++ b/test/Core/Compile/Positive.hs @@ -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 diff --git a/test/Core/Eval/Positive.hs b/test/Core/Eval/Positive.hs index ccd2de8f68..a8451a1884 100644 --- a/test/Core/Eval/Positive.hs +++ b/test/Core/Eval/Positive.hs @@ -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") ] diff --git a/test/Nockma/Compile/Tree/Positive.hs b/test/Nockma/Compile/Tree/Positive.hs index 74e3ea60ee..dc72690406 100644 --- a/test/Nockma/Compile/Tree/Positive.hs +++ b/test/Nockma/Compile/Tree/Positive.hs @@ -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] diff --git a/test/Nockma/Eval/Positive.hs b/test/Nockma/Eval/Positive.hs index a9ccd12953..1f2cd66369 100644 --- a/test/Nockma/Eval/Positive.hs +++ b/test/Nockma/Eval/Positive.hs @@ -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] diff --git a/test/Tree/Asm/Positive.hs b/test/Tree/Asm/Positive.hs index cea544c9c8..0979093b1d 100644 --- a/test/Tree/Asm/Positive.hs +++ b/test/Tree/Asm/Positive.hs @@ -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)) diff --git a/test/Tree/Eval/Positive.hs b/test/Tree/Eval/Positive.hs index 8cf0797ed3..659f3a9fbd 100644 --- a/test/Tree/Eval/Positive.hs +++ b/test/Tree/Eval/Positive.hs @@ -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") ] diff --git a/tests/Anoma/Compilation/positive/test082.juvix b/tests/Anoma/Compilation/positive/test082.juvix new file mode 100644 index 0000000000..723c87ebb1 --- /dev/null +++ b/tests/Anoma/Compilation/positive/test082.juvix @@ -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; diff --git a/tests/Core/positive/out/test064.out b/tests/Core/positive/out/test064.out new file mode 100644 index 0000000000..a359f595be --- /dev/null +++ b/tests/Core/positive/out/test064.out @@ -0,0 +1,2 @@ +bytearray-from-list-byte (builtinListCons UInt8 1 (builtinListCons UInt8 2 (builtinListNil UInt8))) +2 diff --git a/tests/Core/positive/test063.jvc b/tests/Core/positive/test063.jvc index 246b87d2c7..fb604e8009 100644 --- a/tests/Core/positive/test063.jvc +++ b/tests/Core/positive/test063.jvc @@ -1,5 +1,5 @@ -- UInt8 -def f := \x x; +def f : UInt8 -> UInt8 := \x x; f 257u8 diff --git a/tests/Core/positive/test064.jvc b/tests/Core/positive/test064.jvc new file mode 100644 index 0000000000..872059ba52 --- /dev/null +++ b/tests/Core/positive/test064.jvc @@ -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) diff --git a/tests/Tree/positive/out/test039.out b/tests/Tree/positive/out/test039.out new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/Tree/positive/out/test039.out @@ -0,0 +1 @@ +1 diff --git a/tests/Tree/positive/out/test040.nockma.out b/tests/Tree/positive/out/test040.nockma.out new file mode 100644 index 0000000000..37b4d37e4c --- /dev/null +++ b/tests/Tree/positive/out/test040.nockma.out @@ -0,0 +1,2 @@ +2 +[2 258] diff --git a/tests/Tree/positive/out/test040.out b/tests/Tree/positive/out/test040.out new file mode 100644 index 0000000000..9e898d76a6 --- /dev/null +++ b/tests/Tree/positive/out/test040.out @@ -0,0 +1,2 @@ +2 +alloc[cons](2u8, alloc[cons](1u8, alloc[nil]())) diff --git a/tests/Tree/positive/test039.jvt b/tests/Tree/positive/test039.jvt new file mode 100644 index 0000000000..5c033a5a5b --- /dev/null +++ b/tests/Tree/positive/test039.jvt @@ -0,0 +1,11 @@ + +function id(uint8) : uint8; +function main() : * + +function id(uint8) : uint8 { + arg[0] +} + +function main() : * { + call[id](257u8) +} diff --git a/tests/Tree/positive/test040.jvt b/tests/Tree/positive/test040.jvt new file mode 100644 index 0000000000..1450b99bd5 --- /dev/null +++ b/tests/Tree/positive/test040.jvt @@ -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]) + } +}