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