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