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