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