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 context_identity_verified, accessor_test): 183 InstructionSequence.__init__(self, instructions) 184 self.refs = refs 185 self.location = location 186 self.context_identity = context_identity 187 self.context_identity_verified = context_identity_verified 188 self.accessor_test = accessor_test 189 190 def references(self): 191 return self.refs 192 193 def access_location(self): 194 return self.location 195 196 def context(self): 197 return self.context_identity 198 199 def context_verified(self): 200 return self.context_identity_verified and self.context() or None 201 202 def tests_accessor(self): 203 return self.accessor_test 204 205 def get_origin(self): 206 return self.refs and len(self.refs) == 1 and first(self.refs).get_origin() 207 208 def has_kind(self, kinds): 209 if not self.refs: 210 return False 211 for ref in self.refs: 212 if ref.has_kind(kinds): 213 return True 214 return False 215 216 def __nonzero__(self): 217 return bool(self.instructions) 218 219 def __str__(self): 220 return encode_instructions(self.instructions) 221 222 def __repr__(self): 223 return "AttrResult(%r, %r, %r, %r, %r)" % (self.instructions, self.refs, 224 self.location, self.context_identity, self.accessor_test) 225 226 class AliasResult(NameRef, Result): 227 228 "An alias for other values." 229 230 def __init__(self, name_ref, refs, location): 231 NameRef.__init__(self, name_ref.name, is_global=name_ref.is_global_name()) 232 self.name_ref = name_ref 233 self.refs = refs 234 self.location = location 235 236 def references(self): 237 ref = self.name_ref.reference() 238 return self.refs or ref and [ref] or None 239 240 def reference(self): 241 refs = self.references() 242 return len(refs) == 1 and first(refs) or None 243 244 def access_location(self): 245 return self.location 246 247 def get_name(self): 248 ref = self.reference() 249 return ref and ref.get_name() 250 251 def get_origin(self): 252 ref = self.reference() 253 return ref and ref.get_origin() 254 255 def static(self): 256 ref = self.reference() 257 return ref and ref.static() 258 259 def final(self): 260 ref = self.reference() 261 return ref and ref.final() 262 263 def has_kind(self, kinds): 264 if not self.refs: 265 return self.name_ref.has_kind(kinds) 266 267 for ref in self.refs: 268 if ref.has_kind(kinds): 269 return True 270 271 return False 272 273 def __str__(self): 274 return str(self.name_ref) 275 276 def __repr__(self): 277 return "AliasResult(%r, %r)" % (self.name_ref, self.refs) 278 279 class InvocationResult(Result, InstructionSequence): 280 281 "A translation result for an invocation." 282 283 def __str__(self): 284 return encode_instructions(self.instructions) 285 286 def __repr__(self): 287 return "InvocationResult(%r)" % self.instructions 288 289 class InstantiationResult(InvocationResult, TrInstanceRef): 290 291 "An instantiation result acting like an invocation result." 292 293 def __init__(self, ref, instructions): 294 InstanceRef.__init__(self, ref) 295 InvocationResult.__init__(self, instructions) 296 297 def __repr__(self): 298 return "InstantiationResult(%r, %r)" % (self.ref, self.instructions) 299 300 class PredefinedConstantRef(Result): 301 302 "A predefined constant reference." 303 304 def __init__(self, value, expr=None): 305 self.value = value 306 self.expr = expr 307 308 def __str__(self): 309 310 # Eliminate predefined constant assignments. 311 312 if self.expr: 313 return "" 314 315 # Generate the specific constants. 316 317 if self.value in ("False", "True"): 318 return encode_path("__builtins__.boolean.%s" % self.value) 319 elif self.value == "None": 320 return encode_path("__builtins__.none.%s" % self.value) 321 elif self.value == "NotImplemented": 322 return encode_path("__builtins__.notimplemented.%s" % self.value) 323 else: 324 return self.value 325 326 def __repr__(self): 327 return "PredefinedConstantRef(%r)" % self.value 328 329 class LogicalResult(Result): 330 331 "A logical expression result." 332 333 def _convert(self, expr): 334 335 "Return 'expr' converted to a testable value." 336 337 if isinstance(expr, LogicalResult): 338 return expr.apply_test() 339 else: 340 return "__BOOL(%s)" % expr 341 342 class NegationResult(LogicalResult): 343 344 "A negation expression result." 345 346 def __init__(self, expr): 347 self.expr = expr 348 349 def apply_test(self): 350 351 "Return the result in a form suitable for direct testing." 352 353 expr = self._convert(self.expr) 354 return "(!%s)" % expr 355 356 def discards_temporary(self, test=True): 357 358 """ 359 Negations should have discarded their operand's temporary names when 360 being instantiated. 361 """ 362 363 return None 364 365 def __str__(self): 366 return "(%s ? %s : %s)" % ( 367 self._convert(self.expr), 368 PredefinedConstantRef("False"), 369 PredefinedConstantRef("True")) 370 371 def __repr__(self): 372 return "NegationResult(%r)" % self.expr 373 374 class LogicalOperationResult(LogicalResult): 375 376 "A logical operation result." 377 378 def __init__(self, exprs, conjunction): 379 self.exprs = exprs 380 self.conjunction = conjunction 381 382 def apply_test(self): 383 384 """ 385 Return the result in a form suitable for direct testing. 386 387 Convert ... to ... 388 389 <a> and <b> 390 ((__BOOL(<a>)) && (__BOOL(<b>))) 391 392 <a> or <b> 393 ((__BOOL(<a>)) || (__BOOL(<b>))) 394 """ 395 396 results = [] 397 for expr in self.exprs: 398 results.append(self._convert(expr)) 399 400 if self.conjunction: 401 return "(%s)" % " && ".join(results) 402 else: 403 return "(%s)" % " || ".join(results) 404 405 def discards_temporary(self, test=True): 406 407 """ 408 Return a list of temporary names that can be discarded if 'test' is 409 specified as a true value (or omitted). 410 """ 411 412 if not test: 413 return None 414 415 temps = ["__tmp_result"] 416 417 for expr in self.exprs: 418 t = expr.discards_temporary(test) 419 if t: 420 temps += t 421 422 return temps 423 424 def __str__(self): 425 426 """ 427 Convert ... to ... 428 429 <a> and <b> 430 (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b> 431 432 <a> or <b> 433 (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b> 434 """ 435 436 results = [] 437 for expr in self.exprs[:-1]: 438 results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or "")) 439 results.append(str(self.exprs[-1])) 440 441 return "(%s)" % "".join(results) 442 443 def __repr__(self): 444 return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction) 445 446 # vim: tabstop=4 expandtab shiftwidth=4