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 # Eliminate assignments between constants. 105 106 if self.static_ref and self.expr.static(): 107 return "" 108 109 # Qualified names must be converted into parent-relative assignments. 110 111 elif self.parent: 112 return "__store_via_attr_ref(__get_object_attr_ref(&%s, %s), %s)" % ( 113 encode_path(self.parent), self.attrname, self.expr) 114 115 # All other assignments involve the names as they were given. 116 # To support value replacement, a special operation is used. 117 118 else: 119 return "__set_attr(&%s, %s)" % (self.attrname, self.expr) 120 121 # Expressions. 122 123 elif self.static_name: 124 return "__ATTRVALUE(&%s)" % self.static_name 125 126 # Qualified names must be converted into parent-relative accesses. 127 128 elif self.parent: 129 return "__load_via_object(&%s, %s)" % ( 130 encode_path(self.parent), self.attrname) 131 132 # All other accesses involve the names as they were given. 133 134 else: 135 return "(%s)" % self.attrname 136 137 class TrConstantValueRef(ConstantValueRef): 138 139 "A constant value reference in the translation." 140 141 def __str__(self): 142 143 # NOTE: Should reference a common variable for the type name. 144 145 if self.ref.get_origin() == "__builtins__.int.int": 146 return "__INTVALUE(%s)" % self.value 147 else: 148 return encode_literal_constant(self.number) 149 150 class TrLiteralSequenceRef(LiteralSequenceRef): 151 152 "A reference representing a sequence of values." 153 154 def __str__(self): 155 return str(self.node) 156 157 class TrInstanceRef(InstanceRef): 158 159 "A reference representing instantiation of a class." 160 161 def __init__(self, ref, expr): 162 163 """ 164 Initialise the reference with 'ref' indicating the nature of the 165 reference and 'expr' being an expression used to create the instance. 166 """ 167 168 InstanceRef.__init__(self, ref) 169 self.expr = expr 170 171 def __str__(self): 172 return self.expr 173 174 def __repr__(self): 175 return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr) 176 177 class AttrResult(Result, InstructionSequence): 178 179 "A translation result for an attribute access." 180 181 def __init__(self, instructions, refs, location, context_identity, 182 context_identity_verified, accessor_test, accessor_stored, 183 attribute_ref_stored): 184 185 InstructionSequence.__init__(self, instructions) 186 self.refs = refs 187 self.location = location 188 self.context_identity = context_identity 189 self.context_identity_verified = context_identity_verified 190 self.accessor_test = accessor_test 191 self.accessor_stored = accessor_stored 192 self.attribute_ref_stored = attribute_ref_stored 193 194 def references(self): 195 return self.refs 196 197 def access_location(self): 198 return self.location 199 200 def context(self): 201 return self.context_identity 202 203 def context_verified(self): 204 return self.context_identity_verified and self.context() or None 205 206 def tests_accessor(self): 207 return self.accessor_test 208 209 def stores_accessor(self): 210 return self.accessor_stored 211 212 def stores_attribute_ref(self): 213 return self.attribute_ref_stored 214 215 def get_origin(self): 216 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 217 218 def has_kind(self, kinds): 219 if not self.refs: 220 return False 221 for ref in self.refs: 222 if ref.has_kind(kinds): 223 return True 224 return False 225 226 def __nonzero__(self): 227 return bool(self.instructions) 228 229 def __str__(self): 230 return encode_instructions(self.instructions) 231 232 def __repr__(self): 233 return "AttrResult(%r, %r, %r, %r, %r, %r, %r, %r)" % ( 234 self.instructions, self.refs, self.location, 235 self.context_identity, self.context_identity_verified, 236 self.accessor_test, self.accessor_stored, 237 self.attribute_ref_stored) 238 239 class AliasResult(NameRef, Result): 240 241 "An alias for other values." 242 243 def __init__(self, name_ref, refs, location): 244 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 245 self.name_ref = name_ref 246 self.refs = refs 247 self.location = location 248 249 def references(self): 250 ref = self.name_ref.reference() 251 return self.refs or ref and [ref] or None 252 253 def reference(self): 254 refs = self.references() 255 return len(refs) == 1 and first(refs) or None 256 257 def access_location(self): 258 return self.location 259 260 def get_name(self): 261 ref = self.reference() 262 return ref and ref.get_name() 263 264 def get_origin(self): 265 ref = self.reference() 266 return ref and ref.get_origin() 267 268 def static(self): 269 ref = self.reference() 270 return ref and ref.static() 271 272 def final(self): 273 ref = self.reference() 274 return ref and ref.final() 275 276 def has_kind(self, kinds): 277 if not self.refs: 278 return self.name_ref.has_kind(kinds) 279 280 for ref in self.refs: 281 if ref.has_kind(kinds): 282 return True 283 284 return False 285 286 def __str__(self): 287 return str(self.name_ref) 288 289 def __repr__(self): 290 return "AliasResult(%r, %r)" % (self.name_ref, self.refs) 291 292 class InvocationResult(Result, InstructionSequence): 293 294 "A translation result for an invocation." 295 296 def __init__(self, result_target, instructions): 297 InstructionSequence.__init__(self, instructions) 298 self.result_target = result_target 299 300 def __str__(self): 301 return encode_instructions(self.instructions) 302 303 def __repr__(self): 304 return "InvocationResult(%r, %r)" % (self.result_target, self.instructions) 305 306 class InstantiationResult(InvocationResult, TrInstanceRef): 307 308 "An instantiation result acting like an invocation result." 309 310 def __init__(self, ref, instructions): 311 InstanceRef.__init__(self, ref) 312 InvocationResult.__init__(self, "__NULL", instructions) 313 314 def __repr__(self): 315 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 316 317 class PredefinedConstantRef(Result): 318 319 "A predefined constant reference." 320 321 def __init__(self, value, expr=None): 322 self.value = value 323 self.expr = expr 324 325 def __str__(self): 326 327 # Eliminate predefined constant assignments. 328 329 if self.expr: 330 return "" 331 332 # Generate the specific constants. 333 334 if self.value in ("False", "True"): 335 return encode_path("__builtins__.boolean.%s" % self.value) 336 elif self.value == "None": 337 return encode_path("__builtins__.none.%s" % self.value) 338 elif self.value == "NotImplemented": 339 return encode_path("__builtins__.notimplemented.%s" % self.value) 340 else: 341 return self.value 342 343 def __repr__(self): 344 return "PredefinedConstantRef(%r)" % self.value 345 346 class LogicalResult(Result): 347 348 "A logical expression result." 349 350 def _convert(self, expr): 351 352 "Return 'expr' converted to a testable value." 353 354 if isinstance(expr, LogicalResult): 355 return expr.apply_test() 356 else: 357 return "__BOOL(%s)" % expr 358 359 class NegationResult(LogicalResult): 360 361 "A negation expression result." 362 363 def __init__(self, expr): 364 self.expr = expr 365 366 def apply_test(self): 367 368 "Return the result in a form suitable for direct testing." 369 370 expr = self._convert(self.expr) 371 return "(!%s)" % expr 372 373 def discards_temporary(self, test=True): 374 375 """ 376 Negations should have discarded their operand's temporary names when 377 being instantiated. 378 """ 379 380 return None 381 382 def __str__(self): 383 return "(%s ? %s : %s)" % ( 384 self._convert(self.expr), 385 PredefinedConstantRef("False"), 386 PredefinedConstantRef("True")) 387 388 def __repr__(self): 389 return "NegationResult(%r)" % self.expr 390 391 class LogicalOperationResult(LogicalResult): 392 393 "A logical operation result." 394 395 def __init__(self, exprs, conjunction): 396 self.exprs = exprs 397 self.conjunction = conjunction 398 399 def apply_test(self): 400 401 """ 402 Return the result in a form suitable for direct testing. 403 404 Convert ... to ... 405 406 <a> and <b> 407 ((__BOOL(<a>)) && (__BOOL(<b>))) 408 409 <a> or <b> 410 ((__BOOL(<a>)) || (__BOOL(<b>))) 411 """ 412 413 results = [] 414 for expr in self.exprs: 415 results.append(self._convert(expr)) 416 417 if self.conjunction: 418 return "(%s)" % " && ".join(results) 419 else: 420 return "(%s)" % " || ".join(results) 421 422 def discards_temporary(self, test=True): 423 424 """ 425 Return a list of temporary names that can be discarded if 'test' is 426 specified as a true value (or omitted). 427 """ 428 429 if not test: 430 return None 431 432 temps = ["__tmp_result"] 433 434 for expr in self.exprs: 435 t = expr.discards_temporary(test) 436 if t: 437 temps += t 438 439 return temps 440 441 def __str__(self): 442 443 """ 444 Convert ... to ... 445 446 <a> and <b> 447 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 448 449 <a> or <b> 450 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 451 """ 452 453 results = [] 454 for expr in self.exprs[:-1]: 455 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 456 results.append(str(self.exprs[-1])) 457 458 return "(%s)" % "".join(results) 459 460 def __repr__(self): 461 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 462 463 # vim: tabstop=4 expandtab shiftwidth=4