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, parameter=None, location=None): 67 ResolvedNameRef.__init__(self, name, ref, expr, is_global) 68 self.parameter = parameter 69 self.location = location 70 71 def access_location(self): 72 return self.location 73 74 def access_locations(self): 75 return self.location and [self.location] 76 77 def __str__(self): 78 79 "Return an output representation of the referenced name." 80 81 # Temporary names are output program locals. 82 83 if self.name.startswith("$t"): 84 if self.expr: 85 return "%s = %s" % (encode_path(self.name), self.expr) 86 else: 87 return encode_path(self.name) 88 89 # For sources, any identified static origin will be constant and thus 90 # usable directly. For targets, no constant should be assigned and thus 91 # the alias (or any plain name) will be used. 92 93 ref = self.static() 94 origin = ref and self.get_origin() 95 static_name = origin and encode_path(origin) 96 97 # Determine whether a qualified name is involved. 98 99 t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1) 100 parent = len(t) > 1 and t[0] or None 101 attrname = t[-1] and encode_path(t[-1]) 102 103 # Assignments. 104 105 if self.expr: 106 107 # Eliminate assignments between constants. 108 109 if ref and self.expr.static(): 110 return "" 111 112 # Qualified names must be converted into parent-relative assignments. 113 114 elif parent: 115 return "__store_via_object(&%s, %s, %s)" % ( 116 encode_path(parent), attrname, self.expr) 117 118 # All other assignments involve the names as they were given. 119 120 else: 121 return "(%s%s) = %s" % (self.parameter and "*" or "", attrname, self.expr) 122 123 # Expressions. 124 125 elif static_name: 126 parent = ref.parent() 127 context = ref.has_kind("<function>") and encode_path(parent) or None 128 return "__ATTRVALUE(&%s)" % static_name 129 130 # Qualified names must be converted into parent-relative accesses. 131 132 elif parent: 133 return "__load_via_object(&%s, %s)" % ( 134 encode_path(parent), attrname) 135 136 # All other accesses involve the names as they were given. 137 138 else: 139 return "(%s%s)" % (self.parameter and "*" or "", attrname) 140 141 class TrConstantValueRef(ConstantValueRef): 142 143 "A constant value reference in the translation." 144 145 def __str__(self): 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 InstructionSequence.__init__(self, instructions) 181 self.refs = refs 182 self.location = location 183 self.context_identity = context_identity 184 185 def references(self): 186 return self.refs 187 188 def access_location(self): 189 return self.location 190 191 def access_locations(self): 192 return self.location and [self.location] 193 194 def context(self): 195 return self.context_identity 196 197 def get_origin(self): 198 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 199 200 def has_kind(self, kinds): 201 if not self.refs: 202 return False 203 for ref in self.refs: 204 if ref.has_kind(kinds): 205 return True 206 return False 207 208 def __nonzero__(self): 209 return bool(self.instructions) 210 211 def __str__(self): 212 return encode_instructions(self.instructions) 213 214 def __repr__(self): 215 return "AttrResult(%r, %r, %r)" % (self.instructions, self.refs, self.location) 216 217 class AliasResult(NameRef, Result): 218 219 "An alias for other values." 220 221 def __init__(self, name_ref, refs, locations): 222 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 223 self.name_ref = name_ref 224 self.refs = refs 225 self.locations = locations 226 227 def references(self): 228 ref = self.name_ref.reference() 229 return self.refs or ref and [ref] or None 230 231 def reference(self): 232 refs = self.references() 233 return len(refs) == 1 and first(refs) or None 234 235 def access_location(self): 236 return len(self.locations) == 1 and first(self.locations) or None 237 238 def access_locations(self): 239 return self.locations 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