Skip to content

Commit

Permalink
Initial implementation of check docstrings from python (#1712)
Browse files Browse the repository at this point in the history
* Initial implementation of "check docstrings" from python

* Improve and synchronize the unlit and docstring code fence parser to better match GitHub markdown syntax

* Associate identifiers with docstring comes from

* Add :set proverTimeout support for goal-level timeouts

* Adds and evaluates docstrings on top-level module declarations
  • Loading branch information
glguy authored Aug 6, 2024
1 parent c650eb2 commit df4c9df
Show file tree
Hide file tree
Showing 35 changed files with 634 additions and 107 deletions.
10 changes: 9 additions & 1 deletion cryptol-remote-api/cryptol-eval-server/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Options.Applicative
value )

import CryptolServer.Check ( check, checkDescr )
import CryptolServer.CheckDocstrings ( checkDocstrings, checkDocstringsDescr )
import CryptolServer.ClearState
( clearState, clearStateDescr, clearAllStates, clearAllStatesDescr)
import Cryptol.Eval (EvalOpts(..), defaultPPOpts)
Expand Down Expand Up @@ -49,6 +50,7 @@ import CryptolServer.FocusedModule
import CryptolServer.Names ( visibleNamesDescr, visibleNames )
import CryptolServer.TypeCheck ( checkType )
import CryptolServer.Sat ( proveSatDescr, proveSat )
import Cryptol.REPL.Command (CommandResult, DocstringResult)

main :: IO ()
main = customMain initMod initMod initMod initMod description buildApp
Expand Down Expand Up @@ -97,7 +99,9 @@ evalServerDocs =
Doc.Text " that supports only type checking and evaluation of Cryptol code."]]
, Doc.Section "Terms and Types"
[Doc.datatype @Expression,
Doc.datatype @JSONSchema]
Doc.datatype @JSONSchema,
Doc.datatype @DocstringResult,
Doc.datatype @CommandResult]
]

description :: String
Expand Down Expand Up @@ -168,6 +172,10 @@ cryptolEvalMethods =
"clear all states"
clearAllStatesDescr
clearAllStates
, command
"check docstrings"
checkDocstringsDescr
checkDocstrings
, notification
"interrupt"
interruptServerDescr
Expand Down
7 changes: 4 additions & 3 deletions cryptol-remote-api/cryptol-remote-api.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,22 @@ library
CryptolServer
CryptolServer.Call
CryptolServer.Check
CryptolServer.CheckDocstrings
CryptolServer.ClearState
CryptolServer.Data.Expression
CryptolServer.Data.Type
CryptolServer.EvalExpr
CryptolServer.ExtendSearchPath
CryptolServer.Exceptions
CryptolServer.ExtendSearchPath
CryptolServer.FileDeps
CryptolServer.FocusedModule
CryptolServer.Interrupt
CryptolServer.LoadModule
CryptolServer.Options
CryptolServer.Names
CryptolServer.Options
CryptolServer.Sat
CryptolServer.TypeCheck
CryptolServer.Version
CryptolServer.FileDeps

other-modules:
CryptolServer.AesonCompat
Expand Down
14 changes: 11 additions & 3 deletions cryptol-remote-api/cryptol-remote-api/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import CryptolServer
( command, notification, initialState, extendSearchPath, ServerState )
import CryptolServer.Call ( call, callDescr )
import CryptolServer.Check ( check, checkDescr )
import CryptolServer.CheckDocstrings ( checkDocstrings, checkDocstringsDescr )
import CryptolServer.ClearState
( clearState, clearStateDescr, clearAllStates, clearAllStatesDescr )
import CryptolServer.Data.Expression ( Expression )
Expand All @@ -35,6 +36,7 @@ import CryptolServer.Sat ( proveSat, proveSatDescr )
import CryptolServer.TypeCheck ( checkType, checkTypeDescr )
import CryptolServer.Version ( version, versionDescr )
import CryptolServer.FileDeps( fileDeps, fileDepsDescr )
import Cryptol.REPL.Command (CommandResult, DocstringResult)

main :: IO ()
main =
Expand All @@ -50,13 +52,15 @@ main =

serverDocs :: [Doc.Block]
serverDocs =
[ Doc.Section "Summary" $ [ Doc.Paragraph
[Doc.Text "An RCP server for ",
[ Doc.Section "Summary" [ Doc.Paragraph
[Doc.Text "An RPC server for ",
Doc.Link (Doc.URL "https://https://cryptol.net/") "Cryptol",
Doc.Text " that supports type checking and evaluation of Cryptol code via the methods documented below."]]
, Doc.Section "Terms and Types"
[Doc.datatype @Expression,
Doc.datatype @JSONSchema]
Doc.datatype @JSONSchema,
Doc.datatype @DocstringResult,
Doc.datatype @CommandResult]
]

description :: String
Expand Down Expand Up @@ -125,6 +129,10 @@ cryptolMethods =
"prove or satisfy"
proveSatDescr
proveSat
, command
"check docstrings"
checkDocstringsDescr
checkDocstrings
, notification
"interrupt"
interruptServerDescr
Expand Down
62 changes: 61 additions & 1 deletion cryptol-remote-api/docs/Cryptol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ The precise structure of states is considered an implementation detail that coul
Summary
-------

An RCP server for `Cryptol <https://https://cryptol.net/>`_ that supports type checking and evaluation of Cryptol code via the methods documented below.
An RPC server for `Cryptol <https://https://cryptol.net/>`_ that supports type checking and evaluation of Cryptol code via the methods documented below.


Terms and Types
Expand Down Expand Up @@ -233,6 +233,45 @@ JSON representations of types are type schemas. A type schema has three fields:



.. _DocstringResult:
DocstringResult
~~~~~~~~~~~~~~~

The result of evaluating the code fences in a docstring


``name``
The definition assocated with the docstring



``fences``
An array code fences each containing an array of individual :ref:`command results <CommandResult>`



.. _CommandResult:
CommandResult
~~~~~~~~~~~~~

The result of executing a single REPL command.


``success``
Boolean indicating successful execution of the command



``type``
The string representation of the type returned or null



``value``
The string representation of the value returned or null




Methods
-------
Expand Down Expand Up @@ -726,6 +765,27 @@ Return fields



check docstrings (command)
~~~~~~~~~~~~~~~~~~~~~~~~~~

Check docstrings

Parameter fields
++++++++++++++++

No parameters


Return fields
+++++++++++++


``results``
A list of :ref:`docstring results <DocstringResult>` correspoding to each definition in the current module.




interrupt (notification)
~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
12 changes: 12 additions & 0 deletions cryptol-remote-api/python/cryptol/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,15 @@ def __init__(self, connection : HasProtocolState) -> None:
{},
connection
)

class CryptolCheckDocstrings(argo.Command):
def __init__(self, connection : HasProtocolState, timeout: Optional[float]) -> None:
super(CryptolCheckDocstrings, self).__init__(
'check docstrings',
{},
connection,
timeout=timeout
)

def process_result(self, res : Any) -> Any:
return res
9 changes: 9 additions & 0 deletions cryptol-remote-api/python/cryptol/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,15 @@ def load_file(self, filename : str, *, timeout:Optional[float] = None) -> argo.C
timeout = timeout if timeout is not None else self.timeout
self.most_recent_result = CryptolLoadFile(self, filename, timeout)
return self.most_recent_result

def check_docstrings(self, timeout: Optional[float] = None) -> argo.Command:
"""Check docstrings
:param timeout: Optional timeout for this request (in seconds).
"""
timeout = timeout if timeout is not None else self.timeout
self.most_recent_result = CryptolCheckDocstrings(self, timeout)
return self.most_recent_result

def load_module(self, module_name : str, *, timeout:Optional[float] = None) -> argo.Command:
"""Load a Cryptol module, like ``:module`` at the Cryptol REPL.
Expand Down
143 changes: 143 additions & 0 deletions cryptol-remote-api/src/CryptolServer/CheckDocstrings.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE InstanceSigs #-}
module CryptolServer.CheckDocstrings
( checkDocstrings
, checkDocstringsDescr
, CheckDocstringsParams(..)
, CheckDocstringsResult(..)
)
where

import qualified Argo.Doc as Doc
import Control.Monad.IO.Class (MonadIO(liftIO))
import Data.Aeson ((.=),FromJSON, ToJSON)
import qualified Data.Aeson as Aeson
import qualified Data.Aeson as JSON
import Data.IORef (newIORef)
import qualified Cryptol.ModuleSystem.Env as M
import Cryptol.REPL.Command
import CryptolServer
import CryptolServer.Exceptions (noModule, moduleNotLoaded)
import qualified Cryptol.REPL.Monad as REPL
import Cryptol.Utils.Logger (quietLogger)
import Cryptol.Parser.AST (ImpName(..))
import qualified System.Random.TF as TF
import qualified Cryptol.Symbolic.SBV as SBV
import Cryptol.REPL.Monad (mkUserEnv, userOptions)
import Cryptol.Utils.PP (pp)
import Data.Proxy (Proxy(Proxy))
import Data.Typeable (typeRep)

checkDocstringsDescr :: Doc.Block
checkDocstringsDescr =
Doc.Paragraph
[ Doc.Text "Check docstrings" ]

checkDocstrings :: CheckDocstringsParams -> CryptolCommand CheckDocstringsResult
checkDocstrings CheckDocstringsParams = do
env <- getModuleEnv
ln <- case M.meFocusedModule env of
Just (ImpTop n) -> pure n
_ -> raise noModule
m <- case M.lookupModule ln env of
Nothing -> raise (moduleNotLoaded ln)
Just m -> pure m
solver <- getTCSolver
cfg <- getTCSolverConfig
liftIO $
do rng <- TF.newTFGen
rwRef <- newIORef REPL.RW
{ REPL.eLoadedMod = Nothing
, REPL.eEditFile = Nothing
, REPL.eContinue = True
, REPL.eIsBatch = False
, REPL.eModuleEnv = env
, REPL.eUserEnv = mkUserEnv userOptions
, REPL.eLogger = quietLogger
, REPL.eCallStacks = False
, REPL.eUpdateTitle = return ()
, REPL.eProverConfig = Left SBV.defaultProver
, REPL.eTCConfig = cfg
, REPL.eTCSolver = Just solver
, REPL.eTCSolverRestarts = 0
, REPL.eRandomGen = rng
}
REPL.unREPL (CheckDocstringsResult <$> checkDocStrings m) rwRef

newtype CheckDocstringsResult = CheckDocstringsResult [DocstringResult]

instance ToJSON CheckDocstringsResult where
toJSON (CheckDocstringsResult r) = JSON.object ["results" .= r]

instance ToJSON DocstringResult where
toJSON dr = JSON.object ["name" .= show (pp (drName dr)), "fences" .= drFences dr]

instance ToJSON SubcommandResult where
toJSON r = JSON.object
[ "input" .= srInput r
, "log" .= srLog r
, "result" .= srResult r
]

instance ToJSON CommandResult where
toJSON r = JSON.object
[ "type" .= crType r
, "value" .= crValue r
, "success" .= crSuccess r
]

data CheckDocstringsParams = CheckDocstringsParams

instance FromJSON CheckDocstringsParams where
parseJSON =
JSON.withObject "check docstrings parameters" $
\_ -> pure CheckDocstringsParams


instance Doc.DescribedMethod CheckDocstringsParams CheckDocstringsResult where
parameterFieldDescription = []

resultFieldDescription =
[("results",
Doc.Paragraph
[ Doc.Text "A list of "
, Doc.Link (Doc.TypeDesc (typeRep (Proxy :: Proxy DocstringResult))) "docstring results"
, Doc.Text " correspoding to each definition in the current module."
]

)]

instance Doc.Described DocstringResult where
typeName = "DocstringResult"
description =
[ Doc.Paragraph [Doc.Text "The result of evaluating the code fences in a docstring"]
, Doc.DescriptionList
[ ( pure (Doc.Literal "name")
, Doc.Paragraph[Doc.Text "The definition assocated with the docstring"]
)
, ( pure (Doc.Literal "fences")
, Doc.Paragraph[Doc.Text "An array code fences each containing an array of individual "
, Doc.Link (Doc.TypeDesc (typeRep (Proxy :: Proxy CommandResult))) "command results"]
)
]
]

instance Doc.Described CommandResult where
typeName = "CommandResult"
description =
[ Doc.Paragraph [Doc.Text "The result of executing a single REPL command."]
, Doc.DescriptionList
[ ( pure (Doc.Literal "success")
, Doc.Paragraph [Doc.Text "Boolean indicating successful execution of the command"]
)
, ( pure (Doc.Literal "type")
, Doc.Paragraph[Doc.Text "The string representation of the type returned or null"]
)
, ( pure (Doc.Literal "value")
, Doc.Paragraph[Doc.Text "The string representation of the value returned or null"]
)
]
]
7 changes: 7 additions & 0 deletions cryptol-remote-api/src/CryptolServer/Exceptions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module CryptolServer.Exceptions
, cryptolParseErr
, cryptolError
, moduleNotLoaded
, noModule
) where

import qualified Data.Aeson as JSON
Expand Down Expand Up @@ -216,3 +217,9 @@ moduleNotLoaded m =
20100 "Module not loaded"
(Just (JSON.object ["error" .= show (pretty m)]))

noModule :: JSONRPCException
noModule =
makeJSONRPCException
20110 "No module"
(Nothing :: Maybe JSON.Value)

Loading

0 comments on commit df4c9df

Please sign in to comment.