1 #!/usr/bin/env python 2 3 """ 4 Translation result abstractions. 5 6 Copyright (C) 2016, 2017, 2018, 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 def access_location(self): 72 return self.location 73 74 def __str__(self): 75 76 "Return an output representation of the referenced name." 77 78 # Temporary names are output program locals. 79 80 if self.name.startswith("$t"): 81 if self.expr: 82 return "%s = %s" % (encode_path(self.name), self.expr) 83 else: 84 return encode_path(self.name) 85 86 # For sources, any identified static origin will be constant and thus 87 # usable directly. For targets, no constant should be assigned and thus 88 # the alias (or any plain name) will be used. 89 90 ref = self.static() 91 origin = ref and self.get_origin() 92 static_name = origin and encode_path(origin) 93 94 # Determine whether a qualified name is involved. 95 96 t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1) 97 parent = len(t) > 1 and t[0] or None 98 attrname = t[-1] and encode_path(t[-1]) 99 100 # Assignments. 101 102 if self.expr: 103 104 # Eliminate assignments between constants. 105 106 if ref and self.expr.static(): 107 return "" 108 109 # Qualified names must be converted into parent-relative assignments. 110 111 elif parent: 112 return "__store_via_object(&%s, %s, %s)" % ( 113 encode_path(parent), 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_local(&%s, %s)" % (attrname, self.expr) 120 121 # Expressions. 122 123 elif static_name: 124 parent = ref.parent() 125 context = ref.has_kind("<function>") and encode_path(parent) or None 126 return "__ATTRVALUE(&%s)" % static_name 127 128 # Qualified names must be converted into parent-relative accesses. 129 130 elif parent: 131 return "__load_via_object(&%s, %s)" % ( 132 encode_path(parent), attrname) 133 134 # All other accesses involve the names as they were given. 135 136 else: 137 return "(%s)" % 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 186 InstructionSequence.__init__(self, instructions) 187 self.refs = refs 188 self.location = location 189 self.context_identity = context_identity 190 self.context_identity_verified = context_identity_verified 191 self.accessor_test = accessor_test 192 self.accessor_stored = accessor_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 get_origin(self): 213 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 214 215 def has_kind(self, kinds): 216 if not self.refs: 217 return False 218 for ref in self.refs: 219 if ref.has_kind(kinds): 220 return True 221 return False 222 223 def __nonzero__(self): 224 return bool(self.instructions) 225 226 def __str__(self): 227 return encode_instructions(self.instructions) 228 229 def __repr__(self): 230 return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % ( 231 self.instructions, self.refs, self.location, 232 self.context_identity, self.context_identity_verified, 233 self.accessor_test, self.accessor_stored) 234 235 class AliasResult(NameRef, Result): 236 237 "An alias for other values." 238 239 def __init__(self, name_ref, refs, location): 240 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 241 self.name_ref = name_ref 242 self.refs = refs 243 self.location = location 244 245 def references(self): 246 ref = self.name_ref.reference() 247 return self.refs or ref and [ref] or None 248 249 def reference(self): 250 refs = self.references() 251 return len(refs) == 1 and first(refs) or None 252 253 def access_location(self): 254 return self.location 255 256 def get_name(self): 257 ref = self.reference() 258 return ref and ref.get_name() 259 260 def get_origin(self): 261 ref = self.reference() 262 return ref and ref.get_origin() 263 264 def static(self): 265 ref = self.reference() 266 return ref and ref.static() 267 268 def final(self): 269 ref = self.reference() 270 return ref and ref.final() 271 272 def has_kind(self, kinds): 273 if not self.refs: 274 return self.name_ref.has_kind(kinds) 275 276 for ref in self.refs: 277 if ref.has_kind(kinds): 278 return True 279 280 return False 281 282 def __str__(self): 283 return str(self.name_ref) 284 285 def __repr__(self): 286 return "AliasResult(%r, %r)" % (self.name_ref, self.refs) 287 288 class InvocationResult(Result, InstructionSequence): 289 290 "A translation result for an invocation." 291 292 def __init__(self, result_target, instructions): 293 InstructionSequence.__init__(self, instructions) 294 self.result_target = result_target 295 296 def __str__(self): 297 return encode_instructions(self.instructions) 298 299 def __repr__(self): 300 return "InvocationResult(%r, %r)" % (self.result_target, self.instructions) 301 302 class InstantiationResult(InvocationResult, TrInstanceRef): 303 304 "An instantiation result acting like an invocation result." 305 306 def __init__(self, ref, instructions): 307 InstanceRef.__init__(self, ref) 308 InvocationResult.__init__(self, "__NULL", instructions) 309 310 def __repr__(self): 311 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 312 313 class PredefinedConstantRef(Result): 314 315 "A predefined constant reference." 316 317 def __init__(self, value, expr=None): 318 self.value = value 319 self.expr = expr 320 321 def __str__(self): 322 323 # Eliminate predefined constant assignments. 324 325 if self.expr: 326 return "" 327 328 # Generate the specific constants. 329 330 if self.value in ("False", "True"): 331 return encode_path("__builtins__.boolean.%s" % self.value) 332 elif self.value == "None": 333 return encode_path("__builtins__.none.%s" % self.value) 334 elif self.value == "NotImplemented": 335 return encode_path("__builtins__.notimplemented.%s" % self.value) 336 else: 337 return self.value 338 339 def __repr__(self): 340 return "PredefinedConstantRef(%r)" % self.value 341 342 class LogicalResult(Result): 343 344 "A logical expression result." 345 346 def _convert(self, expr): 347 348 "Return 'expr' converted to a testable value." 349 350 if isinstance(expr, LogicalResult): 351 return expr.apply_test() 352 else: 353 return "__BOOL(%s)" % expr 354 355 class NegationResult(LogicalResult): 356 357 "A negation expression result." 358 359 def __init__(self, expr): 360 self.expr = expr 361 362 def apply_test(self): 363 364 "Return the result in a form suitable for direct testing." 365 366 expr = self._convert(self.expr) 367 return "(!%s)" % expr 368 369 def discards_temporary(self, test=True): 370 371 """ 372 Negations should have discarded their operand's temporary names when 373 being instantiated. 374 """ 375 376 return None 377 378 def __str__(self): 379 return "(%s ? %s : %s)" % ( 380 self._convert(self.expr), 381 PredefinedConstantRef("False"), 382 PredefinedConstantRef("True")) 383 384 def __repr__(self): 385 return "NegationResult(%r)" % self.expr 386 387 class LogicalOperationResult(LogicalResult): 388 389 "A logical operation result." 390 391 def __init__(self, exprs, conjunction): 392 self.exprs = exprs 393 self.conjunction = conjunction 394 395 def apply_test(self): 396 397 """ 398 Return the result in a form suitable for direct testing. 399 400 Convert ... to ... 401 402 <a> and <b> 403 ((__BOOL(<a>)) && (__BOOL(<b>))) 404 405 <a> or <b> 406 ((__BOOL(<a>)) || (__BOOL(<b>))) 407 """ 408 409 results = [] 410 for expr in self.exprs: 411 results.append(self._convert(expr)) 412 413 if self.conjunction: 414 return "(%s)" % " && ".join(results) 415 else: 416 return "(%s)" % " || ".join(results) 417 418 def discards_temporary(self, test=True): 419 420 """ 421 Return a list of temporary names that can be discarded if 'test' is 422 specified as a true value (or omitted). 423 """ 424 425 if not test: 426 return None 427 428 temps = ["__tmp_result"] 429 430 for expr in self.exprs: 431 t = expr.discards_temporary(test) 432 if t: 433 temps += t 434 435 return temps 436 437 def __str__(self): 438 439 """ 440 Convert ... to ... 441 442 <a> and <b> 443 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 444 445 <a> or <b> 446 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 447 """ 448 449 results = [] 450 for expr in self.exprs[:-1]: 451 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 452 results.append(str(self.exprs[-1])) 453 454 return "(%s)" % "".join(results) 455 456 def __repr__(self): 457 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 458 459 # vim: tabstop=4 expandtab shiftwidth=4