Lichen

Annotated translator.py

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