diff --git a/docs/builtins/Repl/load.md b/docs/builtins/Repl/load.md new file mode 100644 index 00000000..e413482a --- /dev/null +++ b/docs/builtins/Repl/load.md @@ -0,0 +1,42 @@ +## load + +Load and evaluate a file, resetting repl state beforehand if optional RESET is +true. + + +### Basic syntax + +To load a separate pact or repl file, call + +```pact +(load "my-file.pact") +``` + +If the load requires resetting repl state, use + +```pact +(load "my-file.pact" true) +``` + + +## Arguments + +Use the following argument when using the `load` Pact function. + +| Argument | Type | Description | +|----------|----------|--------------------------------------------------------------| +| File | string | The file to load | +| Reset | bool | (Optional) Reset the repl state before loading | + +### Return value + +`load` returns the unit value `()` + +### Example + +The following example demonstrates how to use the `load` function to set "my-key" and "admin-key" as the current transaction signing keys in a Pact REPL: + +```pact +pact> (load "hello-world.repl") +"Hello pact!" +``` diff --git a/pact-lsp/Pact/Core/LanguageServer.hs b/pact-lsp/Pact/Core/LanguageServer.hs index 748e332f..a9ad1fdf 100644 --- a/pact-lsp/Pact/Core/LanguageServer.hs +++ b/pact-lsp/Pact/Core/LanguageServer.hs @@ -40,6 +40,8 @@ import qualified Data.Text.IO as T import qualified Data.Text as T import System.Exit +import Control.Monad +import Control.Monad.State.Strict(put) import Control.Monad.Reader (ReaderT, runReaderT, ask) import Control.Monad.Trans (lift) import Control.Concurrent.MVar @@ -49,6 +51,7 @@ import qualified Pact.Core.Syntax.ParseTree as Lisp import qualified Pact.Core.Syntax.Lexer as Lisp import qualified Pact.Core.Syntax.Parser as Lisp import Pact.Core.IR.Term +import Pact.Core.Persistence import Pact.Core.LanguageServer.Utils import Pact.Core.LanguageServer.Renaming import Pact.Core.Repl.BuiltinDocs @@ -59,12 +62,13 @@ import qualified Pact.Core.IR.ModuleHashing as MHash import qualified Pact.Core.IR.ConstEval as ConstEval import qualified Pact.Core.Repl.Compile as Repl import Pact.Core.Interpreter +import Data.Default data LSState = LSState { _lsReplState :: M.Map NormalizedUri (ReplState ReplCoreBuiltin) -- ^ Post-Compilation State for each opened file - , _lsTopLevel :: M.Map NormalizedUri [EvalTopLevel ReplCoreBuiltin SpanInfo] + , _lsTopLevel :: M.Map NormalizedUri [EvalTopLevel ReplCoreBuiltin FileLocSpanInfo] -- ^ Top-level terms for each opened file. Used to find the match of a -- particular (cursor) position inside the file. } @@ -201,9 +205,9 @@ sendDiagnostics nuri mv content = liftIO (setupAndProcessFile nuri content) >>= -- We emit an empty set of diagnostics publishDiagnostics 0 nuri mv $ partitionBySource [] where - pactErrorToDiagnostic :: PactError SpanInfo -> Diagnostic + pactErrorToDiagnostic :: PactError FileLocSpanInfo -> Diagnostic pactErrorToDiagnostic err = Diagnostic - { _range = err ^. peInfo .to spanInfoToRange + { _range = err ^. peInfo . spanInfo . to spanInfoToRange , _severity = Just DiagnosticSeverity_Error , _code = Nothing -- We do not have any error code right now , _codeDescription = Nothing @@ -226,11 +230,11 @@ sendDiagnostics nuri mv content = liftIO (setupAndProcessFile nuri content) >>= setupAndProcessFile :: NormalizedUri -> Text - -> IO (Either (PactError SpanInfo) + -> IO (Either (PactError FileLocSpanInfo) (ReplState ReplCoreBuiltin - ,M.Map NormalizedUri [EvalTopLevel ReplCoreBuiltin SpanInfo])) + ,M.Map NormalizedUri [EvalTopLevel ReplCoreBuiltin FileLocSpanInfo])) setupAndProcessFile nuri content = do - pdb <- mockPactDb serialisePact_repl_spaninfo + pdb <- mockPactDb serialisePact_repl_fileLocSpanInfo let builtinMap = if isReplScript fp then replBuiltinMap @@ -250,11 +254,14 @@ setupAndProcessFile nuri content = do -- since there may be no way for us to set it for the LSP from pact directly. -- Once this is possible, we can set it to `False` as is the default , _replNativesEnabled = True + , _replLoad = doLoad + , _replLogType = ReplStdOut + , _replLoadedFiles = mempty , _replOutputLine = const (pure ()) , _replTestResults = [] } stateRef <- newIORef rstate - res <- evalReplM stateRef (processFile Repl.interpretEvalBigStep nuri content) + res <- evalReplM stateRef (processFile Repl.interpretEvalBigStep nuri src) st <- readIORef stateRef pure $ (st,) <$> res where @@ -269,9 +276,10 @@ spanInfoToRange (SpanInfo sl sc el ec) = mkRange getMatch - :: Position - -> [EvalTopLevel ReplCoreBuiltin SpanInfo] - -> Maybe (PositionMatch ReplCoreBuiltin SpanInfo) + :: HasSpanInfo i + => Position + -> [EvalTopLevel ReplCoreBuiltin i] + -> Maybe (PositionMatch ReplCoreBuiltin i) getMatch pos tl = getAlt (foldMap (Alt . topLevelTermAt pos) tl) documentDefinitionRequestHandler :: Handlers LSM @@ -291,7 +299,7 @@ documentDefinitionRequestHandler = requestHandler SMethod_TextDocumentDefinition pure Nothing _ -> pure Nothing debug $ "documentDefinition request: " <> renderText nuri - let loc = Location uri' . spanInfoToRange + let loc = Location uri' . spanInfoToRange . view spanInfo case loc <$> tlDefSpan of Just x -> resp (Right $ InL $ Definition (InL x)) Nothing -> resp (Right $ InR $ InR Null) @@ -310,7 +318,7 @@ documentHoverRequestHandler = requestHandler SMethod_TextDocumentHover $ \req re (M.lookup (replCoreBuiltinToUserText builtin) builtinDocs) mc = MarkupContent MarkupKind_Markdown (_markdownDoc docs) - range = spanInfoToRange i + range = spanInfoToRange (view spanInfo i) hover = Hover (InL mc) (Just range) in resp (Right (InL hover)) @@ -349,40 +357,61 @@ documentRenameRequestHandler = requestHandler SMethod_TextDocumentRename $ \req we = WorkspaceEdit Nothing (Just [InL te]) Nothing resp (Right (InL we)) +doLoad :: FilePath -> Bool -> EvalM ReplRuntime ReplCoreBuiltin FileLocSpanInfo () +doLoad fp reset = do + oldSrc <- useReplState replCurrSource + fp' <- mangleFilePath fp + res <- liftIO $ E.try (T.readFile fp') + pactdb <- liftIO (mockPactDb serialisePact_repl_fileLocSpanInfo) + oldEE <- useReplState replEvalEnv + when reset $ do + ee <- liftIO (defaultEvalEnv pactdb replBuiltinMap) + put def + replEvalEnv .== ee + when (Repl.isPactFile fp) $ esLoaded . loToplevel .= mempty + _ <- case res of + Left (_e:: E.IOException) -> + throwExecutionError def $ EvalError $ "File not found: " <> T.pack fp + Right txt -> do + let source = SourceCode fp txt + replCurrSource .== source + let nfp = normalizedFilePathToUri (toNormalizedFilePath fp') + processFile Repl.interpretEvalBigStep nfp source + replCurrSource .== oldSrc + unless reset $ do + replEvalEnv .== oldEE + pure () + + +mangleFilePath :: FilePath -> EvalM ReplRuntime b FileLocSpanInfo FilePath +mangleFilePath fp = do + (SourceCode currFile _) <- useReplState replCurrSource + case currFile of + "" -> pure fp + _ | isAbsolute fp -> pure fp + | takeFileName currFile == currFile -> pure fp + | otherwise -> pure $ combine (takeDirectory currFile) fp + processFile - :: Interpreter ReplRuntime ReplCoreBuiltin SpanInfo + :: Interpreter ReplRuntime ReplCoreBuiltin FileLocSpanInfo -> NormalizedUri - -> Text - -> ReplM ReplCoreBuiltin (M.Map NormalizedUri [EvalTopLevel ReplCoreBuiltin SpanInfo]) -processFile replEnv nuri source = do - lexx <- liftEither (Lisp.lexer source) - parsed <- liftEither $ Lisp.parseReplProgram lexx + -> SourceCode + -> ReplM ReplCoreBuiltin (M.Map NormalizedUri [EvalTopLevel ReplCoreBuiltin FileLocSpanInfo]) +processFile replEnv nuri (SourceCode f source) = do + lexx <- liftEither $ over _Left (fmap toFileLoc) (Lisp.lexer source) + parsed <- liftEither $ bimap (fmap toFileLoc) ((fmap.fmap) toFileLoc) $ Lisp.parseReplProgram lexx M.unionsWith (<>) <$> traverse pipe parsed where - currFile = maybe "" fromNormalizedFilePath (uriToNormalizedFilePath nuri) - mangleFilePath fp = case currFile of - "" -> pure fp - _ | isAbsolute fp -> pure fp - | takeFileName currFile == currFile -> pure fp - | otherwise -> pure $ combine (takeDirectory currFile) fp - pipe rtl = case Repl.topLevelIsReplLoad rtl of - Right (Repl.ReplLoadFile fp _ i) -> do - fp' <- mangleFilePath (T.unpack fp) - res <- liftIO $ E.try (T.readFile fp') - case res of - Left (_e:: E.IOException) -> - throwExecutionError i $ EvalError $ "File not found: " <> fp - Right txt -> do - let nfp = normalizedFilePathToUri (toNormalizedFilePath fp') - processFile replEnv nfp txt - Left (Lisp.RTLTopLevel tl) -> do - functionDocs tl - (ds, deps) <- compileDesugarOnly replEnv tl - constEvaled <- ConstEval.evalTLConsts replEnv ds - tlFinal <- MHash.hashTopLevel constEvaled - let act = M.singleton nuri [ds] <$ evalTopLevel replEnv (RawCode mempty) tlFinal deps - catchError act (const (pure mempty)) - _ -> pure mempty + toFileLoc = FileLocSpanInfo f + pipe (Lisp.RTLTopLevel tl) = do + functionDocs tl + (ds, deps) <- compileDesugarOnly replEnv tl + constEvaled <- ConstEval.evalTLConsts replEnv ds + tlFinal <- MHash.hashTopLevel constEvaled + let act = M.singleton nuri [ds] <$ evalTopLevel replEnv (RawCode mempty) tlFinal deps + catchError act (const (pure mempty)) + pipe _ = pure mempty + sshow :: Show a => a -> Text sshow = T.pack . show diff --git a/pact-lsp/Pact/Core/LanguageServer/Renaming.hs b/pact-lsp/Pact/Core/LanguageServer/Renaming.hs index 80c68da8..5dc9baa6 100644 --- a/pact-lsp/Pact/Core/LanguageServer/Renaming.hs +++ b/pact-lsp/Pact/Core/LanguageServer/Renaming.hs @@ -17,10 +17,10 @@ import Data.Maybe matchingDefs - :: [EvalTopLevel ReplCoreBuiltin SpanInfo] + :: [EvalTopLevel ReplCoreBuiltin i] -> ModuleName -> Text - -> (Maybe (EvalIfDef ReplCoreBuiltin SpanInfo), Maybe (EvalDef ReplCoreBuiltin SpanInfo)) + -> (Maybe (EvalIfDef ReplCoreBuiltin i), Maybe (EvalDef ReplCoreBuiltin i)) matchingDefs tls mn n = (interfaceDef, moduleDef) where interfaceDef = do @@ -41,23 +41,25 @@ matchingDefs tls mn n = (interfaceDef, moduleDef) matchingTerms - :: (EvalTerm ReplCoreBuiltin SpanInfo -> Bool) - -> EvalTopLevel ReplCoreBuiltin SpanInfo - -> [EvalTerm ReplCoreBuiltin SpanInfo] + :: forall i. () + => (EvalTerm ReplCoreBuiltin i -> Bool) + -> EvalTopLevel ReplCoreBuiltin i + -> [EvalTerm ReplCoreBuiltin i] matchingTerms predicate topLevel = let terms = toListOf topLevelTerms topLevel in concatMap (toListOf filteredTerms) terms where filteredTerms :: Traversal' - (EvalTerm ReplCoreBuiltin SpanInfo) (EvalTerm ReplCoreBuiltin SpanInfo) + (EvalTerm ReplCoreBuiltin i) (EvalTerm ReplCoreBuiltin i) filteredTerms = traverseTerm . filtered predicate getRenameSpanInfo - :: [EvalTopLevel ReplCoreBuiltin SpanInfo] - -> PositionMatch ReplCoreBuiltin SpanInfo + :: HasSpanInfo i + => [EvalTopLevel ReplCoreBuiltin i] + -> PositionMatch ReplCoreBuiltin i -> [SpanInfo] getRenameSpanInfo tls = \case TermMatch (Var (Name n vt) _) -> case vt of @@ -68,13 +70,13 @@ getRenameSpanInfo tls = \case _ -> False termOccurences = toListOf (each . termInfo) $ concatMap (matchingTerms isSameVar) tls (mInterfPos, mDefPos) = bimap (fmap ifDefNameInfo) (fmap defNameInfo) (matchingDefs tls mn n) - concat [maybeToList mInterfPos, maybeToList mDefPos, termOccurences] + fmap (view spanInfo) $ concat [maybeToList mInterfPos, maybeToList mDefPos, termOccurences] _ -> mempty DefunMatch (Defun spec _args _body _) -> do let dName = _argName spec isSameVar = \case Var (Name n _) _ -> n == dName _ -> False - termOccurences = toListOf (each . termInfo) $ concatMap (matchingTerms isSameVar) tls - _argInfo spec : termOccurences + termOccurences = toListOf (each . termInfo . spanInfo) $ concatMap (matchingTerms isSameVar) tls + view spanInfo (_argInfo spec) : termOccurences _ -> mempty diff --git a/pact-lsp/Pact/Core/LanguageServer/Utils.hs b/pact-lsp/Pact/Core/LanguageServer/Utils.hs index e68119fc..38fa64ed 100644 --- a/pact-lsp/Pact/Core/LanguageServer/Utils.hs +++ b/pact-lsp/Pact/Core/LanguageServer/Utils.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ViewPatterns #-} -- | module Pact.Core.LanguageServer.Utils where @@ -13,9 +14,10 @@ import Control.Lens hiding (inside) import Pact.Core.Imports termAt - :: Position - -> EvalTerm ReplCoreBuiltin SpanInfo - -> Maybe (EvalTerm ReplCoreBuiltin SpanInfo) + :: HasSpanInfo i + => Position + -> EvalTerm ReplCoreBuiltin i + -> Maybe (EvalTerm ReplCoreBuiltin i) termAt p term | p `inside` view termInfo term = case term of t@(Lam _ b _) -> termAt p b <|> Just t @@ -55,9 +57,10 @@ data PositionMatch b i deriving Show topLevelTermAt - :: Position - -> EvalTopLevel ReplCoreBuiltin SpanInfo - -> Maybe (PositionMatch ReplCoreBuiltin SpanInfo) + :: HasSpanInfo i + => Position + -> EvalTopLevel ReplCoreBuiltin i + -> Maybe (PositionMatch ReplCoreBuiltin i) topLevelTermAt p = \case TLModule m -> goModule m TLInterface i -> goInterface i @@ -77,7 +80,7 @@ topLevelTermAt p = \case -- otherwise, we follow as usual. case termAt p tm of Nothing -> Just (DefunMatch d) - Just tm' -> if i == view termInfo tm' + Just tm' -> if view spanInfo i == view (termInfo.spanInfo) tm' then Just (DefunMatch d) else TermMatch <$> termAt p tm | otherwise -> Nothing @@ -108,8 +111,8 @@ topLevelTermAt p = \case _ -> Nothing -- | Check if a `Position` is contained within a `Span` -inside :: Position -> SpanInfo -> Bool -inside pos (SpanInfo sl sc el ec) = sPos <= pos && pos < ePos +inside :: HasSpanInfo i => Position -> i -> Bool +inside pos (view spanInfo -> SpanInfo sl sc el ec) = sPos <= pos && pos < ePos where sPos = Position (fromIntegral sl) (fromIntegral sc) ePos = Position (fromIntegral el) (fromIntegral ec) diff --git a/pact-repl/Pact/Core/IR/Eval/Direct/CoreBuiltin.hs b/pact-repl/Pact/Core/IR/Eval/Direct/CoreBuiltin.hs index d22cda51..2b60a1a1 100644 --- a/pact-repl/Pact/Core/IR/Eval/Direct/CoreBuiltin.hs +++ b/pact-repl/Pact/Core/IR/Eval/Direct/CoreBuiltin.hs @@ -624,7 +624,7 @@ zipList info b _env = \case where go x y = do chargeUnconsWork info - enforcePactValue info =<< applyLam clo [VPactValue x, VPactValue y] + enforcePactValue info =<< applyLam info clo [VPactValue x, VPactValue y] args -> argsError info b args coreMap :: (IsBuiltin b) => NativeFunction e b i @@ -634,7 +634,7 @@ coreMap info b _env = \case where go x = do chargeUnconsWork info - applyLam clo [VPactValue x] >>= enforcePactValue info + applyLam info clo [VPactValue x] >>= enforcePactValue info args -> argsError info b args coreFilter :: (IsBuiltin b) => NativeFunction e b i @@ -644,7 +644,7 @@ coreFilter info b _env = \case where go e = do chargeUnconsWork info - applyLam clo [VPactValue e] >>= enforceBool info + applyLam info clo [VPactValue e] >>= enforceBool info args -> argsError info b args coreFold :: (IsBuiltin b) => NativeFunction e b i @@ -654,7 +654,7 @@ coreFold info b _env = \case where go e inc = do chargeUnconsWork info - applyLam clo [VPactValue e, VPactValue inc] >>= enforcePactValue info + applyLam info clo [VPactValue e, VPactValue inc] >>= enforcePactValue info args -> argsError info b args coreEnumerate :: (IsBuiltin b) => NativeFunction e b i @@ -756,7 +756,7 @@ coreResume info b env = \case Nothing -> throwExecutionError info (NoYieldInDefPactStep pactStep) Just y@(Yield resumeObj _ _) -> do enforceYield info y - applyLam clo [VObject resumeObj] + applyLam info clo [VObject resumeObj] args -> argsError info b args ----------------------------------- @@ -978,7 +978,7 @@ coreReadKeyset info b _env = \case coreBind :: (IsBuiltin b) => NativeFunction e b i coreBind info b _env = \case [v@VObject{}, VClosure clo] -> - applyLam clo [v] >>= enforcePactValue' info + applyLam info clo [v] >>= enforcePactValue' info args -> argsError info b args @@ -1016,7 +1016,7 @@ dbSelect info b env = \case go k = liftGasM info (_pdbRead pdb (tvToDomain tv) k) >>= \case Just (RowData r) -> do - cond <- enforceBool info =<< applyLam clo [VObject r] + cond <- enforceBool info =<< applyLam info clo [VObject r] if cond then pure $ Just r else pure Nothing Nothing -> failInvariant info (InvariantNoSuchKeyInTable (_tvName tv) k) @@ -1033,9 +1033,9 @@ foldDb info b env = \case go rk@(RowKey raw) = do liftGasM info (_pdbRead pdb (tvToDomain tv) rk) >>= \case Just (RowData row) -> do - qryCond <- enforceBool info =<< applyLam queryClo [VString raw, VObject row] + qryCond <- enforceBool info =<< applyLam info queryClo [VString raw, VObject row] if qryCond then do - v <- enforcePactValue info =<< applyLam consumer [VString raw, VObject row] + v <- enforcePactValue info =<< applyLam info consumer [VString raw, VObject row] pure (Just v) else pure Nothing Nothing -> @@ -1079,7 +1079,7 @@ dbWithRead :: (IsBuiltin b) => NativeFunction e b i dbWithRead info b env = \case [VTable tv, VString rk, VClosure clo] -> do v <- dbRead info b env [VTable tv, VString rk] - applyLam clo [v] >>= enforcePactValue' info + applyLam info clo [v] >>= enforcePactValue' info args -> argsError info b args dbWithDefaultRead :: (IsBuiltin b) => NativeFunction e b i @@ -1090,9 +1090,9 @@ dbWithDefaultRead info b env = \case Just (RowData o) -> do bytes <- sizeOf info SizeOfV0 o chargeGasArgs info (GRead bytes) - applyLam clo [VObject o] >>= enforcePactValue' info + applyLam info clo [VObject o] >>= enforcePactValue' info Nothing -> - applyLam clo [VObject defaultObj] >>= enforcePactValue' info + applyLam info clo [VObject defaultObj] >>= enforcePactValue' info args -> argsError info b args -- | Todo: schema checking here? Or only on writes? @@ -1402,23 +1402,23 @@ integerToBS v = BS.pack $ reverse $ go v coreAndQ :: (IsBuiltin b) => NativeFunction e b i coreAndQ info b _env = \case [VClosure l, VClosure r, VPactValue v] -> do - c1 <- enforceBool info =<< applyLam l [VPactValue v] - if c1 then applyLam r [VPactValue v] >>= enforceBool' info + c1 <- enforceBool info =<< applyLam info l [VPactValue v] + if c1 then applyLam info r [VPactValue v] >>= enforceBool' info else return (VBool False) args -> argsError info b args coreOrQ :: (IsBuiltin b) => NativeFunction e b i coreOrQ info b _env = \case [VClosure l, VClosure r, VPactValue v] -> do - c1 <- enforceBool info =<< applyLam l [VPactValue v] + c1 <- enforceBool info =<< applyLam info l [VPactValue v] if c1 then return (VBool True) - else applyLam r [VPactValue v] >>= enforceBool' info + else applyLam info r [VPactValue v] >>= enforceBool' info args -> argsError info b args coreNotQ :: (IsBuiltin b) => NativeFunction e b i coreNotQ info b _env = \case [VClosure clo, VPactValue v] -> do - c <- enforceBool info =<< applyLam clo [VPactValue v] + c <- enforceBool info =<< applyLam info clo [VPactValue v] return (VBool (not c)) args -> argsError info b args @@ -1428,7 +1428,7 @@ coreWhere info b _env = \case chargeGasArgs info (GObjOp (ObjOpLookup field (M.size o))) case M.lookup (Field field) o of Just v -> do - applyLam app [VPactValue v] >>= enforceBool' info + applyLam info app [VPactValue v] >>= enforceBool' info Nothing -> throwExecutionError info (ObjectIsMissingField (Field field) (ObjectData o)) args -> argsError info b args @@ -1606,8 +1606,8 @@ dbDescribeKeySet info b env = \case coreCompose :: (IsBuiltin b) => NativeFunction e b i coreCompose info b _env = \case [VClosure clo1, VClosure clo2, v] -> do - v' <- enforcePactValue info =<< applyLam clo1 [v] - applyLam clo2 [VPactValue v'] >>= enforcePactValue' info + v' <- enforcePactValue info =<< applyLam info clo1 [v] + applyLam info clo2 [VPactValue v'] >>= enforcePactValue' info -- let cont' = Fn clo2 env [] [] cont -- applyLam clo1 [v] cont' handler args -> argsError info b args @@ -1648,7 +1648,7 @@ coreValidatePrincipal info b _env = \case coreCond :: (IsBuiltin b) => NativeFunction e b i coreCond info b _env = \case [VClosure clo] -> - applyLam clo [] >>= enforcePactValue' info + applyLam info clo [] >>= enforcePactValue' info args -> argsError info b args coreIdentity :: (IsBuiltin b) => NativeFunction e b i @@ -1707,7 +1707,7 @@ coreDefineNamespace info b env = \case SmartNamespacePolicy _ fun -> getModuleMemberWithHash info fun >>= \case (Dfun d, mh) -> do clo <- mkDefunClosure d (qualNameToFqn fun mh) env - allow <- enforceBool info =<< applyLam (C clo) [VString n, VGuard adminG] + allow <- enforceBool info =<< applyLam info (C clo) [VString n, VGuard adminG] writeNs allow nsn ns _ -> throwNativeExecutionError info b $ "Fatal error: namespace manager function is not a defun" args -> argsError info b args diff --git a/pact-repl/Pact/Core/IR/Eval/Direct/Evaluator.hs b/pact-repl/Pact/Core/IR/Eval/Direct/Evaluator.hs index a7f4f810..25a1a502 100644 --- a/pact-repl/Pact/Core/IR/Eval/Direct/Evaluator.hs +++ b/pact-repl/Pact/Core/IR/Eval/Direct/Evaluator.hs @@ -235,7 +235,7 @@ evaluate env = \case App ufn uargs info -> do fn <- enforceUserAppClosure info =<< evaluate env ufn args <- traverse (evaluate env) uargs - applyLam fn args + applyLam info fn args Sequence e1 e2 info -> do v <- evaluate env e1 enforceSaturatedApp info v @@ -452,7 +452,7 @@ evalCap info env origToken@(CapToken fqn args) popType ecType contbody = do _ <- evalWithStackFrame info capStackFrame Nothing (evaluate inCapEnv capBody) (esCaps . csCapsBeingEvaluated) .= oldCapsBeingEvaluated when (ecType == NormalCapEval) $ do - updatedV <- enforcePactValue info =<< applyLam (C dfunClo) [VPactValue oldV, VPactValue newV] + updatedV <- enforcePactValue info =<< applyLam info (C dfunClo) [VPactValue oldV, VPactValue newV] let mcap' = unsafeUpdateManagedParam updatedV managedCap (esCaps . csManaged) %= S.insert mcap' evalWithCapBody info popType (Just qualCapToken) emitted env contbody @@ -667,17 +667,19 @@ evalWithStackFrame info sf mty act = do applyLamUnsafe :: (IsBuiltin b) - => CanApply e b i + => i + -> CanApply e b i -> [EvalValue e b i] -> EvalM e b i (EvalValue e b i) applyLamUnsafe = applyLam applyLam :: (IsBuiltin b) - => CanApply e b i + => i + -> CanApply e b i -> [EvalValue e b i] -> EvalM e b i (EvalValue e b i) -applyLam nclo@(N (NativeFn b env fn arity i)) args +applyLam i nclo@(N (NativeFn b env fn arity _)) args | arity == argLen = do when (builtinChargesGas b) $ chargeFlatNativeGas i b fn i b env args @@ -690,7 +692,7 @@ applyLam nclo@(N (NativeFn b env fn arity i)) args apply' !a pa (x:xs) = apply' (a - 1) (x:pa) xs apply' !a pa [] = return (VPartialNative (PartialNativeFn b env fn a pa i)) -applyLam (CT (CapTokenClosure fqn argtys arity i)) args +applyLam i (CT (CapTokenClosure fqn argtys arity _)) args | arity == argLen = do chargeGasArgs i (GAApplyLam (Just fqn) (fromIntegral argLen)) args' <- traverse (enforcePactValue i) args @@ -699,7 +701,7 @@ applyLam (CT (CapTokenClosure fqn argtys arity i)) args | otherwise = throwExecutionError i ClosureAppliedToTooManyArgs where argLen = length args -applyLam vc@(C (Closure fqn ca arity term mty env cloi)) args +applyLam cloi vc@(C (Closure fqn ca arity term mty env _)) args | arity == argLen = case ca of ArgClosure cloargs -> do chargeGasArgs cloi (GAApplyLam (Just fqn) argLen) @@ -733,7 +735,7 @@ applyLam vc@(C (Closure fqn ca arity term mty env cloi)) args return (VPartialClosure pclo) apply' _ [] _ = throwExecutionError cloi ClosureAppliedToTooManyArgs -applyLam (LC (LamClosure ca arity term mty env cloi)) args +applyLam cloi (LC (LamClosure ca arity term mty env _)) args | arity == argLen = case ca of ArgClosure _ -> do -- Todo: maybe lambda application should mangle some sort of name? @@ -761,7 +763,7 @@ applyLam (LC (LamClosure ca arity term mty env cloi)) args apply' e (ty:tys) [] = return (VPartialClosure (PartialClosure Nothing (ty :| tys) argLen (length tys + 1) term mty (set ceLocal e env) cloi)) apply' _ [] _ = throwExecutionError cloi ClosureAppliedToTooManyArgs -applyLam (PC (PartialClosure li argtys nargs _ term mty env cloi)) args = do +applyLam cloi (PC (PartialClosure li argtys nargs _ term mty env _)) args = do chargeGasArgs cloi (GAApplyLam (_sfName <$> li) (length args)) apply' nargs (view ceLocal env) (NE.toList argtys) args where @@ -780,7 +782,7 @@ applyLam (PC (PartialClosure li argtys nargs _ term mty env cloi)) args = do return (VPartialClosure pclo) apply' _ _ [] _ = throwExecutionError cloi ClosureAppliedToTooManyArgs -applyLam (PN (PartialNativeFn b env fn arity pArgs i)) args +applyLam i (PN (PartialNativeFn b env fn arity pArgs _)) args | arity == argLen = do chargeFlatNativeGas i b fn i b env (reverse pArgs ++ args) @@ -791,7 +793,7 @@ applyLam (PN (PartialNativeFn b env fn arity pArgs i)) args apply' !a pa (x:xs) = apply' (a - 1) (x:pa) xs apply' !a pa [] = return (VPartialNative (PartialNativeFn b env fn a pa i)) -applyLam (DPC (DefPactClosure fqn argtys arity env i)) args +applyLam i (DPC (DefPactClosure fqn argtys arity env _)) args | arity == argLen = case argtys of ArgClosure cloargs -> do -- Todo: defpact has much higher overhead, we must charge a bit more gas for this @@ -888,7 +890,7 @@ runUserGuard info env (UserGuard qn args) = let env' = sysOnlyEnv env clo <- mkDefunClosure d (qualNameToFqn qn mh) env' -- Todo: sys only here - True <$ (applyLam (C clo) (VPactValue <$> args) >>= enforcePactValue info) + True <$ (applyLam info (C clo) (VPactValue <$> args) >>= enforcePactValue info) (d, _) -> throwExecutionError info (UserGuardMustBeADefun qn (defKind (_qnModName qn) d)) enforceCapGuard @@ -939,7 +941,7 @@ isKeysetInSigs info env (KeySet kskeys ksPred) = do getModuleMemberWithHash info qn >>= \case (Dfun d, mh) -> do clo <- mkDefunClosure d (qualNameToFqn qn mh) env - p <- enforceBool info =<< applyLam (C clo) [VInteger (fromIntegral count), VInteger (fromIntegral matched)] + p <- enforceBool info =<< applyLam info (C clo) [VInteger (fromIntegral count), VInteger (fromIntegral matched)] unless p $ throwUserRecoverableError info $ KeysetPredicateFailure ksPred kskeys pure p _ -> throwExecutionError info (InvalidCustomKeysetPredicate "expected defun") @@ -949,7 +951,7 @@ isKeysetInSigs info env (KeySet kskeys ksPred) = do Just b -> do let builtins = view ceBuiltins env let nativeclo = builtins info b env - p <- enforceBool info =<< applyLam (N nativeclo) [VInteger (fromIntegral count), VInteger (fromIntegral matched)] + p <- enforceBool info =<< applyLam info (N nativeclo) [VInteger (fromIntegral count), VInteger (fromIntegral matched)] unless p $ throwUserRecoverableError info $ KeysetPredicateFailure ksPred kskeys pure p Nothing -> diff --git a/pact-repl/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs b/pact-repl/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs index 513f8a84..c9591dc3 100644 --- a/pact-repl/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs +++ b/pact-repl/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs @@ -60,7 +60,7 @@ prettyShowValue = \case VTable (TableValue (TableName tn mn) _ _) -> "table{" <> renderModuleName mn <> "_" <> tn <> "}" VClosure _ -> "<#closure>" -corePrint :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +corePrint :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo corePrint info b _env = \case [v] -> do liftIO $ putStrLn $ T.unpack (prettyShowValue v) @@ -68,30 +68,30 @@ corePrint info b _env = \case args -> argsError info b args returnTestFailure - :: SpanInfo + :: FileLocSpanInfo -> Text -> Text - -> EvalM ReplRuntime b SpanInfo (EvalValue ReplRuntime b SpanInfo) + -> EvalM ReplRuntime b FileLocSpanInfo (EvalValue ReplRuntime b FileLocSpanInfo) returnTestFailure info testName msg = do recordTestFailure testName info msg return (VLiteral (LString msg)) returnTestSuccess - :: SpanInfo + :: FileLocSpanInfo -> Text -> Text - -> EvalM ReplRuntime b SpanInfo (EvalValue ReplRuntime b SpanInfo) + -> EvalM ReplRuntime b FileLocSpanInfo (EvalValue ReplRuntime b FileLocSpanInfo) returnTestSuccess info testName msg = do recordTestSuccess testName info return (VString msg) -coreExpect :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +coreExpect :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreExpect info b _env = \case [VLiteral (LString testName), VClosure expected, VClosure provided] -> do es <- get - tryError (applyLamUnsafe provided []) >>= \case + tryError (applyLamUnsafe info provided []) >>= \case Right (VPactValue v2) -> do - applyLamUnsafe expected [] >>= enforcePactValue info >>= \case + applyLamUnsafe info expected [] >>= enforcePactValue info >>= \case v1 -> do if v1 /= v2 then do let v1s = prettyShowValue (VPactValue v1) @@ -104,15 +104,14 @@ coreExpect info b _env = \case throwUserRecoverableError info $ UserEnforceError failureMsg Left err -> do put es - currSource <- useReplState replCurrSource returnTestFailure info testName $ "FAILURE: " <> testName <> " evaluation of actual failed with error message:\n" <> - replError currSource err + renderCompactText err args -> argsError info b args -coreExpectThat :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +coreExpectThat :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreExpectThat info b _env = \case [VLiteral (LString testName), VClosure vclo, v] -> do - applyLamUnsafe vclo [v] >>= \case + applyLamUnsafe info vclo [v] >>= \case VLiteral (LBool c) -> if c then returnTestSuccess info testName ("Expect-that: success " <> testName) else returnTestFailure info testName ("FAILURE: Expect-that: Did not satisfy condition: " <> testName) @@ -121,11 +120,11 @@ coreExpectThat info b _env = \case throwNativeExecutionError info b "Expect-that: condition did not return a boolean" args -> argsError info b args -coreExpectFailure :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +coreExpectFailure :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreExpectFailure info b _env = \case [VString testName, VClosure vclo] -> do es <- get - tryError (applyLamUnsafe vclo []) >>= \case + tryError (applyLamUnsafe info vclo []) >>= \case Left (PEUserRecoverableError _ _ _) -> do put es returnTestSuccess info testName $ "Expect failure: Success: " <> testName @@ -136,7 +135,7 @@ coreExpectFailure info b _env = \case returnTestFailure info testName $ "FAILURE: " <> testName <> ": expected failure, got result" [VString testName, VString toMatch, VClosure vclo] -> do es <- get - tryError (applyLamUnsafe vclo []) >>= \case + tryError (applyLamUnsafe info vclo []) >>= \case Left userErr -> do put es let err = renderCompactText userErr @@ -149,7 +148,7 @@ coreExpectFailure info b _env = \case args -> argsError info b args -continuePact :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +continuePact :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo continuePact info b env = \case [VInteger s] -> go s False Nothing Nothing [VInteger s, VBool r] -> go s r Nothing Nothing @@ -180,7 +179,7 @@ continuePact info b env = \case replEvalEnv . eeDefPactStep .== Nothing liftEither merr -pactState :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +pactState :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo pactState info b _env = \case [] -> go False [VBool clear] -> go clear @@ -201,14 +200,14 @@ pactState info b _env = \case return (VObject (M.fromList ps)) Nothing -> throwUserRecoverableError info $ UserEnforceError "pact-state: no pact exec in context" -coreplEvalEnvStackFrame :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +coreplEvalEnvStackFrame :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreplEvalEnvStackFrame info b _env = \case [] -> do sfs <- fmap (PString . T.pack . show) <$> use esStack return $ VList (V.fromList sfs) args -> argsError info b args -envEvents :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envEvents :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envEvents info b _env = \case [VBool clear] -> do events <- reverse . fmap envToObj <$> use esEvents @@ -224,7 +223,7 @@ envEvents info b _env = \case , ("module-hash", PString (hashToText (_mhHash mh)))] args -> argsError info b args -envHash :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envHash :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envHash info b _env = \case [VString s] -> do case decodeBase64UrlUnpadded (T.encodeUtf8 s) of @@ -234,7 +233,7 @@ envHash info b _env = \case return $ VString $ "Set tx hash to " <> s args -> argsError info b args -envData :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envData :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envData info b _env = \case [VPactValue pv] -> do -- to mimic prod, we must roundtrip here @@ -244,7 +243,7 @@ envData info b _env = \case return (VString "Setting transaction data") args -> argsError info b args -envChainData :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envChainData :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envChainData info b _env = \case [VObject cdataObj] -> do pd <- viewEvalEnv eePublicData @@ -275,7 +274,7 @@ envChainData info b _env = \case throwUserRecoverableError info $ UserEnforceError $ "envChainData: bad public metadata value for key: " <> _field k args -> argsError info b args -envKeys :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envKeys :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envKeys info b _env = \case [VList ks] -> do keys <- traverse (asString info b) ks @@ -283,7 +282,7 @@ envKeys info b _env = \case return (VString "Setting transaction keys") args -> argsError info b args -envSigs :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envSigs :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envSigs info b _env = \case [VList ks] -> case traverse keyCapObj ks of @@ -305,7 +304,7 @@ envSigs info b _env = \case _ -> Nothing args -> argsError info b args -beginTx :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +beginTx :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo beginTx info b _env = \case [VString s] -> begin' info (Just s) >>= renderTx info "Begin Tx" [] -> begin' info Nothing >>= renderTx info "Begin Tx" @@ -317,7 +316,7 @@ renderTx _info start (Just (TxId tid, mt)) = renderTx info start Nothing = throwUserRecoverableError info $ UserEnforceError ("tx-function failure " <> start) -begin' :: SpanInfo -> Maybe Text -> ReplM b (Maybe (TxId, Maybe Text)) +begin' :: FileLocSpanInfo -> Maybe Text -> ReplM b (Maybe (TxId, Maybe Text)) begin' info mt = do pdb <- useReplState (replEvalEnv . eePactDb) mode <- viewEvalEnv eeMode @@ -336,7 +335,7 @@ emptyTxState = do $ set esCheckRecursion esc def put newEvalState -envSetDebug :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envSetDebug :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envSetDebug info b _env = \case [VString flag] -> do flags <- case T.strip flag of @@ -361,7 +360,7 @@ envSetDebug info b _env = \case return VUnit args -> argsError info b args -commitTx :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +commitTx :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo commitTx info b _env = \case [] -> do pdb <- useReplState (replEvalEnv . eePactDb) @@ -375,7 +374,7 @@ commitTx info b _env = \case args -> argsError info b args -rollbackTx :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +rollbackTx :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo rollbackTx info b _env = \case [] -> do pdb <- useReplState (replEvalEnv . eePactDb) @@ -388,7 +387,7 @@ rollbackTx info b _env = \case Nothing -> renderTx info "Rollback Tx" Nothing args -> argsError info b args -sigKeyset :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +sigKeyset :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo sigKeyset info b _env = \case [] -> do sigs <- S.fromList . M.keys <$> viewEvalEnv eeMsgSigs @@ -396,7 +395,7 @@ sigKeyset info b _env = \case args -> argsError info b args -testCapability :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +testCapability :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo testCapability info b env = \case [VCapToken origToken] -> do d <- getDefCap info (_ctName origToken) @@ -410,7 +409,7 @@ testCapability info b env = \case installCap info env origToken False *> evalCap info env origToken PopCapInvoke TestCapEval cBody args -> argsError info b args -envExecConfig :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envExecConfig :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envExecConfig info b _env = \case [VList s] -> do s' <- traverse go (V.toList s) @@ -426,7 +425,7 @@ envExecConfig info b _env = \case --failInvariant info $ "Invalid flag, allowed: " <> T.pack (show (M.keys flagReps)) args -> argsError info b args -envNamespacePolicy :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envNamespacePolicy :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envNamespacePolicy info b _env = \case [VBool allowRoot, VClosure (C clo)] -> do let qn = fqnToQualName (_cloFqName clo) @@ -441,7 +440,7 @@ envNamespacePolicy info b _env = \case throwUserRecoverableError info $ UserEnforceError "invalid namespace manager function type" args -> argsError info b args -envGas :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envGas :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGas info b _env = \case [] -> do Gas gas <- milliGasToGas <$> getGas @@ -451,7 +450,7 @@ envGas info b _env = \case return $ VString $ "Set gas to " <> T.pack (show g) args -> argsError info b args -envMilliGas :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envMilliGas :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envMilliGas info b _env = \case [] -> do MilliGas gas <- getGas @@ -461,14 +460,14 @@ envMilliGas info b _env = \case return $ VString $ "Set milligas to" <> T.pack (show g) args -> argsError info b args -envGasLimit :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envGasLimit :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGasLimit info b _env = \case [VInteger g] -> do (replEvalEnv . eeGasEnv . geGasModel . gmGasLimit) .== Just (MilliGasLimit (gasToMilliGas (Gas (fromInteger g)))) return $ VString $ "Set gas limit to " <> T.pack (show g) args -> argsError info b args -envGasLog :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envGasLog :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGasLog info b _env = \case [] -> do (gasLogRef, logsJustEnabled) <- viewEvalEnv (eeGasEnv . geGasLog) >>= \case @@ -488,7 +487,7 @@ envGasLog info b _env = \case return (VList $ V.fromList (totalLine:logLines)) args -> argsError info b args -envEnableReplNatives :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envEnableReplNatives :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envEnableReplNatives info b _env = \case [VBool enabled] -> do let s = if enabled then "enabled" else "disabled" @@ -496,7 +495,7 @@ envEnableReplNatives info b _env = \case return $ VString $ "repl natives " <> s args -> argsError info b args -envGasModel :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envGasModel :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGasModel info b _env = \case [] -> do gm <- viewEvalEnv (eeGasEnv . geGasModel) @@ -544,7 +543,7 @@ coreEnforceVersion info b _env = \case Left _msg -> throwExecutionError info (EnforcePactVersionParseFailure s) Right li -> pure (V.makeVersion li) -envModuleAdmin :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envModuleAdmin :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envModuleAdmin info b _env = \case [VModRef modRef] -> do let modName = _mrModule modRef @@ -552,7 +551,7 @@ envModuleAdmin info b _env = \case return $ VString $ "Acquired module admin for: " <> renderModuleName modName args -> argsError info b args -envVerifiers :: NativeFunction ReplRuntime ReplCoreBuiltin SpanInfo +envVerifiers :: NativeFunction ReplRuntime ReplCoreBuiltin FileLocSpanInfo envVerifiers info b _env = \case [VList ks] -> case traverse verifCapObj ks of @@ -574,15 +573,26 @@ envVerifiers info b _env = \case _ -> Nothing args -> argsError info b args +load :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo +load info b _env = \case + [VString s] -> load' s False + [VString s, VBool reset] -> load' s reset + args -> argsError info b args + where + load' sourceFile reset = do + replPrintLn $ PString $ "Loading " <> sourceFile <> "..." + fload <- useReplState replLoad + fload (T.unpack sourceFile) reset + return VUnit replBuiltinEnv - :: BuiltinEnv ReplRuntime (ReplBuiltin CoreBuiltin) SpanInfo + :: BuiltinEnv ReplRuntime (ReplBuiltin CoreBuiltin) FileLocSpanInfo replBuiltinEnv i b env = mkDirectBuiltinFn i b env (replCoreBuiltinRuntime b) replCoreBuiltinRuntime :: ReplBuiltin CoreBuiltin - -> NativeFunction ReplRuntime (ReplBuiltin CoreBuiltin) SpanInfo + -> NativeFunction ReplRuntime (ReplBuiltin CoreBuiltin) FileLocSpanInfo replCoreBuiltinRuntime = \case RBuiltinWrap cb -> coreBuiltinRuntime cb @@ -629,3 +639,5 @@ replCoreBuiltinRuntime = \case REnvModuleAdmin -> envModuleAdmin REnvVerifiers -> envVerifiers REnvSetDebugFlag -> envSetDebug + RLoad -> load + RLoadWithEnv -> load diff --git a/pact-repl/Pact/Core/Repl.hs b/pact-repl/Pact/Core/Repl.hs index 3f2d7fa0..130e10d1 100644 --- a/pact-repl/Pact/Core/Repl.hs +++ b/pact-repl/Pact/Core/Repl.hs @@ -2,6 +2,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE PartialTypeSignatures #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE DataKinds #-} @@ -36,13 +37,15 @@ import Pact.Core.Repl.Utils import Pact.Core.Serialise import Pact.Core.Info import Pact.Core.Errors +import Control.Lens +import qualified Data.Map.Strict as M -execScript :: Bool -> FilePath -> IO (Either (PactError SpanInfo) [ReplCompileValue], ReplState ReplCoreBuiltin) +execScript :: Bool -> FilePath -> IO (Either (PactError FileLocSpanInfo) [ReplCompileValue], ReplState ReplCoreBuiltin) execScript dolog f = do - pdb <- mockPactDb serialisePact_repl_spaninfo + pdb <- mockPactDb serialisePact_repl_fileLocSpanInfo ee <- defaultEvalEnv pdb replBuiltinMap - ref <- newIORef (mkReplState ee logger) - v <- evalReplM ref $ loadFile f interpretEvalDirect + ref <- newIORef (mkReplState' ee logger) + v <- evalReplM ref $ loadFile interpretEvalDirect f True state <- readIORef ref pure (v, state) where @@ -51,12 +54,13 @@ execScript dolog f = do | dolog = liftIO . T.putStrLn | otherwise = const (pure ()) + runRepl :: IO () runRepl = do - pdb <- mockPactDb serialisePact_repl_spaninfo + pdb <- mockPactDb serialisePact_repl_fileLocSpanInfo ee <- defaultEvalEnv pdb replBuiltinMap let display' rcv = runInputT replSettings (displayOutput rcv) - ref <- newIORef (mkReplState ee display') + ref <- newIORef (mkReplState' ee display') evalReplM ref (runInputT replSettings loop) >>= \case Left err -> do putStrLn "Exited repl session with error:" @@ -79,7 +83,10 @@ runRepl = do case eout of Right _ -> pure () Left err -> do - rs <- lift (useReplState replCurrSource) + let replInfo = view peInfo err + rs <- lift (usesReplState replLoadedFiles (M.lookup (_flsiFile replInfo))) >>= \case + Just sc -> pure sc + Nothing -> lift (useReplState replCurrSource) lift (replCurrSource .== defaultSrc) outputStrLn (T.unpack (replError rs err)) loop diff --git a/pact-repl/Pact/Core/Repl/Compile.hs b/pact-repl/Pact/Core/Repl/Compile.hs index 232ca9cc..0f658644 100644 --- a/pact-repl/Pact/Core/Repl/Compile.hs +++ b/pact-repl/Pact/Core/Repl/Compile.hs @@ -12,15 +12,16 @@ module Pact.Core.Repl.Compile ( ReplCompileValue(..) , interpretReplProgramBigStep - , loadFile , interpretReplProgramDirect , interpretEvalBigStep , interpretEvalDirect , interpretReplProgram , ReplInterpreter , isPactFile - , ReplLoadFile(..) - , topLevelIsReplLoad + , loadFile + , defaultLoadFile + , mkReplState + , mkReplState' ) where import Control.Lens @@ -35,7 +36,6 @@ import System.FilePath.Posix import qualified Data.Map.Strict as M -import qualified Data.Text as T import qualified Data.Text.IO as T import Pact.Core.Persistence @@ -49,12 +49,10 @@ import Pact.Core.Compile import Pact.Core.Type import Pact.Core.Environment import Pact.Core.Info -import Pact.Core.PactValue import Pact.Core.Errors import Pact.Core.Interpreter -import Pact.Core.Literal import Pact.Core.Pretty hiding (pipe) -import Pact.Core.Serialise (serialisePact_repl_spaninfo) +import Pact.Core.Serialise import Pact.Core.IR.Eval.Runtime @@ -70,18 +68,63 @@ import qualified Pact.Core.IR.Eval.CEK.Evaluator as CEK import qualified Pact.Core.IR.Eval.Direct.Evaluator as Direct import qualified Pact.Core.IR.Eval.Direct.ReplBuiltin as Direct -type ReplInterpreter = Interpreter ReplRuntime ReplCoreBuiltin SpanInfo +type ReplInterpreter = Interpreter ReplRuntime ReplCoreBuiltin FileLocSpanInfo -- Small internal debugging function for playing with file loading within -- this module data ReplCompileValue - = RCompileValue (CompileValue SpanInfo) + = RCompileValue (CompileValue FileLocSpanInfo) | RLoadedDefun Text | RLoadedDefConst Text | RBuiltinDoc Text - | RUserDoc (EvalDef ReplCoreBuiltin SpanInfo) (Maybe Text) + | RUserDoc (EvalDef ReplCoreBuiltin FileLocSpanInfo) (Maybe Text) deriving Show +mkReplState + :: EvalEnv b FileLocSpanInfo + -> (Text -> EvalM 'ReplRuntime b FileLocSpanInfo ()) + -> (FilePath -> Bool -> EvalM 'ReplRuntime b FileLocSpanInfo ()) + -> ReplState b +mkReplState ee printfn loadFn = + ReplState + { _replFlags = mempty + , _replEvalEnv = ee + , _replLogType = ReplStdOut + , _replCurrSource = defaultSrc + , _replUserDocs = mempty + , _replTLDefPos = mempty + , _replTx = Nothing + , _replNativesEnabled = False + , _replOutputLine = printfn + , _replLoad = loadFn + , _replLoadedFiles = mempty + , _replTestResults = [] + } + where + defaultSrc = SourceCode "(interactive)" mempty + +mkReplState' + :: EvalEnv ReplCoreBuiltin FileLocSpanInfo + -> (Text -> EvalM 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo ()) + -> ReplState ReplCoreBuiltin +mkReplState' ee printfn = + ReplState + { _replFlags = mempty + , _replEvalEnv = ee + , _replLogType = ReplStdOut + , _replCurrSource = defaultSrc + , _replUserDocs = mempty + , _replTLDefPos = mempty + , _replTx = Nothing + , _replNativesEnabled = False + , _replOutputLine = printfn + , _replLoad = \f reset -> void (loadFile interpretEvalDirect f reset) + , _replLoadedFiles = mempty + , _replTestResults = [] + } + where + defaultSrc = SourceCode "(interactive)" mempty + instance Pretty ReplCompileValue where pretty = \case RCompileValue cv -> pretty cv @@ -94,17 +137,6 @@ instance Pretty ReplCompileValue where vsep [pretty qn, "Docs:", maybe mempty pretty doc] --- | Internal function for loading a file. --- Exported because it is used in the tests. -loadFile - :: FilePath - -> ReplInterpreter - -> ReplM ReplCoreBuiltin [ReplCompileValue] -loadFile loc rEnv = do - source <- SourceCode loc <$> liftIO (T.readFile loc) - replCurrSource .== source - interpretReplProgram rEnv source - interpretReplProgramBigStep :: SourceCode @@ -117,7 +149,7 @@ interpretReplProgramDirect -> ReplM ReplCoreBuiltin [ReplCompileValue] interpretReplProgramDirect = interpretReplProgram interpretEvalDirect -checkReplNativesEnabled :: TopLevel n t (ReplBuiltin b) SpanInfo -> ReplM ReplCoreBuiltin () +checkReplNativesEnabled :: TopLevel n t (ReplBuiltin b) FileLocSpanInfo -> ReplM ReplCoreBuiltin () checkReplNativesEnabled = \case TLModule m -> do flag <- useReplState replNativesEnabled @@ -166,90 +198,71 @@ interpretEvalDirect = isPactFile :: FilePath -> Bool isPactFile f = takeExtension f == ".pact" -pattern PReplLoadWithClear :: Text -> Bool -> i -> Lisp.ReplTopLevel i -pattern PReplLoadWithClear file reset info <- - Lisp.RTLTopLevel ( - Lisp.TLTerm (Lisp.App (Lisp.Var (BN (BareName "load")) _) - [ Lisp.Constant (LString file) _ - , Lisp.Constant (LBool reset) _] - info) - ) -pattern PReplLoad :: Text -> i -> Lisp.ReplTopLevel i -pattern PReplLoad file info <- - Lisp.RTLTopLevel ( - Lisp.TLTerm (Lisp.App (Lisp.Var (BN (BareName "load")) _) - [ Lisp.Constant (LString file) _] - info) - ) +setBuiltinResolution :: SourceCode -> ReplM (ReplBuiltin CoreBuiltin) () +setBuiltinResolution (SourceCode fp _) + | sourceIsPactFile = + replEvalEnv . eeNatives .== replCoreBuiltinOnlyMap + | otherwise = + replEvalEnv . eeNatives .== replBuiltinMap + where + sourceIsPactFile = isPactFile fp + +defaultLoadFile :: FilePath -> Bool -> EvalM ReplRuntime ReplCoreBuiltin FileLocSpanInfo () +defaultLoadFile f reset = () <$ loadFile interpretEvalDirect f reset -data ReplLoadFile i - = ReplLoadFile - { _rlFile :: Text - , _rlReset :: Bool - , _rlInfo :: i - } deriving (Show) +loadFile :: ReplInterpreter -> FilePath -> Bool -> EvalM ReplRuntime ReplCoreBuiltin FileLocSpanInfo [ReplCompileValue] +loadFile interpreter txt reset = do + oldSrc <- useReplState replCurrSource + pactdb <- liftIO (mockPactDb serialisePact_repl_fileLocSpanInfo) + oldEE <- useReplState replEvalEnv + when reset $ do + ee <- liftIO (defaultEvalEnv pactdb replBuiltinMap) + put def + replEvalEnv .== ee + fp <- mangleFilePath txt + when (isPactFile fp) $ esLoaded . loToplevel .= mempty + source <- SourceCode fp <$> liftIO (T.readFile fp) + replCurrSource .== source + out <- interpretReplProgram interpreter source + replCurrSource .== oldSrc + unless reset $ do + replEvalEnv .== oldEE + setBuiltinResolution oldSrc + pure out -topLevelIsReplLoad :: Lisp.ReplTopLevel i -> Either (Lisp.ReplTopLevel i) (ReplLoadFile i) -topLevelIsReplLoad = \case - PReplLoad file i -> Right (ReplLoadFile file False i) - PReplLoadWithClear file reset i -> Right (ReplLoadFile file reset i) - t -> Left t +mangleFilePath :: FilePath -> EvalM ReplRuntime b FileLocSpanInfo FilePath +mangleFilePath fp = do + (SourceCode currFile _) <- useReplState replCurrSource + case currFile of + "(interactive)" -> pure fp + _ | isAbsolute fp -> pure fp + | takeFileName currFile == currFile -> pure fp + | otherwise -> pure $ combine (takeDirectory currFile) fp interpretReplProgram :: ReplInterpreter -> SourceCode -> ReplM ReplCoreBuiltin [ReplCompileValue] -interpretReplProgram interpreter (SourceCode sourceFp source) = do - lexx <- liftEither (Lisp.lexer source) +interpretReplProgram interpreter sc@(SourceCode sourceFp source) = do + replLoadedFiles %== M.insert sourceFp sc + lexx <- liftEither $ over _Left (fmap toFileLoc) (Lisp.lexer source) debugIfFlagSet ReplDebugLexer lexx - parsed <- parseSource lexx - setBuiltinResolution - concat <$> traverse pipe parsed + parsed <- liftEither $ bimap (fmap toFileLoc) ((fmap.fmap) toFileLoc) (parseSource lexx) + setBuiltinResolution sc + traverse pipe' parsed where + toFileLoc = FileLocSpanInfo sourceFp sourceIsPactFile = isPactFile sourceFp parseSource lexerOutput - | sourceIsPactFile = (fmap.fmap) (Lisp.RTLTopLevel) $ liftEither $ Lisp.parseProgram lexerOutput - | otherwise = liftEither $ Lisp.parseReplProgram lexerOutput - setBuiltinResolution - | sourceIsPactFile = - replEvalEnv . eeNatives .== replCoreBuiltinOnlyMap - | otherwise = - replEvalEnv . eeNatives .== replBuiltinMap - pipe t = case topLevelIsReplLoad t of - Left tl -> pure <$> pipe' tl - Right (ReplLoadFile file reset info) -> doLoadFile file reset info + | sourceIsPactFile = (fmap.fmap) (Lisp.RTLTopLevel) $ Lisp.parseProgram lexerOutput + | otherwise = Lisp.parseReplProgram lexerOutput displayValue p = p <$ replPrintLn p sliceCode = \case Lisp.TLModule{} -> sliceFromSource Lisp.TLInterface{} -> sliceFromSource Lisp.TLTerm{} -> \_ _ -> mempty Lisp.TLUse{} -> \_ _ -> mempty - doLoadFile txt reset i = do - let loading = RCompileValue (InterpretValue (PString ("Loading " <> txt <> "...")) i) - replPrintLn loading - oldSrc <- useReplState replCurrSource - pactdb <- liftIO (mockPactDb serialisePact_repl_spaninfo) - oldEE <- useReplState replEvalEnv - when reset $ do - ee <- liftIO (defaultEvalEnv pactdb replBuiltinMap) - put def - replEvalEnv .== ee - fp <- mangleFilePath (T.unpack txt) - when (isPactFile fp) $ esLoaded . loToplevel .= mempty - out <- loadFile fp interpreter - replCurrSource .== oldSrc - unless reset $ do - replEvalEnv .== oldEE - setBuiltinResolution - pure out - mangleFilePath fp = do - (SourceCode currFile _) <- useReplState replCurrSource - case currFile of - "(interactive)" -> pure fp - _ | isAbsolute fp -> pure fp - | takeFileName currFile == currFile -> pure fp - | otherwise -> pure $ combine (takeDirectory currFile) fp pipe' tl = case tl of Lisp.RTLTopLevel toplevel -> case topLevelHasDocs toplevel of Just doc -> displayValue $ RBuiltinDoc doc @@ -268,7 +281,7 @@ interpretReplProgram interpreter (SourceCode sourceFp source) = do Nothing -> throwExecutionError varI $ EvalError "repl invariant violated: resolved to a top level free variable without a binder" _ -> do - let sliced = sliceCode toplevel source (view Lisp.topLevelInfo toplevel) + let sliced = sliceCode toplevel source (view (Lisp.topLevelInfo.spanInfo) toplevel) v <- evalTopLevel interpreter (RawCode sliced) ds deps emitWarnings replPrintLn v diff --git a/pact-repl/Pact/Core/Repl/Runtime/ReplBuiltin.hs b/pact-repl/Pact/Core/Repl/Runtime/ReplBuiltin.hs index 6e88ec55..68cfd68a 100644 --- a/pact-repl/Pact/Core/Repl/Runtime/ReplBuiltin.hs +++ b/pact-repl/Pact/Core/Repl/Runtime/ReplBuiltin.hs @@ -58,7 +58,7 @@ prettyShowValue = \case VTable (TableValue (TableName tn mn) _ _) -> "table{" <> renderModuleName mn <> "_" <> tn <> "}" VClosure _ -> "<#closure>" -corePrint :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +corePrint :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo corePrint info b cont handler _env = \case [v] -> do liftIO $ putStrLn $ T.unpack (prettyShowValue v) @@ -67,30 +67,30 @@ corePrint info b cont handler _env = \case returnTestFailure :: IsBuiltin b - => SpanInfo + => FileLocSpanInfo -> Text - -> Cont ReplRuntime b SpanInfo - -> CEKErrorHandler ReplRuntime b SpanInfo + -> Cont ReplRuntime b FileLocSpanInfo + -> CEKErrorHandler ReplRuntime b FileLocSpanInfo -> Text - -> EvalM ReplRuntime b SpanInfo (EvalResult ReplRuntime b SpanInfo) + -> EvalM ReplRuntime b FileLocSpanInfo (EvalResult ReplRuntime b FileLocSpanInfo) returnTestFailure info testName cont handler msg = do recordTestFailure testName info msg returnCEKValue cont handler (VLiteral (LString msg)) returnTestSuccess :: IsBuiltin b - => SpanInfo + => FileLocSpanInfo -> Text - -> Cont ReplRuntime b SpanInfo - -> CEKErrorHandler ReplRuntime b SpanInfo + -> Cont ReplRuntime b FileLocSpanInfo + -> CEKErrorHandler ReplRuntime b FileLocSpanInfo -> Text - -> EvalM ReplRuntime b SpanInfo (EvalResult ReplRuntime b SpanInfo) + -> EvalM ReplRuntime b FileLocSpanInfo (EvalResult ReplRuntime b FileLocSpanInfo) returnTestSuccess info testName cont handler msg = do recordTestSuccess testName info returnCEKValue cont handler (VString msg) -coreExpect :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +coreExpect :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreExpect info b cont handler _env = \case [VLiteral (LString testName), VClosure expected, VClosure provided] -> do -- Get the state of execution before running the test @@ -119,12 +119,11 @@ coreExpect info b cont handler _env = \case returnCEKError info cont handler $ UserEnforceError "FAILURE: expect expression did not return a pact value for comparison" Left err -> do put es - currSource <- useReplState replCurrSource - let failureMsg = "FAILURE: " <> testName <> " evaluation of actual failed with error message:\n" <> replError currSource err + let failureMsg = "FAILURE: " <> testName <> " evaluation of actual failed with error message:\n" <> renderCompactText err returnTestFailure info testName cont handler failureMsg args -> argsError info b args -coreExpectThat :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +coreExpectThat :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreExpectThat info b cont handler _env = \case [VLiteral (LString testName), VClosure vclo, v] -> do applyLamUnsafe vclo [v] Mt CEKNoHandler >>= \case @@ -144,7 +143,7 @@ coreExpectThat info b cont handler _env = \case returnCEK cont handler ve args -> argsError info b args -coreExpectFailure :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +coreExpectFailure :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreExpectFailure info b cont handler _env = \case [VString testName, VClosure vclo] -> do es <- get @@ -179,7 +178,7 @@ coreExpectFailure info b cont handler _env = \case args -> argsError info b args -continuePact :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +continuePact :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo continuePact info b cont handler env = \case [VInteger s] -> go s False Nothing Nothing [VInteger s, VBool r] -> go s r Nothing Nothing @@ -211,7 +210,7 @@ continuePact info b cont handler env = \case v <- liftEither merr returnCEK cont handler v -pactState :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +pactState :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo pactState info b cont handler _env = \case [] -> go False [VBool clear] -> go clear @@ -232,14 +231,14 @@ pactState info b cont handler _env = \case returnCEKValue cont handler (VObject (M.fromList ps)) Nothing -> returnCEKError info cont handler $ UserEnforceError "pact-state: no pact exec in context" -coreplEvalEnvStackFrame :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +coreplEvalEnvStackFrame :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo coreplEvalEnvStackFrame info b cont handler _env = \case [] -> do sfs <- fmap (PString . T.pack . show) <$> use esStack returnCEKValue cont handler $ VList (V.fromList sfs) args -> argsError info b args -envEvents :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envEvents :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envEvents info b cont handler _env = \case [VBool clear] -> do events <- reverse . fmap envToObj <$> use esEvents @@ -255,7 +254,7 @@ envEvents info b cont handler _env = \case , ("module-hash", PString (hashToText (_mhHash mh)))] args -> argsError info b args -envHash :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envHash :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envHash info b cont handler _env = \case [VString s] -> do case decodeBase64UrlUnpadded (T.encodeUtf8 s) of @@ -265,7 +264,7 @@ envHash info b cont handler _env = \case returnCEKValue cont handler $ VString $ "Set tx hash to " <> s args -> argsError info b args -envData :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envData :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envData info b cont handler _env = \case [VPactValue pv] -> do -- to mimic prod, we must roundtrip here @@ -275,7 +274,7 @@ envData info b cont handler _env = \case returnCEKValue cont handler (VString "Setting transaction data") args -> argsError info b args -envChainData :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envChainData :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envChainData info b cont handler _env = \case [VObject cdataObj] -> do pd <- viewEvalEnv eePublicData @@ -305,7 +304,7 @@ envChainData info b cont handler _env = \case _ -> returnCEKError info cont handler $ UserEnforceError $ "envChainData: bad public metadata value for key: " <> _field k args -> argsError info b args -envKeys :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envKeys :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envKeys info b cont handler _env = \case [VList ks] -> do keys <- traverse (asString info b) ks @@ -313,7 +312,7 @@ envKeys info b cont handler _env = \case returnCEKValue cont handler (VString "Setting transaction keys") args -> argsError info b args -envSigs :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envSigs :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envSigs info b cont handler _env = \case [VList ks] -> case traverse keyCapObj ks of @@ -334,7 +333,7 @@ envSigs info b cont handler _env = \case _ -> Nothing args -> argsError info b args -envVerifiers :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envVerifiers :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envVerifiers info b cont handler _env = \case [VList ks] -> case traverse verifCapObj ks of @@ -356,7 +355,7 @@ envVerifiers info b cont handler _env = \case _ -> Nothing args -> argsError info b args -beginTx :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +beginTx :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo beginTx info b cont handler _env = \case [VString s] -> begin' info (Just s) >>= returnCEK cont handler . renderTx info "Begin Tx" [] -> begin' info Nothing >>= returnCEK cont handler . renderTx info "Begin Tx" @@ -367,7 +366,7 @@ renderTx _info start (Just (TxId tid, mt)) = EvalValue $ VString $ start <> " " <> T.pack (show tid) <> maybe mempty (" " <>) mt renderTx info start Nothing = VError [] (UserEnforceError ("tx-function failure " <> start)) info -begin' :: SpanInfo -> Maybe Text -> ReplM b (Maybe (TxId, Maybe Text)) +begin' :: FileLocSpanInfo -> Maybe Text -> ReplM b (Maybe (TxId, Maybe Text)) begin' info mt = do pdb <- useReplState (replEvalEnv . eePactDb) mode <- viewEvalEnv eeMode @@ -387,7 +386,7 @@ emptyTxState = do put newEvalState -commitTx :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +commitTx :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo commitTx info b cont handler _env = \case [] -> do pdb <- useReplState (replEvalEnv . eePactDb) @@ -401,7 +400,7 @@ commitTx info b cont handler _env = \case args -> argsError info b args -rollbackTx :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +rollbackTx :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo rollbackTx info b cont handler _env = \case [] -> do pdb <- useReplState (replEvalEnv . eePactDb) @@ -414,7 +413,7 @@ rollbackTx info b cont handler _env = \case Nothing -> returnCEK cont handler (renderTx info "Rollback Tx" Nothing) args -> argsError info b args -sigKeyset :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +sigKeyset :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo sigKeyset info b cont handler _env = \case [] -> do sigs <- S.fromList . M.keys <$> viewEvalEnv eeMsgSigs @@ -422,7 +421,7 @@ sigKeyset info b cont handler _env = \case args -> argsError info b args -testCapability :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +testCapability :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo testCapability info b cont handler env = \case [VCapToken origToken] -> do d <- getDefCap info (_ctName origToken) @@ -437,7 +436,7 @@ testCapability info b cont handler env = \case installCap info env origToken False *> evalCap info cont' handler env origToken PopCapInvoke TestCapEval cBody args -> argsError info b args -envExecConfig :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envExecConfig :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envExecConfig info b cont handler _env = \case [VList s] -> do s' <- traverse go (V.toList s) @@ -454,7 +453,7 @@ envExecConfig info b cont handler _env = \case args -> argsError info b args -envNamespacePolicy :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envNamespacePolicy :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envNamespacePolicy info b cont handler _env = \case [VBool allowRoot, VClosure (C clo)] -> do let qn = fqnToQualName (_cloFqName clo) @@ -467,7 +466,7 @@ envNamespacePolicy info b cont handler _env = \case _ -> returnCEKError info cont handler $ UserEnforceError "invalid namespace manager function type" args -> argsError info b args -envGas :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envGas :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGas info b cont handler _env = \case [] -> do Gas gas <- milliGasToGas <$> getGas @@ -477,7 +476,7 @@ envGas info b cont handler _env = \case returnCEKValue cont handler $ VString $ "Set gas to " <> T.pack (show g) args -> argsError info b args -envMilliGas :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envMilliGas :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envMilliGas info b cont handler _env = \case [] -> do MilliGas gas <- getGas @@ -487,14 +486,14 @@ envMilliGas info b cont handler _env = \case returnCEKValue cont handler $ VString $ "Set milligas to" <> T.pack (show g) args -> argsError info b args -envGasLimit :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envGasLimit :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGasLimit info b cont handler _env = \case [VInteger g] -> do (replEvalEnv . eeGasEnv . geGasModel . gmGasLimit) .== Just (MilliGasLimit (gasToMilliGas (Gas (fromInteger g)))) returnCEKValue cont handler $ VString $ "Set gas limit to " <> T.pack (show g) args -> argsError info b args -envGasLog :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envGasLog :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGasLog info b cont handler _env = \case [] -> do (gasLogRef, logsJustEnabled) <- viewEvalEnv (eeGasEnv . geGasLog) >>= \case @@ -514,7 +513,7 @@ envGasLog info b cont handler _env = \case returnCEKValue cont handler (VList $ V.fromList (totalLine:logLines)) args -> argsError info b args -envEnableReplNatives :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envEnableReplNatives :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envEnableReplNatives info b cont handler _env = \case [VBool enabled] -> do let s = if enabled then "enabled" else "disabled" @@ -522,7 +521,7 @@ envEnableReplNatives info b cont handler _env = \case returnCEKValue cont handler $ VString $ "repl natives " <> s args -> argsError info b args -envGasModel :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envGasModel :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envGasModel info b cont handler _env = \case [] -> do gm <- viewEvalEnv (eeGasEnv . geGasModel) @@ -539,7 +538,7 @@ envGasModel info b cont handler _env = \case args -> argsError info b args -envModuleAdmin :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envModuleAdmin :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envModuleAdmin info b cont handler _env = \case [VModRef modRef] -> do let modName = _mrModule modRef @@ -559,7 +558,7 @@ coreVersion info b cont handler _env = \case in returnCEKValue cont handler (VString v) args -> argsError info b args -envSetDebug :: NativeFunction 'ReplRuntime ReplCoreBuiltin SpanInfo +envSetDebug :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo envSetDebug info b cont handler _env = \case [VString flag] -> do flags <- case T.strip flag of @@ -605,16 +604,27 @@ coreEnforceVersion info b cont handler _env = \case Left _msg -> throwExecutionError info (EnforcePactVersionParseFailure s) Right li -> pure (V.makeVersion li) +load :: NativeFunction 'ReplRuntime ReplCoreBuiltin FileLocSpanInfo +load info b cont handler _env = \case + [VString s] -> load' s False + [VString s, VBool reset] -> load' s reset + args -> argsError info b args + where + load' sourceFile reset = do + replPrintLn $ PString $ "Loading " <> sourceFile <> "..." + fload <- useReplState replLoad + fload (T.unpack sourceFile) reset + returnCEKValue cont handler VUnit replBuiltinEnv - :: BuiltinEnv 'ReplRuntime (ReplBuiltin CoreBuiltin) SpanInfo + :: BuiltinEnv 'ReplRuntime (ReplBuiltin CoreBuiltin) FileLocSpanInfo replBuiltinEnv i b env = mkBuiltinFn i b env (replCoreBuiltinRuntime b) replCoreBuiltinRuntime :: ReplBuiltin CoreBuiltin - -> NativeFunction 'ReplRuntime (ReplBuiltin CoreBuiltin) SpanInfo + -> NativeFunction 'ReplRuntime (ReplBuiltin CoreBuiltin) FileLocSpanInfo replCoreBuiltinRuntime = \case RBuiltinWrap cb -> coreBuiltinRuntime cb @@ -661,3 +671,5 @@ replCoreBuiltinRuntime = \case REnvModuleAdmin -> envModuleAdmin REnvVerifiers -> envVerifiers REnvSetDebugFlag -> envSetDebug + RLoad -> load + RLoadWithEnv -> load diff --git a/pact-repl/Pact/Core/Repl/UserDocs.hs b/pact-repl/Pact/Core/Repl/UserDocs.hs index df86c9e0..500eecb5 100644 --- a/pact-repl/Pact/Core/Repl/UserDocs.hs +++ b/pact-repl/Pact/Core/Repl/UserDocs.hs @@ -10,7 +10,7 @@ import qualified Pact.Core.Syntax.ParseTree as Lisp import Data.Foldable (traverse_) functionDocs - :: Lisp.TopLevel SpanInfo + :: Lisp.TopLevel FileLocSpanInfo -- The original module syntax -> ReplM ReplCoreBuiltin () functionDocs = \case diff --git a/pact-repl/Pact/Core/Repl/Utils.hs b/pact-repl/Pact/Core/Repl/Utils.hs index 9f4eeffa..3bd8ff0e 100644 --- a/pact-repl/Pact/Core/Repl/Utils.hs +++ b/pact-repl/Pact/Core/Repl/Utils.hs @@ -204,17 +204,18 @@ replCompletion natives = dns = defNames ems in fmap ((renderModuleName mn <> ".") <>) dns -evalReplM :: IORef (ReplState b) -> ReplM b a -> IO (Either (PactError SpanInfo) a) +evalReplM :: IORef (ReplState b) -> ReplM b a -> IO (Either (PactError FileLocSpanInfo) a) evalReplM env st = runEvalMResult (ReplEnv env) def st replError - :: SourceCode - -> PactErrorI + :: (HasSpanInfo i, Pretty i) + => SourceCode + -> PactError i -> Text replError (SourceCode srcFile src) pe = let file = T.pack srcFile srcLines = T.lines src - pei = view peInfo pe + pei = view (peInfo.spanInfo) pe -- Note: The startline is 0-indexed, but we want our -- repl to output errors which are 1-indexed. start = _liStartLine pei @@ -231,40 +232,44 @@ replError (SourceCode srcFile src) pe = where sfRender = case viewErrorStack pe of [] -> mempty - sfs -> renderText' $ vsep ((" at" <+>) . pretty <$> sfs) + sfs -> + let renderSf sf = " at" <> pretty sf <> ":" <> pretty (_sfInfo sf) + in renderText' $ vsep (renderSf <$> sfs) padLeft t pad = T.replicate (pad - (T.length t)) " " <> t <> " " -- Zip the line number with the source text, and apply the number padding correctly withLine st pad lns = zipWith (\i e -> padLeft (T.pack (show i)) pad <> "| " <> e) [st+1..] lns -gasLogEntrytoPactValue :: GasLogEntry (ReplBuiltin CoreBuiltin) SpanInfo -> PactValue +gasLogEntrytoPactValue :: Pretty i => GasLogEntry (ReplBuiltin CoreBuiltin) i -> PactValue gasLogEntrytoPactValue entry = PString $ renderCompactText' $ n <> ": " <> pretty (_gleThisUsed entry) where n = pretty (_gleArgs entry) <+> pretty (_gleInfo entry) -replPrintLn :: Pretty a => a -> EvalM 'ReplRuntime b SpanInfo () +replPrintLn :: Pretty a => a -> EvalM 'ReplRuntime b FileLocSpanInfo () replPrintLn p = replPrintLn' (renderCompactText p) -replPrintLn' :: Text -> EvalM 'ReplRuntime b SpanInfo () +replPrintLn' :: Text -> EvalM 'ReplRuntime b FileLocSpanInfo () replPrintLn' p = do r <- getReplState - _replOutputLine r p + case _replLogType r of + ReplStdOut -> _replOutputLine r p + ReplLogOut v -> + liftIO (modifyIORef' v (p:)) recordTestResult :: Text -- ^ Test name - -> SpanInfo + -> FileLocSpanInfo -- ^ Test location -> ReplTestStatus -> ReplM b () recordTestResult name loc status = do - SourceCode file _src <- useReplState replCurrSource - let testResult = ReplTestResult name loc file status + let testResult = ReplTestResult name loc status replTestResults %== (testResult :) -recordTestSuccess :: Text -> SpanInfo -> ReplM b () +recordTestSuccess :: Text -> FileLocSpanInfo -> ReplM b () recordTestSuccess name loc = recordTestResult name loc ReplTestPassed -recordTestFailure :: Text -> SpanInfo -> Text -> ReplM b () +recordTestFailure :: Text -> FileLocSpanInfo -> Text -> ReplM b () recordTestFailure name loc failmsg = recordTestResult name loc (ReplTestFailed failmsg) -- This orphan instance allows us to separate diff --git a/pact-tests/Pact/Core/Test/DocsTests.hs b/pact-tests/Pact/Core/Test/DocsTests.hs index b84e6ddd..46688033 100644 --- a/pact-tests/Pact/Core/Test/DocsTests.hs +++ b/pact-tests/Pact/Core/Test/DocsTests.hs @@ -33,4 +33,4 @@ docsExistsTest b = testCase "Builtins should have docs" $ do ,"env-gaslog", "env-gasmodel-fixed", "env-milligas", "env-module-admin" ,"env-set-milligas", "env-stackframe", "env-verifiers", "negate" ,"pact-state", "print", "reset-pact-state", "rollback-tx", "show" - ,"sig-keyset", "test-capability", "env-set-debug-flag"] + ,"sig-keyset", "test-capability", "env-set-debug-flag","load-with-env"] diff --git a/pact-tests/Pact/Core/Test/GasGolden.hs b/pact-tests/Pact/Core/Test/GasGolden.hs index 2108e092..e655fd14 100644 --- a/pact-tests/Pact/Core/Test/GasGolden.hs +++ b/pact-tests/Pact/Core/Test/GasGolden.hs @@ -12,6 +12,7 @@ import Pact.Core.Builtin import Pact.Core.Environment import Pact.Core.Gas import Pact.Core.Persistence.MockPersistence +import Pact.Core.Repl import Pact.Core.Repl.Compile import Pact.Core.Repl.Utils import Pact.Core.Serialise @@ -29,15 +30,13 @@ import qualified Data.Text.IO as T import Data.List (sort) import Control.Lens -type InterpretPact = SourceCode -> ReplM ReplCoreBuiltin [ReplCompileValue] - tests :: IO TestTree tests = do cases <- gasTestFiles pure $ testGroup "Gas Goldens" [ testCase "Capture all builtins" $ captureBuiltins (fst <$> cases) - , goldenVsStringDiff "Gas Goldens: CEK" runDiff (gasTestDir "builtinGas.golden") (gasGoldenTests cases interpretReplProgramBigStep) - , goldenVsStringDiff "Gas Goldens: Direct" runDiff (gasTestDir "builtinGas.golden") (gasGoldenTests cases interpretReplProgramDirect) + , goldenVsStringDiff "Gas Goldens: CEK" runDiff (gasTestDir "builtinGas.golden") (gasGoldenTests cases interpretEvalBigStep) + , goldenVsStringDiff "Gas Goldens: Direct" runDiff (gasTestDir "builtinGas.golden") (gasGoldenTests cases interpretEvalDirect) ] where runDiff = \ref new -> ["diff", "-u", ref, new] @@ -68,7 +67,7 @@ lookupOp :: Text -> Text lookupOp n = fromMaybe n (M.lookup n fileNameToOp) -gasGoldenTests :: [(Text, FilePath)] -> InterpretPact -> IO BS.ByteString +gasGoldenTests :: [(Text, FilePath)] -> ReplInterpreter -> IO BS.ByteString gasGoldenTests c interp = do gasOutputs <- forM c $ \(fn, fp) -> do mGas <- runGasTest (gasTestDir fp) interp @@ -104,16 +103,16 @@ opToFileName = M.fromList fileNameToOp :: M.Map Text Text fileNameToOp = M.fromList [(v,k) | (k, v) <- M.toList opToFileName] -runGasTest :: FilePath -> InterpretPact -> IO (Maybe MilliGas) +runGasTest :: FilePath -> ReplInterpreter -> IO (Maybe MilliGas) runGasTest file interpret = do src <- T.readFile file - pdb <- mockPactDb serialisePact_repl_spaninfo + pdb <- mockPactDb serialisePact_repl_fileLocSpanInfo ee <- defaultEvalEnv pdb replBuiltinMap let ee' = ee & eeGasEnv . geGasModel .~ replTableGasModel (Just (maxBound :: MilliGasLimit)) gasRef = ee' ^. eeGasEnv . geGasRef let source = SourceCode file src - let rstate = mkReplState ee' (const (pure ())) & replCurrSource .~ source + let rstate = mkReplState ee' (const (pure ())) (\f r -> void (loadFile interpret f r)) & replCurrSource .~ source stateRef <- newIORef rstate - evalReplM stateRef (interpret source) >>= \case + evalReplM stateRef (interpretReplProgram interpret source) >>= \case Left _ -> pure Nothing Right _ -> Just <$> readIORef gasRef diff --git a/pact-tests/Pact/Core/Test/LegacySerialiseTests.hs b/pact-tests/Pact/Core/Test/LegacySerialiseTests.hs index 481d93f2..bc420562 100644 --- a/pact-tests/Pact/Core/Test/LegacySerialiseTests.hs +++ b/pact-tests/Pact/Core/Test/LegacySerialiseTests.hs @@ -58,11 +58,11 @@ legacyTests = do Nothing -> error "Reading existing modules failed" Just ms -> do modTests <- fmap concat $ forM repl $ \r -> do - sequence [runTest r interpretReplProgramBigStep "CEK", runTest r interpretReplProgramDirect "Direct"] + sequence [runTest r interpretEvalBigStep "CEK", runTest r interpretEvalDirect "Direct"] pure (testGroup p modTests) where runTest r interpreter interpName = do - pdb <- mockPactDb serialisePact_repl_spaninfo + pdb <- mockPactDb serialisePact_repl_fileLocSpanInfo -- add default spaninfo let ms' = (fmap.fmap) (const def) ms diff --git a/pact-tests/Pact/Core/Test/ReplTests.hs b/pact-tests/Pact/Core/Test/ReplTests.hs index 9a0bda43..4666b973 100644 --- a/pact-tests/Pact/Core/Test/ReplTests.hs +++ b/pact-tests/Pact/Core/Test/ReplTests.hs @@ -26,27 +26,25 @@ import Pact.Core.Persistence.MockPersistence import Pact.Core.Repl.Utils import Pact.Core.Persistence.SQLite (withSqlitePactDb) -import Pact.Core.Info (SpanInfo) -import Pact.Core.Repl.Compile +import Pact.Core.Info import Pact.Core.Environment import Pact.Core.Builtin import Pact.Core.Errors import Pact.Core.Serialise import Pact.Core.Persistence import Pact.Core.IR.Term +import Pact.Core.Repl.Compile import qualified Pact.Core.IR.ModuleHashing as MH -type Interpreter = SourceCode -> ReplM ReplCoreBuiltin [ReplCompileValue] - tests :: IO TestTree tests = do files <- replTestFiles pure $ testGroup "ReplTests" - [ testGroup "in-memory db:bigstep" (runFileReplTest interpretReplProgramBigStep <$> files) - , testGroup "sqlite db:bigstep" (runFileReplTestSqlite interpretReplProgramBigStep <$> files) - , testGroup "in-memory db:direct" (runFileReplTest interpretReplProgramDirect <$> files) - , testGroup "sqlite db:direct" (runFileReplTestSqlite interpretReplProgramDirect <$> files) + [ testGroup "in-memory db:bigstep" (runFileReplTest interpretEvalBigStep <$> files) + , testGroup "sqlite db:bigstep" (runFileReplTestSqlite interpretEvalBigStep <$> files) + , testGroup "in-memory db:direct" (runFileReplTest interpretEvalDirect <$> files) + , testGroup "sqlite db:direct" (runFileReplTestSqlite interpretEvalDirect <$> files) ] newtype ReplSourceDir @@ -59,32 +57,32 @@ defaultReplTestDir = "pact-tests" "pact-tests" replTestFiles :: IO [FilePath] replTestFiles = filter (\f -> isExtensionOf "repl" f || isExtensionOf "pact" f) <$> getDirectoryContents defaultReplTestDir -runFileReplTest :: Interpreter -> TestName -> TestTree +runFileReplTest :: ReplInterpreter -> TestName -> TestTree runFileReplTest interp file = testCase file $ do - pdb <- mockPactDb serialisePact_repl_spaninfo + pdb <- mockPactDb serialisePact_repl_fileLocSpanInfo src <- T.readFile (defaultReplTestDir file) runReplTest (ReplSourceDir defaultReplTestDir) pdb file src interp -runFileReplTestSqlite :: Interpreter -> TestName -> TestTree +runFileReplTestSqlite :: ReplInterpreter -> TestName -> TestTree runFileReplTestSqlite interp file = testCase file $ do ctnt <- T.readFile (defaultReplTestDir file) - withSqlitePactDb serialisePact_repl_spaninfo ":memory:" $ \pdb -> do + withSqlitePactDb serialisePact_repl_fileLocSpanInfo ":memory:" $ \pdb -> do runReplTest (ReplSourceDir defaultReplTestDir) pdb file ctnt interp runReplTest :: ReplSourceDir - -> PactDb ReplCoreBuiltin SpanInfo + -> PactDb ReplCoreBuiltin FileLocSpanInfo -> FilePath -> T.Text - -> Interpreter + -> ReplInterpreter -> Assertion runReplTest (ReplSourceDir path) pdb file src interp = do ee <- defaultEvalEnv pdb replBuiltinMap let source = SourceCode (path file) src - let rstate = mkReplState ee (const (pure ())) & replCurrSource .~ source + let rstate = mkReplState ee (const (pure ())) (\f reset -> void (loadFile interp f reset)) & replCurrSource .~ source stateRef <- newIORef rstate - evalReplM stateRef (interp source) >>= \case + evalReplM stateRef (interpretReplProgram interp source) >>= \case Left e -> let rendered = replError (SourceCode file src) e in assertFailure (T.unpack rendered) @@ -103,8 +101,9 @@ runReplTest (ReplSourceDir path) pdb file src interp = do ensureModuleHashesMatch = do keys <- ignoreGas def $ _pdbKeys pdb DModules traverse_ moduleHashMatches keys - ensurePassing (ReplTestResult _testName loc _ res) = case res of + ensurePassing (ReplTestResult _testName loc res) = case res of ReplTestPassed -> pure() ReplTestFailed msg -> do + -- Todo: refine this with file locs let render = replError (SourceCode file src) (PEExecutionError (EvalError msg) [] loc) assertFailure (T.unpack render) diff --git a/pact-tests/Pact/Core/Test/StaticErrorTests.hs b/pact-tests/Pact/Core/Test/StaticErrorTests.hs index 8a556d6f..f9d088dd 100644 --- a/pact-tests/Pact/Core/Test/StaticErrorTests.hs +++ b/pact-tests/Pact/Core/Test/StaticErrorTests.hs @@ -10,6 +10,7 @@ import qualified Data.Text as T import Control.Lens import Data.IORef import Data.Text (Text) +import Data.Functor (void) import NeatInterpolation (text) import Pact.Core.Builtin @@ -18,26 +19,27 @@ import Pact.Core.Errors import Pact.Core.Persistence.MockPersistence (mockPactDb) import Pact.Core.Repl.Compile import Pact.Core.Repl.Utils -import Pact.Core.Serialise (serialisePact_repl_spaninfo) +import Pact.Core.Serialise +import Pact.Core.Info -isParseError :: Prism' ParseError a -> PactErrorI -> Bool +isParseError :: Prism' ParseError a -> PactError FileLocSpanInfo -> Bool isParseError p s = has (_PEParseError . _1 . p) s -isDesugarError :: Prism' DesugarError a -> PactErrorI -> Bool +isDesugarError :: Prism' DesugarError a -> PactError FileLocSpanInfo -> Bool isDesugarError p s = has (_PEDesugarError . _1 . p) s -isExecutionError :: Prism' EvalError a -> PactErrorI -> Bool +isExecutionError :: Prism' EvalError a -> PactError FileLocSpanInfo -> Bool isExecutionError p s = has (_PEExecutionError . _1 . p) s -isUserRecoverableError :: Prism' UserRecoverableError a -> PactErrorI -> Bool +isUserRecoverableError :: Prism' UserRecoverableError a -> PactError FileLocSpanInfo -> Bool isUserRecoverableError p s = has (_PEUserRecoverableError . _1 . p) s -runStaticTest :: String -> Text -> ReplInterpreter -> (PactErrorI -> Bool) -> Assertion +runStaticTest :: String -> Text -> ReplInterpreter -> (PactError FileLocSpanInfo -> Bool) -> Assertion runStaticTest label src interp predicate = do - pdb <- mockPactDb serialisePact_repl_spaninfo + pdb <- mockPactDb serialisePact_repl_fileLocSpanInfo ee <- defaultEvalEnv pdb replBuiltinMap let source = SourceCode label src - rstate = mkReplState ee (const (pure ())) + rstate = mkReplState ee (const (pure ())) (\f reset -> void (loadFile interp f reset)) & replCurrSource .~ source & replNativesEnabled .~ True stateRef <- newIORef rstate @@ -47,7 +49,7 @@ runStaticTest label src interp predicate = do assertBool ("Expected Error to match predicate, but got " <> show err <> " instead") (predicate err) Right _v -> assertFailure ("Error: Static failure test succeeded for test: " <> label) -parseTests :: [(String, PactErrorI -> Bool, Text)] +parseTests :: [(String, PactError FileLocSpanInfo -> Bool, Text)] parseTests = [ ("defpact_empty", isParseError _ParsingError, [text| (module m g (defcap g () true) @@ -64,7 +66,7 @@ parseTests = |]) ] -desugarTests :: [(String, PactErrorI -> Bool, Text)] +desugarTests :: [(String, PactError FileLocSpanInfo -> Bool, Text)] desugarTests = [ ("no_bind_body", isDesugarError _EmptyBindingBody, [text|(bind {"a":1} {"a":=a})|]) , ("defpact_last_step_rollback", isDesugarError _LastStepWithRollback, [text| @@ -600,7 +602,7 @@ desugarTests = |]) ] -executionTests :: [(String, PactErrorI -> Bool, Text)] +executionTests :: [(String, PactError FileLocSpanInfo -> Bool, Text)] executionTests = [ ("enforce_ns_install_module", isExecutionError _RootNamespaceInstallError, [text| (module m g (defcap g () true) @@ -1105,7 +1107,7 @@ executionTests = |]) ] -builtinTests :: [(String, PactErrorI -> Bool, Text)] +builtinTests :: [(String, PactError FileLocSpanInfo -> Bool, Text)] builtinTests = [ ("integer_pow_negative", isExecutionError _ArithmeticException, "(^ 0 -1)") , ("floating_pow_negative", isExecutionError _FloatingPointError, "(^ 0.0 -1.0)") diff --git a/pact-tests/gas-goldens/builtinGas.golden b/pact-tests/gas-goldens/builtinGas.golden index 81f259b8..2aff9014 100644 --- a/pact-tests/gas-goldens/builtinGas.golden +++ b/pact-tests/gas-goldens/builtinGas.golden @@ -11,7 +11,7 @@ >=: 264 ^: 868 abs: 100 -acquire-module-admin: 295594 +acquire-module-admin: 295598 add-time: 750 and?: 628 at: 706 diff --git a/pact/Pact/Core/Builtin.hs b/pact/Pact/Core/Builtin.hs index 8d3a438a..80c26ee2 100644 --- a/pact/Pact/Core/Builtin.hs +++ b/pact/Pact/Core/Builtin.hs @@ -781,6 +781,8 @@ data ReplOnlyBuiltin | REnvModuleAdmin | REnvVerifiers | REnvSetDebugFlag + | RLoad + | RLoadWithEnv deriving (Show, Enum, Bounded, Eq, Generic) @@ -830,9 +832,8 @@ instance IsBuiltin ReplOnlyBuiltin where REnvModuleAdmin -> 1 REnvVerifiers -> 1 REnvSetDebugFlag -> 1 - - -- RLoad -> 1 - -- RLoadWithEnv -> 2 + RLoad -> 1 + RLoadWithEnv -> 2 -- Note: commented out natives are -- to be implemented later data ReplBuiltin b @@ -916,6 +917,8 @@ replBuiltinsToText = \case REnvModuleAdmin -> "env-module-admin" REnvVerifiers -> "env-verifiers" REnvSetDebugFlag -> "env-set-debug-flag" + RLoad -> "load" + RLoadWithEnv -> "load-with-env" replBuiltinToText :: (t -> Text) -> ReplBuiltin t -> Text replBuiltinToText f = \case diff --git a/pact/Pact/Core/Environment/Types.hs b/pact/Pact/Core/Environment/Types.hs index 4c9b20aa..86461af8 100644 --- a/pact/Pact/Core/Environment/Types.hs +++ b/pact/Pact/Core/Environment/Types.hs @@ -72,8 +72,11 @@ module Pact.Core.Environment.Types , replTx , replOutputLine , replTestResults - , mkReplState + , replLoad + , replLoadedFiles + , replLogType , ReplM + , ReplOutput(..) , ReplDebugFlag(..) , SourceCode(..) , PactWarning(..) @@ -353,34 +356,44 @@ data ReplTestStatus data ReplTestResult = ReplTestResult { _trName :: Text - , _trLoc :: SpanInfo - , _trSourceFile :: String + , _trLoc :: FileLocSpanInfo , _trResult :: ReplTestStatus } deriving (Show, Eq) +data ReplOutput where + ReplStdOut :: ReplOutput + ReplLogOut :: IORef [Text] -> ReplOutput + -- | Passed in repl environment data ReplState b = ReplState { _replFlags :: Set ReplDebugFlag -- ^ The currently enabled debug flags - , _replEvalEnv :: EvalEnv b SpanInfo + , _replEvalEnv :: EvalEnv b FileLocSpanInfo -- ^ The current eval environment + , _replLogType :: ReplOutput + -- ^ The repl log mode , _replCurrSource :: SourceCode -- ^ The current source code for source being evaluated , _replUserDocs :: Map QualifiedName Text -- ^ Used by Repl and LSP Server, reflects the user -- annotated @doc string. - , _replTLDefPos :: Map QualifiedName SpanInfo + , _replTLDefPos :: Map QualifiedName FileLocSpanInfo -- ^ Used by LSP Server, reflects the span information -- of the TL definitions for the qualified name. , _replTx :: Maybe (TxId, Maybe Text) -- ^ The current repl tx, if one has been initiated , _replNativesEnabled :: Bool -- ^ Are repl natives enabled in module code - , _replOutputLine :: !(Text -> EvalM 'ReplRuntime b SpanInfo ()) + , _replOutputLine :: !(Text -> EvalM 'ReplRuntime b FileLocSpanInfo ()) -- ^ The output line function, as an entry in the repl env -- to allow for custom output handling, e.g haskeline + , _replLoad :: !(FilePath -> Bool -> EvalM 'ReplRuntime b FileLocSpanInfo ()) + -- ^ Our load function, which serves to tie a knot + , _replLoadedFiles :: Map FilePath SourceCode + -- ^ The files currently loaded in the repl , _replTestResults :: [ReplTestResult] + -- ^ The current repl tests results } data RuntimeMode @@ -390,7 +403,7 @@ data RuntimeMode data EvalMEnv e b i where ExecEnv :: EvalEnv b i -> EvalMEnv ExecRuntime b i - ReplEnv :: IORef (ReplState b) -> EvalMEnv ReplRuntime b SpanInfo + ReplEnv :: IORef (ReplState b) -> EvalMEnv ReplRuntime b FileLocSpanInfo -- Todo: are we going to inject state as the reader monad here? @@ -406,7 +419,7 @@ newtype EvalM e b i a = , MonadState (EvalState b i) , MonadError (PactError i)) -type ReplM b = EvalM ReplRuntime b SpanInfo +type ReplM b = EvalM ReplRuntime b FileLocSpanInfo runEvalM @@ -429,9 +442,3 @@ runEvalMResult env st (EvalM action) = makeLenses ''ReplState makePrisms ''ReplTestStatus - -mkReplState :: EvalEnv b SpanInfo -> (Text -> EvalM 'ReplRuntime b SpanInfo ()) -> ReplState b -mkReplState ee printfn = - ReplState mempty ee defaultSrc mempty mempty Nothing False printfn [] - where - defaultSrc = SourceCode "(interactive)" mempty diff --git a/pact/Pact/Core/IR/Desugar.hs b/pact/Pact/Core/IR/Desugar.hs index 30df332d..16741ea0 100644 --- a/pact/Pact/Core/IR/Desugar.hs +++ b/pact/Pact/Core/IR/Desugar.hs @@ -186,6 +186,8 @@ instance DesugarBuiltin (ReplBuiltin CoreBuiltin) where desugarAppArity i (RBuiltinWrap b) ne = desugarCoreBuiltinArity RBuiltinWrap i b ne -- (expect ) + desugarAppArity i (RBuiltinRepl RLoad) [e1, e2] = + App (Builtin (RBuiltinRepl RLoadWithEnv) i) [e1, e2] i desugarAppArity i (RBuiltinRepl RExpect) ([e1, e2, e3]) | isn't _Nullary e3 = App (Builtin (RBuiltinRepl RExpect) i) ([e1, suspendTerm e2, suspendTerm e3]) i -- (expect-failure ) diff --git a/pact/Pact/Core/Info.hs b/pact/Pact/Core/Info.hs index d1c56967..23a697bb 100644 --- a/pact/Pact/Core/Info.hs +++ b/pact/Pact/Core/Info.hs @@ -1,7 +1,9 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE GeneralisedNewtypeDeriving #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE DeriveTraversable #-} +{-# LANGUAGE InstanceSigs #-} module Pact.Core.Info ( SpanInfo(..) @@ -12,6 +14,8 @@ module Pact.Core.Info , sliceFromSourceLines , LineInfo(..) , spanInfoToLineInfo + , FileLocSpanInfo(..) + , HasSpanInfo(..) ) where import Control.Lens @@ -71,6 +75,12 @@ instance Pretty SpanInfo where spanInfoToLineInfo :: SpanInfo -> LineInfo spanInfoToLineInfo = LineInfo . _liStartLine +data FileLocSpanInfo + = FileLocSpanInfo + { _flsiFile :: !String + , _flsiSpan :: !SpanInfo + } deriving (Eq, Show, Generic, NFData) + -- | Combine two Span infos -- and spit out how far down the expression spans. combineSpan :: SpanInfo -> SpanInfo -> SpanInfo @@ -96,3 +106,14 @@ data Located i a { _locLocation :: i , _locElem :: a } deriving (Show, Eq, Ord, Functor, Foldable, Traversable) + +makeClassy ''SpanInfo + +instance HasSpanInfo FileLocSpanInfo where + spanInfo = lens _flsiSpan (\s i -> s { _flsiSpan = i }) + +instance Pretty FileLocSpanInfo where + pretty (FileLocSpanInfo f s) = pretty f <> " " <> pretty s + +instance Default FileLocSpanInfo where + def = FileLocSpanInfo "" def diff --git a/pact/Pact/Core/Serialise.hs b/pact/Pact/Core/Serialise.hs index 03e8a780..5aa5384b 100644 --- a/pact/Pact/Core/Serialise.hs +++ b/pact/Pact/Core/Serialise.hs @@ -18,6 +18,7 @@ module Pact.Core.Serialise , serialisePact_raw_spaninfo , serialisePact_lineinfo , serialisePact_repl_spaninfo + , serialisePact_repl_fileLocSpanInfo , decodeVersion , encodeVersion , liftReplBuiltin @@ -166,6 +167,18 @@ serialisePact_repl_spaninfo = serialisePact , _encodeRowData = gEncodeRowData } +serialisePact_repl_fileLocSpanInfo :: PactSerialise ReplCoreBuiltin FileLocSpanInfo +serialisePact_repl_fileLocSpanInfo = serialisePact + { _encodeModuleData = docEncode V1.encodeModuleData_repl_flspaninfo + , _decodeModuleData = + \bs -> + (LegacyDocument . fmap (\_ -> def) . liftReplBuiltin <$> LegacyPact.decodeModuleData bs) + <|> docDecode bs (\case + V1_CBOR -> V1.decodeModuleData_repl_flspaninfo + ) + , _encodeRowData = gEncodeRowData + } + docEncode :: (a -> ByteString) -> a -> ByteString docEncode enc o = toStrictByteString (encodeVersion V1_CBOR <> S.encodeBytes (enc o)) {-# INLINE docEncode #-} diff --git a/pact/Pact/Core/Serialise/CBOR_V1.hs b/pact/Pact/Core/Serialise/CBOR_V1.hs index 77818069..2157f636 100644 --- a/pact/Pact/Core/Serialise/CBOR_V1.hs +++ b/pact/Pact/Core/Serialise/CBOR_V1.hs @@ -14,6 +14,7 @@ module Pact.Core.Serialise.CBOR_V1 ( encodeModuleData, decodeModuleData , encodeModuleData_repl_spaninfo, decodeModuleData_repl_spaninfo , encodeModuleData_raw_spaninfo, decodeModuleData_raw_spaninfo + , encodeModuleData_repl_flspaninfo, decodeModuleData_repl_flspaninfo , encodeModuleData_lineinfo, decodeModuleData_lineinfo , encodeKeySet, decodeKeySet , encodeDefPactExec, decodeDefPactExec @@ -68,6 +69,9 @@ encodeModuleData = toStrictByteString . encodeS encodeModuleData_repl_spaninfo :: ModuleData ReplCoreBuiltin SpanInfo -> ByteString encodeModuleData_repl_spaninfo = toStrictByteString . encodeS +encodeModuleData_repl_flspaninfo :: ModuleData ReplCoreBuiltin FileLocSpanInfo -> ByteString +encodeModuleData_repl_flspaninfo = toStrictByteString . encodeS + encodeModuleData_raw_spaninfo :: ModuleData CoreBuiltin SpanInfo -> ByteString encodeModuleData_raw_spaninfo = toStrictByteString . encodeS @@ -89,6 +93,9 @@ decodeModuleData_raw_spaninfo bs = either (const Nothing) (Just . _getSV1) (dese decodeModuleData_lineinfo :: ByteString -> Maybe (ModuleData CoreBuiltin LineInfo) decodeModuleData_lineinfo bs = either (const Nothing) (Just . _getSV1) (deserialiseOrFail (fromStrict bs)) +decodeModuleData_repl_flspaninfo :: ByteString -> Maybe (ModuleData ReplCoreBuiltin FileLocSpanInfo) +decodeModuleData_repl_flspaninfo bs = either (const Nothing) (Just . _getSV1) (deserialiseOrFail (fromStrict bs)) + encodeModuleName :: ModuleName -> ByteString encodeModuleName = toStrictByteString . encodeS @@ -854,6 +861,15 @@ instance Serialise (SerialiseV1 SpanInfo) where SerialiseV1 <$> (SpanInfo <$> decode <*> decode <*> decode <*> decode) {-# INLINE decode #-} +instance Serialise (SerialiseV1 FileLocSpanInfo) where + encode (SerialiseV1 (FileLocSpanInfo f s)) = + encodeListLen 2 <> encode f <> encodeS s + {-# INLINE encode #-} + decode = do + safeDecodeListLen 2 "FileLocSpanInfo" + SerialiseV1 <$> (FileLocSpanInfo <$> decode <*> decodeS) + {-# INLINE decode #-} + instance Serialise (SerialiseV1 LineInfo) where encode (SerialiseV1 (LineInfo li)) = encode li {-# INLINE encode #-} diff --git a/pact/Pact/Core/SizeOf.hs b/pact/Pact/Core/SizeOf.hs index 645f1aa8..4ebb03ff 100644 --- a/pact/Pact/Core/SizeOf.hs +++ b/pact/Pact/Core/SizeOf.hs @@ -339,6 +339,11 @@ instance SizeOf (TableSchema name) where makeSizeOf ''SpanInfo +-- Note: this is a pass through instance, since this is repl-only +instance SizeOf FileLocSpanInfo where + estimateSize (FileLocSpanInfo _f s) = + estimateSize s + -- builtins instance SizeOf CoreBuiltin where estimateSize _ = countBytes (tagOverhead + 1) diff --git a/pact/Pact/Core/Syntax/ParseTree.hs b/pact/Pact/Core/Syntax/ParseTree.hs index c9a300e4..9c9ee8b8 100644 --- a/pact/Pact/Core/Syntax/ParseTree.hs +++ b/pact/Pact/Core/Syntax/ParseTree.hs @@ -636,7 +636,7 @@ data ReplTopLevel i = RTLTopLevel (TopLevel i) | RTLDefun (Defun i) | RTLDefConst (DefConst i) - deriving (Show, Generic, NFData) + deriving (Show, Generic, NFData, Functor) pattern RTLModule :: Module i -> ReplTopLevel i pattern RTLModule m = RTLTopLevel (TLModule m)