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 144 # NOTE: Should reference a common variable for the type name. 145 146 if self.ref.get_origin() == "__builtins__.int.int": 147 return "__INTVALUE(%s)" % self.value 148 else: 149 return encode_literal_constant(self.number) 150 151 class TrLiteralSequenceRef(LiteralSequenceRef): 152 153 "A reference representing a sequence of values." 154 155 def __str__(self): 156 return str(self.node) 157 158 class TrInstanceRef(InstanceRef): 159 160 "A reference representing instantiation of a class." 161 162 def __init__(self, ref, expr): 163 164 """ 165 Initialise the reference with 'ref' indicating the nature of the 166 reference and 'expr' being an expression used to create the instance. 167 """ 168 169 InstanceRef.__init__(self, ref) 170 self.expr = expr 171 172 def __str__(self): 173 return self.expr 174 175 def __repr__(self): 176 return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr) 177 178 class AttrResult(Result, InstructionSequence): 179 180 "A translation result for an attribute access." 181 182 def __init__(self, instructions, refs, location, context_identity): 183 InstructionSequence.__init__(self, instructions) 184 self.refs = refs 185 self.location = location 186 self.context_identity = context_identity 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 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, %r)" % (self.instructions, self.refs, self.location, self.context_identity) 216 217 class AliasResult(NameRef, Result): 218 219 "An alias for other values." 220 221 def __init__(self, name_ref, refs, location): 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.location = location 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 self.location 237 238 def get_name(self): 239 ref = self.reference() 240 return ref and ref.get_name() 241 242 def get_origin(self): 243 ref = self.reference() 244 return ref and ref.get_origin() 245 246 def static(self): 247 ref = self.reference() 248 return ref and ref.static() 249 250 def final(self): 251 ref = self.reference() 252 return ref and ref.final() 253 254 def has_kind(self, kinds): 255 if not self.refs: 256 return self.name_ref.has_kind(kinds) 257 258 for ref in self.refs: 259 if ref.has_kind(kinds): 260 return True 261 262 return False 263 264 def __str__(self): 265 return str(self.name_ref) 266 267 def __repr__(self): 268 return "AliasResult(%r, %r)" % (self.name_ref, self.refs) 269 270 class InvocationResult(Result, InstructionSequence): 271 272 "A translation result for an invocation." 273 274 def __str__(self): 275 return encode_instructions(self.instructions) 276 277 def __repr__(self): 278 return "InvocationResult(%r)" % self.instructions 279 280 class InstantiationResult(InvocationResult, TrInstanceRef): 281 282 "An instantiation result acting like an invocation result." 283 284 def __init__(self, ref, instructions): 285 InstanceRef.__init__(self, ref) 286 InvocationResult.__init__(self, instructions) 287 288 def __repr__(self): 289 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 290 291 class PredefinedConstantRef(Result): 292 293 "A predefined constant reference." 294 295 def __init__(self, value, expr=None): 296 self.value = value 297 self.expr = expr 298 299 def __str__(self): 300 301 # Eliminate predefined constant assignments. 302 303 if self.expr: 304 return "" 305 306 # Generate the specific constants. 307 308 if self.value in ("False", "True"): 309 return encode_path("__builtins__.boolean.%s" % self.value) 310 elif self.value == "None": 311 return encode_path("__builtins__.none.%s" % self.value) 312 elif self.value == "NotImplemented": 313 return encode_path("__builtins__.notimplemented.%s" % self.value) 314 else: 315 return self.value 316 317 def __repr__(self): 318 return "PredefinedConstantRef(%r)" % self.value 319 320 class LogicalResult(Result): 321 322 "A logical expression result." 323 324 def _convert(self, expr): 325 326 "Return 'expr' converted to a testable value." 327 328 if isinstance(expr, LogicalResult): 329 return expr.apply_test() 330 else: 331 return "__BOOL(%s)" % expr 332 333 class NegationResult(LogicalResult): 334 335 "A negation expression result." 336 337 def __init__(self, expr): 338 self.expr = expr 339 340 def apply_test(self): 341 342 "Return the result in a form suitable for direct testing." 343 344 expr = self._convert(self.expr) 345 return "(!%s)" % expr 346 347 def discards_temporary(self, test=True): 348 349 """ 350 Negations should have discarded their operand's temporary names when 351 being instantiated. 352 """ 353 354 return None 355 356 def __str__(self): 357 return "(%s ? %s : %s)" % ( 358 self._convert(self.expr), 359 PredefinedConstantRef("False"), 360 PredefinedConstantRef("True")) 361 362 def __repr__(self): 363 return "NegationResult(%r)" % self.expr 364 365 class LogicalOperationResult(LogicalResult): 366 367 "A logical operation result." 368 369 def __init__(self, exprs, conjunction): 370 self.exprs = exprs 371 self.conjunction = conjunction 372 373 def apply_test(self): 374 375 """ 376 Return the result in a form suitable for direct testing. 377 378 Convert ... to ... 379 380 <a> and <b> 381 ((__BOOL(<a>)) && (__BOOL(<b>))) 382 383 <a> or <b> 384 ((__BOOL(<a>)) || (__BOOL(<b>))) 385 """ 386 387 results = [] 388 for expr in self.exprs: 389 results.append(self._convert(expr)) 390 391 if self.conjunction: 392 return "(%s)" % " && ".join(results) 393 else: 394 return "(%s)" % " || ".join(results) 395 396 def discards_temporary(self, test=True): 397 398 """ 399 Return a list of temporary names that can be discarded if 'test' is 400 specified as a true value (or omitted). 401 """ 402 403 if not test: 404 return None 405 406 temps = ["__tmp_result"] 407 408 for expr in self.exprs: 409 t = expr.discards_temporary(test) 410 if t: 411 temps += t 412 413 return temps 414 415 def __str__(self): 416 417 """ 418 Convert ... to ... 419 420 <a> and <b> 421 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 422 423 <a> or <b> 424 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 425 """ 426 427 results = [] 428 for expr in self.exprs[:-1]: 429 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 430 results.append(str(self.exprs[-1])) 431 432 return "(%s)" % "".join(results) 433 434 def __repr__(self): 435 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 436 437 # vim: tabstop=4 expandtab shiftwidth=4