1 #!/usr/bin/env python 2 3 """ 4 Translation result abstractions. 5 6 Copyright (C) 2016, 2017 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, context_identity_verified): 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 188 def references(self): 189 return self.refs 190 191 def access_location(self): 192 return self.location 193 194 def context(self): 195 return self.context_identity 196 197 def context_verified(self): 198 return self.context_identity_verified and self.context() or None 199 200 def get_origin(self): 201 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 202 203 def has_kind(self, kinds): 204 if not self.refs: 205 return False 206 for ref in self.refs: 207 if ref.has_kind(kinds): 208 return True 209 return False 210 211 def __nonzero__(self): 212 return bool(self.instructions) 213 214 def __str__(self): 215 return encode_instructions(self.instructions) 216 217 def __repr__(self): 218 return "AttrResult(%r, %r, %r, %r)" % (self.instructions, self.refs, self.location, self.context_identity) 219 220 class AliasResult(NameRef, Result): 221 222 "An alias for other values." 223 224 def __init__(self, name_ref, refs, location): 225 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 226 self.name_ref = name_ref 227 self.refs = refs 228 self.location = location 229 230 def references(self): 231 ref = self.name_ref.reference() 232 return self.refs or ref and [ref] or None 233 234 def reference(self): 235 refs = self.references() 236 return len(refs) == 1 and first(refs) or None 237 238 def access_location(self): 239 return self.location 240 241 def get_name(self): 242 ref = self.reference() 243 return ref and ref.get_name() 244 245 def get_origin(self): 246 ref = self.reference() 247 return ref and ref.get_origin() 248 249 def static(self): 250 ref = self.reference() 251 return ref and ref.static() 252 253 def final(self): 254 ref = self.reference() 255 return ref and ref.final() 256 257 def has_kind(self, kinds): 258 if not self.refs: 259 return self.name_ref.has_kind(kinds) 260 261 for ref in self.refs: 262 if ref.has_kind(kinds): 263 return True 264 265 return False 266 267 def __str__(self): 268 return str(self.name_ref) 269 270 def __repr__(self): 271 return "AliasResult(%r, %r)" % (self.name_ref, self.refs) 272 273 class InvocationResult(Result, InstructionSequence): 274 275 "A translation result for an invocation." 276 277 def __str__(self): 278 return encode_instructions(self.instructions) 279 280 def __repr__(self): 281 return "InvocationResult(%r)" % self.instructions 282 283 class InstantiationResult(InvocationResult, TrInstanceRef): 284 285 "An instantiation result acting like an invocation result." 286 287 def __init__(self, ref, instructions): 288 InstanceRef.__init__(self, ref) 289 InvocationResult.__init__(self, instructions) 290 291 def __repr__(self): 292 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 293 294 class PredefinedConstantRef(Result): 295 296 "A predefined constant reference." 297 298 def __init__(self, value, expr=None): 299 self.value = value 300 self.expr = expr 301 302 def __str__(self): 303 304 # Eliminate predefined constant assignments. 305 306 if self.expr: 307 return "" 308 309 # Generate the specific constants. 310 311 if self.value in ("False", "True"): 312 return encode_path("__builtins__.boolean.%s" % self.value) 313 elif self.value == "None": 314 return encode_path("__builtins__.none.%s" % self.value) 315 elif self.value == "NotImplemented": 316 return encode_path("__builtins__.notimplemented.%s" % self.value) 317 else: 318 return self.value 319 320 def __repr__(self): 321 return "PredefinedConstantRef(%r)" % self.value 322 323 class LogicalResult(Result): 324 325 "A logical expression result." 326 327 def _convert(self, expr): 328 329 "Return 'expr' converted to a testable value." 330 331 if isinstance(expr, LogicalResult): 332 return expr.apply_test() 333 else: 334 return "__BOOL(%s)" % expr 335 336 class NegationResult(LogicalResult): 337 338 "A negation expression result." 339 340 def __init__(self, expr): 341 self.expr = expr 342 343 def apply_test(self): 344 345 "Return the result in a form suitable for direct testing." 346 347 expr = self._convert(self.expr) 348 return "(!%s)" % expr 349 350 def discards_temporary(self, test=True): 351 352 """ 353 Negations should have discarded their operand's temporary names when 354 being instantiated. 355 """ 356 357 return None 358 359 def __str__(self): 360 return "(%s ? %s : %s)" % ( 361 self._convert(self.expr), 362 PredefinedConstantRef("False"), 363 PredefinedConstantRef("True")) 364 365 def __repr__(self): 366 return "NegationResult(%r)" % self.expr 367 368 class LogicalOperationResult(LogicalResult): 369 370 "A logical operation result." 371 372 def __init__(self, exprs, conjunction): 373 self.exprs = exprs 374 self.conjunction = conjunction 375 376 def apply_test(self): 377 378 """ 379 Return the result in a form suitable for direct testing. 380 381 Convert ... to ... 382 383 <a> and <b> 384 ((__BOOL(<a>)) && (__BOOL(<b>))) 385 386 <a> or <b> 387 ((__BOOL(<a>)) || (__BOOL(<b>))) 388 """ 389 390 results = [] 391 for expr in self.exprs: 392 results.append(self._convert(expr)) 393 394 if self.conjunction: 395 return "(%s)" % " && ".join(results) 396 else: 397 return "(%s)" % " || ".join(results) 398 399 def discards_temporary(self, test=True): 400 401 """ 402 Return a list of temporary names that can be discarded if 'test' is 403 specified as a true value (or omitted). 404 """ 405 406 if not test: 407 return None 408 409 temps = ["__tmp_result"] 410 411 for expr in self.exprs: 412 t = expr.discards_temporary(test) 413 if t: 414 temps += t 415 416 return temps 417 418 def __str__(self): 419 420 """ 421 Convert ... to ... 422 423 <a> and <b> 424 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 425 426 <a> or <b> 427 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 428 """ 429 430 results = [] 431 for expr in self.exprs[:-1]: 432 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 433 results.append(str(self.exprs[-1])) 434 435 return "(%s)" % "".join(results) 436 437 def __repr__(self): 438 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 439 440 # vim: tabstop=4 expandtab shiftwidth=4