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