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 __str__(self): 75 76 "Return an output representation of the referenced name." 77 78 # Temporary names are output program locals. 79 80 if self.name.startswith("$t"): 81 if self.expr: 82 return "%s = %s" % (encode_path(self.name), self.expr) 83 else: 84 return encode_path(self.name) 85 86 # For sources, any identified static origin will be constant and thus 87 # usable directly. For targets, no constant should be assigned and thus 88 # the alias (or any plain name) will be used. 89 90 ref = self.static() 91 origin = ref and self.get_origin() 92 static_name = origin and encode_path(origin) 93 94 # Determine whether a qualified name is involved. 95 96 t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1) 97 parent = len(t) > 1 and t[0] or None 98 attrname = t[-1] and encode_path(t[-1]) 99 100 # Assignments. 101 102 if self.expr: 103 104 # Eliminate assignments between constants. 105 106 if ref and self.expr.static(): 107 return "" 108 109 # Qualified names must be converted into parent-relative assignments. 110 111 elif parent: 112 return "__store_via_object(&%s, %s, %s)" % ( 113 encode_path(parent), attrname, self.expr) 114 115 # All other assignments involve the names as they were given. 116 117 else: 118 return "(%s%s) = %s" % (self.parameter and "*" or "", attrname, self.expr) 119 120 # Expressions. 121 122 elif static_name: 123 parent = ref.parent() 124 context = ref.has_kind("<function>") and encode_path(parent) or None 125 return "__ATTRVALUE(&%s)" % static_name 126 127 # Qualified names must be converted into parent-relative accesses. 128 129 elif parent: 130 return "__load_via_object(&%s, %s)" % ( 131 encode_path(parent), attrname) 132 133 # All other accesses involve the names as they were given. 134 135 else: 136 return "(%s%s)" % (self.parameter and "*" or "", attrname) 137 138 class TrConstantValueRef(ConstantValueRef): 139 140 "A constant value reference in the translation." 141 142 def __str__(self): 143 return encode_literal_constant(self.number) 144 145 class TrLiteralSequenceRef(LiteralSequenceRef): 146 147 "A reference representing a sequence of values." 148 149 def __str__(self): 150 return str(self.node) 151 152 class TrInstanceRef(InstanceRef): 153 154 "A reference representing instantiation of a class." 155 156 def __init__(self, ref, expr): 157 158 """ 159 Initialise the reference with 'ref' indicating the nature of the 160 reference and 'expr' being an expression used to create the instance. 161 """ 162 163 InstanceRef.__init__(self, ref) 164 self.expr = expr 165 166 def __str__(self): 167 return self.expr 168 169 def __repr__(self): 170 return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr) 171 172 class AttrResult(Result, InstructionSequence): 173 174 "A translation result for an attribute access." 175 176 def __init__(self, instructions, refs, location, context_identity): 177 InstructionSequence.__init__(self, instructions) 178 self.refs = refs 179 self.location = location 180 self.context_identity = context_identity 181 182 def references(self): 183 return self.refs 184 185 def access_location(self): 186 return self.location 187 188 def context(self): 189 return self.context_identity 190 191 def get_origin(self): 192 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 193 194 def has_kind(self, kinds): 195 if not self.refs: 196 return False 197 for ref in self.refs: 198 if ref.has_kind(kinds): 199 return True 200 return False 201 202 def __nonzero__(self): 203 return bool(self.instructions) 204 205 def __str__(self): 206 return encode_instructions(self.instructions) 207 208 def __repr__(self): 209 return "AttrResult(%r, %r, %r, %r)" % (self.instructions, self.refs, self.location, self.context_identity) 210 211 class AliasResult(NameRef, Result): 212 213 "An alias for other values." 214 215 def __init__(self, name_ref, refs, location): 216 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 217 self.name_ref = name_ref 218 self.refs = refs 219 self.location = location 220 221 def references(self): 222 ref = self.name_ref.reference() 223 return self.refs or ref and [ref] or None 224 225 def reference(self): 226 refs = self.references() 227 return len(refs) == 1 and first(refs) or None 228 229 def access_location(self): 230 return self.location 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