crosshair-tool 0.0.56__cp39-cp39-macosx_11_0_arm64.whl → 0.0.100__cp39-cp39-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. _crosshair_tracers.cpython-39-darwin.so +0 -0
  2. crosshair/__init__.py +1 -1
  3. crosshair/_mark_stacks.h +51 -24
  4. crosshair/_tracers.h +9 -5
  5. crosshair/_tracers_test.py +19 -9
  6. crosshair/auditwall.py +9 -8
  7. crosshair/auditwall_test.py +31 -19
  8. crosshair/codeconfig.py +3 -2
  9. crosshair/condition_parser.py +17 -133
  10. crosshair/condition_parser_test.py +54 -96
  11. crosshair/conftest.py +1 -1
  12. crosshair/copyext.py +91 -22
  13. crosshair/copyext_test.py +33 -0
  14. crosshair/core.py +259 -203
  15. crosshair/core_and_libs.py +20 -0
  16. crosshair/core_regestered_types_test.py +82 -0
  17. crosshair/core_test.py +693 -664
  18. crosshair/diff_behavior.py +76 -21
  19. crosshair/diff_behavior_test.py +132 -23
  20. crosshair/dynamic_typing.py +128 -18
  21. crosshair/dynamic_typing_test.py +91 -4
  22. crosshair/enforce.py +1 -6
  23. crosshair/enforce_test.py +15 -23
  24. crosshair/examples/check_examples_test.py +2 -1
  25. crosshair/fnutil.py +2 -3
  26. crosshair/fnutil_test.py +0 -7
  27. crosshair/fuzz_core_test.py +70 -83
  28. crosshair/libimpl/arraylib.py +10 -7
  29. crosshair/libimpl/binascii_ch_test.py +30 -0
  30. crosshair/libimpl/binascii_test.py +67 -0
  31. crosshair/libimpl/binasciilib.py +150 -0
  32. crosshair/libimpl/bisectlib_test.py +5 -5
  33. crosshair/libimpl/builtinslib.py +1002 -682
  34. crosshair/libimpl/builtinslib_ch_test.py +108 -30
  35. crosshair/libimpl/builtinslib_test.py +431 -143
  36. crosshair/libimpl/codecslib.py +22 -2
  37. crosshair/libimpl/codecslib_test.py +41 -9
  38. crosshair/libimpl/collectionslib.py +44 -8
  39. crosshair/libimpl/collectionslib_test.py +108 -20
  40. crosshair/libimpl/copylib.py +1 -1
  41. crosshair/libimpl/copylib_test.py +18 -0
  42. crosshair/libimpl/datetimelib.py +84 -67
  43. crosshair/libimpl/datetimelib_ch_test.py +12 -7
  44. crosshair/libimpl/datetimelib_test.py +5 -6
  45. crosshair/libimpl/decimallib.py +5257 -0
  46. crosshair/libimpl/decimallib_ch_test.py +78 -0
  47. crosshair/libimpl/decimallib_test.py +76 -0
  48. crosshair/libimpl/encodings/_encutil.py +21 -11
  49. crosshair/libimpl/fractionlib.py +16 -0
  50. crosshair/libimpl/fractionlib_test.py +80 -0
  51. crosshair/libimpl/functoolslib.py +19 -7
  52. crosshair/libimpl/functoolslib_test.py +22 -6
  53. crosshair/libimpl/hashliblib.py +30 -0
  54. crosshair/libimpl/hashliblib_test.py +18 -0
  55. crosshair/libimpl/heapqlib.py +32 -5
  56. crosshair/libimpl/heapqlib_test.py +15 -12
  57. crosshair/libimpl/iolib.py +7 -4
  58. crosshair/libimpl/ipaddresslib.py +8 -0
  59. crosshair/libimpl/itertoolslib_test.py +1 -1
  60. crosshair/libimpl/mathlib.py +165 -2
  61. crosshair/libimpl/mathlib_ch_test.py +44 -0
  62. crosshair/libimpl/mathlib_test.py +59 -16
  63. crosshair/libimpl/oslib.py +7 -0
  64. crosshair/libimpl/pathliblib_test.py +10 -0
  65. crosshair/libimpl/randomlib.py +1 -0
  66. crosshair/libimpl/randomlib_test.py +6 -4
  67. crosshair/libimpl/relib.py +180 -59
  68. crosshair/libimpl/relib_ch_test.py +26 -2
  69. crosshair/libimpl/relib_test.py +77 -14
  70. crosshair/libimpl/timelib.py +35 -13
  71. crosshair/libimpl/timelib_test.py +13 -3
  72. crosshair/libimpl/typeslib.py +15 -0
  73. crosshair/libimpl/typeslib_test.py +36 -0
  74. crosshair/libimpl/unicodedatalib_test.py +3 -3
  75. crosshair/libimpl/weakreflib.py +13 -0
  76. crosshair/libimpl/weakreflib_test.py +69 -0
  77. crosshair/libimpl/zliblib.py +15 -0
  78. crosshair/libimpl/zliblib_test.py +13 -0
  79. crosshair/lsp_server.py +21 -10
  80. crosshair/main.py +48 -28
  81. crosshair/main_test.py +59 -14
  82. crosshair/objectproxy.py +39 -14
  83. crosshair/objectproxy_test.py +27 -13
  84. crosshair/opcode_intercept.py +212 -24
  85. crosshair/opcode_intercept_test.py +172 -18
  86. crosshair/options.py +0 -1
  87. crosshair/patch_equivalence_test.py +5 -21
  88. crosshair/path_cover.py +7 -5
  89. crosshair/path_search.py +6 -4
  90. crosshair/path_search_test.py +1 -2
  91. crosshair/pathing_oracle.py +53 -10
  92. crosshair/pathing_oracle_test.py +21 -0
  93. crosshair/pure_importer_test.py +5 -21
  94. crosshair/register_contract.py +16 -6
  95. crosshair/register_contract_test.py +2 -14
  96. crosshair/simplestructs.py +154 -85
  97. crosshair/simplestructs_test.py +16 -2
  98. crosshair/smtlib.py +24 -0
  99. crosshair/smtlib_test.py +14 -0
  100. crosshair/statespace.py +319 -196
  101. crosshair/statespace_test.py +45 -0
  102. crosshair/stubs_parser.py +0 -2
  103. crosshair/test_util.py +87 -25
  104. crosshair/test_util_test.py +26 -0
  105. crosshair/tools/check_init_and_setup_coincide.py +0 -3
  106. crosshair/tools/generate_demo_table.py +2 -2
  107. crosshair/tracers.py +141 -49
  108. crosshair/type_repo.py +11 -4
  109. crosshair/unicode_categories.py +1 -0
  110. crosshair/util.py +158 -76
  111. crosshair/util_test.py +13 -20
  112. crosshair/watcher.py +4 -4
  113. crosshair/z3util.py +1 -1
  114. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/METADATA +45 -36
  115. crosshair_tool-0.0.100.dist-info/RECORD +176 -0
  116. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/WHEEL +2 -1
  117. crosshair/examples/hypothesis/__init__.py +0 -2
  118. crosshair/examples/hypothesis/bugs_detected/simple_strategies.py +0 -74
  119. crosshair_tool-0.0.56.dist-info/RECORD +0 -152
  120. /crosshair/{examples/hypothesis/bugs_detected/__init__.py → py.typed} +0 -0
  121. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/entry_points.txt +0 -0
  122. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info/licenses}/LICENSE +0 -0
  123. {crosshair_tool-0.0.56.dist-info → crosshair_tool-0.0.100.dist-info}/top_level.txt +0 -0
@@ -20,9 +20,15 @@ from typing import (
20
20
  Union,
21
21
  )
22
22
 
23
- from crosshair.core import CrossHairValue, deep_realize
24
- from crosshair.tracers import NoTracing, tracing_iter
25
- from crosshair.util import is_hashable, is_iterable, name_of_type
23
+ from crosshair.core import deep_realize
24
+ from crosshair.tracers import NoTracing, ResumedTracing, tracing_iter
25
+ from crosshair.util import (
26
+ CrossHairValue,
27
+ assert_tracing,
28
+ is_hashable,
29
+ is_iterable,
30
+ name_of_type,
31
+ )
26
32
 
27
33
 
28
34
  class MapBase(collections.abc.MutableMapping):
@@ -32,10 +38,10 @@ class MapBase(collections.abc.MutableMapping):
32
38
  return NotImplemented
33
39
  if len(self) != len(other):
34
40
  return False
35
- for (k, self_value) in self.items():
41
+ for k, self_value in self.items():
36
42
  found = False
37
43
  # We do a slow nested loop search because we don't want to hash the key.
38
- for (other_key, other_value) in other.items():
44
+ for other_key, other_value in other.items():
39
45
  if other_key != k:
40
46
  continue
41
47
  if self_value == other_value:
@@ -53,6 +59,16 @@ class MapBase(collections.abc.MutableMapping):
53
59
  def __ch_pytype__(self):
54
60
  return dict
55
61
 
62
+ def __ch_realize__(self):
63
+ memo = {}
64
+ return {deep_realize(k, memo): v for k, v in tracing_iter(self.items())}
65
+
66
+ def __ch_deep_realize__(self, memo):
67
+ return {
68
+ deep_realize(k, memo): deep_realize(v, memo)
69
+ for k, v in tracing_iter(self.items())
70
+ }
71
+
56
72
  def __repr__(self):
57
73
  contents = ", ".join(f"{repr(k)}: {repr(v)}" for (k, v) in self.items())
58
74
  return "{" + contents + "}"
@@ -106,8 +122,10 @@ class SimpleDict(MapBase):
106
122
  def __getitem__(self, key, default=_MISSING):
107
123
  if not is_hashable(key):
108
124
  raise TypeError("unhashable type")
109
- for (k, v) in self.contents_:
110
- if k == key:
125
+ for k, v in self.contents_:
126
+ # Note that the identity check below is not just an optimization;
127
+ # it is required to implement the semantics of NaN dict keys
128
+ if k is key or k == key:
111
129
  return v
112
130
  if default is _MISSING:
113
131
  raise KeyError
@@ -116,7 +134,7 @@ class SimpleDict(MapBase):
116
134
  def __setitem__(self, key, value):
117
135
  if not is_hashable(key):
118
136
  raise TypeError("unhashable type")
119
- for (i, (k, v)) in enumerate(self.contents_):
137
+ for i, (k, v) in enumerate(self.contents_):
120
138
  if k == key:
121
139
  self.contents_[i] = (k, value)
122
140
  return
@@ -125,7 +143,7 @@ class SimpleDict(MapBase):
125
143
  def __delitem__(self, key):
126
144
  if not is_hashable(key):
127
145
  raise TypeError("unhashable type")
128
- for (i, (k, v)) in enumerate(self.contents_):
146
+ for i, (k, v) in enumerate(self.contents_):
129
147
  if k == key:
130
148
  del self.contents_[i]
131
149
  return
@@ -143,9 +161,6 @@ class SimpleDict(MapBase):
143
161
  def __len__(self):
144
162
  return self.contents_.__len__()
145
163
 
146
- def items(self):
147
- return self.contents_
148
-
149
164
  def popitem(self):
150
165
  if not self.contents_:
151
166
  raise KeyError
@@ -478,9 +493,11 @@ class SequenceConcatenation(collections.abc.Sequence, SeqBase):
478
493
  return second.__getitem__(
479
494
  slice(
480
495
  i.start - firstlen,
481
- i.stop
482
- if i.stop is None or i.stop < 0
483
- else i.stop - firstlen,
496
+ (
497
+ i.stop
498
+ if i.stop is None or i.stop < 0
499
+ else i.stop - firstlen
500
+ ),
484
501
  i.step,
485
502
  )
486
503
  )
@@ -728,14 +745,23 @@ class ShellMutableSequence(collections.abc.MutableSequence, SeqBase):
728
745
 
729
746
 
730
747
  AbcSet = collections.abc.Set
748
+ AbcMutableSet = collections.abc.MutableSet
731
749
 
732
750
 
733
751
  def _force_arg_to_set(x: object) -> AbcSet:
734
- if isinstance(x, AbcSet):
735
- return x
736
- if is_iterable(x):
737
- return LinearSet.check_unique_and_create(x)
738
- raise TypeError
752
+ with NoTracing():
753
+ if isinstance(x, AbcSet):
754
+ while isinstance(x, ShellMutableSet):
755
+ x = x._inner
756
+ if isinstance(x, AbcMutableSet):
757
+ # Already known to have unique elements:
758
+ return LinearSet(list(tracing_iter(x)))
759
+ elif isinstance(x, (frozenset, FrozenSetBase)):
760
+ return x # Immutable set
761
+ if is_iterable(x):
762
+ with ResumedTracing():
763
+ return LinearSet.check_unique_and_create(x)
764
+ raise TypeError
739
765
 
740
766
 
741
767
  class SetBase(CrossHairValue):
@@ -750,9 +776,6 @@ class SetBase(CrossHairValue):
750
776
  def __repr__(self):
751
777
  return deep_realize(self).__repr__()
752
778
 
753
- def __hash__(self):
754
- return hash(set(self))
755
-
756
779
  def __and__(self, x):
757
780
  if not isinstance(x, AbcSet):
758
781
  return NotImplemented
@@ -813,7 +836,34 @@ class SetBase(CrossHairValue):
813
836
  return self
814
837
 
815
838
 
816
- class SingletonSet(SetBase, AbcSet):
839
+ class FrozenSetBase(SetBase, AbcSet):
840
+ def __ch_realize__(self):
841
+ # We are going to have to hash all of our contents,
842
+ # so just realize everything.
843
+ return self.__ch_deep_realize__({})
844
+
845
+ @assert_tracing(False)
846
+ def __ch_deep_realize__(self, memo):
847
+ contents = []
848
+ for item in tracing_iter(self):
849
+ contents.append(deep_realize(item, memo))
850
+ # TODO: This fails to preserve the iteration order;
851
+ # should we do something about that?:
852
+ return frozenset(contents)
853
+
854
+ def __ch_pytype__(self):
855
+ return frozenset
856
+
857
+ @classmethod
858
+ def _from_iterable(cls, it):
859
+ # overrides collections.abc.Set's version
860
+ return LinearSet.check_unique_and_create(it)
861
+
862
+ def __hash__(self):
863
+ return hash(deep_realize(self))
864
+
865
+
866
+ class SingletonSet(FrozenSetBase):
817
867
  # Primarily this exists to avoid hashing values.
818
868
  # TODO: should we fold uses of this into LinearSet, below?
819
869
 
@@ -830,9 +880,7 @@ class SingletonSet(SetBase, AbcSet):
830
880
  return 1
831
881
 
832
882
 
833
- class EmptySet(SetBase, AbcSet):
834
- # Primarily this exists to avoid hashing values.
835
-
883
+ class EmptySet(FrozenSetBase):
836
884
  def __contains__(self, x):
837
885
  if not is_hashable(x):
838
886
  raise TypeError
@@ -846,7 +894,7 @@ class EmptySet(SetBase, AbcSet):
846
894
  return 0
847
895
 
848
896
 
849
- class LinearSet(SetBase, AbcSet):
897
+ class LinearSet(FrozenSetBase):
850
898
  # Primarily this exists to avoid hashing values.
851
899
  # Presumes that its arguments are already unique.
852
900
 
@@ -881,7 +929,7 @@ class LinearSet(SetBase, AbcSet):
881
929
  return len(self._items)
882
930
 
883
931
 
884
- class LazySetCombination(SetBase, AbcSet):
932
+ class LazySetCombination(FrozenSetBase):
885
933
  """
886
934
  Provide a view over two sets and a logical operation in-between.
887
935
 
@@ -927,23 +975,29 @@ class LazySetCombination(SetBase, AbcSet):
927
975
  return sum(1 for i in self.__iter__())
928
976
 
929
977
 
930
- class ShellMutableSet(SetBase, collections.abc.MutableSet):
978
+ class ShellMutableSet(SetBase, AbcMutableSet):
931
979
  """
932
- Provide a view over an immutable set.
980
+ Provide a mutable view over an immutable set.
933
981
 
934
- The view give the immutable set mutating operations that replace the underlying
982
+ The mutating operations simply replace the underlying
935
983
  data structure entirely.
936
984
  This set also attempts to preserve insertion order of the set,
937
985
  assuming the underlying set(s) do so as well.
938
986
  """
939
987
 
940
- _inner: Set
988
+ _inner: Union[frozenset, FrozenSetBase]
941
989
 
942
- def __init__(self, inner=EmptySet()):
943
- if isinstance(inner, AbcSet):
990
+ @assert_tracing(False)
991
+ def __init__(self, inner: Iterable = EmptySet()):
992
+ if isinstance(inner, FrozenSetBase):
944
993
  self._inner = inner
994
+ elif isinstance(inner, frozenset):
995
+ self._inner = LinearSet(inner)
996
+ elif isinstance(inner, set):
997
+ self._inner = LinearSet(frozenset(inner))
945
998
  elif is_iterable(inner):
946
- self._inner = LinearSet.check_unique_and_create(inner)
999
+ with ResumedTracing():
1000
+ self._inner = LinearSet.check_unique_and_create(inner)
947
1001
  else:
948
1002
  raise TypeError
949
1003
 
@@ -989,7 +1043,7 @@ class ShellMutableSet(SetBase, collections.abc.MutableSet):
989
1043
  # mutation operations
990
1044
  def add(self, x):
991
1045
  if not is_hashable(x):
992
- raise TypeError("unhashable type")
1046
+ raise TypeError(f"unhashable type: '{name_of_type(type(x))}'")
993
1047
  self.__ior__(SingletonSet(x))
994
1048
 
995
1049
  def clear(self):
@@ -1000,7 +1054,7 @@ class ShellMutableSet(SetBase, collections.abc.MutableSet):
1000
1054
 
1001
1055
  def discard(self, x):
1002
1056
  if not is_hashable(x):
1003
- raise TypeError("unhashable type")
1057
+ raise TypeError(f"unhashable type: '{name_of_type(type(x))}'")
1004
1058
  self.__isub__(SingletonSet(x))
1005
1059
 
1006
1060
  def intersection_update(self, x):
@@ -1023,74 +1077,89 @@ class ShellMutableSet(SetBase, collections.abc.MutableSet):
1023
1077
  self._inner = self._inner.symmetric_difference(x)
1024
1078
 
1025
1079
  def update(self, *iterables):
1026
- additions = ShellMutableSet([value for itr in iterables for value in iter(itr)])
1027
- self._inner = LazySetCombination(operator.or_, self._inner, additions)
1080
+ for itr in iterables:
1081
+ additions = _force_arg_to_set(itr)
1082
+ self._inner = LazySetCombination(operator.or_, self._inner, additions)
1028
1083
 
1029
1084
  def __or__(self, x):
1030
- if not isinstance(x, AbcSet):
1031
- return NotImplemented
1032
- return ShellMutableSet(LazySetCombination(operator.or_, self._inner, x))
1085
+ with NoTracing():
1086
+ if not isinstance(x, AbcSet):
1087
+ return NotImplemented
1088
+ return ShellMutableSet(
1089
+ LazySetCombination(operator.or_, self._inner, _force_arg_to_set(x))
1090
+ )
1033
1091
 
1034
1092
  __ror__ = __or__
1035
1093
 
1036
1094
  def __and__(self, x):
1037
- if not isinstance(x, AbcSet):
1038
- return NotImplemented
1039
- return ShellMutableSet(LazySetCombination(operator.and_, self._inner, x))
1095
+ with NoTracing():
1096
+ if not isinstance(x, AbcSet):
1097
+ return NotImplemented
1098
+ return ShellMutableSet(
1099
+ LazySetCombination(operator.and_, self._inner, _force_arg_to_set(x))
1100
+ )
1040
1101
 
1041
1102
  __rand__ = __and__
1042
1103
 
1043
1104
  def __xor__(self, x):
1044
- if not isinstance(x, AbcSet):
1045
- return NotImplemented
1046
- return ShellMutableSet(LazySetCombination(operator.xor, self._inner, x))
1105
+ with NoTracing():
1106
+ if not isinstance(x, AbcSet):
1107
+ return NotImplemented
1108
+ return ShellMutableSet(
1109
+ LazySetCombination(operator.xor, self._inner, _force_arg_to_set(x))
1110
+ )
1047
1111
 
1048
1112
  __rxor__ = __xor__
1049
1113
 
1050
1114
  def __sub__(self, x):
1051
- if not isinstance(x, AbcSet):
1052
- return NotImplemented
1053
- return ShellMutableSet(
1054
- LazySetCombination(lambda x, y: (x and not y), self._inner, x)
1055
- )
1115
+ with NoTracing():
1116
+ if not isinstance(x, AbcSet):
1117
+ return NotImplemented
1118
+ # TODO: why not lazy set combination here?
1119
+ return ShellMutableSet(
1120
+ LazySetCombination(lambda x, y: (x and not y), self._inner, x)
1121
+ )
1056
1122
 
1057
1123
  def __rsub__(self, x):
1058
- if not isinstance(x, AbcSet):
1059
- return NotImplemented
1060
- return ShellMutableSet(
1061
- LazySetCombination(lambda x, y: (y and not x), self._inner, x)
1062
- )
1124
+ with NoTracing():
1125
+ if not isinstance(x, AbcSet):
1126
+ return NotImplemented
1127
+ return ShellMutableSet(
1128
+ LazySetCombination(lambda x, y: (y and not x), self._inner, x)
1129
+ )
1063
1130
 
1064
1131
  def __ior__(self, x):
1065
- if not isinstance(x, AbcSet):
1066
- return NotImplemented
1067
- self._inner = LazySetCombination(operator.or_, self._inner, x)
1068
- return self
1132
+ with NoTracing():
1133
+ if not isinstance(x, AbcSet):
1134
+ return NotImplemented
1135
+ self._inner = LazySetCombination(
1136
+ operator.or_, self._inner, _force_arg_to_set(x)
1137
+ )
1138
+ return self
1069
1139
 
1070
1140
  def __iand__(self, x):
1071
- if not isinstance(x, AbcSet):
1072
- return NotImplemented
1073
- self._inner = LazySetCombination(operator.and_, self._inner, x)
1074
- return self
1141
+ with NoTracing():
1142
+ if not isinstance(x, AbcSet):
1143
+ return NotImplemented
1144
+ self._inner = LazySetCombination(
1145
+ operator.and_, self._inner, _force_arg_to_set(x)
1146
+ )
1147
+ return self
1075
1148
 
1076
1149
  def __ixor__(self, x):
1077
- if not isinstance(x, AbcSet):
1078
- return NotImplemented
1079
- self._inner = LazySetCombination(operator.xor, self._inner, x)
1080
- return self
1150
+ with NoTracing():
1151
+ if not isinstance(x, AbcSet):
1152
+ return NotImplemented
1153
+ self._inner = LazySetCombination(
1154
+ operator.xor, self._inner, _force_arg_to_set(x)
1155
+ )
1156
+ return self
1081
1157
 
1082
1158
  def __isub__(self, x):
1083
- if not isinstance(x, AbcSet):
1084
- return NotImplemented
1085
- self._inner = LazySetCombination(lambda x, y: (x and not y), self._inner, x)
1086
- return self
1087
-
1088
-
1089
- def _test_seq_concat(seq: SequenceConcatenation, i: slice):
1090
- """
1091
- Test that slices of SequenceConcatenations are correct.
1092
-
1093
- raises: IndexError
1094
- post: _[0] == _[1]
1095
- """
1096
- return (seq[i], (seq._first + seq._second)[i]) # type: ignore
1159
+ with NoTracing():
1160
+ if not isinstance(x, AbcSet):
1161
+ return NotImplemented
1162
+ self._inner = LazySetCombination(
1163
+ lambda x, y: (x and not y), self._inner, _force_arg_to_set(x)
1164
+ )
1165
+ return self
@@ -3,7 +3,7 @@ import sys
3
3
 
4
4
  import pytest
5
5
 
6
- from crosshair.core import deep_realize
6
+ from crosshair.core import deep_realize, realize
7
7
  from crosshair.simplestructs import (
8
8
  LazySetCombination,
9
9
  SequenceConcatenation,
@@ -87,6 +87,20 @@ def test_LazySetCombination_xor() -> None:
87
87
  assert 5 in s
88
88
 
89
89
 
90
+ def test_ShellMutableDict_realize() -> None:
91
+ shell = ShellMutableMap({1: ShellMutableMap({2: 3})})
92
+ realized = realize(shell)
93
+ assert isinstance(realized, dict)
94
+ assert isinstance(realized[1], ShellMutableMap)
95
+
96
+
97
+ def test_ShellMutableDict_deep_realize() -> None:
98
+ shell = ShellMutableMap({1: ShellMutableMap({2: 3})})
99
+ realized = deep_realize(shell)
100
+ assert isinstance(realized, dict)
101
+ assert isinstance(realized[1], dict)
102
+
103
+
90
104
  def test_ShellMutableSet_deepcopy() -> None:
91
105
  ls = ["0", "1", "2", "3"]
92
106
  shell = ShellMutableSet(ls)
@@ -223,7 +237,7 @@ def test_ShellMutableSet_errors() -> None:
223
237
  with pytest.raises(KeyError):
224
238
  ShellMutableSet([]).pop()
225
239
  with pytest.raises(TypeError):
226
- ShellMutableSet(4)
240
+ ShellMutableSet(4) # type: ignore
227
241
 
228
242
 
229
243
  @pytest.mark.parametrize("start", [-3, -1, 0, 1, 3, None])
crosshair/smtlib.py ADDED
@@ -0,0 +1,24 @@
1
+ import math
2
+ import re
3
+ import struct
4
+
5
+ _SL2_PARSE_FLOAT_RE = re.compile(r"\(fp #(b\d+) #(b\d+) #(x.+)\)")
6
+ LITERAL_CONSTS = {
7
+ "(_ -zero 11 53)": -0.0,
8
+ "(_ +zero 11 53)": 0.0,
9
+ "(_ -oo 11 53)": -math.inf,
10
+ "(_ +oo 11 53)": math.inf,
11
+ "(_ NaN 11 53)": math.nan,
12
+ }
13
+
14
+
15
+ def parse_smtlib_literal(input: str):
16
+ literal_const = LITERAL_CONSTS.get(input, None)
17
+ if literal_const is not None:
18
+ return literal_const
19
+ match = _SL2_PARSE_FLOAT_RE.fullmatch(input)
20
+ if match:
21
+ sign, exp, significand = (int("0" + g, base=0) for g in match.groups())
22
+ return struct.unpack(
23
+ "d", struct.pack("Q", sign << 63 | exp << 52 | significand)
24
+ )[0]
@@ -0,0 +1,14 @@
1
+ import math
2
+
3
+ import z3 # type: ignore
4
+
5
+ from crosshair.smtlib import parse_smtlib_literal
6
+
7
+
8
+ def test_parse_smtlib_literal():
9
+ assert parse_smtlib_literal(z3.FPVal(1.23, z3.Float64).sexpr()) == 1.23
10
+ assert math.isnan(parse_smtlib_literal(z3.FPVal(math.nan, z3.Float64).sexpr()))
11
+ assert parse_smtlib_literal(z3.FPVal(-math.inf, z3.Float64).sexpr()) <= -math.inf
12
+ negzero = parse_smtlib_literal(z3.FPVal(-0.0, z3.Float64).sexpr())
13
+ assert negzero == 0
14
+ assert math.copysign(42, negzero) == -42