Lichen

Annotated transresults.py

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