Lichen

Annotated translator.py

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