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_object(&%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_local(&%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 184 InstructionSequence.__init__(self, instructions) 185 self.refs = refs 186 self.location = location 187 self.context_identity = context_identity 188 self.context_identity_verified = context_identity_verified 189 self.accessor_test = accessor_test 190 self.accessor_stored = accessor_stored 191 192 def references(self): 193 return self.refs 194 195 def access_location(self): 196 return self.location 197 198 def context(self): 199 return self.context_identity 200 201 def context_verified(self): 202 return self.context_identity_verified and self.context() or None 203 204 def tests_accessor(self): 205 return self.accessor_test 206 207 def stores_accessor(self): 208 return self.accessor_stored 209 210 def get_origin(self): 211 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 212 213 def has_kind(self, kinds): 214 if not self.refs: 215 return False 216 for ref in self.refs: 217 if ref.has_kind(kinds): 218 return True 219 return False 220 221 def __nonzero__(self): 222 return bool(self.instructions) 223 224 def __str__(self): 225 return encode_instructions(self.instructions) 226 227 def __repr__(self): 228 return "AttrResult(%r, %r, %r, %r, %r, %r, %r)" % ( 229 self.instructions, self.refs, self.location, 230 self.context_identity, self.context_identity_verified, 231 self.accessor_test, self.accessor_stored) 232 233 class AliasResult(NameRef, Result): 234 235 "An alias for other values." 236 237 def __init__(self, name_ref, refs, location): 238 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 239 self.name_ref = name_ref 240 self.refs = refs 241 self.location = location 242 243 def references(self): 244 ref = self.name_ref.reference() 245 return self.refs or ref and [ref] or None 246 247 def reference(self): 248 refs = self.references() 249 return len(refs) == 1 and first(refs) or None 250 251 def access_location(self): 252 return self.location 253 254 def get_name(self): 255 ref = self.reference() 256 return ref and ref.get_name() 257 258 def get_origin(self): 259 ref = self.reference() 260 return ref and ref.get_origin() 261 262 def static(self): 263 ref = self.reference() 264 return ref and ref.static() 265 266 def final(self): 267 ref = self.reference() 268 return ref and ref.final() 269 270 def has_kind(self, kinds): 271 if not self.refs: 272 return self.name_ref.has_kind(kinds) 273 274 for ref in self.refs: 275 if ref.has_kind(kinds): 276 return True 277 278 return False 279 280 def __str__(self): 281 return str(self.name_ref) 282 283 def __repr__(self): 284 return "AliasResult(%r, %r)" % (self.name_ref, self.refs) 285 286 class InvocationResult(Result, InstructionSequence): 287 288 "A translation result for an invocation." 289 290 def __init__(self, result_target, instructions): 291 InstructionSequence.__init__(self, instructions) 292 self.result_target = result_target 293 294 def __str__(self): 295 return encode_instructions(self.instructions) 296 297 def __repr__(self): 298 return "InvocationResult(%r, %r)" % (self.result_target, self.instructions) 299 300 class InstantiationResult(InvocationResult, TrInstanceRef): 301 302 "An instantiation result acting like an invocation result." 303 304 def __init__(self, ref, instructions): 305 InstanceRef.__init__(self, ref) 306 InvocationResult.__init__(self, "__NULL", instructions) 307 308 def __repr__(self): 309 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 310 311 class PredefinedConstantRef(Result): 312 313 "A predefined constant reference." 314 315 def __init__(self, value, expr=None): 316 self.value = value 317 self.expr = expr 318 319 def __str__(self): 320 321 # Eliminate predefined constant assignments. 322 323 if self.expr: 324 return "" 325 326 # Generate the specific constants. 327 328 if self.value in ("False", "True"): 329 return encode_path("__builtins__.boolean.%s" % self.value) 330 elif self.value == "None": 331 return encode_path("__builtins__.none.%s" % self.value) 332 elif self.value == "NotImplemented": 333 return encode_path("__builtins__.notimplemented.%s" % self.value) 334 else: 335 return self.value 336 337 def __repr__(self): 338 return "PredefinedConstantRef(%r)" % self.value 339 340 class LogicalResult(Result): 341 342 "A logical expression result." 343 344 def _convert(self, expr): 345 346 "Return 'expr' converted to a testable value." 347 348 if isinstance(expr, LogicalResult): 349 return expr.apply_test() 350 else: 351 return "__BOOL(%s)" % expr 352 353 class NegationResult(LogicalResult): 354 355 "A negation expression result." 356 357 def __init__(self, expr): 358 self.expr = expr 359 360 def apply_test(self): 361 362 "Return the result in a form suitable for direct testing." 363 364 expr = self._convert(self.expr) 365 return "(!%s)" % expr 366 367 def discards_temporary(self, test=True): 368 369 """ 370 Negations should have discarded their operand's temporary names when 371 being instantiated. 372 """ 373 374 return None 375 376 def __str__(self): 377 return "(%s ? %s : %s)" % ( 378 self._convert(self.expr), 379 PredefinedConstantRef("False"), 380 PredefinedConstantRef("True")) 381 382 def __repr__(self): 383 return "NegationResult(%r)" % self.expr 384 385 class LogicalOperationResult(LogicalResult): 386 387 "A logical operation result." 388 389 def __init__(self, exprs, conjunction): 390 self.exprs = exprs 391 self.conjunction = conjunction 392 393 def apply_test(self): 394 395 """ 396 Return the result in a form suitable for direct testing. 397 398 Convert ... to ... 399 400 <a> and <b> 401 ((__BOOL(<a>)) && (__BOOL(<b>))) 402 403 <a> or <b> 404 ((__BOOL(<a>)) || (__BOOL(<b>))) 405 """ 406 407 results = [] 408 for expr in self.exprs: 409 results.append(self._convert(expr)) 410 411 if self.conjunction: 412 return "(%s)" % " && ".join(results) 413 else: 414 return "(%s)" % " || ".join(results) 415 416 def discards_temporary(self, test=True): 417 418 """ 419 Return a list of temporary names that can be discarded if 'test' is 420 specified as a true value (or omitted). 421 """ 422 423 if not test: 424 return None 425 426 temps = ["__tmp_result"] 427 428 for expr in self.exprs: 429 t = expr.discards_temporary(test) 430 if t: 431 temps += t 432 433 return temps 434 435 def __str__(self): 436 437 """ 438 Convert ... to ... 439 440 <a> and <b> 441 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 442 443 <a> or <b> 444 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 445 """ 446 447 results = [] 448 for expr in self.exprs[:-1]: 449 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 450 results.append(str(self.exprs[-1])) 451 452 return "(%s)" % "".join(results) 453 454 def __repr__(self): 455 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 456 457 # vim: tabstop=4 expandtab shiftwidth=4