Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an eval_int command to the remote API and Python interface #1660

Merged
merged 9 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions saw-remote-api/docs/SAW.rst
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,54 @@ Return fields



SAW/eval int (command)
~~~~~~~~~~~~~~~~~~~~~~

Attempt to evaluate the given term to a concrete integer.

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


``expr``
The Cryptol expression to evaluate.



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


``value``
The integer value of the expresssion.




SAW/eval bool (command)
~~~~~~~~~~~~~~~~~~~~~~~

Attempt to evaluate the given term to a concrete boolean.

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


``expr``
The Cryptol expression to evaluate.



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


``value``
The boolean value of the expresssion.




SAW/set option (command)
~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
41 changes: 21 additions & 20 deletions saw-remote-api/python/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions saw-remote-api/python/saw_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,26 @@ def prove(goal: cryptoltypes.CryptolJSON,
pr.counterexample = None
return pr

def eval_int(expr: cryptoltypes.CryptolJSON) -> int:
"""Atempts to evaluate the given expression as a concrete integer.
"""
conn = __get_designated_connection()
res = conn.eval_int(expr).result()
v = res['value']
if not isinstance(v, int):
raise ValueError(str(v) + " is not an integer")
return v

def eval_bool(expr: cryptoltypes.CryptolJSON) -> int:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def eval_bool(expr: cryptoltypes.CryptolJSON) -> int:
def eval_bool(expr: cryptoltypes.CryptolJSON) -> bool:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's curious that the Mypy run in CI didn't catch this. Testing locally: if I remove the isinstance check, I get the expected type error ("returning Any from a function declared to return bool"), but I don't get any error if the isinstance check mismatches the declared return type.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bool is an int:

>>> isinstance(False, int)
True
>>> isinstance(True, int)
True
>>> issubclass(bool, int)
True

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that would explain it. TIL. Thanks!

"""Atempts to evaluate the given expression as a concrete boolean.
"""
conn = __get_designated_connection()
res = conn.eval_bool(expr).result()
v = res['value']
if not isinstance(v, bool):
raise ValueError(str(v) + " is not a boolean")
return v

def set_option(option : option.SAWOption, value : bool) -> None:
"""Set a boolean-valued SAW option."""
global __designated_connection
Expand Down
24 changes: 24 additions & 0 deletions saw-remote-api/python/saw_client/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,30 @@ def __init__(
def process_result(self, res : Any) -> Any:
return res

class EvalInt(SAWCommand):
def __init__(
self,
connection : argo.HasProtocolState,
expr : cryptoltypes.CryptolJSON,
timeout : Optional[float]) -> None:
params = {'expr': cryptoltypes.to_cryptol(expr)}
super(EvalInt, self).__init__('SAW/eval int', params, connection, timeout=timeout)

def process_result(self, res : Any) -> Any:
return res

class EvalBool(SAWCommand):
def __init__(
self,
connection : argo.HasProtocolState,
expr : cryptoltypes.CryptolJSON,
timeout : Optional[float]) -> None:
params = {'expr': cryptoltypes.to_cryptol(expr)}
super(EvalBool, self).__init__('SAW/eval bool', params, connection, timeout=timeout)

def process_result(self, res : Any) -> Any:
return res

class SAWReset(argo.Notification):
def __init__(self, connection : argo.HasProtocolState) -> None:
super(SAWReset, self).__init__(
Expand Down
18 changes: 18 additions & 0 deletions saw-remote-api/python/saw_client/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,24 @@ def prove(self,
self.most_recent_result = Prove(self, goal, proof_script, timeout)
return self.most_recent_result

def eval_int(self,
expr: cryptoltypes.CryptolJSON,
timeout : Optional[float] = None) -> Command:
"""Create an instance of the `EvalInt` command. Documentation on the purpose and
use of this command is associated with the top-level `eval_int` function.
"""
self.most_recent_result = EvalInt(self, expr, timeout)
return self.most_recent_result

def eval_bool(self,
expr: cryptoltypes.CryptolJSON,
timeout : Optional[float] = None) -> Command:
"""Create an instance of the `EvalBool` command. Documentation on the purpose and
use of this command is associated with the top-level `eval_int` function.
chameco marked this conversation as resolved.
Show resolved Hide resolved
"""
self.most_recent_result = EvalBool(self, expr, timeout)
return self.most_recent_result

def set_option(self,
option : SAWOption,
value : bool,
Expand Down
26 changes: 26 additions & 0 deletions saw-remote-api/python/tests/saw/test_eval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from cryptol import cryptoltypes
import saw_client as saw

import unittest
from pathlib import Path


def cry(exp):
return cryptoltypes.CryptolLiteral(exp)

class ProverTest(unittest.TestCase):

def test_provers(self):
saw.connect(reset_server=True)

if __name__ == "__main__": saw.view(saw.LogResults())

expr1 = cry('(6 : [8]) * 7')
chameco marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual(saw.eval_int(expr1), 42)

expr2 = cry('(1 < 2) : Bit')
chameco marked this conversation as resolved.
Show resolved Hide resolved
self.assertTrue(saw.eval_bool(expr2))


if __name__ == "__main__":
unittest.main()
1 change: 1 addition & 0 deletions saw-remote-api/saw-remote-api.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ library
SAWServer.Data.JVMType,
SAWServer.Data.LLVMType,
SAWServer.Data.SetupValue,
SAWServer.Eval,
SAWServer.Exceptions,
SAWServer.Ghost,
SAWServer.JVMCrucibleSetup,
Expand Down
13 changes: 13 additions & 0 deletions saw-remote-api/saw-remote-api/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import SAWServer.CryptolSetup
cryptolLoadFileDescr,
cryptolLoadFile )
import SAWServer.Data.JVMType()
import SAWServer.Eval
( evalIntDescr,
evalInt,
evalBoolDescr,
evalBool )
import SAWServer.Ghost
( createGhostVariableDescr,
createGhostVariable )
Expand Down Expand Up @@ -117,6 +122,14 @@ sawMethods =
"SAW/prove"
proveDescr
prove
, Argo.command
"SAW/eval int"
evalIntDescr
evalInt
, Argo.command
"SAW/eval bool"
evalBoolDescr
evalBool
, Argo.command
"SAW/set option"
setOptionDescr
Expand Down
Loading