Lichen

Annotated translator.py

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