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