property_.py
1 # SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-or-later OR CERN-OHL-S-2.0+ OR Apache-2.0 2 from typing import Any, Iterable, Tuple, Union, ClassVar, overload 3 4 from . import rule as _rle 5 6 7 __all__ = [ 8 "Enclosure", "PropertyT", "EnclosurePropertyT", "ComparisonT", 9 "Operators", "Ops", 10 ] 11 12 13 class Enclosure: 14 """Enclosure object are representing the enclosure value of one layer 15 over another layer. Most of the time it is used to indicae the 16 minimum required enclosure of one layer over another layer. 17 18 Enclosure constraints can symmetric or asymmetric. A symmetric 19 enclosure can be specified with a single float value, an assymetric one 20 with two float values. Internally always two float values are stored and 21 be accessed through the `spec` attribute of an Enclosure object. For a 22 symmetric object the two float values are the same. 23 24 When an enclosure is used as a constraint symmetric means that the 25 enclosure has to be met in all directions. Asymmetric normally means 26 that a smaller enclosure in one direction is allowed when both enclosures 27 in the other direction are bigger than the bigger enclosure value. 28 For this case the order of the two value don't have a meaning. 29 30 An enclosure can also be used to specify when doing layout generation. 31 The PDKMaster convention here is that the order has a meaning; the first 32 value is for the horizontal direction and the second value for the 33 vertical one. Also giving meaning to the `wide()` and `tall()` object methods 34 35 Enclosure objects are implemented as immutable objects. 36 """ 37 def __init__(self, spec: Union[float, Iterable[float]]): 38 self._spec: Tuple[float, float] 39 if isinstance(spec, float): 40 self._spec = (spec, spec) 41 else: 42 self._spec = tuple(spec) 43 if len(self.spec) != 2: 44 raise ValueError( 45 f"spec for Enclosure is either a float or 2 floats" 46 ) 47 48 @property 49 def spec(self) -> Tuple[float, float]: 50 return self._spec 51 52 @staticmethod 53 def cast(v: Union[float, Iterable[float], "Enclosure"]) -> "Enclosure": 54 if isinstance(v, Enclosure): 55 return v 56 else: 57 return Enclosure(spec=v) 58 59 @property 60 def first(self) -> float: 61 return self.spec[0] 62 63 @property 64 def second(self) -> float: 65 return self.spec[1] 66 67 @property 68 def is_assymetric(self) -> bool: 69 return self.first != self.second 70 71 def min(self) -> float: 72 return min(self.spec) 73 74 def max(self) -> float: 75 return max(self.spec) 76 77 def wide(self) -> "Enclosure": 78 # Put bigger enclosure value first 79 if self.first >= self.second: 80 return self 81 else: 82 return Enclosure(spec=(self.second, self.first)) 83 84 def tall(self) -> "Enclosure": 85 if self.first <= self.second: 86 return self 87 else: 88 return Enclosure(spec=(self.second, self.first)) 89 90 def __hash__(self) -> int: 91 return hash(self._spec) 92 93 def __eq__(self, other: Any) -> bool: 94 if not isinstance(other, Enclosure): 95 return False 96 else: 97 return ( 98 (self.first == other.first) 99 and (self.second == other.second) 100 ) 101 102 def __repr__(self) -> str: 103 if not self.is_assymetric: 104 return f"Enclosure({round(self.first, 6)})" 105 else: 106 return f"Enclosure(({round(self.spec[0], 6)},{round(self.spec[1], 6)}))" 107 108 109 class _Property: 110 """This class represents a property of an object. Rules may be built from 111 from a comparison operation. 112 113 o = Property(name='width') 114 rule = o >= 3.5 115 116 Then `rule` represents the rule for width greater or equal to 3.5. 117 """ 118 value_conv: Any = None 119 value_type: Union[type, Tuple[type, ...]] = (float, int) 120 value_type_str: str = "float" 121 122 def __init__(self, *, name: str, allow_none: bool=False): 123 self.name = name 124 self.allow_none = allow_none 125 126 value_type = self.value_type 127 if not isinstance(value_type, tuple): 128 if issubclass(value_type, _Property): 129 raise TypeError("Property.value_type may not be 'Property'") 130 131 self.dependencies = set() 132 133 def __gt__(self, other) -> "ComparisonT": 134 return Ops.Greater(left=self, right=other) 135 def __ge__(self, other) -> "ComparisonT": 136 return Ops.GreaterEqual(left=self, right=other) 137 def __lt__(self, other) -> "ComparisonT": 138 return Ops.Smaller(left=self, right=other) 139 def __le__(self, other) -> "ComparisonT": 140 return Ops.SmallerEqual(left=self, right=other) 141 @overload 142 def __eq__(self, other: "_Property") -> bool: 143 ... # pragma: no cover 144 @overload 145 def __eq__(self, other: Any) -> "ComparisonT": 146 ... # pragma: no cover 147 def __eq__(self, other: Any) -> Union[bool, "ComparisonT"]: 148 """The __eq__() method for Property can have two meanings. If it is 149 compared with another Property object it will check if it is the same 150 property. For another object it will generate a Rule object representing 151 that property being equal to the provided value. 152 """ 153 try: 154 return Ops.Equal(left=self, right=other) 155 except TypeError: 156 return isinstance(other, _Property) and (self.name == other.name) 157 158 def __str__(self) -> str: 159 return f"{self.name}" 160 def __repr__(self) -> str: 161 return f"{self.__class__.__name__}(name={self.name!r}, allow_non={self.allow_none!r})" 162 163 def __hash__(self) -> int: 164 return hash(self.name) 165 166 def cast(self, value): 167 if value is None: 168 if self.allow_none: 169 return None 170 else: 171 raise TypeError( 172 f"property '{self.name}' given value '{value!r}' is not of type " 173 f"'{self.value_type_str}'" 174 ) 175 176 value_conv = self.__class__.value_conv 177 if value_conv is not None: 178 try: 179 value = value_conv(value) 180 except: 181 raise TypeError("could not convert property value {!r} to type '{}'".format( 182 value, self.value_type_str, 183 )) 184 if not isinstance(value, self.value_type): 185 raise TypeError( 186 f"value '{value!r}' for property '{self.name}' is not of type " 187 f"'{self.value_type_str}'", 188 ) 189 return value 190 PropertyT = _Property 191 192 193 class _EnclosureProperty(_Property): 194 """An EnclosureProperty object is a Property with an Enclosure object as value. 195 """ 196 value_conv = Enclosure.cast 197 value_type = Enclosure 198 value_type_str = "'Enclosure'" 199 EnclosurePropertyT = _EnclosureProperty 200 201 202 class _Comparison(_rle._Condition): 203 """A _Comparison object is a _Condition which represent the comparison of a Property 204 object with a value. The operator for the comparison is represented as a string class 205 variable names `symbol`. 206 """ 207 symbol: ClassVar[str] 208 209 def __init__(self, *, left: _Property, right: Any): 210 try: 211 self.symbol 212 except AttributeError: 213 raise TypeError( 214 f"class '{self.__class__.__name__}' does not have the" 215 " symbol class variable defined" 216 ) 217 218 right2 = left.cast(right) 219 220 super().__init__(elements=(left, right2)) 221 self._elements: Tuple[_Property, Any] 222 223 @property 224 def left(self) -> _Property: 225 return self._elements[0] 226 @property 227 def right(self) -> Any: 228 return self._elements[1] 229 230 def __eq__(self, other: object) -> bool: 231 if not isinstance(other, _Comparison): 232 return False 233 else: 234 return ( 235 (self.symbol == other.symbol) 236 and (self.left == other.left) 237 and (self.right == other.right) 238 ) 239 240 def __str__(self) -> str: 241 return f"{self.left} {self.symbol} {self.right}" 242 def __repr__(self) -> str: 243 return f"{self.left!r} {self.symbol} {self.right!r}" 244 245 def __bool__(self) -> bool: 246 raise TypeError("BinaryPropertyCondition can't be converted to a bool") 247 ComparisonT = _Comparison 248 249 250 class Operators: 251 """Operators is a class representing a bunch of boolean operators. 252 """ 253 class Greater(_Comparison): 254 symbol = ">" 255 class GreaterEqual(_Comparison): 256 symbol = ">=" 257 class Smaller(_Comparison): 258 symbol = "<" 259 class SmallerEqual(_Comparison): 260 symbol = "<=" 261 class Equal(_Comparison): 262 symbol = "==" 263 264 def __bool__(self): 265 # When == needs to be interpreted as a bool inside the script it is False 266 # It has only to be True when it compared to another Property. 267 return False 268 269 # Convenience assigns 270 GT = Greater 271 GE = GreaterEqual 272 ST = Smaller 273 SE = SmallerEqual 274 EQ = Equal 275 # Convenience assigns 276 Ops = Operators