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