Lichen

Annotated transresults.py

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