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