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