Lichen

Annotated transresults.py

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