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