Lichen

Annotated translator.py

600:f92610e27e92
2017-02-19 Paul Boddie Merged changes from the default branch. method-wrapper-for-context
paul@113 1
#!/usr/bin/env python
paul@113 2
paul@113 3
"""
paul@113 4
Translate programs.
paul@113 5
paul@482 6
Copyright (C) 2015, 2016, 2017 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@491 22
from common import CommonModule, CommonOutput, InstructionSequence, \
paul@538 23
                   first, get_builtin_class, init_item, predefined_constants
paul@523 24
from encoders import encode_access_instruction, \
paul@430 25
                     encode_function_pointer, encode_literal_constant, \
paul@430 26
                     encode_literal_instantiator, encode_instantiator_pointer, \
paul@491 27
                     encode_instructions, \
paul@430 28
                     encode_path, encode_symbol, encode_type_attribute, \
paul@430 29
                     is_type_attribute
paul@545 30
from errors import InspectError, TranslateError
paul@113 31
from os.path import exists, join
paul@113 32
from os import makedirs
paul@314 33
from referencing import Reference
paul@482 34
from StringIO import StringIO
paul@113 35
import compiler
paul@113 36
import results
paul@556 37
import sys
paul@113 38
paul@113 39
class Translator(CommonOutput):
paul@113 40
paul@113 41
    "A program translator."
paul@113 42
paul@113 43
    def __init__(self, importer, deducer, optimiser, output):
paul@113 44
        self.importer = importer
paul@113 45
        self.deducer = deducer
paul@113 46
        self.optimiser = optimiser
paul@113 47
        self.output = output
paul@482 48
        self.modules = {}
paul@113 49
paul@113 50
    def to_output(self):
paul@113 51
        output = join(self.output, "src")
paul@113 52
paul@113 53
        if not exists(output):
paul@113 54
            makedirs(output)
paul@113 55
paul@113 56
        self.check_output()
paul@113 57
paul@113 58
        for module in self.importer.modules.values():
paul@354 59
            parts = module.name.split(".")
paul@354 60
            if parts[0] != "native":
paul@161 61
                tm = TranslatedModule(module.name, self.importer, self.deducer, self.optimiser)
paul@161 62
                tm.translate(module.filename, join(output, "%s.c" % module.name))
paul@482 63
                self.modules[module.name] = tm 
paul@113 64
paul@113 65
# Classes representing intermediate translation results.
paul@113 66
paul@113 67
class TranslationResult:
paul@113 68
paul@113 69
    "An abstract translation result mix-in."
paul@113 70
paul@554 71
    pass
paul@113 72
paul@144 73
class ReturnRef(TranslationResult):
paul@144 74
paul@144 75
    "Indicates usage of a return statement."
paul@144 76
paul@144 77
    pass
paul@144 78
paul@113 79
class Expression(results.Result, TranslationResult):
paul@113 80
paul@113 81
    "A general expression."
paul@113 82
paul@113 83
    def __init__(self, s):
paul@113 84
        self.s = s
paul@113 85
    def __str__(self):
paul@113 86
        return self.s
paul@113 87
    def __repr__(self):
paul@113 88
        return "Expression(%r)" % self.s
paul@113 89
paul@113 90
class TrResolvedNameRef(results.ResolvedNameRef, TranslationResult):
paul@113 91
paul@113 92
    "A reference to a name in the translation."
paul@113 93
paul@554 94
    def __init__(self, name, ref, expr=None, parameter=None, location=None):
paul@208 95
        results.ResolvedNameRef.__init__(self, name, ref, expr)
paul@208 96
        self.parameter = parameter
paul@554 97
        self.location = location
paul@554 98
paul@554 99
    def access_location(self):
paul@554 100
        return self.location
paul@208 101
paul@113 102
    def __str__(self):
paul@113 103
paul@113 104
        "Return an output representation of the referenced name."
paul@113 105
paul@113 106
        # For sources, any identified static origin will be constant and thus
paul@113 107
        # usable directly. For targets, no constant should be assigned and thus
paul@113 108
        # the alias (or any plain name) will be used.
paul@113 109
paul@137 110
        ref = self.static()
paul@137 111
        origin = ref and self.get_origin()
paul@137 112
        static_name = origin and encode_path(origin)
paul@149 113
paul@149 114
        # Determine whether a qualified name is involved.
paul@149 115
paul@338 116
        t = (not self.is_constant_alias() and self.get_name() or self.name).rsplit(".", 1)
paul@149 117
        parent = len(t) > 1 and t[0] or None
paul@338 118
        attrname = t[-1] and encode_path(t[-1])
paul@113 119
paul@113 120
        # Assignments.
paul@113 121
paul@113 122
        if self.expr:
paul@113 123
paul@113 124
            # Eliminate assignments between constants.
paul@113 125
paul@196 126
            if ref and isinstance(self.expr, results.ResolvedNameRef) and self.expr.static():
paul@113 127
                return ""
paul@149 128
paul@149 129
            # Qualified names must be converted into parent-relative assignments.
paul@149 130
paul@149 131
            elif parent:
paul@149 132
                return "__store_via_object(&%s, %s, %s)" % (
paul@149 133
                    encode_path(parent), encode_symbol("pos", attrname), self.expr)
paul@149 134
paul@149 135
            # All other assignments involve the names as they were given.
paul@149 136
paul@113 137
            else:
paul@208 138
                return "(%s%s) = %s" % (self.parameter and "*" or "", attrname, self.expr)
paul@113 139
paul@113 140
        # Expressions.
paul@113 141
paul@137 142
        elif static_name:
paul@137 143
            parent = ref.parent()
paul@141 144
            context = ref.has_kind("<function>") and encode_path(parent) or None
paul@577 145
            return "((__attr) {.value=&%s})" % static_name
paul@137 146
paul@152 147
        # Qualified names must be converted into parent-relative accesses.
paul@152 148
paul@152 149
        elif parent:
paul@152 150
            return "__load_via_object(&%s, %s)" % (
paul@152 151
                encode_path(parent), encode_symbol("pos", attrname))
paul@152 152
paul@152 153
        # All other accesses involve the names as they were given.
paul@152 154
paul@113 155
        else:
paul@208 156
            return "(%s%s)" % (self.parameter and "*" or "", attrname)
paul@113 157
paul@113 158
class TrConstantValueRef(results.ConstantValueRef, TranslationResult):
paul@113 159
paul@113 160
    "A constant value reference in the translation."
paul@113 161
paul@113 162
    def __str__(self):
paul@136 163
        return encode_literal_constant(self.number)
paul@113 164
paul@113 165
class TrLiteralSequenceRef(results.LiteralSequenceRef, TranslationResult):
paul@113 166
paul@113 167
    "A reference representing a sequence of values."
paul@113 168
paul@113 169
    def __str__(self):
paul@113 170
        return str(self.node)
paul@113 171
paul@317 172
class TrInstanceRef(results.InstanceRef, TranslationResult):
paul@317 173
paul@317 174
    "A reference representing instantiation of a class."
paul@317 175
paul@317 176
    def __init__(self, ref, expr):
paul@317 177
paul@317 178
        """
paul@317 179
        Initialise the reference with 'ref' indicating the nature of the
paul@317 180
        reference and 'expr' being an expression used to create the instance.
paul@317 181
        """
paul@317 182
paul@317 183
        results.InstanceRef.__init__(self, ref)
paul@317 184
        self.expr = expr
paul@317 185
paul@317 186
    def __str__(self):
paul@317 187
        return self.expr
paul@317 188
paul@317 189
    def __repr__(self):
paul@317 190
        return "TrResolvedInstanceRef(%r, %r)" % (self.ref, self.expr)
paul@317 191
paul@491 192
class AttrResult(Expression, TranslationResult, InstructionSequence):
paul@113 193
paul@113 194
    "A translation result for an attribute access."
paul@113 195
paul@554 196
    def __init__(self, instructions, refs, location):
paul@491 197
        InstructionSequence.__init__(self, instructions)
paul@113 198
        self.refs = refs
paul@554 199
        self.location = location
paul@113 200
paul@552 201
    def references(self):
paul@552 202
        return self.refs
paul@552 203
paul@554 204
    def access_location(self):
paul@554 205
        return self.location
paul@553 206
paul@113 207
    def get_origin(self):
paul@113 208
        return self.refs and len(self.refs) == 1 and first(self.refs).get_origin()
paul@113 209
paul@118 210
    def has_kind(self, kinds):
paul@118 211
        if not self.refs:
paul@118 212
            return False
paul@118 213
        for ref in self.refs:
paul@118 214
            if ref.has_kind(kinds):
paul@118 215
                return True
paul@118 216
        return False
paul@118 217
paul@491 218
    def __str__(self):
paul@491 219
        return encode_instructions(self.instructions)
paul@491 220
paul@113 221
    def __repr__(self):
paul@554 222
        return "AttrResult(%r, %r, %r)" % (self.instructions, self.refs, self.location)
paul@491 223
paul@498 224
class InvocationResult(Expression, TranslationResult, InstructionSequence):
paul@498 225
paul@498 226
    "A translation result for an invocation."
paul@498 227
paul@498 228
    def __init__(self, instructions):
paul@498 229
        InstructionSequence.__init__(self, instructions)
paul@498 230
paul@498 231
    def __str__(self):
paul@498 232
        return encode_instructions(self.instructions)
paul@498 233
paul@498 234
    def __repr__(self):
paul@498 235
        return "InvocationResult(%r)" % self.instructions
paul@498 236
paul@498 237
class InstantiationResult(InvocationResult, TrInstanceRef):
paul@498 238
paul@498 239
    "An instantiation result acting like an invocation result."
paul@498 240
paul@498 241
    def __init__(self, ref, instructions):
paul@498 242
        results.InstanceRef.__init__(self, ref)
paul@498 243
        InvocationResult.__init__(self, instructions)
paul@498 244
paul@498 245
    def __repr__(self):
paul@498 246
        return "InstantiationResult(%r, %r)" % (self.ref, self.instructions)
paul@498 247
paul@491 248
class PredefinedConstantRef(Expression, TranslationResult):
paul@113 249
paul@113 250
    "A predefined constant reference."
paul@113 251
paul@399 252
    def __init__(self, value, expr=None):
paul@113 253
        self.value = value
paul@399 254
        self.expr = expr
paul@113 255
paul@113 256
    def __str__(self):
paul@399 257
paul@399 258
        # Eliminate predefined constant assignments.
paul@399 259
paul@399 260
        if self.expr:
paul@399 261
            return ""
paul@399 262
paul@399 263
        # Generate the specific constants.
paul@399 264
paul@136 265
        if self.value in ("False", "True"):
paul@158 266
            return encode_path("__builtins__.boolean.%s" % self.value)
paul@136 267
        elif self.value == "None":
paul@136 268
            return encode_path("__builtins__.none.%s" % self.value)
paul@136 269
        elif self.value == "NotImplemented":
paul@136 270
            return encode_path("__builtins__.notimplemented.%s" % self.value)
paul@136 271
        else:
paul@136 272
            return self.value
paul@113 273
paul@113 274
    def __repr__(self):
paul@113 275
        return "PredefinedConstantRef(%r)" % self.value
paul@113 276
paul@141 277
class BooleanResult(Expression, TranslationResult):
paul@141 278
paul@141 279
    "A expression producing a boolean result."
paul@141 280
paul@141 281
    def __str__(self):
paul@141 282
        return "__builtins___bool_bool(%s)" % self.s
paul@141 283
paul@141 284
    def __repr__(self):
paul@141 285
        return "BooleanResult(%r)" % self.s
paul@141 286
paul@113 287
def make_expression(expr):
paul@113 288
paul@113 289
    "Make a new expression from the existing 'expr'."
paul@113 290
paul@113 291
    if isinstance(expr, results.Result):
paul@113 292
        return expr
paul@113 293
    else:
paul@113 294
        return Expression(str(expr))
paul@113 295
paul@552 296
paul@552 297
paul@113 298
# The actual translation process itself.
paul@113 299
paul@113 300
class TranslatedModule(CommonModule):
paul@113 301
paul@113 302
    "A module translator."
paul@113 303
paul@113 304
    def __init__(self, name, importer, deducer, optimiser):
paul@113 305
        CommonModule.__init__(self, name, importer)
paul@113 306
        self.deducer = deducer
paul@113 307
        self.optimiser = optimiser
paul@113 308
paul@113 309
        # Output stream.
paul@113 310
paul@482 311
        self.out_toplevel = self.out = None
paul@113 312
        self.indent = 0
paul@113 313
        self.tabstop = "    "
paul@113 314
paul@113 315
        # Recorded namespaces.
paul@113 316
paul@113 317
        self.namespaces = []
paul@113 318
        self.in_conditional = False
paul@113 319
paul@144 320
        # Exception raising adjustments.
paul@144 321
paul@144 322
        self.in_try_finally = False
paul@189 323
        self.in_try_except = False
paul@144 324
paul@237 325
        # Attribute access and accessor counting.
paul@113 326
paul@113 327
        self.attr_accesses = {}
paul@237 328
        self.attr_accessors = {}
paul@113 329
paul@482 330
        # Special variable usage.
paul@482 331
paul@482 332
        self.temp_usage = {}
paul@482 333
paul@593 334
        # Initialise some data used for attribute access generation.
paul@593 335
paul@593 336
        self.init_substitutions()
paul@593 337
paul@113 338
    def __repr__(self):
paul@113 339
        return "TranslatedModule(%r, %r)" % (self.name, self.importer)
paul@113 340
paul@113 341
    def translate(self, filename, output_filename):
paul@113 342
paul@113 343
        """
paul@113 344
        Parse the file having the given 'filename', writing the translation to
paul@113 345
        the given 'output_filename'.
paul@113 346
        """
paul@113 347
paul@113 348
        self.parse_file(filename)
paul@113 349
paul@113 350
        # Collect function namespaces for separate processing.
paul@113 351
paul@113 352
        self.record_namespaces(self.astnode)
paul@113 353
paul@113 354
        # Reset the lambda naming (in order to obtain the same names again) and
paul@113 355
        # translate the program.
paul@113 356
paul@113 357
        self.reset_lambdas()
paul@113 358
paul@482 359
        self.out_toplevel = self.out = open(output_filename, "w")
paul@113 360
        try:
paul@128 361
            self.start_output()
paul@128 362
paul@113 363
            # Process namespaces, writing the translation.
paul@113 364
paul@113 365
            for path, node in self.namespaces:
paul@113 366
                self.process_namespace(path, node)
paul@113 367
paul@113 368
            # Process the module namespace including class namespaces.
paul@113 369
paul@113 370
            self.process_namespace([], self.astnode)
paul@113 371
paul@113 372
        finally:
paul@113 373
            self.out.close()
paul@113 374
paul@113 375
    def have_object(self):
paul@113 376
paul@113 377
        "Return whether a namespace is a recorded object."
paul@113 378
paul@113 379
        return self.importer.objects.get(self.get_namespace_path())
paul@113 380
paul@156 381
    def get_builtin_class(self, name):
paul@156 382
paul@156 383
        "Return a reference to the actual object providing 'name'."
paul@156 384
paul@538 385
        return self.importer.get_object(get_builtin_class(name))
paul@156 386
paul@156 387
    def is_method(self, path):
paul@156 388
paul@156 389
        "Return whether 'path' is a method."
paul@156 390
paul@113 391
        class_name, method_name = path.rsplit(".", 1)
paul@267 392
        return self.importer.classes.has_key(class_name) and class_name or None
paul@113 393
paul@208 394
    def in_method(self):
paul@208 395
paul@208 396
        "Return whether the current namespace provides a method."
paul@208 397
paul@208 398
        return self.in_function and self.is_method(self.get_namespace_path())
paul@208 399
paul@113 400
    # Namespace recording.
paul@113 401
paul@113 402
    def record_namespaces(self, node):
paul@113 403
paul@113 404
        "Process the program structure 'node', recording namespaces."
paul@113 405
paul@113 406
        for n in node.getChildNodes():
paul@113 407
            self.record_namespaces_in_node(n)
paul@113 408
paul@113 409
    def record_namespaces_in_node(self, node):
paul@113 410
paul@113 411
        "Process the program structure 'node', recording namespaces."
paul@113 412
paul@113 413
        # Function namespaces within modules, classes and other functions.
paul@113 414
        # Functions appearing within conditional statements are given arbitrary
paul@113 415
        # names.
paul@113 416
paul@113 417
        if isinstance(node, compiler.ast.Function):
paul@113 418
            self.record_function_node(node, (self.in_conditional or self.in_function) and self.get_lambda_name() or node.name)
paul@113 419
paul@113 420
        elif isinstance(node, compiler.ast.Lambda):
paul@113 421
            self.record_function_node(node, self.get_lambda_name())
paul@113 422
paul@113 423
        # Classes are visited, but may be ignored if inside functions.
paul@113 424
paul@113 425
        elif isinstance(node, compiler.ast.Class):
paul@113 426
            self.enter_namespace(node.name)
paul@113 427
            if self.have_object():
paul@113 428
                self.record_namespaces(node)
paul@113 429
            self.exit_namespace()
paul@113 430
paul@113 431
        # Conditional nodes are tracked so that function definitions may be
paul@113 432
        # handled. Since "for" loops are converted to "while" loops, they are
paul@113 433
        # included here.
paul@113 434
paul@113 435
        elif isinstance(node, (compiler.ast.For, compiler.ast.If, compiler.ast.While)):
paul@113 436
            in_conditional = self.in_conditional
paul@113 437
            self.in_conditional = True
paul@113 438
            self.record_namespaces(node)
paul@113 439
            self.in_conditional = in_conditional
paul@113 440
paul@113 441
        # All other nodes are processed depth-first.
paul@113 442
paul@113 443
        else:
paul@113 444
            self.record_namespaces(node)
paul@113 445
paul@113 446
    def record_function_node(self, n, name):
paul@113 447
paul@113 448
        """
paul@113 449
        Record the given function, lambda, if expression or list comprehension
paul@113 450
        node 'n' with the given 'name'.
paul@113 451
        """
paul@113 452
paul@113 453
        self.in_function = True
paul@113 454
        self.enter_namespace(name)
paul@113 455
paul@113 456
        if self.have_object():
paul@113 457
paul@113 458
            # Record the namespace path and the node itself.
paul@113 459
paul@113 460
            self.namespaces.append((self.namespace_path[:], n))
paul@113 461
            self.record_namespaces_in_node(n.code)
paul@113 462
paul@113 463
        self.exit_namespace()
paul@113 464
        self.in_function = False
paul@113 465
paul@113 466
    # Constant referencing.
paul@113 467
paul@405 468
    def get_literal_instance(self, n, name=None):
paul@113 469
paul@113 470
        """
paul@405 471
        For node 'n', return a reference for the type of the given 'name', or if
paul@405 472
        'name' is not specified, deduce the type from the value.
paul@113 473
        """
paul@113 474
paul@366 475
        # Handle stray None constants (Sliceobj seems to produce them).
paul@366 476
paul@405 477
        if name is None and n.value is None:
paul@366 478
            return self.process_name_node(compiler.ast.Name("None"))
paul@366 479
paul@113 480
        if name in ("dict", "list", "tuple"):
paul@405 481
            ref = self.get_builtin_class(name)
paul@113 482
            return self.process_literal_sequence_node(n, name, ref, TrLiteralSequenceRef)
paul@113 483
        else:
paul@537 484
            value, typename, encoding = self.get_constant_value(n.value, n.literals)
paul@538 485
            ref = self.get_builtin_class(typename)
paul@397 486
            value_type = ref.get_origin()
paul@397 487
paul@113 488
            path = self.get_namespace_path()
paul@406 489
paul@406 490
            # Obtain the local numbering of the constant and thus the
paul@406 491
            # locally-qualified name.
paul@406 492
paul@406 493
            local_number = self.importer.all_constants[path][(value, value_type, encoding)]
paul@113 494
            constant_name = "$c%d" % local_number
paul@113 495
            objpath = self.get_object_path(constant_name)
paul@406 496
paul@406 497
            # Obtain the unique identifier for the constant.
paul@406 498
paul@113 499
            number = self.optimiser.constant_numbers[objpath]
paul@394 500
            return TrConstantValueRef(constant_name, ref.instance_of(), value, number)
paul@113 501
paul@113 502
    # Namespace translation.
paul@113 503
paul@113 504
    def process_namespace(self, path, node):
paul@113 505
paul@113 506
        """
paul@113 507
        Process the namespace for the given 'path' defined by the given 'node'.
paul@113 508
        """
paul@113 509
paul@113 510
        self.namespace_path = path
paul@113 511
paul@113 512
        if isinstance(node, (compiler.ast.Function, compiler.ast.Lambda)):
paul@113 513
            self.in_function = True
paul@113 514
            self.process_function_body_node(node)
paul@113 515
        else:
paul@113 516
            self.in_function = False
paul@192 517
            self.function_target = 0
paul@113 518
            self.start_module()
paul@113 519
            self.process_structure(node)
paul@113 520
            self.end_module()
paul@113 521
paul@113 522
    def process_structure(self, node):
paul@113 523
paul@113 524
        "Process the given 'node' or result."
paul@113 525
paul@144 526
        # Handle processing requests on results.
paul@144 527
paul@113 528
        if isinstance(node, results.Result):
paul@113 529
            return node
paul@144 530
paul@144 531
        # Handle processing requests on nodes.
paul@144 532
paul@113 533
        else:
paul@144 534
            l = CommonModule.process_structure(self, node)
paul@144 535
paul@144 536
            # Return indications of return statement usage.
paul@144 537
paul@144 538
            if l and isinstance(l[-1], ReturnRef):
paul@144 539
                return l[-1]
paul@144 540
            else:
paul@144 541
                return None
paul@113 542
paul@113 543
    def process_structure_node(self, n):
paul@113 544
paul@113 545
        "Process the individual node 'n'."
paul@113 546
paul@113 547
        # Plain statements emit their expressions.
paul@113 548
paul@113 549
        if isinstance(n, compiler.ast.Discard):
paul@113 550
            expr = self.process_structure_node(n.expr)
paul@113 551
            self.statement(expr)
paul@113 552
paul@314 553
        # Module import declarations.
paul@314 554
paul@314 555
        elif isinstance(n, compiler.ast.From):
paul@314 556
            self.process_from_node(n)
paul@314 557
paul@113 558
        # Nodes using operator module functions.
paul@113 559
paul@113 560
        elif isinstance(n, compiler.ast.Operator):
paul@113 561
            return self.process_operator_node(n)
paul@113 562
paul@113 563
        elif isinstance(n, compiler.ast.AugAssign):
paul@113 564
            self.process_augassign_node(n)
paul@113 565
paul@113 566
        elif isinstance(n, compiler.ast.Compare):
paul@113 567
            return self.process_compare_node(n)
paul@113 568
paul@113 569
        elif isinstance(n, compiler.ast.Slice):
paul@113 570
            return self.process_slice_node(n)
paul@113 571
paul@113 572
        elif isinstance(n, compiler.ast.Sliceobj):
paul@113 573
            return self.process_sliceobj_node(n)
paul@113 574
paul@113 575
        elif isinstance(n, compiler.ast.Subscript):
paul@113 576
            return self.process_subscript_node(n)
paul@113 577
paul@113 578
        # Classes are visited, but may be ignored if inside functions.
paul@113 579
paul@113 580
        elif isinstance(n, compiler.ast.Class):
paul@113 581
            self.process_class_node(n)
paul@113 582
paul@113 583
        # Functions within namespaces have any dynamic defaults initialised.
paul@113 584
paul@113 585
        elif isinstance(n, compiler.ast.Function):
paul@113 586
            self.process_function_node(n)
paul@113 587
paul@113 588
        # Lambdas are replaced with references to separately-generated
paul@113 589
        # functions.
paul@113 590
paul@113 591
        elif isinstance(n, compiler.ast.Lambda):
paul@113 592
            return self.process_lambda_node(n)
paul@113 593
paul@113 594
        # Assignments.
paul@113 595
paul@113 596
        elif isinstance(n, compiler.ast.Assign):
paul@113 597
paul@113 598
            # Handle each assignment node.
paul@113 599
paul@113 600
            for node in n.nodes:
paul@113 601
                self.process_assignment_node(node, n.expr)
paul@113 602
paul@113 603
        # Accesses.
paul@113 604
paul@113 605
        elif isinstance(n, compiler.ast.Getattr):
paul@113 606
            return self.process_attribute_access(n)
paul@113 607
paul@113 608
        # Names.
paul@113 609
paul@113 610
        elif isinstance(n, compiler.ast.Name):
paul@113 611
            return self.process_name_node(n)
paul@113 612
paul@113 613
        # Loops and conditionals.
paul@113 614
paul@113 615
        elif isinstance(n, compiler.ast.For):
paul@113 616
            self.process_for_node(n)
paul@113 617
paul@113 618
        elif isinstance(n, compiler.ast.While):
paul@113 619
            self.process_while_node(n)
paul@113 620
paul@113 621
        elif isinstance(n, compiler.ast.If):
paul@113 622
            self.process_if_node(n)
paul@113 623
paul@113 624
        elif isinstance(n, (compiler.ast.And, compiler.ast.Or)):
paul@113 625
            return self.process_logical_node(n)
paul@113 626
paul@113 627
        elif isinstance(n, compiler.ast.Not):
paul@113 628
            return self.process_not_node(n)
paul@113 629
paul@113 630
        # Exception control-flow tracking.
paul@113 631
paul@113 632
        elif isinstance(n, compiler.ast.TryExcept):
paul@113 633
            self.process_try_node(n)
paul@113 634
paul@113 635
        elif isinstance(n, compiler.ast.TryFinally):
paul@113 636
            self.process_try_finally_node(n)
paul@113 637
paul@113 638
        # Control-flow modification statements.
paul@113 639
paul@113 640
        elif isinstance(n, compiler.ast.Break):
paul@128 641
            self.writestmt("break;")
paul@113 642
paul@113 643
        elif isinstance(n, compiler.ast.Continue):
paul@128 644
            self.writestmt("continue;")
paul@113 645
paul@144 646
        elif isinstance(n, compiler.ast.Raise):
paul@144 647
            self.process_raise_node(n)
paul@144 648
paul@113 649
        elif isinstance(n, compiler.ast.Return):
paul@144 650
            return self.process_return_node(n)
paul@113 651
paul@173 652
        # Print statements.
paul@173 653
paul@173 654
        elif isinstance(n, (compiler.ast.Print, compiler.ast.Printnl)):
paul@173 655
            self.statement(self.process_print_node(n))
paul@173 656
paul@113 657
        # Invocations.
paul@113 658
paul@113 659
        elif isinstance(n, compiler.ast.CallFunc):
paul@113 660
            return self.process_invocation_node(n)
paul@113 661
paul@113 662
        elif isinstance(n, compiler.ast.Keyword):
paul@113 663
            return self.process_structure_node(n.expr)
paul@113 664
paul@113 665
        # Constant usage.
paul@113 666
paul@113 667
        elif isinstance(n, compiler.ast.Const):
paul@405 668
            return self.get_literal_instance(n)
paul@113 669
paul@113 670
        elif isinstance(n, compiler.ast.Dict):
paul@113 671
            return self.get_literal_instance(n, "dict")
paul@113 672
paul@113 673
        elif isinstance(n, compiler.ast.List):
paul@113 674
            return self.get_literal_instance(n, "list")
paul@113 675
paul@113 676
        elif isinstance(n, compiler.ast.Tuple):
paul@113 677
            return self.get_literal_instance(n, "tuple")
paul@113 678
paul@113 679
        # All other nodes are processed depth-first.
paul@113 680
paul@113 681
        else:
paul@144 682
            return self.process_structure(n)
paul@113 683
paul@113 684
    def process_assignment_node(self, n, expr):
paul@113 685
paul@113 686
        "Process the individual node 'n' to be assigned the contents of 'expr'."
paul@113 687
paul@113 688
        # Names and attributes are assigned the entire expression.
paul@113 689
paul@113 690
        if isinstance(n, compiler.ast.AssName):
paul@113 691
            name_ref = self.process_name_node(n, self.process_structure_node(expr))
paul@113 692
            self.statement(name_ref)
paul@113 693
paul@238 694
            # Employ guards after assignments if required.
paul@238 695
paul@238 696
            if expr and name_ref.is_name():
paul@238 697
                self.generate_guard(name_ref.name)
paul@238 698
paul@113 699
        elif isinstance(n, compiler.ast.AssAttr):
paul@124 700
            in_assignment = self.in_assignment
paul@124 701
            self.in_assignment = self.process_structure_node(expr)
paul@124 702
            self.statement(self.process_attribute_access(n))
paul@124 703
            self.in_assignment = in_assignment
paul@113 704
paul@113 705
        # Lists and tuples are matched against the expression and their
paul@113 706
        # items assigned to expression items.
paul@113 707
paul@113 708
        elif isinstance(n, (compiler.ast.AssList, compiler.ast.AssTuple)):
paul@113 709
            self.process_assignment_node_items(n, expr)
paul@113 710
paul@113 711
        # Slices and subscripts are permitted within assignment nodes.
paul@113 712
paul@113 713
        elif isinstance(n, compiler.ast.Slice):
paul@113 714
            self.statement(self.process_slice_node(n, expr))
paul@113 715
paul@113 716
        elif isinstance(n, compiler.ast.Subscript):
paul@113 717
            self.statement(self.process_subscript_node(n, expr))
paul@113 718
paul@124 719
    def process_attribute_access(self, n):
paul@113 720
paul@368 721
        "Process the given attribute access node 'n'."
paul@113 722
paul@113 723
        # Obtain any completed chain and return the reference to it.
paul@113 724
paul@113 725
        attr_expr = self.process_attribute_chain(n)
paul@113 726
        if self.have_access_expression(n):
paul@113 727
            return attr_expr
paul@113 728
paul@113 729
        # Where the start of the chain of attributes has been reached, process
paul@113 730
        # the complete access.
paul@113 731
paul@113 732
        name_ref = attr_expr and attr_expr.is_name() and attr_expr
paul@597 733
        name = name_ref and self.get_name_for_tracking(name_ref.name, name_ref and name_ref.reference()) or None
paul@113 734
paul@553 735
        location = self.get_access_location(name, self.attrs)
paul@113 736
        refs = self.get_referenced_attributes(location)
paul@113 737
paul@113 738
        # Generate access instructions.
paul@113 739
paul@113 740
        subs = {
paul@491 741
            "<expr>" : attr_expr,
paul@491 742
            "<assexpr>" : self.in_assignment,
paul@482 743
            }
paul@482 744
paul@593 745
        subs.update(self.temp_subs)
paul@593 746
        subs.update(self.op_subs)
paul@482 747
paul@113 748
        output = []
paul@482 749
        substituted = set()
paul@482 750
paul@591 751
        # The context set or retrieved will be that used by any enclosing
paul@591 752
        # invocation.
paul@591 753
paul@591 754
        context_index = self.function_target - 1
paul@591 755
paul@482 756
        # Obtain encoded versions of each instruction, accumulating temporary
paul@482 757
        # variables.
paul@113 758
paul@113 759
        for instruction in self.optimiser.access_instructions[location]:
paul@591 760
            encoded, _substituted = encode_access_instruction(instruction, subs, context_index)
paul@482 761
            output.append(encoded)
paul@482 762
            substituted.update(_substituted)
paul@482 763
paul@482 764
        # Record temporary name usage.
paul@482 765
paul@482 766
        for sub in substituted:
paul@593 767
            if self.temp_subs.has_key(sub):
paul@593 768
                self.record_temp(self.temp_subs[sub])
paul@482 769
paul@113 770
        del self.attrs[0]
paul@554 771
        return AttrResult(output, refs, location)
paul@113 772
paul@593 773
    def init_substitutions(self):
paul@593 774
paul@593 775
        """
paul@593 776
        Initialise substitutions, defining temporary variable mappings, some of
paul@593 777
        which are also used as substitutions, together with operation mappings
paul@593 778
        used as substitutions in instructions defined by the optimiser.
paul@593 779
        """
paul@593 780
paul@593 781
        self.temp_subs = {
paul@593 782
paul@593 783
            # Substitutions used by instructions.
paul@593 784
paul@593 785
            "<private_context>" : "__tmp_private_context",
paul@593 786
            "<accessor>" : "__tmp_value",
paul@593 787
            "<target_accessor>" : "__tmp_target_value",
paul@593 788
paul@593 789
            # Mappings to be replaced by those given below.
paul@593 790
paul@593 791
            "<context>" : "__tmp_contexts",
paul@594 792
            "<test_context>" : "__tmp_contexts",
paul@595 793
            "<test_context_static>" : "__tmp_contexts",
paul@593 794
            "<set_context>" : "__tmp_contexts",
paul@593 795
            "<set_private_context>" : "__tmp_private_context",
paul@593 796
            "<set_accessor>" : "__tmp_value",
paul@593 797
            "<set_target_accessor>" : "__tmp_target_value",
paul@593 798
            }
paul@593 799
paul@593 800
        self.op_subs = {
paul@593 801
            "<context>" : "__get_context",
paul@594 802
            "<test_context>" : "__test_context_set",
paul@595 803
            "<test_context_static>" : "__test_context_static",
paul@593 804
            "<set_context>" : "__set_context",
paul@593 805
            "<set_private_context>" : "__set_private_context",
paul@593 806
            "<set_accessor>" : "__set_accessor",
paul@593 807
            "<set_target_accessor>" : "__set_target_accessor",
paul@593 808
            }
paul@593 809
paul@113 810
    def get_referenced_attributes(self, location):
paul@113 811
paul@113 812
        """
paul@113 813
        Convert 'location' to the form used by the deducer and retrieve any
paul@553 814
        identified attributes.
paul@113 815
        """
paul@113 816
paul@113 817
        access_location = self.deducer.const_accesses.get(location)
paul@113 818
        refs = []
paul@113 819
        for attrtype, objpath, attr in self.deducer.referenced_attrs[access_location or location]:
paul@113 820
            refs.append(attr)
paul@113 821
        return refs
paul@113 822
paul@553 823
    def get_referenced_attribute_invocations(self, location):
paul@553 824
paul@553 825
        """
paul@553 826
        Convert 'location' to the form used by the deducer and retrieve any
paul@553 827
        identified attribute invocation details.
paul@553 828
        """
paul@553 829
paul@553 830
        access_location = self.deducer.const_accesses.get(location)
paul@553 831
        return self.deducer.reference_invocations_unsuitable.get(access_location or location)
paul@553 832
paul@234 833
    def get_accessor_kinds(self, location):
paul@234 834
paul@234 835
        "Return the accessor kinds for 'location'."
paul@234 836
paul@554 837
        return self.optimiser.accessor_kinds.get(location)
paul@234 838
paul@553 839
    def get_access_location(self, name, attrnames=None):
paul@113 840
paul@113 841
        """
paul@553 842
        Using the current namespace, the given 'name', and the 'attrnames'
paul@553 843
        employed in an access, return the access location.
paul@113 844
        """
paul@113 845
paul@113 846
        path = self.get_path_for_access()
paul@113 847
paul@113 848
        # Get the location used by the deducer and optimiser and find any
paul@113 849
        # recorded access.
paul@113 850
paul@553 851
        attrnames = attrnames and ".".join(self.attrs)
paul@113 852
        access_number = self.get_access_number(path, name, attrnames)
paul@113 853
        self.update_access_number(path, name, attrnames)
paul@113 854
        return (path, name, attrnames, access_number)
paul@113 855
paul@113 856
    def get_access_number(self, path, name, attrnames):
paul@113 857
        access = name, attrnames
paul@113 858
        if self.attr_accesses.has_key(path) and self.attr_accesses[path].has_key(access):
paul@113 859
            return self.attr_accesses[path][access]
paul@113 860
        else:
paul@113 861
            return 0
paul@113 862
paul@113 863
    def update_access_number(self, path, name, attrnames):
paul@113 864
        access = name, attrnames
paul@113 865
        if name:
paul@113 866
            init_item(self.attr_accesses, path, dict)
paul@144 867
            init_item(self.attr_accesses[path], access, lambda: 0)
paul@144 868
            self.attr_accesses[path][access] += 1
paul@113 869
paul@237 870
    def get_accessor_location(self, name):
paul@237 871
paul@237 872
        """
paul@237 873
        Using the current namespace and the given 'name', return the accessor
paul@237 874
        location.
paul@237 875
        """
paul@237 876
paul@237 877
        path = self.get_path_for_access()
paul@237 878
paul@237 879
        # Get the location used by the deducer and optimiser and find any
paul@237 880
        # recorded accessor.
paul@237 881
paul@237 882
        access_number = self.get_accessor_number(path, name)
paul@237 883
        self.update_accessor_number(path, name)
paul@237 884
        return (path, name, None, access_number)
paul@237 885
paul@237 886
    def get_accessor_number(self, path, name):
paul@237 887
        if self.attr_accessors.has_key(path) and self.attr_accessors[path].has_key(name):
paul@237 888
            return self.attr_accessors[path][name]
paul@237 889
        else:
paul@237 890
            return 0
paul@237 891
paul@237 892
    def update_accessor_number(self, path, name):
paul@237 893
        if name:
paul@237 894
            init_item(self.attr_accessors, path, dict)
paul@237 895
            init_item(self.attr_accessors[path], name, lambda: 0)
paul@237 896
            self.attr_accessors[path][name] += 1
paul@237 897
paul@113 898
    def process_class_node(self, n):
paul@113 899
paul@113 900
        "Process the given class node 'n'."
paul@113 901
paul@320 902
        class_name = self.get_object_path(n.name)
paul@320 903
paul@320 904
        # Where a class is set conditionally or where the name may refer to
paul@320 905
        # different values, assign the name.
paul@320 906
paul@320 907
        ref = self.importer.identify(class_name)
paul@320 908
paul@320 909
        if not ref.static():
paul@320 910
            self.process_assignment_for_object(
paul@577 911
                n.name, make_expression("((__attr) {.value=&%s})" %
paul@320 912
                    encode_path(class_name)))
paul@320 913
paul@113 914
        self.enter_namespace(n.name)
paul@113 915
paul@113 916
        if self.have_object():
paul@113 917
            self.write_comment("Class: %s" % class_name)
paul@113 918
paul@257 919
            self.initialise_inherited_members(class_name)
paul@257 920
paul@113 921
            self.process_structure(n)
paul@257 922
            self.write_comment("End class: %s" % class_name)
paul@113 923
paul@113 924
        self.exit_namespace()
paul@113 925
paul@257 926
    def initialise_inherited_members(self, class_name):
paul@257 927
paul@257 928
        "Initialise members of 'class_name' inherited from its ancestors."
paul@257 929
paul@257 930
        for name, path in self.importer.all_class_attrs[class_name].items():
paul@257 931
            target = "%s.%s" % (class_name, name)
paul@257 932
paul@257 933
            # Ignore attributes with definitions.
paul@257 934
paul@257 935
            ref = self.importer.identify(target)
paul@257 936
            if ref:
paul@257 937
                continue
paul@257 938
paul@320 939
            # Ignore special type attributes.
paul@320 940
paul@320 941
            if is_type_attribute(name):
paul@320 942
                continue
paul@320 943
paul@257 944
            # Reference inherited attributes.
paul@257 945
paul@257 946
            ref = self.importer.identify(path)
paul@257 947
            if ref and not ref.static():
paul@257 948
                parent, attrname = path.rsplit(".", 1)
paul@257 949
paul@257 950
                self.writestmt("__store_via_object(&%s, %s, __load_via_object(&%s, %s));" % (
paul@257 951
                    encode_path(class_name), encode_symbol("pos", name),
paul@257 952
                    encode_path(parent), encode_symbol("pos", attrname)
paul@257 953
                    ))
paul@257 954
paul@314 955
    def process_from_node(self, n):
paul@314 956
paul@314 957
        "Process the given node 'n', importing from another module."
paul@314 958
paul@314 959
        path = self.get_namespace_path()
paul@314 960
paul@314 961
        # Attempt to obtain the referenced objects.
paul@314 962
paul@314 963
        for name, alias in n.names:
paul@314 964
            if name == "*":
paul@314 965
                raise InspectError("Only explicitly specified names can be imported from modules.", path, n)
paul@314 966
paul@314 967
            # Obtain the path of the assigned name.
paul@314 968
paul@314 969
            objpath = self.get_object_path(alias or name)
paul@314 970
paul@314 971
            # Obtain the identity of the name.
paul@314 972
paul@314 973
            ref = self.importer.identify(objpath)
paul@314 974
paul@314 975
            # Where the name is not static, assign the value.
paul@314 976
paul@314 977
            if ref and not ref.static() and ref.get_name():
paul@314 978
                self.writestmt("%s;" % 
paul@314 979
                    TrResolvedNameRef(alias or name, Reference("<var>", None, objpath),
paul@314 980
                                      expr=TrResolvedNameRef(name, ref)))
paul@314 981
paul@113 982
    def process_function_body_node(self, n):
paul@113 983
paul@113 984
        """
paul@113 985
        Process the given function, lambda, if expression or list comprehension
paul@113 986
        node 'n', generating the body.
paul@113 987
        """
paul@113 988
paul@113 989
        function_name = self.get_namespace_path()
paul@113 990
        self.start_function(function_name)
paul@113 991
paul@113 992
        # Process the function body.
paul@113 993
paul@113 994
        in_conditional = self.in_conditional
paul@113 995
        self.in_conditional = False
paul@192 996
        self.function_target = 0
paul@113 997
paul@237 998
        # Process any guards defined for the parameters.
paul@237 999
paul@237 1000
        for name in self.importer.function_parameters.get(function_name):
paul@238 1001
            self.generate_guard(name)
paul@237 1002
paul@237 1003
        # Produce the body and any additional return statement.
paul@237 1004
paul@144 1005
        expr = self.process_structure_node(n.code) or PredefinedConstantRef("None")
paul@144 1006
        if not isinstance(expr, ReturnRef):
paul@128 1007
            self.writestmt("return %s;" % expr)
paul@113 1008
paul@113 1009
        self.in_conditional = in_conditional
paul@113 1010
paul@144 1011
        self.end_function(function_name)
paul@113 1012
paul@238 1013
    def generate_guard(self, name):
paul@238 1014
paul@238 1015
        """
paul@238 1016
        Get the accessor details for 'name', found in the current namespace, and
paul@238 1017
        generate any guards defined for it.
paul@238 1018
        """
paul@238 1019
paul@238 1020
        # Obtain the location, keeping track of assignment versions.
paul@238 1021
paul@238 1022
        location = self.get_accessor_location(name)
paul@238 1023
        test = self.deducer.accessor_guard_tests.get(location)
paul@238 1024
paul@238 1025
        # Generate any guard from the deduced information.
paul@238 1026
paul@238 1027
        if test:
paul@238 1028
            guard, guard_type = test
paul@238 1029
paul@238 1030
            if guard == "specific":
paul@238 1031
                ref = first(self.deducer.accessor_all_types[location])
paul@238 1032
                argstr = "&%s" % encode_path(ref.get_origin())
paul@238 1033
            elif guard == "common":
paul@238 1034
                ref = first(self.deducer.accessor_all_general_types[location])
paul@238 1035
                typeattr = encode_type_attribute(ref.get_origin())
paul@238 1036
                argstr = "%s, %s" % (encode_symbol("pos", typeattr), encode_symbol("code", typeattr))
paul@238 1037
            else:
paul@238 1038
                return
paul@238 1039
paul@257 1040
            # Produce an appropriate access to an attribute's value.
paul@257 1041
paul@257 1042
            parameters = self.importer.function_parameters.get(self.get_namespace_path())
paul@257 1043
            if parameters and name in parameters:
paul@257 1044
                name_to_value = "%s->value" % name
paul@257 1045
            else:
paul@257 1046
                name_to_value = "%s.value" % name
paul@257 1047
paul@238 1048
            # Write a test that raises a TypeError upon failure.
paul@238 1049
paul@257 1050
            self.writestmt("if (!__test_%s_%s(%s, %s)) __raise_type_error();" % (
paul@257 1051
                guard, guard_type, name_to_value, argstr))
paul@238 1052
paul@113 1053
    def process_function_node(self, n):
paul@113 1054
paul@113 1055
        """
paul@113 1056
        Process the given function, lambda, if expression or list comprehension
paul@113 1057
        node 'n', generating any initialisation statements.
paul@113 1058
        """
paul@113 1059
paul@113 1060
        # Where a function is declared conditionally, use a separate name for
paul@113 1061
        # the definition, and assign the definition to the stated name.
paul@113 1062
paul@196 1063
        original_name = n.name
paul@196 1064
paul@113 1065
        if self.in_conditional or self.in_function:
paul@113 1066
            name = self.get_lambda_name()
paul@113 1067
        else:
paul@113 1068
            name = n.name
paul@113 1069
paul@196 1070
        objpath = self.get_object_path(name)
paul@196 1071
paul@113 1072
        # Obtain details of the defaults.
paul@113 1073
paul@285 1074
        defaults = self.process_function_defaults(n, name, objpath)
paul@113 1075
        if defaults:
paul@113 1076
            for default in defaults:
paul@113 1077
                self.writeline("%s;" % default)
paul@113 1078
paul@196 1079
        # Where a function is set conditionally or where the name may refer to
paul@196 1080
        # different values, assign the name.
paul@196 1081
paul@196 1082
        ref = self.importer.identify(objpath)
paul@113 1083
paul@196 1084
        if self.in_conditional or self.in_function:
paul@320 1085
            self.process_assignment_for_object(original_name, compiler.ast.Name(name))
paul@196 1086
        elif not ref.static():
paul@267 1087
            context = self.is_method(objpath)
paul@267 1088
paul@320 1089
            self.process_assignment_for_object(original_name,
paul@577 1090
                make_expression("((__attr) {.value=&%s})" % encode_path(objpath)))
paul@113 1091
paul@285 1092
    def process_function_defaults(self, n, name, objpath, instance_name=None):
paul@113 1093
paul@113 1094
        """
paul@113 1095
        Process the given function or lambda node 'n', initialising defaults
paul@113 1096
        that are dynamically set. The given 'name' indicates the name of the
paul@285 1097
        function. The given 'objpath' indicates the origin of the function.
paul@285 1098
        The given 'instance_name' indicates the name of any separate instance
paul@285 1099
        of the function created to hold the defaults.
paul@113 1100
paul@113 1101
        Return a list of operations setting defaults on a function instance.
paul@113 1102
        """
paul@113 1103
paul@113 1104
        function_name = self.get_object_path(name)
paul@113 1105
        function_defaults = self.importer.function_defaults.get(function_name)
paul@113 1106
        if not function_defaults:
paul@113 1107
            return None
paul@113 1108
paul@113 1109
        # Determine whether any unidentified defaults are involved.
paul@113 1110
paul@285 1111
        for argname, default in function_defaults:
paul@285 1112
            if not default.static():
paul@285 1113
                break
paul@285 1114
        else:
paul@113 1115
            return None
paul@113 1116
paul@285 1117
        # Handle bound methods.
paul@285 1118
paul@285 1119
        if not instance_name:
paul@523 1120
            instance_name = "&%s" % encode_path(objpath)
paul@285 1121
paul@113 1122
        # Where defaults are involved but cannot be identified, obtain a new
paul@113 1123
        # instance of the lambda and populate the defaults.
paul@113 1124
paul@113 1125
        defaults = []
paul@113 1126
paul@113 1127
        # Join the original defaults with the inspected defaults.
paul@113 1128
paul@113 1129
        original_defaults = [(argname, default) for (argname, default) in compiler.ast.get_defaults(n) if default]
paul@113 1130
paul@113 1131
        for i, (original, inspected) in enumerate(map(None, original_defaults, function_defaults)):
paul@113 1132
paul@113 1133
            # Obtain any reference for the default.
paul@113 1134
paul@113 1135
            if original:
paul@113 1136
                argname, default = original
paul@113 1137
                name_ref = self.process_structure_node(default)
paul@113 1138
            elif inspected:
paul@113 1139
                argname, default = inspected
paul@113 1140
                name_ref = TrResolvedNameRef(argname, default)
paul@113 1141
            else:
paul@113 1142
                continue
paul@113 1143
paul@338 1144
            # Generate default initialisers except when constants are employed.
paul@338 1145
            # Constants should be used when populating the function structures.
paul@338 1146
paul@338 1147
            if name_ref and not isinstance(name_ref, TrConstantValueRef):
paul@285 1148
                defaults.append("__SETDEFAULT(%s, %s, %s)" % (instance_name, i, name_ref))
paul@113 1149
paul@113 1150
        return defaults
paul@113 1151
paul@113 1152
    def process_if_node(self, n):
paul@113 1153
paul@113 1154
        """
paul@113 1155
        Process the given "if" node 'n'.
paul@113 1156
        """
paul@113 1157
paul@113 1158
        first = True
paul@113 1159
        for test, body in n.tests:
paul@113 1160
            test_ref = self.process_structure_node(test)
paul@113 1161
            self.start_if(first, test_ref)
paul@113 1162
paul@113 1163
            in_conditional = self.in_conditional
paul@113 1164
            self.in_conditional = True
paul@113 1165
            self.process_structure_node(body)
paul@113 1166
            self.in_conditional = in_conditional
paul@113 1167
paul@113 1168
            self.end_if()
paul@113 1169
            first = False
paul@113 1170
paul@113 1171
        if n.else_:
paul@113 1172
            self.start_else()
paul@113 1173
            self.process_structure_node(n.else_)
paul@113 1174
            self.end_else()
paul@113 1175
paul@113 1176
    def process_invocation_node(self, n):
paul@113 1177
paul@113 1178
        "Process the given invocation node 'n'."
paul@113 1179
paul@590 1180
        # Any invocations in the expression will store target details in a
paul@590 1181
        # different location.
paul@590 1182
paul@590 1183
        self.function_target += 1
paul@590 1184
paul@590 1185
        # Process the expression.
paul@590 1186
paul@113 1187
        expr = self.process_structure_node(n.node)
paul@590 1188
paul@590 1189
        # Reference the current target again.
paul@590 1190
paul@590 1191
        self.function_target -= 1
paul@590 1192
paul@590 1193
        # Obtain details of the invocation expression.
paul@590 1194
paul@113 1195
        objpath = expr.get_origin()
paul@554 1196
        location = expr.access_location()
paul@552 1197
paul@552 1198
        # Identified target details.
paul@552 1199
paul@118 1200
        target = None
paul@407 1201
        target_structure = None
paul@552 1202
paul@552 1203
        # Specific function target information.
paul@552 1204
paul@242 1205
        function = None
paul@552 1206
paul@552 1207
        # Instantiation involvement.
paul@552 1208
paul@317 1209
        instantiation = False
paul@159 1210
        literal_instantiation = False
paul@552 1211
paul@552 1212
        # Invocation requirements.
paul@552 1213
paul@312 1214
        context_required = True
paul@587 1215
        have_access_context = isinstance(expr, AttrResult)
paul@552 1216
        parameters = None
paul@552 1217
paul@552 1218
        # Obtain details of the callable and of its parameters.
paul@113 1219
paul@159 1220
        # Literals may be instantiated specially.
paul@159 1221
paul@159 1222
        if expr.is_name() and expr.name.startswith("$L") and objpath:
paul@317 1223
            instantiation = literal_instantiation = objpath
paul@159 1224
            target = encode_literal_instantiator(objpath)
paul@312 1225
            context_required = False
paul@159 1226
paul@159 1227
        # Identified targets employ function pointers directly.
paul@159 1228
paul@159 1229
        elif objpath:
paul@113 1230
            parameters = self.importer.function_parameters.get(objpath)
paul@234 1231
paul@234 1232
            # Class invocation involves instantiators.
paul@234 1233
paul@118 1234
            if expr.has_kind("<class>"):
paul@317 1235
                instantiation = objpath
paul@118 1236
                target = encode_instantiator_pointer(objpath)
paul@540 1237
                init_ref = self.importer.all_class_attrs[objpath]["__init__"]
paul@540 1238
                target_structure = "&%s" % encode_path(init_ref)
paul@312 1239
                context_required = False
paul@234 1240
paul@234 1241
            # Only plain functions and bound methods employ function pointers.
paul@234 1242
paul@118 1243
            elif expr.has_kind("<function>"):
paul@242 1244
                function = objpath
paul@234 1245
paul@234 1246
                # Test for functions and methods.
paul@234 1247
paul@407 1248
                context_required = self.is_method(objpath)
paul@554 1249
                accessor_kinds = self.get_accessor_kinds(location)
paul@312 1250
                instance_accessor = accessor_kinds and \
paul@312 1251
                                    len(accessor_kinds) == 1 and \
paul@312 1252
                                    first(accessor_kinds) == "<instance>"
paul@234 1253
paul@407 1254
                # Only identify certain bound methods or functions.
paul@407 1255
paul@407 1256
                if not context_required or instance_accessor:
paul@234 1257
                    target = encode_function_pointer(objpath)
paul@407 1258
paul@407 1259
                # Access bound method defaults even if it is not clear whether
paul@407 1260
                # the accessor is appropriate.
paul@407 1261
paul@523 1262
                target_structure = "&%s" % encode_path(objpath)
paul@312 1263
paul@558 1264
        # Other targets are retrieved at run-time. Some information about them
paul@558 1265
        # may be available and be used to provide warnings about argument
paul@558 1266
        # compatibility.
paul@558 1267
paul@558 1268
        elif self.importer.give_warning("args"):
paul@554 1269
            unsuitable = self.get_referenced_attribute_invocations(location)
paul@553 1270
paul@553 1271
            if unsuitable:
paul@553 1272
                for ref in unsuitable:
paul@552 1273
                    _objpath = ref.get_origin()
paul@553 1274
                    num_parameters = len(self.importer.function_parameters[_objpath])
paul@556 1275
                    print >>sys.stderr, \
paul@556 1276
                        "In %s, at line %d, inappropriate number of " \
paul@553 1277
                        "arguments given. Need %d arguments to call %s." % (
paul@553 1278
                        self.get_namespace_path(), n.lineno, num_parameters,
paul@553 1279
                        _objpath)
paul@113 1280
paul@122 1281
        # Arguments are presented in a temporary frame array with any context
paul@312 1282
        # always being the first argument. Where it would be unused, it may be
paul@312 1283
        # set to null.
paul@122 1284
paul@312 1285
        if context_required:
paul@587 1286
            if have_access_context:
paul@591 1287
                self.record_temp("__tmp_contexts")
paul@591 1288
                args = ["(__attr) {.value=__tmp_contexts[%d]}" % self.function_target]
paul@587 1289
            else:
paul@587 1290
                self.record_temp("__tmp_targets")
paul@587 1291
                args = ["__CONTEXT_AS_VALUE(__tmp_targets[%d])" % self.function_target]
paul@312 1292
        else:
paul@477 1293
            args = ["__NULL"]
paul@312 1294
paul@552 1295
        # Complete the array with null values, permitting tests for a complete
paul@552 1296
        # set of arguments.
paul@552 1297
paul@122 1298
        args += [None] * (not parameters and len(n.args) or parameters and len(parameters) or 0)
paul@122 1299
        kwcodes = []
paul@122 1300
        kwargs = []
paul@122 1301
paul@192 1302
        # Any invocations in the arguments will store target details in a
paul@192 1303
        # different location.
paul@192 1304
paul@192 1305
        self.function_target += 1
paul@192 1306
paul@122 1307
        for i, arg in enumerate(n.args):
paul@122 1308
            argexpr = self.process_structure_node(arg)
paul@122 1309
paul@122 1310
            # Store a keyword argument, either in the argument list or
paul@122 1311
            # in a separate keyword argument list for subsequent lookup.
paul@122 1312
paul@122 1313
            if isinstance(arg, compiler.ast.Keyword):
paul@113 1314
paul@122 1315
                # With knowledge of the target, store the keyword
paul@122 1316
                # argument directly.
paul@122 1317
paul@122 1318
                if parameters:
paul@373 1319
                    try:
paul@373 1320
                        argnum = parameters.index(arg.name)
paul@373 1321
                    except ValueError:
paul@373 1322
                        raise TranslateError("Argument %s is not recognised." % arg.name,
paul@373 1323
                                             self.get_namespace_path(), n)
paul@122 1324
                    args[argnum+1] = str(argexpr)
paul@122 1325
paul@122 1326
                # Otherwise, store the details in a separate collection.
paul@122 1327
paul@122 1328
                else:
paul@122 1329
                    kwargs.append(str(argexpr))
paul@122 1330
                    kwcodes.append("{%s, %s}" % (
paul@122 1331
                        encode_symbol("ppos", arg.name),
paul@122 1332
                        encode_symbol("pcode", arg.name)))
paul@122 1333
paul@312 1334
            # Store non-keyword arguments in the argument list, rejecting
paul@312 1335
            # superfluous arguments.
paul@312 1336
paul@122 1337
            else:
paul@225 1338
                try:
paul@225 1339
                    args[i+1] = str(argexpr)
paul@225 1340
                except IndexError:
paul@225 1341
                    raise TranslateError("Too many arguments specified.",
paul@225 1342
                                         self.get_namespace_path(), n)
paul@113 1343
paul@192 1344
        # Reference the current target again.
paul@192 1345
paul@192 1346
        self.function_target -= 1
paul@192 1347
paul@113 1348
        # Defaults are added to the frame where arguments are missing.
paul@113 1349
paul@122 1350
        if parameters:
paul@122 1351
            function_defaults = self.importer.function_defaults.get(objpath)
paul@122 1352
            if function_defaults:
paul@122 1353
paul@122 1354
                # Visit each default and set any missing arguments.
paul@149 1355
                # Use the target structure to obtain defaults, as opposed to the
paul@149 1356
                # actual function involved.
paul@122 1357
paul@122 1358
                for i, (argname, default) in enumerate(function_defaults):
paul@122 1359
                    argnum = parameters.index(argname)
paul@122 1360
                    if not args[argnum+1]:
paul@285 1361
                        args[argnum+1] = "__GETDEFAULT(%s, %d)" % (target_structure, i)
paul@149 1362
paul@173 1363
        # Test for missing arguments.
paul@173 1364
paul@173 1365
        if None in args:
paul@173 1366
            raise TranslateError("Not all arguments supplied.",
paul@173 1367
                                 self.get_namespace_path(), n)
paul@173 1368
paul@149 1369
        # Encode the arguments.
paul@122 1370
paul@122 1371
        argstr = "__ARGS(%s)" % ", ".join(args)
paul@122 1372
        kwargstr = kwargs and ("__ARGS(%s)" % ", ".join(kwargs)) or "0"
paul@122 1373
        kwcodestr = kwcodes and ("__KWARGS(%s)" % ", ".join(kwcodes)) or "0"
paul@122 1374
paul@159 1375
        # Where literal instantiation is occurring, add an argument indicating
paul@159 1376
        # the number of values.
paul@159 1377
paul@159 1378
        if literal_instantiation:
paul@159 1379
            argstr += ", %d" % (len(args) - 1)
paul@159 1380
paul@156 1381
        # First, the invocation expression is presented.
paul@113 1382
paul@156 1383
        stages = []
paul@156 1384
paul@156 1385
        # Without a known specific callable, the expression provides the target.
paul@118 1386
paul@312 1387
        if not target or context_required:
paul@588 1388
            if target:
paul@588 1389
                stages.append(str(expr))
paul@588 1390
            else:
paul@588 1391
                self.record_temp("__tmp_targets")
paul@588 1392
                stages.append("__tmp_targets[%d] = %s" % (self.function_target, expr))
paul@156 1393
paul@156 1394
        # Any specific callable is then obtained.
paul@156 1395
paul@163 1396
        if target:
paul@156 1397
            stages.append(target)
paul@484 1398
paul@484 1399
        # Methods accessed via unidentified accessors are obtained. 
paul@484 1400
paul@242 1401
        elif function:
paul@482 1402
            self.record_temp("__tmp_targets")
paul@523 1403
paul@523 1404
            if context_required:
paul@587 1405
                if have_access_context:
paul@592 1406
                    self.record_temp("__tmp_contexts")
paul@591 1407
                    stages.append("__get_function(__tmp_contexts[%d], __tmp_targets[%d])" % (
paul@591 1408
                        self.function_target, self.function_target))
paul@587 1409
                else:
paul@587 1410
                    stages.append("__get_function(__CONTEXT_AS_VALUE(__tmp_targets[%d]).value, __tmp_targets[%d])" % (
paul@587 1411
                        self.function_target, self.function_target))
paul@523 1412
            else:
paul@523 1413
                stages.append("__load_via_object(__tmp_targets[%d].value, %s).fn" % (
paul@523 1414
                    self.function_target, encode_symbol("pos", "__fn__")))
paul@122 1415
paul@122 1416
        # With a known target, the function is obtained directly and called.
paul@484 1417
        # By putting the invocation at the end of the final element in the
paul@484 1418
        # instruction sequence (the stages), the result becomes the result of
paul@484 1419
        # the sequence. Moreover, the parameters become part of the sequence
paul@484 1420
        # and thereby participate in a guaranteed evaluation order.
paul@122 1421
paul@242 1422
        if target or function:
paul@498 1423
            stages[-1] += "(%s)" % argstr
paul@498 1424
            if instantiation:
paul@498 1425
                return InstantiationResult(instantiation, stages)
paul@498 1426
            else:
paul@498 1427
                return InvocationResult(stages)
paul@113 1428
paul@122 1429
        # With unknown targets, the generic invocation function is applied to
paul@122 1430
        # the callable and argument collections.
paul@113 1431
paul@122 1432
        else:
paul@482 1433
            self.record_temp("__tmp_targets")
paul@498 1434
            stages.append("__invoke(\n__tmp_targets[%d],\n%d, %d, %s, %s,\n%d, %s\n)" % (
paul@192 1435
                self.function_target,
paul@156 1436
                self.always_callable and 1 or 0,
paul@122 1437
                len(kwargs), kwcodestr, kwargstr,
paul@498 1438
                len(args), argstr))
paul@498 1439
            return InvocationResult(stages)
paul@113 1440
paul@113 1441
    def always_callable(self, refs):
paul@113 1442
paul@113 1443
        "Determine whether all 'refs' are callable."
paul@113 1444
paul@113 1445
        for ref in refs:
paul@113 1446
            if not ref.static():
paul@113 1447
                return False
paul@113 1448
            else:
paul@113 1449
                origin = ref.final()
paul@113 1450
                if not self.importer.get_attribute(origin, "__fn__"):
paul@113 1451
                    return False
paul@113 1452
        return True
paul@113 1453
paul@113 1454
    def need_default_arguments(self, objpath, nargs):
paul@113 1455
paul@113 1456
        """
paul@113 1457
        Return whether any default arguments are needed when invoking the object
paul@113 1458
        given by 'objpath'.
paul@113 1459
        """
paul@113 1460
paul@113 1461
        parameters = self.importer.function_parameters.get(objpath)
paul@113 1462
        return nargs < len(parameters)
paul@113 1463
paul@113 1464
    def process_lambda_node(self, n):
paul@113 1465
paul@113 1466
        "Process the given lambda node 'n'."
paul@113 1467
paul@113 1468
        name = self.get_lambda_name()
paul@113 1469
        function_name = self.get_object_path(name)
paul@113 1470
paul@285 1471
        defaults = self.process_function_defaults(n, name, function_name, "__tmp_value")
paul@149 1472
paul@149 1473
        # Without defaults, produce an attribute referring to the function.
paul@149 1474
paul@113 1475
        if not defaults:
paul@577 1476
            return make_expression("((__attr) {.value=&%s})" % encode_path(function_name))
paul@149 1477
paul@149 1478
        # With defaults, copy the function structure and set the defaults on the
paul@149 1479
        # copy.
paul@149 1480
paul@113 1481
        else:
paul@482 1482
            self.record_temp("__tmp_value")
paul@577 1483
            return make_expression("(__tmp_value = __COPY(&%s, sizeof(%s)), %s, (__attr) {.value=__tmp_value})" % (
paul@151 1484
                encode_path(function_name),
paul@151 1485
                encode_symbol("obj", function_name),
paul@151 1486
                ", ".join(defaults)))
paul@113 1487
paul@113 1488
    def process_logical_node(self, n):
paul@113 1489
paul@141 1490
        """
paul@141 1491
        Process the given operator node 'n'.
paul@141 1492
paul@141 1493
        Convert ... to ...
paul@141 1494
paul@141 1495
        <a> and <b>
paul@141 1496
        (__tmp_result = <a>, !__BOOL(__tmp_result)) ? __tmp_result : <b>
paul@141 1497
paul@141 1498
        <a> or <b>
paul@141 1499
        (__tmp_result = <a>, __BOOL(__tmp_result)) ? __tmp_result : <b>
paul@141 1500
        """
paul@113 1501
paul@482 1502
        self.record_temp("__tmp_result")
paul@482 1503
paul@113 1504
        if isinstance(n, compiler.ast.And):
paul@141 1505
            op = "!"
paul@113 1506
        else:
paul@141 1507
            op = ""
paul@141 1508
paul@141 1509
        results = []
paul@113 1510
paul@141 1511
        for node in n.nodes[:-1]:
paul@141 1512
            expr = self.process_structure_node(node)
paul@141 1513
            results.append("(__tmp_result = %s, %s__BOOL(__tmp_result)) ? __tmp_result : " % (expr, op))
paul@113 1514
paul@141 1515
        expr = self.process_structure_node(n.nodes[-1])
paul@141 1516
        results.append(str(expr))
paul@141 1517
paul@141 1518
        return make_expression("(%s)" % "".join(results))
paul@113 1519
paul@113 1520
    def process_name_node(self, n, expr=None):
paul@113 1521
paul@113 1522
        "Process the given name node 'n' with the optional assignment 'expr'."
paul@113 1523
paul@113 1524
        # Determine whether the name refers to a static external entity.
paul@113 1525
paul@113 1526
        if n.name in predefined_constants:
paul@399 1527
            return PredefinedConstantRef(n.name, expr)
paul@113 1528
paul@173 1529
        # Convert literal references, operator function names, and print
paul@173 1530
        # function names to references.
paul@113 1531
paul@173 1532
        elif n.name.startswith("$L") or n.name.startswith("$op") or \
paul@173 1533
             n.name.startswith("$print"):
paul@423 1534
paul@423 1535
            ref, paths = self.importer.get_module(self.name).special[n.name]
paul@113 1536
            return TrResolvedNameRef(n.name, ref)
paul@113 1537
paul@561 1538
        # Temporary names are output program locals.
paul@561 1539
paul@561 1540
        elif n.name.startswith("$t"):
paul@561 1541
            return TrResolvedNameRef(n.name, Reference("<var>"), expr=expr)
paul@561 1542
paul@113 1543
        # Get the appropriate name for the name reference, using the same method
paul@113 1544
        # as in the inspector.
paul@113 1545
paul@250 1546
        path = self.get_namespace_path()
paul@250 1547
        objpath = self.get_object_path(n.name)
paul@250 1548
paul@250 1549
        # Determine any assigned globals.
paul@250 1550
paul@250 1551
        globals = self.importer.get_module(self.name).scope_globals.get(path)
paul@250 1552
        if globals and n.name in globals:
paul@250 1553
            objpath = self.get_global_path(n.name)
paul@113 1554
paul@113 1555
        # Get the static identity of the name.
paul@113 1556
paul@250 1557
        ref = self.importer.identify(objpath)
paul@152 1558
        if ref and not ref.get_name():
paul@250 1559
            ref = ref.alias(objpath)
paul@113 1560
paul@113 1561
        # Obtain any resolved names for non-assignment names.
paul@113 1562
paul@113 1563
        if not expr and not ref and self.in_function:
paul@250 1564
            locals = self.importer.function_locals.get(path)
paul@113 1565
            ref = locals and locals.get(n.name)
paul@113 1566
paul@208 1567
        # Determine whether the name refers to a parameter. The generation of
paul@208 1568
        # parameter references is different from other names.
paul@208 1569
paul@250 1570
        parameters = self.importer.function_parameters.get(path)
paul@208 1571
        parameter = n.name == "self" and self.in_method() or \
paul@208 1572
                    parameters and n.name in parameters
paul@208 1573
paul@553 1574
        # Find any invocation details.
paul@553 1575
paul@553 1576
        location = self.get_access_location(n.name)
paul@553 1577
paul@113 1578
        # Qualified names are used for resolved static references or for
paul@113 1579
        # static namespace members. The reference should be configured to return
paul@113 1580
        # such names.
paul@113 1581
paul@554 1582
        return TrResolvedNameRef(n.name, ref, expr=expr, parameter=parameter, location=location)
paul@113 1583
paul@113 1584
    def process_not_node(self, n):
paul@113 1585
paul@113 1586
        "Process the given operator node 'n'."
paul@113 1587
paul@144 1588
        return make_expression("(__BOOL(%s) ? %s : %s)" %
paul@149 1589
            (self.process_structure_node(n.expr), PredefinedConstantRef("False"),
paul@149 1590
            PredefinedConstantRef("True")))
paul@144 1591
paul@144 1592
    def process_raise_node(self, n):
paul@144 1593
paul@144 1594
        "Process the given raise node 'n'."
paul@144 1595
paul@144 1596
        # NOTE: Determine which raise statement variants should be permitted.
paul@144 1597
paul@176 1598
        if n.expr1:
paul@467 1599
paul@467 1600
            # Names with accompanying arguments are treated like invocations.
paul@467 1601
paul@467 1602
            if n.expr2:
paul@467 1603
                call = compiler.ast.CallFunc(n.expr1, [n.expr2])
paul@467 1604
                exc = self.process_structure_node(call)
paul@467 1605
                self.writestmt("__Raise(%s);" % exc)
paul@317 1606
paul@317 1607
            # Raise instances, testing the kind at run-time if necessary and
paul@317 1608
            # instantiating any non-instance.
paul@317 1609
paul@317 1610
            else:
paul@467 1611
                exc = self.process_structure_node(n.expr1)
paul@467 1612
paul@467 1613
                if isinstance(exc, TrInstanceRef):
paul@467 1614
                    self.writestmt("__Raise(%s);" % exc)
paul@467 1615
                else:
paul@467 1616
                    self.writestmt("__Raise(__ensure_instance(%s));" % exc)
paul@176 1617
        else:
paul@346 1618
            self.writestmt("__Throw(__tmp_exc);")
paul@144 1619
paul@144 1620
    def process_return_node(self, n):
paul@144 1621
paul@144 1622
        "Process the given return node 'n'."
paul@144 1623
paul@144 1624
        expr = self.process_structure_node(n.value) or PredefinedConstantRef("None")
paul@189 1625
        if self.in_try_finally or self.in_try_except:
paul@144 1626
            self.writestmt("__Return(%s);" % expr)
paul@144 1627
        else:
paul@144 1628
            self.writestmt("return %s;" % expr)
paul@144 1629
paul@144 1630
        return ReturnRef()
paul@113 1631
paul@113 1632
    def process_try_node(self, n):
paul@113 1633
paul@113 1634
        """
paul@113 1635
        Process the given "try...except" node 'n'.
paul@113 1636
        """
paul@113 1637
paul@189 1638
        in_try_except = self.in_try_except
paul@189 1639
        self.in_try_except = True
paul@189 1640
paul@144 1641
        # Use macros to implement exception handling.
paul@113 1642
paul@144 1643
        self.writestmt("__Try")
paul@113 1644
        self.writeline("{")
paul@113 1645
        self.indent += 1
paul@113 1646
        self.process_structure_node(n.body)
paul@144 1647
paul@144 1648
        # Put the else statement in another try block that handles any raised
paul@144 1649
        # exceptions and converts them to exceptions that will not be handled by
paul@144 1650
        # the main handling block.
paul@144 1651
paul@144 1652
        if n.else_:
paul@144 1653
            self.writestmt("__Try")
paul@144 1654
            self.writeline("{")
paul@144 1655
            self.indent += 1
paul@144 1656
            self.process_structure_node(n.else_)
paul@144 1657
            self.indent -= 1
paul@144 1658
            self.writeline("}")
paul@144 1659
            self.writeline("__Catch (__tmp_exc)")
paul@144 1660
            self.writeline("{")
paul@144 1661
            self.indent += 1
paul@144 1662
            self.writeline("if (__tmp_exc.raising) __RaiseElse(__tmp_exc.arg);")
paul@191 1663
            self.writeline("else if (__tmp_exc.completing) __Throw(__tmp_exc);")
paul@144 1664
            self.indent -= 1
paul@144 1665
            self.writeline("}")
paul@144 1666
paul@144 1667
        # Complete the try block and enter the finally block, if appropriate.
paul@144 1668
paul@144 1669
        if self.in_try_finally:
paul@144 1670
            self.writestmt("__Complete;")
paul@144 1671
paul@113 1672
        self.indent -= 1
paul@113 1673
        self.writeline("}")
paul@113 1674
paul@189 1675
        self.in_try_except = in_try_except
paul@189 1676
paul@144 1677
        # Handlers are tests within a common handler block.
paul@144 1678
paul@144 1679
        self.writeline("__Catch (__tmp_exc)")
paul@144 1680
        self.writeline("{")
paul@144 1681
        self.indent += 1
paul@144 1682
paul@189 1683
        # Introduce an if statement to handle the completion of a try block.
paul@189 1684
paul@189 1685
        self.process_try_completion()
paul@189 1686
paul@144 1687
        # Handle exceptions in else blocks converted to __RaiseElse, converting
paul@144 1688
        # them back to normal exceptions.
paul@144 1689
paul@144 1690
        if n.else_:
paul@189 1691
            self.writeline("else if (__tmp_exc.raising_else) __Raise(__tmp_exc.arg);")
paul@144 1692
paul@144 1693
        # Exception handling.
paul@144 1694
paul@113 1695
        for name, var, handler in n.handlers:
paul@144 1696
paul@144 1697
            # Test for specific exceptions.
paul@144 1698
paul@113 1699
            if name is not None:
paul@113 1700
                name_ref = self.process_structure_node(name)
paul@462 1701
                self.writeline("else if (__ISINSTANCE(__tmp_exc.arg, %s))" % name_ref)
paul@144 1702
            else:
paul@189 1703
                self.writeline("else if (1)")
paul@113 1704
paul@113 1705
            self.writeline("{")
paul@113 1706
            self.indent += 1
paul@113 1707
paul@113 1708
            # Establish the local for the handler.
paul@113 1709
paul@113 1710
            if var is not None:
paul@261 1711
                self.writestmt("%s;" % self.process_name_node(var, make_expression("__tmp_exc.arg")))
paul@113 1712
paul@113 1713
            if handler is not None:
paul@113 1714
                self.process_structure_node(handler)
paul@113 1715
paul@113 1716
            self.indent -= 1
paul@113 1717
            self.writeline("}")
paul@113 1718
paul@144 1719
        # Re-raise unhandled exceptions.
paul@144 1720
paul@189 1721
        self.writeline("else __Throw(__tmp_exc);")
paul@144 1722
paul@144 1723
        # End the handler block.
paul@144 1724
paul@144 1725
        self.indent -= 1
paul@144 1726
        self.writeline("}")
paul@113 1727
paul@113 1728
    def process_try_finally_node(self, n):
paul@113 1729
paul@113 1730
        """
paul@113 1731
        Process the given "try...finally" node 'n'.
paul@113 1732
        """
paul@113 1733
paul@144 1734
        in_try_finally = self.in_try_finally
paul@144 1735
        self.in_try_finally = True
paul@113 1736
paul@144 1737
        # Use macros to implement exception handling.
paul@144 1738
paul@144 1739
        self.writestmt("__Try")
paul@113 1740
        self.writeline("{")
paul@113 1741
        self.indent += 1
paul@113 1742
        self.process_structure_node(n.body)
paul@113 1743
        self.indent -= 1
paul@113 1744
        self.writeline("}")
paul@144 1745
paul@144 1746
        self.in_try_finally = in_try_finally
paul@144 1747
paul@144 1748
        # Finally clauses handle special exceptions.
paul@144 1749
paul@144 1750
        self.writeline("__Catch (__tmp_exc)")
paul@113 1751
        self.writeline("{")
paul@113 1752
        self.indent += 1
paul@113 1753
        self.process_structure_node(n.final)
paul@144 1754
paul@189 1755
        # Introduce an if statement to handle the completion of a try block.
paul@189 1756
paul@189 1757
        self.process_try_completion()
paul@189 1758
        self.writeline("else __Throw(__tmp_exc);")
paul@189 1759
paul@189 1760
        self.indent -= 1
paul@189 1761
        self.writeline("}")
paul@189 1762
paul@189 1763
    def process_try_completion(self):
paul@189 1764
paul@189 1765
        "Generate a test for the completion of a try block."
paul@144 1766
paul@144 1767
        self.writestmt("if (__tmp_exc.completing)")
paul@144 1768
        self.writeline("{")
paul@144 1769
        self.indent += 1
paul@189 1770
paul@316 1771
        # Do not return anything at the module level.
paul@316 1772
paul@316 1773
        if self.get_namespace_path() != self.name:
paul@189 1774
paul@316 1775
            # Only use the normal return statement if no surrounding try blocks
paul@316 1776
            # apply.
paul@316 1777
paul@316 1778
            if not self.in_try_finally and not self.in_try_except:
paul@316 1779
                self.writeline("if (!__ISNULL(__tmp_exc.arg)) return __tmp_exc.arg;")
paul@316 1780
            else:
paul@316 1781
                self.writeline("if (!__ISNULL(__tmp_exc.arg)) __Throw(__tmp_exc);")
paul@144 1782
paul@113 1783
        self.indent -= 1
paul@113 1784
        self.writeline("}")
paul@113 1785
paul@113 1786
    def process_while_node(self, n):
paul@113 1787
paul@113 1788
        "Process the given while node 'n'."
paul@113 1789
paul@113 1790
        self.writeline("while (1)")
paul@113 1791
        self.writeline("{")
paul@113 1792
        self.indent += 1
paul@113 1793
        test = self.process_structure_node(n.test)
paul@113 1794
paul@113 1795
        # Emit the loop termination condition unless "while <true value>" is
paul@113 1796
        # indicated.
paul@113 1797
paul@113 1798
        if not (isinstance(test, PredefinedConstantRef) and test.value):
paul@113 1799
paul@113 1800
            # NOTE: This needs to evaluate whether the operand is true or false
paul@113 1801
            # NOTE: according to Python rules.
paul@113 1802
paul@144 1803
            self.writeline("if (!__BOOL(%s))" % test)
paul@113 1804
            self.writeline("{")
paul@113 1805
            self.indent += 1
paul@113 1806
            if n.else_:
paul@113 1807
                self.process_structure_node(n.else_)
paul@128 1808
            self.writestmt("break;")
paul@113 1809
            self.indent -= 1
paul@113 1810
            self.writeline("}")
paul@113 1811
paul@113 1812
        in_conditional = self.in_conditional
paul@113 1813
        self.in_conditional = True
paul@113 1814
        self.process_structure_node(n.body)
paul@113 1815
        self.in_conditional = in_conditional
paul@113 1816
paul@113 1817
        self.indent -= 1
paul@113 1818
        self.writeline("}")
paul@113 1819
paul@482 1820
    # Special variable usage.
paul@482 1821
paul@482 1822
    def record_temp(self, name):
paul@482 1823
paul@482 1824
        """
paul@482 1825
        Record the use of the temporary 'name' in the current namespace. At the
paul@482 1826
        class or module level, the temporary name is associated with the module,
paul@482 1827
        since the variable will then be allocated in the module's own main
paul@482 1828
        program.
paul@482 1829
        """
paul@482 1830
paul@482 1831
        if self.in_function:
paul@482 1832
            path = self.get_namespace_path()
paul@482 1833
        else:
paul@482 1834
            path = self.name
paul@482 1835
paul@482 1836
        init_item(self.temp_usage, path, set)
paul@482 1837
        self.temp_usage[path].add(name)
paul@482 1838
paul@482 1839
    def uses_temp(self, path, name):
paul@482 1840
paul@482 1841
        """
paul@482 1842
        Return whether the given namespace 'path' employs a temporary variable
paul@482 1843
        with the given 'name'. Note that 'path' should only be a module or a
paul@482 1844
        function or method, not a class.
paul@482 1845
        """
paul@482 1846
paul@482 1847
        return self.temp_usage.has_key(path) and name in self.temp_usage[path]
paul@482 1848
paul@113 1849
    # Output generation.
paul@113 1850
paul@128 1851
    def start_output(self):
paul@159 1852
paul@159 1853
        "Write the declarations at the top of each source file."
paul@159 1854
paul@128 1855
        print >>self.out, """\
paul@128 1856
#include "types.h"
paul@144 1857
#include "exceptions.h"
paul@128 1858
#include "ops.h"
paul@128 1859
#include "progconsts.h"
paul@128 1860
#include "progops.h"
paul@128 1861
#include "progtypes.h"
paul@137 1862
#include "main.h"
paul@128 1863
"""
paul@128 1864
paul@482 1865
    def start_unit(self):
paul@482 1866
paul@482 1867
        "Record output within a generated function for later use."
paul@482 1868
paul@482 1869
        self.out = StringIO()
paul@482 1870
paul@482 1871
    def end_unit(self, name):
paul@482 1872
paul@482 1873
        "Add declarations and generated code."
paul@482 1874
paul@482 1875
        # Restore the output stream.
paul@482 1876
paul@482 1877
        out = self.out
paul@482 1878
        self.out = self.out_toplevel
paul@482 1879
paul@482 1880
        self.write_temporaries(name)
paul@482 1881
        out.seek(0)
paul@482 1882
        self.out.write(out.read())
paul@482 1883
paul@482 1884
        self.indent -= 1
paul@482 1885
        print >>self.out, "}"
paul@482 1886
paul@113 1887
    def start_module(self):
paul@159 1888
paul@159 1889
        "Write the start of each module's main function."
paul@159 1890
paul@113 1891
        print >>self.out, "void __main_%s()" % encode_path(self.name)
paul@113 1892
        print >>self.out, "{"
paul@113 1893
        self.indent += 1
paul@561 1894
paul@561 1895
        # Define temporary variables, excluded from the module structure itself.
paul@561 1896
paul@561 1897
        tempnames = []
paul@561 1898
paul@561 1899
        for n in self.importer.all_module_attrs[self.name]:
paul@561 1900
            if n.startswith("$t"):
paul@561 1901
                tempnames.append(encode_path(n))
paul@561 1902
paul@561 1903
        if tempnames:
paul@561 1904
            tempnames.sort()
paul@561 1905
            self.writeline("__attr %s;" % ", ".join(tempnames))
paul@561 1906
paul@482 1907
        self.start_unit()
paul@113 1908
paul@113 1909
    def end_module(self):
paul@159 1910
paul@159 1911
        "End each module by closing its main function."
paul@159 1912
paul@482 1913
        self.end_unit(self.name)
paul@113 1914
paul@113 1915
    def start_function(self, name):
paul@159 1916
paul@159 1917
        "Start the function having the given 'name'."
paul@159 1918
paul@113 1919
        print >>self.out, "__attr %s(__attr __args[])" % encode_function_pointer(name)
paul@113 1920
        print >>self.out, "{"
paul@113 1921
        self.indent += 1
paul@113 1922
paul@113 1923
        # Obtain local names from parameters.
paul@113 1924
paul@113 1925
        parameters = self.importer.function_parameters[name]
paul@144 1926
        locals = self.importer.function_locals[name].keys()
paul@113 1927
        names = []
paul@113 1928
paul@113 1929
        for n in locals:
paul@113 1930
paul@113 1931
            # Filter out special names and parameters. Note that self is a local
paul@113 1932
            # regardless of whether it originally appeared in the parameters or
paul@113 1933
            # not.
paul@113 1934
paul@113 1935
            if n.startswith("$l") or n in parameters or n == "self":
paul@113 1936
                continue
paul@113 1937
            names.append(encode_path(n))
paul@113 1938
paul@113 1939
        # Emit required local names.
paul@113 1940
paul@113 1941
        if names:
paul@113 1942
            names.sort()
paul@113 1943
            self.writeline("__attr %s;" % ", ".join(names))
paul@113 1944
paul@208 1945
        self.write_parameters(name)
paul@482 1946
        self.start_unit()
paul@144 1947
paul@144 1948
    def end_function(self, name):
paul@159 1949
paul@159 1950
        "End the function having the given 'name'."
paul@159 1951
paul@482 1952
        self.end_unit(name)
paul@144 1953
        print >>self.out
paul@144 1954
paul@482 1955
    def write_temporaries(self, name):
paul@482 1956
paul@482 1957
        "Write temporary storage employed by 'name'."
paul@482 1958
paul@482 1959
        # Provide space for the given number of targets.
paul@482 1960
paul@591 1961
        targets = self.importer.function_targets.get(name)
paul@591 1962
paul@482 1963
        if self.uses_temp(name, "__tmp_targets"):
paul@482 1964
            self.writeline("__attr __tmp_targets[%d];" % targets)
paul@591 1965
        if self.uses_temp(name, "__tmp_contexts"):
paul@591 1966
            self.writeline("__ref __tmp_contexts[%d];" % targets)
paul@482 1967
paul@482 1968
        # Add temporary variable usage details.
paul@482 1969
paul@592 1970
        if self.uses_temp(name, "__tmp_private_context"):
paul@592 1971
            self.writeline("__ref __tmp_private_context;")
paul@482 1972
        if self.uses_temp(name, "__tmp_value"):
paul@482 1973
            self.writeline("__ref __tmp_value;")
paul@482 1974
        if self.uses_temp(name, "__tmp_target_value"):
paul@482 1975
            self.writeline("__ref __tmp_target_value;")
paul@482 1976
        if self.uses_temp(name, "__tmp_result"):
paul@482 1977
            self.writeline("__attr __tmp_result;")
paul@479 1978
paul@479 1979
        module = self.importer.get_module(self.name)
paul@482 1980
paul@482 1981
        if name in module.exception_namespaces:
paul@479 1982
            self.writeline("__exc __tmp_exc;")
paul@149 1983
paul@208 1984
    def write_parameters(self, name):
paul@159 1985
paul@159 1986
        """
paul@159 1987
        For the function having the given 'name', write definitions of
paul@208 1988
        parameters found in the arguments array.
paul@159 1989
        """
paul@159 1990
paul@144 1991
        parameters = self.importer.function_parameters[name]
paul@144 1992
paul@113 1993
        # Generate any self reference.
paul@113 1994
paul@156 1995
        if self.is_method(name):
paul@208 1996
            self.writeline("__attr * const self = &__args[0];")
paul@113 1997
paul@113 1998
        # Generate aliases for the parameters.
paul@113 1999
paul@113 2000
        for i, parameter in enumerate(parameters):
paul@208 2001
            self.writeline("__attr * const %s = &__args[%d];" % (encode_path(parameter), i+1))
paul@113 2002
paul@113 2003
    def start_if(self, first, test_ref):
paul@144 2004
        self.writestmt("%sif (__BOOL(%s))" % (not first and "else " or "", test_ref))
paul@113 2005
        self.writeline("{")
paul@113 2006
        self.indent += 1
paul@113 2007
paul@113 2008
    def end_if(self):
paul@113 2009
        self.indent -= 1
paul@113 2010
        self.writeline("}")
paul@113 2011
paul@113 2012
    def start_else(self):
paul@113 2013
        self.writeline("else")
paul@113 2014
        self.writeline("{")
paul@113 2015
        self.indent += 1
paul@113 2016
paul@113 2017
    def end_else(self):
paul@113 2018
        self.indent -= 1
paul@113 2019
        self.writeline("}")
paul@113 2020
paul@113 2021
    def statement(self, expr):
paul@113 2022
        s = str(expr)
paul@113 2023
        if s:
paul@128 2024
            self.writestmt("%s;" % s)
paul@113 2025
paul@113 2026
    def statements(self, results):
paul@113 2027
        for result in results:
paul@113 2028
            self.statement(result)
paul@113 2029
paul@159 2030
    def writeline(self, s):
paul@159 2031
        print >>self.out, "%s%s" % (self.pad(), self.indenttext(s, self.indent + 1))
paul@159 2032
paul@159 2033
    def writestmt(self, s):
paul@159 2034
        print >>self.out
paul@159 2035
        self.writeline(s)
paul@159 2036
paul@159 2037
    def write_comment(self, s):
paul@159 2038
        self.writestmt("/* %s */" % s)
paul@159 2039
paul@113 2040
    def pad(self, extra=0):
paul@113 2041
        return (self.indent + extra) * self.tabstop
paul@113 2042
paul@113 2043
    def indenttext(self, s, levels):
paul@116 2044
        lines = s.split("\n")
paul@116 2045
        out = [lines[0]]
paul@116 2046
        for line in lines[1:]:
paul@116 2047
            out.append(levels * self.tabstop + line)
paul@116 2048
            if line.endswith("("):
paul@116 2049
                levels += 1
paul@122 2050
            elif line.startswith(")"):
paul@116 2051
                levels -= 1
paul@116 2052
        return "\n".join(out)
paul@113 2053
paul@113 2054
# vim: tabstop=4 expandtab shiftwidth=4