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