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