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