1 #!/usr/bin/env python 2 3 """ 4 Translation result abstractions. 5 6 Copyright (C) 2016-2018, 2021, 2023, 2024 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, args=None): 299 InstructionSequence.__init__(self, instructions) 300 self.result_target = result_target 301 self.args = args 302 303 def yields_integer(self): 304 return self.args and True or False 305 306 def __str__(self): 307 if self.yields_integer(): 308 return ", ".join(self.args) 309 else: 310 return encode_instructions(self.instructions) 311 312 def __repr__(self): 313 return "InvocationResult(%r, %r, %r)" % (self.result_target, 314 self.instructions, self.args) 315 316 class InstantiationResult(InvocationResult, TrInstanceRef): 317 318 "An instantiation result acting like an invocation result." 319 320 def __init__(self, ref, instructions): 321 InstanceRef.__init__(self, ref) 322 InvocationResult.__init__(self, "__NULL", instructions) 323 324 def __repr__(self): 325 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 326 327 class PredefinedConstantRef(Result): 328 329 "A predefined constant reference." 330 331 def __init__(self, value, expr=None): 332 self.value = value 333 self.expr = expr 334 335 def __str__(self): 336 337 # Eliminate predefined constant assignments. 338 339 if self.expr: 340 return "" 341 342 # Generate the specific constants. 343 344 if self.value in ("False", "True"): 345 return encode_path("__builtins__.boolean.%s" % self.value) 346 elif self.value == "None": 347 return encode_path("__builtins__.none.%s" % self.value) 348 elif self.value == "NotImplemented": 349 return encode_path("__builtins__.notimplemented.%s" % self.value) 350 else: 351 return self.value 352 353 def __repr__(self): 354 return "PredefinedConstantRef(%r)" % self.value 355 356 class LogicalResult(Result): 357 358 "A logical expression result." 359 360 pass 361 362 class NegationResult(LogicalResult): 363 364 "A negation expression result." 365 366 def __init__(self, expr): 367 self.expr = expr 368 369 def apply_test(self): 370 371 "Return the result in a form suitable for direct testing." 372 373 return "(!%s)" % self.expr.apply_test() 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 yields_integer(self): 385 return self.expr.yields_integer() 386 387 def __str__(self): 388 return "(%s ? %s : %s)" % ( 389 self.expr.apply_test(), 390 PredefinedConstantRef("False"), 391 PredefinedConstantRef("True")) 392 393 def __repr__(self): 394 return "NegationResult(%r)" % self.expr 395 396 class LogicalOperationResult(LogicalResult): 397 398 "A logical operation result." 399 400 def __init__(self, exprs, conjunction): 401 self.exprs = exprs 402 self.conjunction = conjunction 403 404 def apply_test(self): 405 406 """ 407 Return the result in a form suitable for direct testing. 408 409 Convert ... to ... 410 411 <a> and <b> 412 ((__BOOL(<a>)) && (__BOOL(<b>))) 413 414 <a> or <b> 415 ((__BOOL(<a>)) || (__BOOL(<b>))) 416 """ 417 418 results = [] 419 for expr in self.exprs: 420 results.append(expr.apply_test()) 421 422 if self.conjunction: 423 return "(%s)" % " && ".join(results) 424 else: 425 return "(%s)" % " || ".join(results) 426 427 def discards_temporary(self, test=True): 428 429 """ 430 Return a list of temporary names that can be discarded if 'test' is 431 specified as a true value (or omitted). 432 """ 433 434 if not test: 435 return None 436 437 temps = ["__tmp_result"] 438 439 for expr in self.exprs: 440 t = expr.discards_temporary(test) 441 if t: 442 temps += t 443 444 return temps 445 446 def yields_integer(self): 447 for expr in self.exprs: 448 if not expr.yields_integer(): 449 return False 450 return True 451 452 def __str__(self): 453 454 """ 455 Convert ... to ... 456 457 <a> and <b> 458 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 459 460 <a> or <b> 461 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 462 """ 463 464 results = [] 465 for expr in self.exprs[:-1]: 466 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 467 results.append(str(self.exprs[-1])) 468 469 return "(%s)" % "".join(results) 470 471 def __repr__(self): 472 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 473 474 # vim: tabstop=4 expandtab shiftwidth=4