Lichen

Annotated translator.py

211:b9e7f0aae13c
2016-11-22 Paul Boddie Introduced specific instance size constants for functions employing defaults. Removed superfluous structure size parameters from various method signatures.
paul@113 1
#!/usr/bin/env python
paul@113 2
paul@113 3
"""
paul@113 4
Translate programs.
paul@113 5
paul@113 6
Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk>
paul@113 7
paul@113 8
This program is free software; you can redistribute it and/or modify it under
paul@113 9
the terms of the GNU General Public License as published by the Free Software
paul@113 10
Foundation; either version 3 of the License, or (at your option) any later
paul@113 11
version.
paul@113 12
paul@113 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@113 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@113 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@113 16
details.
paul@113 17
paul@113 18
You should have received a copy of the GNU General Public License along with
paul@113 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@113 20
"""
paul@113 21
paul@113 22
from common import *
paul@113 23
from encoders import *
paul@113 24
from os.path import exists, join
paul@113 25
from os import makedirs
paul@113 26
import compiler
paul@113 27
import results
paul@113 28
paul@113 29
class Translator(CommonOutput):
paul@113 30
paul@113 31
    "A program translator."
paul@113 32
paul@113 33
    def __init__(self, importer, deducer, optimiser, output):
paul@113 34
        self.importer = importer
paul@113 35
        self.deducer = deducer
paul@113 36
        self.optimiser = optimiser
paul@113 37
        self.output = output
paul@113 38
paul@113 39
    def to_output(self):
paul@113 40
        output = join(self.output, "src")
paul@113 41
paul@113 42
        if not exists(output):
paul@113 43
            makedirs(output)
paul@113 44
paul@113 45
        self.check_output()
paul@113 46
paul@113 47
        for module in self.importer.modules.values():
paul@161 48
            if module.name != "native":
paul@161 49
                tm = TranslatedModule(module.name, self.importer, self.deducer, self.optimiser)
paul@161 50
                tm.translate(module.filename, join(output, "%s.c" % module.name))
paul@113 51
paul@113 52
# Classes representing intermediate translation results.
paul@113 53
paul@113 54
class TranslationResult:
paul@113 55
paul@113 56
    "An abstract translation result mix-in."
paul@113 57
paul@113 58
    pass
paul@113 59
paul@144 60
class ReturnRef(TranslationResult):
paul@144 61
paul@144 62
    "Indicates usage of a return statement."
paul@144 63
paul@144 64
    pass
paul@144 65
paul@113 66
class Expression(results.Result, TranslationResult):
paul@113 67
paul@113 68
    "A general expression."
paul@113 69
paul@113 70
    def __init__(self, s):
paul@113 71
        self.s = s
paul@113 72
    def __str__(self):
paul@113 73
        return self.s
paul@113 74
    def __repr__(self):
paul@113 75
        return "Expression(%r)" % self.s
paul@113 76
paul@113 77
class TrResolvedNameRef(results.ResolvedNameRef, TranslationResult):
paul@113 78
paul@113 79
    "A reference to a name in the translation."
paul@113 80
paul@208 81
    def __init__(self, name, ref, expr=None, parameter=None):
paul@208 82
        results.ResolvedNameRef.__init__(self, name, ref, expr)
paul@208 83
        self.parameter = parameter
paul@208 84
paul@113 85
    def __str__(self):
paul@113 86
paul@113 87
        "Return an output representation of the referenced name."
paul@113 88
paul@113 89
        # For sources, any identified static origin will be constant and thus
paul@113 90
        # usable directly. For targets, no constant should be assigned and thus
paul@113 91
        # the alias (or any plain name) will be used.
paul@113 92
paul@137 93
        ref = self.static()
paul@137 94
        origin = ref and self.get_origin()
paul@137 95
        static_name = origin and encode_path(origin)
paul@149 96
paul@149 97
        # Determine whether a qualified name is involved.
paul@149 98
paul@152 99
        t = (self.get_name() or self.name).rsplit(".", 1)
paul@149 100
        parent = len(t) > 1 and t[0] or None
paul@149 101
        attrname = encode_path(t[-1])
paul@113 102
paul@113 103
        # Assignments.
paul@113 104
paul@113 105
        if self.expr:
paul@113 106
paul@113 107
            # Eliminate assignments between constants.
paul@113 108
paul@196 109
            if ref and isinstance(self.expr, results.ResolvedNameRef) and self.expr.static():
paul@113 110
                return ""
paul@149 111
paul@149 112
            # Qualified names must be converted into parent-relative assignments.
paul@149 113
paul@149 114
            elif parent:
paul@149 115
                return "__store_via_object(&%s, %s, %s)" % (
paul@149 116
                    encode_path(parent), encode_symbol("pos", attrname), self.expr)
paul@149 117
paul@149 118
            # All other assignments involve the names as they were given.
paul@149 119
paul@113 120
            else:
paul@208 121
                return "(%s%s) = %s" % (self.parameter and "*" or "", attrname, self.expr)
paul@113 122
paul@113 123
        # Expressions.
paul@113 124
paul@137 125
        elif static_name:
paul@137 126
            parent = ref.parent()
paul@141 127
            context = ref.has_kind("<function>") and encode_path(parent) or None
paul@144 128
            return "((__attr) {%s, &%s})" % (context and "&%s" % context or "0", static_name)
paul@137 129
paul@152 130
        # Qualified names must be converted into parent-relative accesses.
paul@152 131
paul@152 132
        elif parent:
paul@152 133
            return "__load_via_object(&%s, %s)" % (
paul@152 134
                encode_path(parent), encode_symbol("pos", attrname))
paul@152 135
paul@152 136
        # All other accesses involve the names as they were given.
paul@152 137
paul@113 138
        else:
paul@208 139
            return "(%s%s)" % (self.parameter and "*" or "", attrname)
paul@113 140
paul@113 141
class TrConstantValueRef(results.ConstantValueRef, TranslationResult):
paul@113 142
paul@113 143
    "A constant value reference in the translation."
paul@113 144
paul@113 145
    def __str__(self):
paul@136 146
        return encode_literal_constant(self.number)
paul@113 147
paul@113 148
class TrLiteralSequenceRef(results.LiteralSequenceRef, TranslationResult):
paul@113 149
paul@113 150
    "A reference representing a sequence of values."
paul@113 151
paul@113 152
    def __str__(self):
paul@113 153
        return str(self.node)
paul@113 154
paul@113 155
class AttrResult(Expression, TranslationResult):
paul@113 156
paul@113 157
    "A translation result for an attribute access."
paul@113 158
paul@113 159
    def __init__(self, s, refs):
paul@113 160
        Expression.__init__(self, s)
paul@113 161
        self.refs = refs
paul@113 162
paul@113 163
    def get_origin(self):
paul@113 164
        return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
paul@113 165
paul@118 166
    def has_kind(self, kinds):
paul@118 167
        if not self.refs:
paul@118 168
            return False
paul@118 169
        for ref in self.refs:
paul@118 170
            if ref.has_kind(kinds):
paul@118 171
                return True
paul@118 172
        return False
paul@118 173
paul@113 174
    def __repr__(self):
paul@136 175
        return "AttrResult(%r, %r)" % (self.s, self.get_origin())
paul@113 176
paul@113 177
class PredefinedConstantRef(AttrResult):
paul@113 178
paul@113 179
    "A predefined constant reference."
paul@113 180
paul@113 181
    def __init__(self, value):
paul@113 182
        self.value = value
paul@113 183
paul@113 184
    def __str__(self):
paul@136 185
        if self.value in ("False", "True"):
paul@158 186
            return encode_path("__builtins__.boolean.%s" % self.value)
paul@136 187
        elif self.value == "None":
paul@136 188
            return encode_path("__builtins__.none.%s" % self.value)
paul@136 189
        elif self.value == "NotImplemented":
paul@136 190
            return encode_path("__builtins__.notimplemented.%s" % self.value)
paul@136 191
        else:
paul@136 192
            return self.value
paul@113 193
paul@113 194
    def __repr__(self):
paul@113 195
        return "PredefinedConstantRef(%r)" % self.value
paul@113 196
paul@141 197
class BooleanResult(Expression, TranslationResult):
paul@141 198
paul@141 199
    "A expression producing a boolean result."
paul@141 200
paul@141 201
    def __str__(self):
paul@141 202
        return "__builtins___bool_bool(%s)" % self.s
paul@141 203
paul@141 204
    def __repr__(self):
paul@141 205
        return "BooleanResult(%r)" % self.s
paul@141 206
paul@113 207
def make_expression(expr):
paul@113 208
paul@113 209
    "Make a new expression from the existing 'expr'."
paul@113 210
paul@113 211
    if isinstance(expr, results.Result):
paul@113 212
        return expr
paul@113 213
    else:
paul@113 214
        return Expression(str(expr))
paul@113 215
paul@113 216
# The actual translation process itself.
paul@113 217
paul@113 218
class TranslatedModule(CommonModule):
paul@113 219
paul@113 220
    "A module translator."
paul@113 221
paul@113 222
    def __init__(self, name, importer, deducer, optimiser):
paul@113 223
        CommonModule.__init__(self, name, importer)
paul@113 224
        self.deducer = deducer
paul@113 225
        self.optimiser = optimiser
paul@113 226
paul@113 227
        # Output stream.
paul@113 228
paul@113 229
        self.out = None
paul@113 230
        self.indent = 0
paul@113 231
        self.tabstop = "    "
paul@113 232
paul@113 233
        # Recorded namespaces.
paul@113 234
paul@113 235
        self.namespaces = []
paul@113 236
        self.in_conditional = False
paul@113 237
paul@144 238
        # Exception raising adjustments.
paul@144 239
paul@144 240
        self.in_try_finally = False
paul@189 241
        self.in_try_except = False
paul@144 242
paul@113 243
        # Attribute access counting.
paul@113 244
paul@113 245
        self.attr_accesses = {}
paul@113 246
paul@113 247
    def __repr__(self):
paul@113 248
        return "TranslatedModule(%r, %r)" % (self.name, self.importer)
paul@113 249
paul@113 250
    def translate(self, filename, output_filename):
paul@113 251
paul@113 252
        """
paul@113 253
        Parse the file having the given 'filename', writing the translation to
paul@113 254
        the given 'output_filename'.
paul@113 255
        """
paul@113 256
paul@113 257
        self.parse_file(filename)
paul@113 258
paul@113 259
        # Collect function namespaces for separate processing.
paul@113 260
paul@113 261
        self.record_namespaces(self.astnode)
paul@113 262
paul@113 263
        # Reset the lambda naming (in order to obtain the same names again) and
paul@113 264
        # translate the program.
paul@113 265
paul@113 266
        self.reset_lambdas()
paul@113 267
paul@113 268
        self.out = open(output_filename, "w")
paul@113 269
        try:
paul@128 270
            self.start_output()
paul@128 271
paul@113 272
            # Process namespaces, writing the translation.
paul@113 273
paul@113 274
            for path, node in self.namespaces:
paul@113 275
                self.process_namespace(path, node)
paul@113 276
paul@113 277
            # Process the module namespace including class namespaces.
paul@113 278
paul@113 279
            self.process_namespace([], self.astnode)
paul@113 280
paul@113 281
        finally:
paul@113 282
            self.out.close()
paul@113 283
paul@113 284
    def have_object(self):
paul@113 285
paul@113 286
        "Return whether a namespace is a recorded object."
paul@113 287
paul@113 288
        return self.importer.objects.get(self.get_namespace_path())
paul@113 289
paul@156 290
    def get_builtin_class(self, name):
paul@156 291
paul@156 292
        "Return a reference to the actual object providing 'name'."
paul@156 293
paul@156 294
        # NOTE: This makes assumptions about the __builtins__ structure.
paul@113 295
paul@158 296
        return self.importer.get_object("__builtins__.%s.%s" % (name, name))
paul@156 297
paul@156 298
    def is_method(self, path):
paul@156 299
paul@156 300
        "Return whether 'path' is a method."
paul@156 301
paul@113 302
        class_name, method_name = path.rsplit(".", 1)
paul@113 303
        return self.importer.classes.has_key(class_name) and class_name
paul@113 304
paul@208 305
    def in_method(self):
paul@208 306
paul@208 307
        "Return whether the current namespace provides a method."
paul@208 308
paul@208 309
        return self.in_function and self.is_method(self.get_namespace_path())
paul@208 310
paul@113 311
    # Namespace recording.
paul@113 312
paul@113 313
    def record_namespaces(self, node):
paul@113 314
paul@113 315
        "Process the program structure 'node', recording namespaces."
paul@113 316
paul@113 317
        for n in node.getChildNodes():
paul@113 318
            self.record_namespaces_in_node(n)
paul@113 319
paul@113 320
    def record_namespaces_in_node(self, node):
paul@113 321
paul@113 322
        "Process the program structure 'node', recording namespaces."
paul@113 323
paul@113 324
        # Function namespaces within modules, classes and other functions.
paul@113 325
        # Functions appearing within conditional statements are given arbitrary
paul@113 326
        # names.
paul@113 327
paul@113 328
        if isinstance(node, compiler.ast.Function):
paul@113 329
            self.record_function_node(node, (self.in_conditional or self.in_function) and self.get_lambda_name() or node.name)
paul@113 330
paul@113 331
        elif isinstance(node, compiler.ast.Lambda):
paul@113 332
            self.record_function_node(node, self.get_lambda_name())
paul@113 333
paul@113 334
        # Classes are visited, but may be ignored if inside functions.
paul@113 335
paul@113 336
        elif isinstance(node, compiler.ast.Class):
paul@113 337
            self.enter_namespace(node.name)
paul@113 338
            if self.have_object():
paul@113 339
                self.record_namespaces(node)
paul@113 340
            self.exit_namespace()
paul@113 341
paul@113 342
        # Conditional nodes are tracked so that function definitions may be
paul@113 343
        # handled. Since "for" loops are converted to "while" loops, they are
paul@113 344
        # included here.
paul@113 345
paul@113 346
        elif isinstance(node, (compiler.ast.For, compiler.ast.If, compiler.ast.While)):
paul@113 347
            in_conditional = self.in_conditional
paul@113 348
            self.in_conditional = True
paul@113 349
            self.record_namespaces(node)
paul@113 350
            self.in_conditional = in_conditional
paul@113 351
paul@113 352
        # All other nodes are processed depth-first.
paul@113 353
paul@113 354
        else:
paul@113 355
            self.record_namespaces(node)
paul@113 356
paul@113 357
    def record_function_node(self, n, name):
paul@113 358
paul@113 359
        """
paul@113 360
        Record the given function, lambda, if expression or list comprehension
paul@113 361
        node 'n' with the given 'name'.
paul@113 362
        """
paul@113 363
paul@113 364
        self.in_function = True
paul@113 365
        self.enter_namespace(name)
paul@113 366
paul@113 367
        if self.have_object():
paul@113 368
paul@113 369
            # Record the namespace path and the node itself.
paul@113 370
paul@113 371
            self.namespaces.append((self.namespace_path[:], n))
paul@113 372
            self.record_namespaces_in_node(n.code)
paul@113 373
paul@113 374
        self.exit_namespace()
paul@113 375
        self.in_function = False
paul@113 376
paul@113 377
    # Constant referencing.
paul@113 378
paul@113 379
    def get_literal_instance(self, n, name):
paul@113 380
paul@113 381
        """
paul@113 382
        For node 'n', return a reference for the type of the given 'name'.
paul@113 383
        """
paul@113 384
paul@156 385
        ref = self.get_builtin_class(name)
paul@113 386
paul@113 387
        if name in ("dict", "list", "tuple"):
paul@113 388
            return self.process_literal_sequence_node(n, name, ref, TrLiteralSequenceRef)
paul@113 389
        else:
paul@113 390
            path = self.get_namespace_path()
paul@113 391
            local_number = self.importer.all_constants[path][n.value]
paul@113 392
            constant_name = "$c%d" % local_number
paul@113 393
            objpath = self.get_object_path(constant_name)
paul@113 394
            number = self.optimiser.constant_numbers[objpath]
paul@113 395
            return TrConstantValueRef(constant_name, ref.instance_of(), n.value, number)
paul@113 396
paul@113 397
    # Namespace translation.
paul@113 398
paul@113 399
    def process_namespace(self, path, node):
paul@113 400
paul@113 401
        """
paul@113 402
        Process the namespace for the given 'path' defined by the given 'node'.
paul@113 403
        """
paul@113 404
paul@113 405
        self.namespace_path = path
paul@113 406
paul@113 407
        if isinstance(node, (compiler.ast.Function, compiler.ast.Lambda)):
paul@113 408
            self.in_function = True
paul@113 409
            self.process_function_body_node(node)
paul@113 410
        else:
paul@113 411
            self.in_function = False
paul@192 412
            self.function_target = 0
paul@113 413
            self.start_module()
paul@113 414
            self.process_structure(node)
paul@113 415
            self.end_module()
paul@113 416
paul@113 417
    def process_structure(self, node):
paul@113 418
paul@113 419
        "Process the given 'node' or result."
paul@113 420
paul@144 421
        # Handle processing requests on results.
paul@144 422
paul@113 423
        if isinstance(node, results.Result):
paul@113 424
            return node
paul@144 425
paul@144 426
        # Handle processing requests on nodes.
paul@144 427
paul@113 428
        else:
paul@144 429
            l = CommonModule.process_structure(self, node)
paul@144 430
paul@144 431
            # Return indications of return statement usage.
paul@144 432
paul@144 433
            if l and isinstance(l[-1], ReturnRef):
paul@144 434
                return l[-1]
paul@144 435
            else:
paul@144 436
                return None
paul@113 437
paul@113 438
    def process_structure_node(self, n):
paul@113 439
paul@113 440
        "Process the individual node 'n'."
paul@113 441
paul@113 442
        # Plain statements emit their expressions.
paul@113 443
paul@113 444
        if isinstance(n, compiler.ast.Discard):
paul@113 445
            expr = self.process_structure_node(n.expr)
paul@113 446
            self.statement(expr)
paul@113 447
paul@113 448
        # Nodes using operator module functions.
paul@113 449
paul@113 450
        elif isinstance(n, compiler.ast.Operator):
paul@113 451
            return self.process_operator_node(n)
paul@113 452
paul@113 453
        elif isinstance(n, compiler.ast.AugAssign):
paul@113 454
            self.process_augassign_node(n)
paul@113 455
paul@113 456
        elif isinstance(n, compiler.ast.Compare):
paul@113 457
            return self.process_compare_node(n)
paul@113 458
paul@113 459
        elif isinstance(n, compiler.ast.Slice):
paul@113 460
            return self.process_slice_node(n)
paul@113 461
paul@113 462
        elif isinstance(n, compiler.ast.Sliceobj):
paul@113 463
            return self.process_sliceobj_node(n)
paul@113 464
paul@113 465
        elif isinstance(n, compiler.ast.Subscript):
paul@113 466
            return self.process_subscript_node(n)
paul@113 467
paul@113 468
        # Classes are visited, but may be ignored if inside functions.
paul@113 469
paul@113 470
        elif isinstance(n, compiler.ast.Class):
paul@113 471
            self.process_class_node(n)
paul@113 472
paul@113 473
        # Functions within namespaces have any dynamic defaults initialised.
paul@113 474
paul@113 475
        elif isinstance(n, compiler.ast.Function):
paul@113 476
            self.process_function_node(n)
paul@113 477
paul@113 478
        # Lambdas are replaced with references to separately-generated
paul@113 479
        # functions.
paul@113 480
paul@113 481
        elif isinstance(n, compiler.ast.Lambda):
paul@113 482
            return self.process_lambda_node(n)
paul@113 483
paul@113 484
        # Assignments.
paul@113 485
paul@113 486
        elif isinstance(n, compiler.ast.Assign):
paul@113 487
paul@113 488
            # Handle each assignment node.
paul@113 489
paul@113 490
            for node in n.nodes:
paul@113 491
                self.process_assignment_node(node, n.expr)
paul@113 492
paul@113 493
        # Accesses.
paul@113 494
paul@113 495
        elif isinstance(n, compiler.ast.Getattr):
paul@113 496
            return self.process_attribute_access(n)
paul@113 497
paul@113 498
        # Names.
paul@113 499
paul@113 500
        elif isinstance(n, compiler.ast.Name):
paul@113 501
            return self.process_name_node(n)
paul@113 502
paul@113 503
        # Loops and conditionals.
paul@113 504
paul@113 505
        elif isinstance(n, compiler.ast.For):
paul@113 506
            self.process_for_node(n)
paul@113 507
paul@113 508
        elif isinstance(n, compiler.ast.While):
paul@113 509
            self.process_while_node(n)
paul@113 510
paul@113 511
        elif isinstance(n, compiler.ast.If):
paul@113 512
            self.process_if_node(n)
paul@113 513
paul@113 514
        elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
paul@113 515
            return self.process_logical_node(n)
paul@113 516
paul@113 517
        elif isinstance(n, compiler.ast.Not):
paul@113 518
            return self.process_not_node(n)
paul@113 519
paul@113 520
        # Exception control-flow tracking.
paul@113 521
paul@113 522
        elif isinstance(n, compiler.ast.TryExcept):
paul@113 523
            self.process_try_node(n)
paul@113 524
paul@113 525
        elif isinstance(n, compiler.ast.TryFinally):
paul@113 526
            self.process_try_finally_node(n)
paul@113 527
paul@113 528
        # Control-flow modification statements.
paul@113 529
paul@113 530
        elif isinstance(n, compiler.ast.Break):
paul@128 531
            self.writestmt("break;")
paul@113 532
paul@113 533
        elif isinstance(n, compiler.ast.Continue):
paul@128 534
            self.writestmt("continue;")
paul@113 535
paul@144 536
        elif isinstance(n, compiler.ast.Raise):
paul@144 537
            self.process_raise_node(n)
paul@144 538
paul@113 539
        elif isinstance(n, compiler.ast.Return):
paul@144 540
            return self.process_return_node(n)
paul@113 541
paul@173 542
        # Print statements.
paul@173 543
paul@173 544
        elif isinstance(n, (compiler.ast.Print, compiler.ast.Printnl)):
paul@173 545
            self.statement(self.process_print_node(n))
paul@173 546
paul@113 547
        # Invocations.
paul@113 548
paul@113 549
        elif isinstance(n, compiler.ast.CallFunc):
paul@113 550
            return self.process_invocation_node(n)
paul@113 551
paul@113 552
        elif isinstance(n, compiler.ast.Keyword):
paul@113 553
            return self.process_structure_node(n.expr)
paul@113 554
paul@113 555
        # Constant usage.
paul@113 556
paul@113 557
        elif isinstance(n, compiler.ast.Const):
paul@113 558
            return self.get_literal_instance(n, n.value.__class__.__name__)
paul@113 559
paul@113 560
        elif isinstance(n, compiler.ast.Dict):
paul@113 561
            return self.get_literal_instance(n, "dict")
paul@113 562
paul@113 563
        elif isinstance(n, compiler.ast.List):
paul@113 564
            return self.get_literal_instance(n, "list")
paul@113 565
paul@113 566
        elif isinstance(n, compiler.ast.Tuple):
paul@113 567
            return self.get_literal_instance(n, "tuple")
paul@113 568
paul@113 569
        # All other nodes are processed depth-first.
paul@113 570
paul@113 571
        else:
paul@144 572
            return self.process_structure(n)
paul@113 573
paul@113 574
    def process_assignment_node(self, n, expr):
paul@113 575
paul@113 576
        "Process the individual node 'n' to be assigned the contents of 'expr'."
paul@113 577
paul@113 578
        # Names and attributes are assigned the entire expression.
paul@113 579
paul@113 580
        if isinstance(n, compiler.ast.AssName):
paul@113 581
            name_ref = self.process_name_node(n, self.process_structure_node(expr))
paul@113 582
            self.statement(name_ref)
paul@113 583
paul@113 584
        elif isinstance(n, compiler.ast.AssAttr):
paul@124 585
            in_assignment = self.in_assignment
paul@124 586
            self.in_assignment = self.process_structure_node(expr)
paul@124 587
            self.statement(self.process_attribute_access(n))
paul@124 588
            self.in_assignment = in_assignment
paul@113 589
paul@113 590
        # Lists and tuples are matched against the expression and their
paul@113 591
        # items assigned to expression items.
paul@113 592
paul@113 593
        elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):
paul@113 594
            self.process_assignment_node_items(n, expr)
paul@113 595
paul@113 596
        # Slices and subscripts are permitted within assignment nodes.
paul@113 597
paul@113 598
        elif isinstance(n, compiler.ast.Slice):
paul@113 599
            self.statement(self.process_slice_node(n, expr))
paul@113 600
paul@113 601
        elif isinstance(n, compiler.ast.Subscript):
paul@113 602
            self.statement(self.process_subscript_node(n, expr))
paul@113 603
paul@124 604
    def process_attribute_access(self, n):
paul@113 605
paul@113 606
        """
paul@113 607
        Process the given attribute access node 'n'.
paul@113 608
paul@113 609
        Where a name is provided, a single access should be recorded
paul@113 610
        involving potentially many attributes, thus providing a path to an
paul@113 611
        object. The remaining attributes are then accessed dynamically.
paul@113 612
        The remaining accesses could be deduced and computed, but they would
paul@113 613
        also need to be tested.
paul@113 614
paul@113 615
        Where no name is provided, potentially many accesses should be
paul@113 616
        recorded, one per attribute name. These could be used to provide
paul@113 617
        computed accesses, but the accessors would need to be tested in each
paul@113 618
        case.
paul@113 619
        """
paul@113 620
paul@113 621
        # Obtain any completed chain and return the reference to it.
paul@113 622
paul@113 623
        attr_expr = self.process_attribute_chain(n)
paul@113 624
        if self.have_access_expression(n):
paul@113 625
            return attr_expr
paul@113 626
paul@113 627
        # Where the start of the chain of attributes has been reached, process
paul@113 628
        # the complete access.
paul@113 629
paul@113 630
        name_ref = attr_expr and attr_expr.is_name() and attr_expr
paul@152 631
        name = name_ref and self.get_name_for_tracking(name_ref.name, name_ref and name_ref.final()) or None
paul@113 632
paul@113 633
        location = self.get_access_location(name)
paul@113 634
        refs = self.get_referenced_attributes(location)
paul@113 635
paul@113 636
        # Generate access instructions.
paul@113 637
paul@113 638
        subs = {
paul@113 639
            "<expr>" : str(attr_expr),
paul@124 640
            "<assexpr>" : str(self.in_assignment),
paul@113 641
            "<context>" : "__tmp_context",
paul@113 642
            "<accessor>" : "__tmp_value",
paul@113 643
            }
paul@113 644
paul@113 645
        output = []
paul@113 646
paul@113 647
        for instruction in self.optimiser.access_instructions[location]:
paul@113 648
            output.append(encode_access_instruction(instruction, subs))
paul@113 649
paul@128 650
        if len(output) == 1:
paul@128 651
            out = output[0]
paul@128 652
        else:
paul@128 653
            out = "(\n%s\n)" % ",\n".join(output)
paul@113 654
paul@113 655
        del self.attrs[0]
paul@113 656
        return AttrResult(out, refs)
paul@113 657
paul@113 658
    def get_referenced_attributes(self, location):
paul@113 659
paul@113 660
        """
paul@113 661
        Convert 'location' to the form used by the deducer and retrieve any
paul@113 662
        identified attribute.
paul@113 663
        """
paul@113 664
paul@113 665
        access_location = self.deducer.const_accesses.get(location)
paul@113 666
        refs = []
paul@113 667
        for attrtype, objpath, attr in self.deducer.referenced_attrs[access_location or location]:
paul@113 668
            refs.append(attr)
paul@113 669
        return refs
paul@113 670
paul@113 671
    def get_access_location(self, name):
paul@113 672
paul@113 673
        """
paul@113 674
        Using the current namespace and the given 'name', return the access
paul@113 675
        location.
paul@113 676
        """
paul@113 677
paul@113 678
        path = self.get_path_for_access()
paul@113 679
paul@113 680
        # Get the location used by the deducer and optimiser and find any
paul@113 681
        # recorded access.
paul@113 682
paul@113 683
        attrnames = ".".join(self.attrs)
paul@113 684
        access_number = self.get_access_number(path, name, attrnames)
paul@113 685
        self.update_access_number(path, name, attrnames)
paul@113 686
        return (path, name, attrnames, access_number)
paul@113 687
paul@113 688
    def get_access_number(self, path, name, attrnames):
paul@113 689
        access = name, attrnames
paul@113 690
        if self.attr_accesses.has_key(path) and self.attr_accesses[path].has_key(access):
paul@113 691
            return self.attr_accesses[path][access]
paul@113 692
        else:
paul@113 693
            return 0
paul@113 694
paul@113 695
    def update_access_number(self, path, name, attrnames):
paul@113 696
        access = name, attrnames
paul@113 697
        if name:
paul@113 698
            init_item(self.attr_accesses, path, dict)
paul@144 699
            init_item(self.attr_accesses[path], access, lambda: 0)
paul@144 700
            self.attr_accesses[path][access] += 1
paul@113 701
paul@113 702
    def process_class_node(self, n):
paul@113 703
paul@113 704
        "Process the given class node 'n'."
paul@113 705
paul@113 706
        self.enter_namespace(n.name)
paul@113 707
paul@113 708
        if self.have_object():
paul@113 709
            class_name = self.get_namespace_path()
paul@113 710
            self.write_comment("Class: %s" % class_name)
paul@113 711
paul@113 712
            self.process_structure(n)
paul@113 713
paul@113 714
        self.exit_namespace()
paul@113 715
paul@113 716
    def process_function_body_node(self, n):
paul@113 717
paul@113 718
        """
paul@113 719
        Process the given function, lambda, if expression or list comprehension
paul@113 720
        node 'n', generating the body.
paul@113 721
        """
paul@113 722
paul@113 723
        function_name = self.get_namespace_path()
paul@113 724
        self.start_function(function_name)
paul@113 725
paul@113 726
        # Process the function body.
paul@113 727
paul@113 728
        in_conditional = self.in_conditional
paul@113 729
        self.in_conditional = False
paul@192 730
        self.function_target = 0
paul@113 731
paul@144 732
        expr = self.process_structure_node(n.code) or PredefinedConstantRef("None")
paul@144 733
        if not isinstance(expr, ReturnRef):
paul@128 734
            self.writestmt("return %s;" % expr)
paul@113 735
paul@113 736
        self.in_conditional = in_conditional
paul@113 737
paul@144 738
        self.end_function(function_name)
paul@113 739
paul@113 740
    def process_function_node(self, n):
paul@113 741
paul@113 742
        """
paul@113 743
        Process the given function, lambda, if expression or list comprehension
paul@113 744
        node 'n', generating any initialisation statements.
paul@113 745
        """
paul@113 746
paul@113 747
        # Where a function is declared conditionally, use a separate name for
paul@113 748
        # the definition, and assign the definition to the stated name.
paul@113 749
paul@196 750
        original_name = n.name
paul@196 751
paul@113 752
        if self.in_conditional or self.in_function:
paul@113 753
            name = self.get_lambda_name()
paul@113 754
        else:
paul@113 755
            name = n.name
paul@113 756
paul@196 757
        objpath = self.get_object_path(name)
paul@196 758
paul@113 759
        # Obtain details of the defaults.
paul@113 760
paul@196 761
        defaults = self.process_function_defaults(n, name, "&%s" % objpath)
paul@113 762
        if defaults:
paul@113 763
            for default in defaults:
paul@113 764
                self.writeline("%s;" % default)
paul@113 765
paul@196 766
        # Where a function is set conditionally or where the name may refer to
paul@196 767
        # different values, assign the name.
paul@196 768
paul@196 769
        ref = self.importer.identify(objpath)
paul@113 770
paul@196 771
        if self.in_conditional or self.in_function:
paul@196 772
            self.process_assignment_for_function(original_name, compiler.ast.Name(name))
paul@196 773
        elif not ref.static():
paul@196 774
            self.process_assignment_for_function(original_name,
paul@196 775
                make_expression("((__attr) {0, &%s})" % encode_path(objpath)))
paul@113 776
paul@113 777
    def process_function_defaults(self, n, name, instance_name):
paul@113 778
paul@113 779
        """
paul@113 780
        Process the given function or lambda node 'n', initialising defaults
paul@113 781
        that are dynamically set. The given 'name' indicates the name of the
paul@113 782
        function. The given 'instance_name' indicates the name of any separate
paul@113 783
        instance of the function created to hold the defaults.
paul@113 784
paul@113 785
        Return a list of operations setting defaults on a function instance.
paul@113 786
        """
paul@113 787
paul@113 788
        function_name = self.get_object_path(name)
paul@113 789
        function_defaults = self.importer.function_defaults.get(function_name)
paul@113 790
        if not function_defaults:
paul@113 791
            return None
paul@113 792
paul@113 793
        # Determine whether any unidentified defaults are involved.
paul@113 794
paul@113 795
        need_defaults = [argname for argname, default in function_defaults if default.has_kind("<var>")]
paul@113 796
        if not need_defaults:
paul@113 797
            return None
paul@113 798
paul@113 799
        # Where defaults are involved but cannot be identified, obtain a new
paul@113 800
        # instance of the lambda and populate the defaults.
paul@113 801
paul@113 802
        defaults = []
paul@113 803
paul@113 804
        # Join the original defaults with the inspected defaults.
paul@113 805
paul@113 806
        original_defaults = [(argname, default) for (argname, default) in compiler.ast.get_defaults(n) if default]
paul@113 807
paul@113 808
        for i, (original, inspected) in enumerate(map(None, original_defaults, function_defaults)):
paul@113 809
paul@113 810
            # Obtain any reference for the default.
paul@113 811
paul@113 812
            if original:
paul@113 813
                argname, default = original
paul@113 814
                name_ref = self.process_structure_node(default)
paul@113 815
            elif inspected:
paul@113 816
                argname, default = inspected
paul@113 817
                name_ref = TrResolvedNameRef(argname, default)
paul@113 818
            else:
paul@113 819
                continue
paul@113 820
paul@113 821
            if name_ref:
paul@149 822
                defaults.append("__SETDEFAULT(%s, %s, %s)" % (encode_path(instance_name), i, name_ref))
paul@113 823
paul@113 824
        return defaults
paul@113 825
paul@113 826
    def process_if_node(self, n):
paul@113 827
paul@113 828
        """
paul@113 829
        Process the given "if" node 'n'.
paul@113 830
        """
paul@113 831
paul@113 832
        first = True
paul@113 833
        for test, body in n.tests:
paul@113 834
            test_ref = self.process_structure_node(test)
paul@113 835
            self.start_if(first, test_ref)
paul@113 836
paul@113 837
            in_conditional = self.in_conditional
paul@113 838
            self.in_conditional = True
paul@113 839
            self.process_structure_node(body)
paul@113 840
            self.in_conditional = in_conditional
paul@113 841
paul@113 842
            self.end_if()
paul@113 843
            first = False
paul@113 844
paul@113 845
        if n.else_:
paul@113 846
            self.start_else()
paul@113 847
            self.process_structure_node(n.else_)
paul@113 848
            self.end_else()
paul@113 849
paul@113 850
    def process_invocation_node(self, n):
paul@113 851
paul@113 852
        "Process the given invocation node 'n'."
paul@113 853
paul@113 854
        expr = self.process_structure_node(n.node)
paul@113 855
        objpath = expr.get_origin()
paul@118 856
        target = None
paul@159 857
        literal_instantiation = False
paul@113 858
paul@113 859
        # Obtain details of the callable.
paul@113 860
paul@159 861
        # Literals may be instantiated specially.
paul@159 862
paul@159 863
        if expr.is_name() and expr.name.startswith("$L") and objpath:
paul@159 864
            literal_instantiation = True
paul@159 865
            parameters = None
paul@159 866
            target = encode_literal_instantiator(objpath)
paul@159 867
paul@159 868
        # Identified targets employ function pointers directly.
paul@159 869
paul@159 870
        elif objpath:
paul@113 871
            parameters = self.importer.function_parameters.get(objpath)
paul@118 872
            if expr.has_kind("<class>"):
paul@118 873
                target = encode_instantiator_pointer(objpath)
paul@149 874
                target_structure = encode_initialiser_pointer(objpath)
paul@118 875
            elif expr.has_kind("<function>"):
paul@118 876
                target = encode_function_pointer(objpath)
paul@149 877
                target_structure = encode_path(objpath)
paul@159 878
paul@159 879
        # Other targets are retrieved at run-time.
paul@159 880
paul@113 881
        else:
paul@113 882
            parameters = None
paul@113 883
paul@122 884
        # Arguments are presented in a temporary frame array with any context
paul@122 885
        # always being the first argument (although it may be set to null for
paul@122 886
        # invocations where it would be unused).
paul@122 887
paul@192 888
        args = ["__CONTEXT_AS_VALUE(__tmp_targets[%d])" % self.function_target]
paul@122 889
        args += [None] * (not parameters and len(n.args) or parameters and len(parameters) or 0)
paul@122 890
        kwcodes = []
paul@122 891
        kwargs = []
paul@122 892
paul@192 893
        # Any invocations in the arguments will store target details in a
paul@192 894
        # different location.
paul@192 895
paul@192 896
        self.function_target += 1
paul@192 897
paul@122 898
        for i, arg in enumerate(n.args):
paul@122 899
            argexpr = self.process_structure_node(arg)
paul@122 900
paul@122 901
            # Store a keyword argument, either in the argument list or
paul@122 902
            # in a separate keyword argument list for subsequent lookup.
paul@122 903
paul@122 904
            if isinstance(arg, compiler.ast.Keyword):
paul@113 905
paul@122 906
                # With knowledge of the target, store the keyword
paul@122 907
                # argument directly.
paul@122 908
paul@122 909
                if parameters:
paul@122 910
                    argnum = parameters.index(arg.name)
paul@122 911
                    args[argnum+1] = str(argexpr)
paul@122 912
paul@122 913
                # Otherwise, store the details in a separate collection.
paul@122 914
paul@122 915
                else:
paul@122 916
                    kwargs.append(str(argexpr))
paul@122 917
                    kwcodes.append("{%s, %s}" % (
paul@122 918
                        encode_symbol("ppos", arg.name),
paul@122 919
                        encode_symbol("pcode", arg.name)))
paul@122 920
paul@122 921
            else:
paul@122 922
                args[i+1] = str(argexpr)
paul@113 923
paul@192 924
        # Reference the current target again.
paul@192 925
paul@192 926
        self.function_target -= 1
paul@192 927
paul@113 928
        # Defaults are added to the frame where arguments are missing.
paul@113 929
paul@122 930
        if parameters:
paul@122 931
            function_defaults = self.importer.function_defaults.get(objpath)
paul@122 932
            if function_defaults:
paul@122 933
paul@122 934
                # Visit each default and set any missing arguments.
paul@149 935
                # Use the target structure to obtain defaults, as opposed to the
paul@149 936
                # actual function involved.
paul@122 937
paul@122 938
                for i, (argname, default) in enumerate(function_defaults):
paul@122 939
                    argnum = parameters.index(argname)
paul@122 940
                    if not args[argnum+1]:
paul@149 941
                        args[argnum+1] = "__GETDEFAULT(&%s, %d)" % (target_structure, i)
paul@149 942
paul@173 943
        # Test for missing arguments.
paul@173 944
paul@173 945
        if None in args:
paul@173 946
            raise TranslateError("Not all arguments supplied.",
paul@173 947
                                 self.get_namespace_path(), n)
paul@173 948
paul@149 949
        # Encode the arguments.
paul@122 950
paul@122 951
        argstr = "__ARGS(%s)" % ", ".join(args)
paul@122 952
        kwargstr = kwargs and ("__ARGS(%s)" % ", ".join(kwargs)) or "0"
paul@122 953
        kwcodestr = kwcodes and ("__KWARGS(%s)" % ", ".join(kwcodes)) or "0"
paul@122 954
paul@159 955
        # Where literal instantiation is occurring, add an argument indicating
paul@159 956
        # the number of values.
paul@159 957
paul@159 958
        if literal_instantiation:
paul@159 959
            argstr += ", %d" % (len(args) - 1)
paul@159 960
paul@156 961
        # First, the invocation expression is presented.
paul@113 962
paul@156 963
        stages = []
paul@156 964
paul@156 965
        # Without a known specific callable, the expression provides the target.
paul@118 966
paul@192 967
        stages.append("__tmp_targets[%d] = %s" % (self.function_target, expr))
paul@156 968
paul@156 969
        # Any specific callable is then obtained.
paul@156 970
paul@163 971
        if target:
paul@156 972
            stages.append(target)
paul@122 973
paul@122 974
        # With a known target, the function is obtained directly and called.
paul@122 975
paul@122 976
        if target:
paul@136 977
            output = "(\n%s\n)(%s)" % (",\n".join(stages), argstr)
paul@113 978
paul@122 979
        # With unknown targets, the generic invocation function is applied to
paul@122 980
        # the callable and argument collections.
paul@113 981
paul@122 982
        else:
paul@192 983
            output = "(%s, __invoke(\n__tmp_targets[%d],\n%d, %d, %s, %s,\n%d, %s\n))" % (
paul@122 984
                ",\n".join(stages),
paul@192 985
                self.function_target,
paul@156 986
                self.always_callable and 1 or 0,
paul@122 987
                len(kwargs), kwcodestr, kwargstr,
paul@122 988
                len(args), argstr)
paul@122 989
paul@122 990
        return make_expression(output)
paul@113 991
paul@113 992
    def always_callable(self, refs):
paul@113 993
paul@113 994
        "Determine whether all 'refs' are callable."
paul@113 995
paul@113 996
        for ref in refs:
paul@113 997
            if not ref.static():
paul@113 998
                return False
paul@113 999
            else:
paul@113 1000
                origin = ref.final()
paul@113 1001
                if not self.importer.get_attribute(origin, "__fn__"):
paul@113 1002
                    return False
paul@113 1003
        return True
paul@113 1004
paul@113 1005
    def need_default_arguments(self, objpath, nargs):
paul@113 1006
paul@113 1007
        """
paul@113 1008
        Return whether any default arguments are needed when invoking the object
paul@113 1009
        given by 'objpath'.
paul@113 1010
        """
paul@113 1011
paul@113 1012
        parameters = self.importer.function_parameters.get(objpath)
paul@113 1013
        return nargs < len(parameters)
paul@113 1014
paul@113 1015
    def process_lambda_node(self, n):
paul@113 1016
paul@113 1017
        "Process the given lambda node 'n'."
paul@113 1018
paul@113 1019
        name = self.get_lambda_name()
paul@113 1020
        function_name = self.get_object_path(name)
paul@113 1021
paul@155 1022
        defaults = self.process_function_defaults(n, name, "__tmp_value")
paul@149 1023
paul@149 1024
        # Without defaults, produce an attribute referring to the function.
paul@149 1025
paul@113 1026
        if not defaults:
paul@149 1027
            return make_expression("((__attr) {0, &%s})" % encode_path(function_name))
paul@149 1028
paul@149 1029
        # With defaults, copy the function structure and set the defaults on the
paul@149 1030
        # copy.
paul@149 1031
paul@113 1032
        else:
paul@155 1033
            return make_expression("(__tmp_value = __COPY(&%s, sizeof(%s)), %s, (__attr) {0, __tmp_value})" % (
paul@151 1034
                encode_path(function_name),
paul@151 1035
                encode_symbol("obj", function_name),
paul@151 1036
                ", ".join(defaults)))
paul@113 1037
paul@113 1038
    def process_logical_node(self, n):
paul@113 1039
paul@141 1040
        """
paul@141 1041
        Process the given operator node 'n'.
paul@141 1042
paul@141 1043
        Convert ... to ...
paul@141 1044
paul@141 1045
        <a> and <b>
paul@141 1046
        (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b>
paul@141 1047
paul@141 1048
        <a> or <b>
paul@141 1049
        (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b>
paul@141 1050
        """
paul@113 1051
paul@113 1052
        if isinstance(n, compiler.ast.And):
paul@141 1053
            op = "!"
paul@113 1054
        else:
paul@141 1055
            op = ""
paul@141 1056
paul@141 1057
        results = []
paul@113 1058
paul@141 1059
        for node in n.nodes[:-1]:
paul@141 1060
            expr = self.process_structure_node(node)
paul@141 1061
            results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, op))
paul@113 1062
paul@141 1063
        expr = self.process_structure_node(n.nodes[-1])
paul@141 1064
        results.append(str(expr))
paul@141 1065
paul@141 1066
        return make_expression("(%s)" % "".join(results))
paul@113 1067
paul@113 1068
    def process_name_node(self, n, expr=None):
paul@113 1069
paul@113 1070
        "Process the given name node 'n' with the optional assignment 'expr'."
paul@113 1071
paul@113 1072
        # Determine whether the name refers to a static external entity.
paul@113 1073
paul@113 1074
        if n.name in predefined_constants:
paul@113 1075
            return PredefinedConstantRef(n.name)
paul@113 1076
paul@173 1077
        # Convert literal references, operator function names, and print
paul@173 1078
        # function names to references.
paul@113 1079
paul@173 1080
        elif n.name.startswith("$L") or n.name.startswith("$op") or \
paul@173 1081
             n.name.startswith("$print"):
paul@136 1082
            ref = self.importer.get_module(self.name).special.get(n.name)
paul@113 1083
            return TrResolvedNameRef(n.name, ref)
paul@113 1084
paul@113 1085
        # Get the appropriate name for the name reference, using the same method
paul@113 1086
        # as in the inspector.
paul@113 1087
paul@113 1088
        path = self.get_object_path(n.name)
paul@113 1089
paul@113 1090
        # Get the static identity of the name.
paul@113 1091
paul@113 1092
        ref = self.importer.identify(path)
paul@152 1093
        if ref and not ref.get_name():
paul@152 1094
            ref = ref.alias(path)
paul@113 1095
paul@113 1096
        # Obtain any resolved names for non-assignment names.
paul@113 1097
paul@113 1098
        if not expr and not ref and self.in_function:
paul@113 1099
            locals = self.importer.function_locals.get(self.get_namespace_path())
paul@113 1100
            ref = locals and locals.get(n.name)
paul@113 1101
paul@208 1102
        # Determine whether the name refers to a parameter. The generation of
paul@208 1103
        # parameter references is different from other names.
paul@208 1104
paul@208 1105
        parameters = self.importer.function_parameters.get(self.get_namespace_path())
paul@208 1106
        parameter = n.name == "self" and self.in_method() or \
paul@208 1107
                    parameters and n.name in parameters
paul@208 1108
paul@113 1109
        # Qualified names are used for resolved static references or for
paul@113 1110
        # static namespace members. The reference should be configured to return
paul@113 1111
        # such names.
paul@113 1112
paul@208 1113
        return TrResolvedNameRef(n.name, ref, expr=expr, parameter=parameter)
paul@113 1114
paul@113 1115
    def process_not_node(self, n):
paul@113 1116
paul@113 1117
        "Process the given operator node 'n'."
paul@113 1118
paul@144 1119
        return make_expression("(__BOOL(%s) ? %s : %s)" %
paul@149 1120
            (self.process_structure_node(n.expr), PredefinedConstantRef("False"),
paul@149 1121
            PredefinedConstantRef("True")))
paul@144 1122
paul@144 1123
    def process_raise_node(self, n):
paul@144 1124
paul@144 1125
        "Process the given raise node 'n'."
paul@144 1126
paul@144 1127
        # NOTE: Determine which raise statement variants should be permitted.
paul@144 1128
paul@176 1129
        if n.expr1:
paul@176 1130
            self.writestmt("__Raise(%s);" % self.process_structure_node(n.expr1))
paul@176 1131
        else:
paul@176 1132
            self.writestmt("__Complete;")
paul@144 1133
paul@144 1134
    def process_return_node(self, n):
paul@144 1135
paul@144 1136
        "Process the given return node 'n'."
paul@144 1137
paul@144 1138
        expr = self.process_structure_node(n.value) or PredefinedConstantRef("None")
paul@189 1139
        if self.in_try_finally or self.in_try_except:
paul@144 1140
            self.writestmt("__Return(%s);" % expr)
paul@144 1141
        else:
paul@144 1142
            self.writestmt("return %s;" % expr)
paul@144 1143
paul@144 1144
        return ReturnRef()
paul@113 1145
paul@113 1146
    def process_try_node(self, n):
paul@113 1147
paul@113 1148
        """
paul@113 1149
        Process the given "try...except" node 'n'.
paul@113 1150
        """
paul@113 1151
paul@189 1152
        in_try_except = self.in_try_except
paul@189 1153
        self.in_try_except = True
paul@189 1154
paul@144 1155
        # Use macros to implement exception handling.
paul@113 1156
paul@144 1157
        self.writestmt("__Try")
paul@113 1158
        self.writeline("{")
paul@113 1159
        self.indent += 1
paul@113 1160
        self.process_structure_node(n.body)
paul@144 1161
paul@144 1162
        # Put the else statement in another try block that handles any raised
paul@144 1163
        # exceptions and converts them to exceptions that will not be handled by
paul@144 1164
        # the main handling block.
paul@144 1165
paul@144 1166
        if n.else_:
paul@144 1167
            self.writestmt("__Try")
paul@144 1168
            self.writeline("{")
paul@144 1169
            self.indent += 1
paul@144 1170
            self.process_structure_node(n.else_)
paul@144 1171
            self.indent -= 1
paul@144 1172
            self.writeline("}")
paul@144 1173
            self.writeline("__Catch (__tmp_exc)")
paul@144 1174
            self.writeline("{")
paul@144 1175
            self.indent += 1
paul@144 1176
            self.writeline("if (__tmp_exc.raising) __RaiseElse(__tmp_exc.arg);")
paul@191 1177
            self.writeline("else if (__tmp_exc.completing) __Throw(__tmp_exc);")
paul@144 1178
            self.indent -= 1
paul@144 1179
            self.writeline("}")
paul@144 1180
paul@144 1181
        # Complete the try block and enter the finally block, if appropriate.
paul@144 1182
paul@144 1183
        if self.in_try_finally:
paul@144 1184
            self.writestmt("__Complete;")
paul@144 1185
paul@113 1186
        self.indent -= 1
paul@113 1187
        self.writeline("}")
paul@113 1188
paul@189 1189
        self.in_try_except = in_try_except
paul@189 1190
paul@144 1191
        # Handlers are tests within a common handler block.
paul@144 1192
paul@144 1193
        self.writeline("__Catch (__tmp_exc)")
paul@144 1194
        self.writeline("{")
paul@144 1195
        self.indent += 1
paul@144 1196
paul@189 1197
        # Introduce an if statement to handle the completion of a try block.
paul@189 1198
paul@189 1199
        self.process_try_completion()
paul@189 1200
paul@144 1201
        # Handle exceptions in else blocks converted to __RaiseElse, converting
paul@144 1202
        # them back to normal exceptions.
paul@144 1203
paul@144 1204
        if n.else_:
paul@189 1205
            self.writeline("else if (__tmp_exc.raising_else) __Raise(__tmp_exc.arg);")
paul@144 1206
paul@144 1207
        # Exception handling.
paul@144 1208
paul@113 1209
        for name, var, handler in n.handlers:
paul@144 1210
paul@144 1211
            # Test for specific exceptions.
paul@144 1212
paul@113 1213
            if name is not None:
paul@113 1214
                name_ref = self.process_structure_node(name)
paul@191 1215
                self.writeline("else if (__BOOL(__fn_native__isinstance((__attr[]) {{0, 0}, __tmp_exc.arg, %s})))" % name_ref)
paul@144 1216
            else:
paul@189 1217
                self.writeline("else if (1)")
paul@113 1218
paul@113 1219
            self.writeline("{")
paul@113 1220
            self.indent += 1
paul@113 1221
paul@113 1222
            # Establish the local for the handler.
paul@113 1223
paul@113 1224
            if var is not None:
paul@144 1225
                var_ref = self.process_name_node(var, make_expression("__tmp_exc"))
paul@113 1226
paul@113 1227
            if handler is not None:
paul@113 1228
                self.process_structure_node(handler)
paul@113 1229
paul@113 1230
            self.indent -= 1
paul@113 1231
            self.writeline("}")
paul@113 1232
paul@144 1233
        # Re-raise unhandled exceptions.
paul@144 1234
paul@189 1235
        self.writeline("else __Throw(__tmp_exc);")
paul@144 1236
paul@144 1237
        # End the handler block.
paul@144 1238
paul@144 1239
        self.indent -= 1
paul@144 1240
        self.writeline("}")
paul@113 1241
paul@113 1242
    def process_try_finally_node(self, n):
paul@113 1243
paul@113 1244
        """
paul@113 1245
        Process the given "try...finally" node 'n'.
paul@113 1246
        """
paul@113 1247
paul@144 1248
        in_try_finally = self.in_try_finally
paul@144 1249
        self.in_try_finally = True
paul@113 1250
paul@144 1251
        # Use macros to implement exception handling.
paul@144 1252
paul@144 1253
        self.writestmt("__Try")
paul@113 1254
        self.writeline("{")
paul@113 1255
        self.indent += 1
paul@113 1256
        self.process_structure_node(n.body)
paul@113 1257
        self.indent -= 1
paul@113 1258
        self.writeline("}")
paul@144 1259
paul@144 1260
        self.in_try_finally = in_try_finally
paul@144 1261
paul@144 1262
        # Finally clauses handle special exceptions.
paul@144 1263
paul@144 1264
        self.writeline("__Catch (__tmp_exc)")
paul@113 1265
        self.writeline("{")
paul@113 1266
        self.indent += 1
paul@113 1267
        self.process_structure_node(n.final)
paul@144 1268
paul@189 1269
        # Introduce an if statement to handle the completion of a try block.
paul@189 1270
paul@189 1271
        self.process_try_completion()
paul@189 1272
        self.writeline("else __Throw(__tmp_exc);")
paul@189 1273
paul@189 1274
        self.indent -= 1
paul@189 1275
        self.writeline("}")
paul@189 1276
paul@189 1277
    def process_try_completion(self):
paul@189 1278
paul@189 1279
        "Generate a test for the completion of a try block."
paul@144 1280
paul@144 1281
        self.writestmt("if (__tmp_exc.completing)")
paul@144 1282
        self.writeline("{")
paul@144 1283
        self.indent += 1
paul@189 1284
paul@189 1285
        # Only use the normal return statement if no surrounding try blocks
paul@189 1286
        # apply.
paul@189 1287
paul@189 1288
        if not self.in_try_finally and not self.in_try_except:
paul@189 1289
            self.writeline("if (!__ISNULL(__tmp_exc.arg)) return __tmp_exc.arg;")
paul@189 1290
        else:
paul@189 1291
            self.writeline("if (!__ISNULL(__tmp_exc.arg)) __Throw(__tmp_exc);")
paul@144 1292
paul@113 1293
        self.indent -= 1
paul@113 1294
        self.writeline("}")
paul@113 1295
paul@113 1296
    def process_while_node(self, n):
paul@113 1297
paul@113 1298
        "Process the given while node 'n'."
paul@113 1299
paul@113 1300
        self.writeline("while (1)")
paul@113 1301
        self.writeline("{")
paul@113 1302
        self.indent += 1
paul@113 1303
        test = self.process_structure_node(n.test)
paul@113 1304
paul@113 1305
        # Emit the loop termination condition unless "while <true value>" is
paul@113 1306
        # indicated.
paul@113 1307
paul@113 1308
        if not (isinstance(test, PredefinedConstantRef) and test.value):
paul@113 1309
paul@113 1310
            # NOTE: This needs to evaluate whether the operand is true or false
paul@113 1311
            # NOTE: according to Python rules.
paul@113 1312
paul@144 1313
            self.writeline("if (!__BOOL(%s))" % test)
paul@113 1314
            self.writeline("{")
paul@113 1315
            self.indent += 1
paul@113 1316
            if n.else_:
paul@113 1317
                self.process_structure_node(n.else_)
paul@128 1318
            self.writestmt("break;")
paul@113 1319
            self.indent -= 1
paul@113 1320
            self.writeline("}")
paul@113 1321
paul@113 1322
        in_conditional = self.in_conditional
paul@113 1323
        self.in_conditional = True
paul@113 1324
        self.process_structure_node(n.body)
paul@113 1325
        self.in_conditional = in_conditional
paul@113 1326
paul@113 1327
        self.indent -= 1
paul@113 1328
        self.writeline("}")
paul@113 1329
paul@113 1330
    # Output generation.
paul@113 1331
paul@128 1332
    def start_output(self):
paul@159 1333
paul@159 1334
        "Write the declarations at the top of each source file."
paul@159 1335
paul@128 1336
        print >>self.out, """\
paul@128 1337
#include "types.h"
paul@144 1338
#include "exceptions.h"
paul@128 1339
#include "ops.h"
paul@128 1340
#include "progconsts.h"
paul@128 1341
#include "progops.h"
paul@128 1342
#include "progtypes.h"
paul@137 1343
#include "main.h"
paul@128 1344
"""
paul@128 1345
paul@113 1346
    def start_module(self):
paul@159 1347
paul@159 1348
        "Write the start of each module's main function."
paul@159 1349
paul@113 1350
        print >>self.out, "void __main_%s()" % encode_path(self.name)
paul@113 1351
        print >>self.out, "{"
paul@113 1352
        self.indent += 1
paul@192 1353
        self.write_temporaries(self.importer.function_targets.get(self.name))
paul@113 1354
paul@113 1355
    def end_module(self):
paul@159 1356
paul@159 1357
        "End each module by closing its main function."
paul@159 1358
paul@113 1359
        self.indent -= 1
paul@144 1360
        print >>self.out, "}"
paul@113 1361
paul@113 1362
    def start_function(self, name):
paul@159 1363
paul@159 1364
        "Start the function having the given 'name'."
paul@159 1365
paul@113 1366
        print >>self.out, "__attr %s(__attr __args[])" % encode_function_pointer(name)
paul@113 1367
        print >>self.out, "{"
paul@113 1368
        self.indent += 1
paul@192 1369
        self.write_temporaries(self.importer.function_targets.get(name))
paul@113 1370
paul@113 1371
        # Obtain local names from parameters.
paul@113 1372
paul@113 1373
        parameters = self.importer.function_parameters[name]
paul@144 1374
        locals = self.importer.function_locals[name].keys()
paul@113 1375
        names = []
paul@113 1376
paul@113 1377
        for n in locals:
paul@113 1378
paul@113 1379
            # Filter out special names and parameters. Note that self is a local
paul@113 1380
            # regardless of whether it originally appeared in the parameters or
paul@113 1381
            # not.
paul@113 1382
paul@113 1383
            if n.startswith("$l") or n in parameters or n == "self":
paul@113 1384
                continue
paul@113 1385
            names.append(encode_path(n))
paul@113 1386
paul@113 1387
        # Emit required local names.
paul@113 1388
paul@113 1389
        if names:
paul@113 1390
            names.sort()
paul@113 1391
            self.writeline("__attr %s;" % ", ".join(names))
paul@113 1392
paul@208 1393
        self.write_parameters(name)
paul@144 1394
paul@144 1395
    def end_function(self, name):
paul@159 1396
paul@159 1397
        "End the function having the given 'name'."
paul@159 1398
paul@144 1399
        self.indent -= 1
paul@144 1400
        print >>self.out, "}"
paul@144 1401
        print >>self.out
paul@144 1402
paul@192 1403
    def write_temporaries(self, targets):
paul@159 1404
paul@192 1405
        """
paul@192 1406
        Write temporary storage employed by functions, providing space for the
paul@192 1407
        given number of 'targets'.
paul@192 1408
        """
paul@192 1409
paul@192 1410
        targets = targets is not None and "__tmp_targets[%d], " % targets or ""
paul@159 1411
paul@149 1412
        self.writeline("__ref __tmp_context, __tmp_value;")
paul@192 1413
        self.writeline("__attr %s__tmp_result;" % targets)
paul@149 1414
        self.writeline("__exc __tmp_exc;")
paul@149 1415
paul@208 1416
    def write_parameters(self, name):
paul@159 1417
paul@159 1418
        """
paul@159 1419
        For the function having the given 'name', write definitions of
paul@208 1420
        parameters found in the arguments array.
paul@159 1421
        """
paul@159 1422
paul@144 1423
        parameters = self.importer.function_parameters[name]
paul@144 1424
paul@113 1425
        # Generate any self reference.
paul@113 1426
paul@156 1427
        if self.is_method(name):
paul@208 1428
            self.writeline("__attr * const self = &__args[0];")
paul@113 1429
paul@113 1430
        # Generate aliases for the parameters.
paul@113 1431
paul@113 1432
        for i, parameter in enumerate(parameters):
paul@208 1433
            self.writeline("__attr * const %s = &__args[%d];" % (encode_path(parameter), i+1))
paul@113 1434
paul@113 1435
    def start_if(self, first, test_ref):
paul@144 1436
        self.writestmt("%sif (__BOOL(%s))" % (not first and "else " or "", test_ref))
paul@113 1437
        self.writeline("{")
paul@113 1438
        self.indent += 1
paul@113 1439
paul@113 1440
    def end_if(self):
paul@113 1441
        self.indent -= 1
paul@113 1442
        self.writeline("}")
paul@113 1443
paul@113 1444
    def start_else(self):
paul@113 1445
        self.writeline("else")
paul@113 1446
        self.writeline("{")
paul@113 1447
        self.indent += 1
paul@113 1448
paul@113 1449
    def end_else(self):
paul@113 1450
        self.indent -= 1
paul@113 1451
        self.writeline("}")
paul@113 1452
paul@113 1453
    def statement(self, expr):
paul@113 1454
        # NOTE: Should never be None.
paul@113 1455
        if not expr:
paul@128 1456
            self.writestmt("...;")
paul@113 1457
        s = str(expr)
paul@113 1458
        if s:
paul@128 1459
            self.writestmt("%s;" % s)
paul@113 1460
paul@113 1461
    def statements(self, results):
paul@113 1462
        for result in results:
paul@113 1463
            self.statement(result)
paul@113 1464
paul@159 1465
    def writeline(self, s):
paul@159 1466
        print >>self.out, "%s%s" % (self.pad(), self.indenttext(s, self.indent + 1))
paul@159 1467
paul@159 1468
    def writestmt(self, s):
paul@159 1469
        print >>self.out
paul@159 1470
        self.writeline(s)
paul@159 1471
paul@159 1472
    def write_comment(self, s):
paul@159 1473
        self.writestmt("/* %s */" % s)
paul@159 1474
paul@113 1475
    def pad(self, extra=0):
paul@113 1476
        return (self.indent + extra) * self.tabstop
paul@113 1477
paul@113 1478
    def indenttext(self, s, levels):
paul@116 1479
        lines = s.split("\n")
paul@116 1480
        out = [lines[0]]
paul@116 1481
        for line in lines[1:]:
paul@116 1482
            out.append(levels * self.tabstop + line)
paul@116 1483
            if line.endswith("("):
paul@116 1484
                levels += 1
paul@122 1485
            elif line.startswith(")"):
paul@116 1486
                levels -= 1
paul@116 1487
        return "\n".join(out)
paul@113 1488
paul@113 1489
# vim: tabstop=4 expandtab shiftwidth=4