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