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, args=None): 289 InstructionSequence.__init__(self, instructions) 290 self.args = args 291 292 def yields_integer(self): 293 return self.args and True or False 294 295 def __str__(self): 296 if self.yields_integer(): 297 return ", ".join(self.args) 298 else: 299 return encode_instructions(self.instructions) 300 301 def __repr__(self): 302 return "InvocationResult(%r, %r)" % (self.instructions, self.args) 303 304 class InstantiationResult(InvocationResult, TrInstanceRef): 305 306 "An instantiation result acting like an invocation result." 307 308 def __init__(self, ref, instructions): 309 InstanceRef.__init__(self, ref) 310 InvocationResult.__init__(self, instructions) 311 312 def __repr__(self): 313 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 314 315 class PredefinedConstantRef(Result): 316 317 "A predefined constant reference." 318 319 def __init__(self, value, expr=None): 320 self.value = value 321 self.expr = expr 322 323 def __str__(self): 324 325 # Eliminate predefined constant assignments. 326 327 if self.expr: 328 return "" 329 330 # Generate the specific constants. 331 332 if self.value in ("False", "True"): 333 return encode_path("__builtins__.boolean.%s" % self.value) 334 elif self.value == "None": 335 return encode_path("__builtins__.none.%s" % self.value) 336 elif self.value == "NotImplemented": 337 return encode_path("__builtins__.notimplemented.%s" % self.value) 338 else: 339 return self.value 340 341 def __repr__(self): 342 return "PredefinedConstantRef(%r)" % self.value 343 344 class LogicalResult(Result): 345 346 "A logical expression result." 347 348 pass 349 350 class NegationResult(LogicalResult): 351 352 "A negation expression result." 353 354 def __init__(self, expr): 355 self.expr = expr 356 357 def apply_test(self): 358 359 "Return the result in a form suitable for direct testing." 360 361 return "(!%s)" % self.expr.apply_test() 362 363 def discards_temporary(self, test=True): 364 365 """ 366 Negations should have discarded their operand's temporary names when 367 being instantiated. 368 """ 369 370 return None 371 372 def yields_integer(self): 373 return self.expr.yields_integer() 374 375 def __str__(self): 376 return "(%s ? %s : %s)" % ( 377 self.expr.apply_test(), 378 PredefinedConstantRef("False"), 379 PredefinedConstantRef("True")) 380 381 def __repr__(self): 382 return "NegationResult(%r)" % self.expr 383 384 class LogicalOperationResult(LogicalResult): 385 386 "A logical operation result." 387 388 def __init__(self, exprs, conjunction): 389 self.exprs = exprs 390 self.conjunction = conjunction 391 392 def apply_test(self): 393 394 """ 395 Return the result in a form suitable for direct testing. 396 397 Convert ... to ... 398 399 <a> and <b> 400 ((__BOOL(<a>)) && (__BOOL(<b>))) 401 402 <a> or <b> 403 ((__BOOL(<a>)) || (__BOOL(<b>))) 404 """ 405 406 results = [] 407 for expr in self.exprs: 408 results.append(expr.apply_test()) 409 410 if self.conjunction: 411 return "(%s)" % " && ".join(results) 412 else: 413 return "(%s)" % " || ".join(results) 414 415 def discards_temporary(self, test=True): 416 417 """ 418 Return a list of temporary names that can be discarded if 'test' is 419 specified as a true value (or omitted). 420 """ 421 422 if not test: 423 return None 424 425 temps = ["__tmp_result"] 426 427 for expr in self.exprs: 428 t = expr.discards_temporary(test) 429 if t: 430 temps += t 431 432 return temps 433 434 def yields_integer(self): 435 for expr in self.exprs: 436 if not expr.yields_integer(): 437 return False 438 return True 439 440 def __str__(self): 441 442 """ 443 Convert ... to ... 444 445 <a> and <b> 446 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 447 448 <a> or <b> 449 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 450 """ 451 452 results = [] 453 for expr in self.exprs[:-1]: 454 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 455 results.append(str(self.exprs[-1])) 456 457 return "(%s)" % "".join(results) 458 459 def __repr__(self): 460 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 461 462 # vim: tabstop=4 expandtab shiftwidth=4