Lichen

Annotated transresults.py

639:93254c89358b
2017-02-28 Paul Boddie Added testing of temporary result variable availability.
paul@636 1
#!/usr/bin/env python
paul@636 2
paul@636 3
"""
paul@636 4
Translation result abstractions.
paul@636 5
paul@636 6
Copyright (C) 2016, 2017 Paul Boddie <paul@boddie.org.uk>
paul@636 7
paul@636 8
This program is free software; you can redistribute it and/or modify it under
paul@636 9
the terms of the GNU General Public License as published by the Free Software
paul@636 10
Foundation; either version 3 of the License, or (at your option) any later
paul@636 11
version.
paul@636 12
paul@636 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@636 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@636 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@636 16
details.
paul@636 17
paul@636 18
You should have received a copy of the GNU General Public License along with
paul@636 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@636 20
"""
paul@636 21
paul@636 22
from common import first, InstructionSequence
paul@636 23
from encoders import encode_instructions, encode_literal_constant, encode_path
paul@636 24
from results import ConstantValueRef, InstanceRef, LiteralSequenceRef, \
paul@636 25
                    ResolvedNameRef, Result
paul@636 26
paul@636 27
# Classes representing intermediate translation results.
paul@636 28
paul@636 29
class ReturnRef:
paul@636 30
paul@636 31
    "Indicates usage of a return statement."
paul@636 32
paul@636 33
    pass
paul@636 34
paul@636 35
class Expression(Result):
paul@636 36
paul@636 37
    "A general expression."
paul@636 38
paul@636 39
    def __init__(self, s):
paul@637 40
        if isinstance(s, Result):
paul@637 41
            self.s = str(s)
paul@637 42
            self.expr = s
paul@637 43
        else:
paul@637 44
            self.s = s
paul@637 45
            self.expr = None
paul@637 46
paul@637 47
    def discards_temporary(self, test=True):
paul@637 48
paul@637 49
        """
paul@637 50
        Return a list of temporary names that can be discarded if 'test' is
paul@637 51
        specified as a true value (or omitted).
paul@637 52
        """
paul@637 53
paul@637 54
        return self.expr and self.expr.discards_temporary(False)
paul@637 55
paul@636 56
    def __str__(self):
paul@636 57
        return self.s
paul@637 58
paul@636 59
    def __repr__(self):
paul@636 60
        return "Expression(%r)" % self.s
paul@636 61
paul@636 62
class TrResolvedNameRef(ResolvedNameRef):
paul@636 63
paul@636 64
    "A reference to a name in the translation."
paul@636 65
paul@636 66
    def __init__(self, name, ref, expr=None, is_global=False, parameter=None, location=None):
paul@636 67
        ResolvedNameRef.__init__(self, name, ref, expr, is_global)
paul@636 68
        self.parameter = parameter
paul@636 69
        self.location = location
paul@636 70
paul@636 71
    def access_location(self):
paul@636 72
        return self.location
paul@636 73
paul@636 74
    def __str__(self):
paul@636 75
paul@636 76
        "Return an output representation of the referenced name."
paul@636 77
paul@636 78
        # For sources, any identified static origin will be constant and thus
paul@636 79
        # usable directly. For targets, no constant should be assigned and thus
paul@636 80
        # the alias (or any plain name) will be used.
paul@636 81
paul@636 82
        ref = self.static()
paul@636 83
        origin = ref and self.get_origin()
paul@636 84
        static_name = origin and encode_path(origin)
paul@636 85
paul@636 86
        # Determine whether a qualified name is involved.
paul@636 87
paul@636 88
        t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1)
paul@636 89
        parent = len(t) > 1 and t[0] or None
paul@636 90
        attrname = t[-1] and encode_path(t[-1])
paul@636 91
paul@636 92
        # Assignments.
paul@636 93
paul@636 94
        if self.expr:
paul@636 95
paul@636 96
            # Eliminate assignments between constants.
paul@636 97
paul@636 98
            if ref and isinstance(self.expr, ResolvedNameRef) and self.expr.static():
paul@636 99
                return ""
paul@636 100
paul@636 101
            # Qualified names must be converted into parent-relative assignments.
paul@636 102
paul@636 103
            elif parent:
paul@636 104
                return "__store_via_object(&%s, %s, %s)" % (
paul@636 105
                    encode_path(parent), attrname, self.expr)
paul@636 106
paul@636 107
            # All other assignments involve the names as they were given.
paul@636 108
paul@636 109
            else:
paul@636 110
                return "(%s%s) = %s" % (self.parameter and "*" or "", attrname, self.expr)
paul@636 111
paul@636 112
        # Expressions.
paul@636 113
paul@636 114
        elif static_name:
paul@636 115
            parent = ref.parent()
paul@636 116
            context = ref.has_kind("<function>") and encode_path(parent) or None
paul@636 117
            return "__ATTRVALUE(&%s)" % static_name
paul@636 118
paul@636 119
        # Qualified names must be converted into parent-relative accesses.
paul@636 120
paul@636 121
        elif parent:
paul@636 122
            return "__load_via_object(&%s, %s)" % (
paul@636 123
                encode_path(parent), attrname)
paul@636 124
paul@636 125
        # All other accesses involve the names as they were given.
paul@636 126
paul@636 127
        else:
paul@636 128
            return "(%s%s)" % (self.parameter and "*" or "", attrname)
paul@636 129
paul@636 130
class TrConstantValueRef(ConstantValueRef):
paul@636 131
paul@636 132
    "A constant value reference in the translation."
paul@636 133
paul@636 134
    def __str__(self):
paul@636 135
        return encode_literal_constant(self.number)
paul@636 136
paul@636 137
class TrLiteralSequenceRef(LiteralSequenceRef):
paul@636 138
paul@636 139
    "A reference representing a sequence of values."
paul@636 140
paul@636 141
    def __str__(self):
paul@636 142
        return str(self.node)
paul@636 143
paul@636 144
class TrInstanceRef(InstanceRef):
paul@636 145
paul@636 146
    "A reference representing instantiation of a class."
paul@636 147
paul@636 148
    def __init__(self, ref, expr):
paul@636 149
paul@636 150
        """
paul@636 151
        Initialise the reference with 'ref' indicating the nature of the
paul@636 152
        reference and 'expr' being an expression used to create the instance.
paul@636 153
        """
paul@636 154
paul@636 155
        InstanceRef.__init__(self, ref)
paul@636 156
        self.expr = expr
paul@636 157
paul@636 158
    def __str__(self):
paul@636 159
        return self.expr
paul@636 160
paul@636 161
    def __repr__(self):
paul@636 162
        return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr)
paul@636 163
paul@636 164
class AttrResult(Result, InstructionSequence):
paul@636 165
paul@636 166
    "A translation result for an attribute access."
paul@636 167
paul@636 168
    def __init__(self, instructions, refs, location, context_identity):
paul@636 169
        InstructionSequence.__init__(self, instructions)
paul@636 170
        self.refs = refs
paul@636 171
        self.location = location
paul@636 172
        self.context_identity = context_identity
paul@636 173
paul@636 174
    def references(self):
paul@636 175
        return self.refs
paul@636 176
paul@636 177
    def access_location(self):
paul@636 178
        return self.location
paul@636 179
paul@636 180
    def context(self):
paul@636 181
        return self.context_identity
paul@636 182
paul@636 183
    def get_origin(self):
paul@636 184
        return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
paul@636 185
paul@636 186
    def has_kind(self, kinds):
paul@636 187
        if not self.refs:
paul@636 188
            return False
paul@636 189
        for ref in self.refs:
paul@636 190
            if ref.has_kind(kinds):
paul@636 191
                return True
paul@636 192
        return False
paul@636 193
paul@636 194
    def __nonzero__(self):
paul@636 195
        return bool(self.instructions)
paul@636 196
paul@636 197
    def __str__(self):
paul@636 198
        return encode_instructions(self.instructions)
paul@636 199
paul@636 200
    def __repr__(self):
paul@636 201
        return "AttrResult(%r, %r, %r)" % (self.instructions, self.refs, self.location)
paul@636 202
paul@636 203
class InvocationResult(Result, InstructionSequence):
paul@636 204
paul@636 205
    "A translation result for an invocation."
paul@636 206
paul@636 207
    def __str__(self):
paul@636 208
        return encode_instructions(self.instructions)
paul@636 209
paul@636 210
    def __repr__(self):
paul@636 211
        return "InvocationResult(%r)" % self.instructions
paul@636 212
paul@636 213
class InstantiationResult(InvocationResult, TrInstanceRef):
paul@636 214
paul@636 215
    "An instantiation result acting like an invocation result."
paul@636 216
paul@636 217
    def __init__(self, ref, instructions):
paul@636 218
        InstanceRef.__init__(self, ref)
paul@636 219
        InvocationResult.__init__(self, instructions)
paul@636 220
paul@636 221
    def __repr__(self):
paul@636 222
        return "InstantiationResult(%r, %r)" % (self.ref, self.instructions)
paul@636 223
paul@636 224
class PredefinedConstantRef(Result):
paul@636 225
paul@636 226
    "A predefined constant reference."
paul@636 227
paul@636 228
    def __init__(self, value, expr=None):
paul@636 229
        self.value = value
paul@636 230
        self.expr = expr
paul@636 231
paul@636 232
    def __str__(self):
paul@636 233
paul@636 234
        # Eliminate predefined constant assignments.
paul@636 235
paul@636 236
        if self.expr:
paul@636 237
            return ""
paul@636 238
paul@636 239
        # Generate the specific constants.
paul@636 240
paul@636 241
        if self.value in ("False", "True"):
paul@636 242
            return encode_path("__builtins__.boolean.%s" % self.value)
paul@636 243
        elif self.value == "None":
paul@636 244
            return encode_path("__builtins__.none.%s" % self.value)
paul@636 245
        elif self.value == "NotImplemented":
paul@636 246
            return encode_path("__builtins__.notimplemented.%s" % self.value)
paul@636 247
        else:
paul@636 248
            return self.value
paul@636 249
paul@636 250
    def __repr__(self):
paul@636 251
        return "PredefinedConstantRef(%r)" % self.value
paul@636 252
paul@636 253
class LogicalResult(Result):
paul@636 254
paul@636 255
    "A logical expression result."
paul@636 256
paul@636 257
    def _convert(self, expr):
paul@636 258
paul@636 259
        "Return 'expr' converted to a testable value."
paul@636 260
paul@636 261
        if isinstance(expr, LogicalResult):
paul@636 262
            return expr.apply_test()
paul@636 263
        else:
paul@636 264
            return "__BOOL(%s)" % expr
paul@636 265
paul@636 266
class NegationResult(LogicalResult):
paul@636 267
paul@636 268
    "A negation expression result."
paul@636 269
paul@636 270
    def __init__(self, expr):
paul@636 271
        self.expr = expr
paul@636 272
paul@636 273
    def apply_test(self):
paul@636 274
paul@636 275
        "Return the result in a form suitable for direct testing."
paul@636 276
paul@636 277
        expr = self._convert(self.expr)
paul@636 278
        return "(!%s)" % expr
paul@636 279
paul@637 280
    def discards_temporary(self, test=True):
paul@637 281
paul@637 282
        """
paul@638 283
        Negations should have discarded their operand's temporary names when
paul@638 284
        being instantiated.
paul@637 285
        """
paul@637 286
paul@638 287
        return None
paul@637 288
paul@636 289
    def __str__(self):
paul@636 290
        return "(%s ? %s : %s)" % (
paul@636 291
            self._convert(self.expr),
paul@636 292
            PredefinedConstantRef("False"),
paul@636 293
            PredefinedConstantRef("True"))
paul@636 294
paul@636 295
    def __repr__(self):
paul@636 296
        return "NegationResult(%r)" % self.expr
paul@636 297
paul@636 298
class LogicalOperationResult(LogicalResult):
paul@636 299
paul@636 300
    "A logical operation result."
paul@636 301
paul@636 302
    def __init__(self, exprs, conjunction):
paul@636 303
        self.exprs = exprs
paul@636 304
        self.conjunction = conjunction
paul@636 305
paul@636 306
    def apply_test(self):
paul@636 307
paul@636 308
        """
paul@636 309
        Return the result in a form suitable for direct testing.
paul@636 310
paul@636 311
        Convert ... to ...
paul@636 312
paul@636 313
        <a> and <b>
paul@636 314
        ((__BOOL(<a>)) && (__BOOL(<b>)))
paul@636 315
paul@636 316
        <a> or <b>
paul@636 317
        ((__BOOL(<a>)) || (__BOOL(<b>)))
paul@636 318
        """
paul@636 319
paul@636 320
        results = []
paul@636 321
        for expr in self.exprs:
paul@636 322
            results.append(self._convert(expr))
paul@636 323
paul@636 324
        if self.conjunction:
paul@636 325
            return "(%s)" % " && ".join(results)
paul@636 326
        else:
paul@636 327
            return "(%s)" % " || ".join(results)
paul@636 328
paul@637 329
    def discards_temporary(self, test=True):
paul@637 330
paul@637 331
        """
paul@637 332
        Return a list of temporary names that can be discarded if 'test' is
paul@637 333
        specified as a true value (or omitted).
paul@637 334
        """
paul@637 335
paul@637 336
        if not test:
paul@637 337
            return None
paul@637 338
paul@637 339
        temps = ["__tmp_result"]
paul@637 340
paul@637 341
        for expr in self.exprs:
paul@637 342
            t = expr.discards_temporary(test)
paul@637 343
            if t:
paul@637 344
                temps += t
paul@637 345
paul@637 346
        return temps
paul@637 347
paul@636 348
    def __str__(self):
paul@636 349
paul@636 350
        """
paul@636 351
        Convert ... to ...
paul@636 352
paul@636 353
        <a> and <b>
paul@636 354
        (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b>
paul@636 355
paul@636 356
        <a> or <b>
paul@636 357
        (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b>
paul@636 358
        """
paul@636 359
paul@636 360
        results = []
paul@636 361
        for expr in self.exprs[:-1]:
paul@636 362
            results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, self.conjunction and "!" or ""))
paul@636 363
        results.append(str(self.exprs[-1]))
paul@636 364
paul@636 365
        return "(%s)" % "".join(results)
paul@636 366
paul@636 367
    def __repr__(self):
paul@636 368
        return "LogicalOperationResult(%r, %r)" % (self.exprs, self.conjunction)
paul@636 369
paul@636 370
# vim: tabstop=4 expandtab shiftwidth=4