Lichen

Annotated translator.py

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