1 #!/usr/bin/env python 2 3 """ 4 Translation result abstractions. 5 6 Copyright (C) 2016, 2017, 2018, 2021, 2023 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from common import first, InstructionSequence 23 from encoders import encode_instructions, encode_literal_constant, encode_path, \ 24 encode_symbol 25 from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, NameRef, \ 26 ResolvedNameRef, Result 27 28 # Classes representing intermediate translation results. 29 30 class ReturnRef: 31 32 "Indicates usage of a return statement." 33 34 pass 35 36 class Expression(Result): 37 38 "A general expression." 39 40 def __init__(self, s): 41 if isinstance(s, Result): 42 self.s = str(s) 43 self.expr = s 44 else: 45 self.s = s 46 self.expr = None 47 48 def discards_temporary(self, test=True): 49 50 """ 51 Return a list of temporary names that can be discarded if 'test' is 52 specified as a true value (or omitted). 53 """ 54 55 return self.expr and self.expr.discards_temporary(False) 56 57 def __str__(self): 58 return self.s 59 60 def __repr__(self): 61 return "Expression(%r)" % self.s 62 63 class TrResolvedNameRef(ResolvedNameRef): 64 65 "A reference to a name in the translation." 66 67 def __init__(self, name, ref, expr=None, is_global=False, location=None): 68 ResolvedNameRef.__init__(self, name, ref, expr, is_global) 69 self.location = location 70 71 # For sources, any identified static origin will be constant and thus 72 # usable directly. For targets, no constant should be assigned and thus 73 # the alias (or any plain name) will be used. 74 75 self.static_ref = self.static() 76 origin = self.static_ref and self.get_origin() 77 self.static_name = origin and encode_path(origin) 78 79 # Determine whether a qualified name is involved. 80 81 t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1) 82 self.parent = len(t) > 1 and t[0] or None 83 self.attrname = t[-1] and encode_path(t[-1]) 84 85 def access_location(self): 86 return self.location 87 88 def __str__(self): 89 90 "Return an output representation of the referenced name." 91 92 # Temporary names are output program locals. 93 94 if self.name.startswith("$t"): 95 if self.expr: 96 return "%s = %s" % (encode_path(self.name), self.expr) 97 else: 98 return encode_path(self.name) 99 100 # Assignments. 101 102 if self.expr: 103 104 store = self.attrname != "__data__" and "__store_via_attr_ref" or "__store_via_attr_ref__" 105 106 # Eliminate assignments between constants. 107 108 if self.static_ref and self.expr.static(): 109 return "" 110 111 # Qualified names must be converted into parent-relative assignments. 112 113 elif self.parent: 114 return "%s(__get_object_attr_ref(&%s, %s), %s)" % ( 115 store, encode_path(self.parent), self.attrname, self.expr) 116 117 # All other assignments involve the names as they were given. 118 # To support value replacement, a special operation is used. 119 120 else: 121 return "%s(&%s, %s)" % (store, self.attrname, self.expr) 122 123 # Expressions. 124 125 elif self.static_name: 126 return "__ATTRVALUE(&%s)" % self.static_name 127 128 # Qualified names must be converted into parent-relative accesses. 129 130 elif self.parent: 131 return "__load_via_object(&%s, %s)" % ( 132 encode_path(self.parent), self.attrname) 133 134 # All other accesses involve the names as they were given. 135 136 else: 137 return "(%s)" % self.attrname 138 139 class TrConstantValueRef(ConstantValueRef): 140 141 "A constant value reference in the translation." 142 143 def __str__(self): 144 145 # NOTE: Should reference a common variable for the type name. 146 147 if self.ref.get_origin() == "__builtins__.int.int": 148 return "__INTVALUE(%s)" % self.value 149 else: 150 return encode_literal_constant(self.number) 151 152 class TrLiteralSequenceRef(LiteralSequenceRef): 153 154 "A reference representing a sequence of values." 155 156 def __str__(self): 157 return str(self.node) 158 159 class TrInstanceRef(InstanceRef): 160 161 "A reference representing instantiation of a class." 162 163 def __init__(self, ref, expr): 164 165 """ 166 Initialise the reference with 'ref' indicating the nature of the 167 reference and 'expr' being an expression used to create the instance. 168 """ 169 170 InstanceRef.__init__(self, ref) 171 self.expr = expr 172 173 def __str__(self): 174 return self.expr 175 176 def __repr__(self): 177 return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr) 178 179 class AttrResult(Result, InstructionSequence): 180 181 "A translation result for an attribute access." 182 183 def __init__(self, instructions, refs, location, context_identity, 184 context_identity_verified, accessor_test, accessor_stored, 185 attribute_ref_stored): 186 187 InstructionSequence.__init__(self, instructions) 188 self.refs = refs 189 self.location = location 190 self.context_identity = context_identity 191 self.context_identity_verified = context_identity_verified 192 self.accessor_test = accessor_test 193 self.accessor_stored = accessor_stored 194 self.attribute_ref_stored = attribute_ref_stored 195 196 def references(self): 197 return self.refs 198 199 def access_location(self): 200 return self.location 201 202 def context(self): 203 return self.context_identity 204 205 def context_verified(self): 206 return self.context_identity_verified and self.context() or None 207 208 def tests_accessor(self): 209 return self.accessor_test 210 211 def stores_accessor(self): 212 return self.accessor_stored 213 214 def stores_attribute_ref(self): 215 return self.attribute_ref_stored 216 217 def get_origin(self): 218 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 219 220 def has_kind(self, kinds): 221 if not self.refs: 222 return False 223 for ref in self.refs: 224 if ref.has_kind(kinds): 225 return True 226 return False 227 228 def __nonzero__(self): 229 return bool(self.instructions) 230 231 def __str__(self): 232 return encode_instructions(self.instructions) 233 234 def __repr__(self): 235 return "AttrResult(%r, %r, %r, %r, %r, %r, %r, %r)" % ( 236 self.instructions, self.refs, self.location, 237 self.context_identity, self.context_identity_verified, 238 self.accessor_test, self.accessor_stored, 239 self.attribute_ref_stored) 240 241 class AliasResult(NameRef, Result): 242 243 "An alias for other values." 244 245 def __init__(self, name_ref, refs, location): 246 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 247 self.name_ref = name_ref 248 self.refs = refs 249 self.location = location 250 251 def references(self): 252 ref = self.name_ref.reference() 253 return self.refs or ref and [ref] or None 254 255 def reference(self): 256 refs = self.references() 257 return len(refs) == 1 and first(refs) or None 258 259 def access_location(self): 260 return self.location 261 262 def get_name(self): 263 ref = self.reference() 264 return ref and ref.get_name() 265 266 def get_origin(self): 267 ref = self.reference() 268 return ref and ref.get_origin() 269 270 def static(self): 271 ref = self.reference() 272 return ref and ref.static() 273 274 def final(self): 275 ref = self.reference() 276 return ref and ref.final() 277 278 def has_kind(self, kinds): 279 if not self.refs: 280 return self.name_ref.has_kind(kinds) 281 282 for ref in self.refs: 283 if ref.has_kind(kinds): 284 return True 285 286 return False 287 288 def __str__(self): 289 return str(self.name_ref) 290 291 def __repr__(self): 292 return "AliasResult(%r, %r)" % (self.name_ref, self.refs) 293 294 class InvocationResult(Result, InstructionSequence): 295 296 "A translation result for an invocation." 297 298 def __init__(self, result_target, instructions): 299 InstructionSequence.__init__(self, instructions) 300 self.result_target = result_target 301 302 def __str__(self): 303 return encode_instructions(self.instructions) 304 305 def __repr__(self): 306 return "InvocationResult(%r, %r)" % (self.result_target, self.instructions) 307 308 class InstantiationResult(InvocationResult, TrInstanceRef): 309 310 "An instantiation result acting like an invocation result." 311 312 def __init__(self, ref, instructions): 313 InstanceRef.__init__(self, ref) 314 InvocationResult.__init__(self, "__NULL", instructions) 315 316 def __repr__(self): 317 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 318 319 class PredefinedConstantRef(Result): 320 321 "A predefined constant reference." 322 323 def __init__(self, value, expr=None): 324 self.value = value 325 self.expr = expr 326 327 def __str__(self): 328 329 # Eliminate predefined constant assignments. 330 331 if self.expr: 332 return "" 333 334 # Generate the specific constants. 335 336 if self.value in ("False", "True"): 337 return encode_path("__builtins__.boolean.%s" % self.value) 338 elif self.value == "None": 339 return encode_path("__builtins__.none.%s" % self.value) 340 elif self.value == "NotImplemented": 341 return encode_path("__builtins__.notimplemented.%s" % self.value) 342 else: 343 return self.value 344 345 def __repr__(self): 346 return "PredefinedConstantRef(%r)" % self.value 347 348 class LogicalResult(Result): 349 350 "A logical expression result." 351 352 def _convert(self, expr): 353 354 "Return 'expr' converted to a testable value." 355 356 if isinstance(expr, LogicalResult): 357 return expr.apply_test() 358 else: 359 return "__BOOL(%s)" % expr 360 361 class NegationResult(LogicalResult): 362 363 "A negation expression result." 364 365 def __init__(self, expr): 366 self.expr = expr 367 368 def apply_test(self): 369 370 "Return the result in a form suitable for direct testing." 371 372 expr = self._convert(self.expr) 373 return "(!%s)" % expr 374 375 def discards_temporary(self, test=True): 376 377 """ 378 Negations should have discarded their operand's temporary names when 379 being instantiated. 380 """ 381 382 return None 383 384 def __str__(self): 385 return "(%s ? %s : %s)" % ( 386 self._convert(self.expr), 387 PredefinedConstantRef("False"), 388 PredefinedConstantRef("True")) 389 390 def __repr__(self): 391 return "NegationResult(%r)" % self.expr 392 393 class LogicalOperationResult(LogicalResult): 394 395 "A logical operation result." 396 397 def __init__(self, exprs, conjunction): 398 self.exprs = exprs 399 self.conjunction = conjunction 400 401 def apply_test(self): 402 403 """ 404 Return the result in a form suitable for direct testing. 405 406 Convert ... to ... 407 408 <a> and <b> 409 ((__BOOL(<a>)) && (__BOOL(<b>))) 410 411 <a> or <b> 412 ((__BOOL(<a>)) || (__BOOL(<b>))) 413 """ 414 415 results = [] 416 for expr in self.exprs: 417 results.append(self._convert(expr)) 418 419 if self.conjunction: 420 return "(%s)" % " && ".join(results) 421 else: 422 return "(%s)" % " || ".join(results) 423 424 def discards_temporary(self, test=True): 425 426 """ 427 Return a list of temporary names that can be discarded if 'test' is 428 specified as a true value (or omitted). 429 """ 430 431 if not test: 432 return None 433 434 temps = ["__tmp_result"] 435 436 for expr in self.exprs: 437 t = expr.discards_temporary(test) 438 if t: 439 temps += t 440 441 return temps 442 443 def __str__(self): 444 445 """ 446 Convert ... to ... 447 448 <a> and <b> 449 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 450 451 <a> or <b> 452 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 453 """ 454 455 results = [] 456 for expr in self.exprs[:-1]: 457 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 458 results.append(str(self.exprs[-1])) 459 460 return "(%s)" % "".join(results) 461 462 def __repr__(self): 463 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 464 465 # vim: tabstop=4 expandtab shiftwidth=4