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