Lichen

Annotated encoders.py

1029:b4d8e43dad4a
5 months ago Paul Boddie Merged changes from the trailing-data branch. value-replacement
paul@0 1
#!/usr/bin/env python
paul@0 2
paul@0 3
"""
paul@0 4
Encoder functions, producing representations of program objects.
paul@0 5
paul@833 6
Copyright (C) 2016, 2017, 2018 Paul Boddie <paul@boddie.org.uk>
paul@0 7
paul@0 8
This program is free software; you can redistribute it and/or modify it under
paul@0 9
the terms of the GNU General Public License as published by the Free Software
paul@0 10
Foundation; either version 3 of the License, or (at your option) any later
paul@0 11
version.
paul@0 12
paul@0 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@0 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@0 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@0 16
details.
paul@0 17
paul@0 18
You should have received a copy of the GNU General Public License along with
paul@0 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@0 20
"""
paul@0 21
paul@498 22
from common import first, InstructionSequence
paul@56 23
paul@609 24
paul@609 25
paul@609 26
# Value digest computation.
paul@609 27
paul@609 28
from base64 import b64encode
paul@609 29
from hashlib import sha1
paul@609 30
paul@609 31
def digest(values):
paul@609 32
    m = sha1()
paul@609 33
    for value in values:
paul@609 34
        m.update(str(value))
paul@609 35
    return b64encode(m.digest()).replace("+", "__").replace("/", "_").rstrip("=")
paul@609 36
paul@609 37
paul@609 38
paul@0 39
# Output encoding and decoding for the summary files.
paul@0 40
paul@0 41
def encode_attrnames(attrnames):
paul@0 42
paul@0 43
    "Encode the 'attrnames' representing usage."
paul@0 44
paul@0 45
    return ", ".join(attrnames) or "{}"
paul@0 46
paul@0 47
def encode_constrained(constrained):
paul@0 48
paul@0 49
    "Encode the 'constrained' status for program summaries."
paul@0 50
paul@0 51
    return constrained and "constrained" or "deduced"
paul@0 52
paul@0 53
def encode_usage(usage):
paul@0 54
paul@0 55
    "Encode attribute details from 'usage'."
paul@0 56
paul@0 57
    all_attrnames = []
paul@0 58
    for t in usage:
paul@107 59
        attrname, invocation, assignment = t
paul@107 60
        all_attrnames.append("%s%s" % (attrname, invocation and "!" or assignment and "=" or ""))
paul@0 61
    return ", ".join(all_attrnames) or "{}"
paul@0 62
paul@88 63
def decode_usage(s):
paul@88 64
paul@88 65
    "Decode attribute details from 's'."
paul@88 66
paul@88 67
    all_attrnames = set()
paul@88 68
    for attrname_str in s.split(", "):
paul@107 69
        all_attrnames.add((attrname_str.rstrip("!="), attrname_str.endswith("!"), attrname_str.endswith("=")))
paul@88 70
paul@88 71
    all_attrnames = list(all_attrnames)
paul@88 72
    all_attrnames.sort()
paul@88 73
    return tuple(all_attrnames)
paul@88 74
paul@0 75
def encode_access_location(t):
paul@0 76
paul@0 77
    "Encode the access location 't'."
paul@0 78
paul@791 79
    return "%s:%s:%s:%d" % (t.path, t.name or "{}", t.attrnames or "{}", t.access_number)
paul@0 80
paul@841 81
def decode_access_location(s):
paul@841 82
paul@841 83
    "Decode the access location 's'."
paul@841 84
paul@841 85
    path, name, attrnames, access_number = s.split(":")
paul@841 86
    return path, name, attrnames, access_number
paul@841 87
paul@789 88
def encode_alias_location(t, invocation=False):
paul@789 89
paul@789 90
    "Encode the alias location 't'."
paul@789 91
paul@791 92
    return "%s:%s:%s%s%s%s" % (t.path, t.name or "{}", t.attrnames or "{}",
paul@791 93
        t.version is not None and ":=%d" % t.version or "",
paul@791 94
        t.access_number is not None and ":#%d" % t.access_number or "",
paul@791 95
        invocation and "!" or "")
paul@789 96
paul@833 97
def decode_alias_location(s):
paul@833 98
paul@833 99
    "Decode the alias location 's'."
paul@833 100
paul@833 101
    path, name, rest = s.split(":", 2)
paul@833 102
    attrnames = version = access_number = None
paul@833 103
    invocation = rest.endswith("!")
paul@833 104
paul@833 105
    t = rest.rstrip("!").split(":#")
paul@833 106
    if len(t) > 1:
paul@833 107
        rest = t[0]; access_number = int(t[1])
paul@833 108
paul@833 109
    t = rest.split(":=")
paul@833 110
    if len(t) > 1:
paul@833 111
        attrnames = t[0]; version = int(t[1])
paul@833 112
    else:
paul@833 113
        attrnames = rest
paul@833 114
paul@833 115
    return path, name, attrnames, version, access_number, invocation
paul@833 116
paul@0 117
def encode_location(t):
paul@0 118
paul@0 119
    "Encode the general location 't' in a concise form."
paul@0 120
paul@791 121
    if t.name is not None and t.version is not None:
paul@791 122
        return "%s:%s:%d" % (t.path, t.name, t.version)
paul@791 123
    elif t.name is not None:
paul@791 124
        return "%s:%s" % (t.path, t.name)
paul@0 125
    else:
paul@791 126
        return "%s::%s" % (t.path, t.attrnames)
paul@0 127
paul@0 128
def encode_modifiers(modifiers):
paul@0 129
paul@553 130
    "Encode assignment and invocation details from 'modifiers'."
paul@0 131
paul@0 132
    all_modifiers = []
paul@0 133
    for t in modifiers:
paul@0 134
        all_modifiers.append(encode_modifier_term(t))
paul@0 135
    return "".join(all_modifiers)
paul@0 136
paul@0 137
def encode_modifier_term(t):
paul@0 138
paul@553 139
    "Encode modifier 't' representing an assignment or an invocation."
paul@0 140
paul@117 141
    assignment, invocation = t
paul@553 142
    if assignment:
paul@553 143
        return "="
paul@553 144
    elif invocation is not None:
paul@557 145
        arguments, keywords = invocation
paul@557 146
        return "(%d;%s)" % (arguments, ",".join(keywords))
paul@553 147
    else:
paul@553 148
        return "_"
paul@0 149
paul@553 150
def decode_modifiers(s):
paul@553 151
paul@553 152
    "Decode 's' containing modifiers."
paul@553 153
paul@553 154
    i = 0
paul@553 155
    end = len(s)
paul@0 156
paul@553 157
    modifiers = []
paul@0 158
paul@553 159
    while i < end:
paul@553 160
        if s[i] == "=":
paul@553 161
            modifiers.append((True, None))
paul@553 162
            i += 1
paul@553 163
        elif s[i] == "(":
paul@557 164
            j = s.index(";", i)
paul@557 165
            arguments = int(s[i+1:j])
paul@557 166
            i = j
paul@553 167
            j = s.index(")", i)
paul@557 168
            keywords = s[i+1:j]
paul@557 169
            keywords = keywords and keywords.split(",") or []
paul@557 170
            modifiers.append((False, (arguments, keywords)))
paul@553 171
            i = j + 1
paul@553 172
        else:
paul@553 173
            modifiers.append((False, None))
paul@553 174
            i += 1
paul@553 175
paul@553 176
    return modifiers
paul@0 177
paul@56 178
paul@56 179
paul@56 180
# Test generation functions.
paul@56 181
paul@56 182
def get_kinds(all_types):
paul@56 183
paul@56 184
    """ 
paul@56 185
    Return object kind details for 'all_types', being a collection of
paul@56 186
    references for program types.
paul@56 187
    """
paul@56 188
paul@56 189
    return map(lambda ref: ref.get_kind(), all_types)
paul@56 190
paul@237 191
def test_label_for_kind(kind):
paul@56 192
paul@237 193
    "Return the label used for 'kind' in test details."
paul@56 194
paul@237 195
    return kind == "<instance>" and "instance" or "type"
paul@56 196
paul@237 197
def test_label_for_type(ref):
paul@56 198
paul@237 199
    "Return the label used for 'ref' in test details."
paul@56 200
paul@237 201
    return test_label_for_kind(ref.get_kind())
paul@56 202
paul@56 203
paul@56 204
paul@94 205
# Instruction representation encoding.
paul@94 206
paul@94 207
def encode_instruction(instruction):
paul@94 208
paul@94 209
    """
paul@94 210
    Encode the 'instruction' - a sequence starting with an operation and
paul@94 211
    followed by arguments, each of which may be an instruction sequence or a
paul@94 212
    plain value - to produce a function call string representation.
paul@94 213
    """
paul@94 214
paul@94 215
    op = instruction[0]
paul@94 216
    args = instruction[1:]
paul@94 217
paul@94 218
    if args:
paul@94 219
        a = []
paul@113 220
        for arg in args:
paul@113 221
            if isinstance(arg, tuple):
paul@113 222
                a.append(encode_instruction(arg))
paul@94 223
            else:
paul@113 224
                a.append(arg or "{}")
paul@94 225
        argstr = "(%s)" % ", ".join(a)
paul@94 226
        return "%s%s" % (op, argstr)
paul@94 227
    else:
paul@94 228
        return op
paul@94 229
paul@94 230
paul@94 231
paul@0 232
# Output program encoding.
paul@0 233
paul@153 234
attribute_loading_ops = (
paul@153 235
    "__load_via_class", "__load_via_object", "__get_class_and_load",
paul@113 236
    "__check_and_load_via_class", "__check_and_load_via_object", "__check_and_load_via_any",
paul@153 237
    )
paul@153 238
paul@989 239
attribute_ref_lookup_ops = (
paul@989 240
    "__get_object_attr_ref", "__get_class_attr_ref",
paul@989 241
    "__check_and_get_object_attr_ref",
paul@113 242
    )
paul@113 243
paul@113 244
typename_ops = (
paul@144 245
    "__test_common_instance", "__test_common_object", "__test_common_type",
paul@113 246
    )
paul@113 247
paul@385 248
type_ops = (
paul@385 249
    "__test_specific_instance", "__test_specific_object", "__test_specific_type",
paul@385 250
    )
paul@385 251
paul@141 252
static_ops = (
paul@595 253
    "__load_static_ignore", "__load_static_replace", "__load_static_test", "<test_context_static>",
paul@141 254
    )
paul@141 255
paul@858 256
accessor_values = (
paul@858 257
    "<accessor>",
paul@858 258
    )
paul@858 259
paul@858 260
accessor_ops = (
paul@858 261
    "<accessor>", "<set_accessor>",
paul@858 262
    )
paul@858 263
paul@989 264
attribute_ref_values = (
paul@989 265
    "<attr_ref>",
paul@989 266
    )
paul@989 267
paul@989 268
attribute_ref_ops = (
paul@989 269
    "<attr_ref>", "<set_attr_ref>",
paul@989 270
    )
paul@989 271
paul@591 272
context_values = (
paul@591 273
    "<context>",
paul@591 274
    )
paul@591 275
paul@591 276
context_ops = (
paul@601 277
    "<context>", "<set_context>", "<test_context_revert>", "<test_context_static>",
paul@591 278
    )
paul@591 279
paul@602 280
context_op_functions = (
paul@602 281
    "<test_context_revert>", "<test_context_static>",
paul@602 282
    )
paul@602 283
paul@989 284
reference_acting_ops = attribute_ref_lookup_ops + attribute_loading_ops + type_ops + typename_ops
paul@989 285
attribute_producing_ops = attribute_loading_ops
paul@153 286
paul@757 287
attribute_producing_variables = (
paul@757 288
    "<accessor>", "<context>", "<name>", "<private_context>", "<target_accessor>"
paul@757 289
    )
paul@757 290
paul@989 291
def encode_access_instruction(instruction, subs, accessor_index, context_index,
paul@989 292
    attribute_ref_index):
paul@113 293
paul@113 294
    """
paul@113 295
    Encode the 'instruction' - a sequence starting with an operation and
paul@113 296
    followed by arguments, each of which may be an instruction sequence or a
paul@113 297
    plain value - to produce a function call string representation.
paul@113 298
paul@113 299
    The 'subs' parameter defines a mapping of substitutions for special values
paul@113 300
    used in instructions.
paul@482 301
paul@858 302
    The 'accessor_index' parameter defines the position in local accessor
paul@858 303
    storage for the referenced accessor or affected by an accessor operation.
paul@858 304
paul@591 305
    The 'context_index' parameter defines the position in local context storage
paul@591 306
    for the referenced context or affected by a context operation.
paul@591 307
paul@989 308
    The 'attribute_ref_index' parameter defines the position in local attribute
paul@989 309
    reference storage for a referenced attribute.
paul@989 310
paul@482 311
    Return both the encoded instruction and a collection of substituted names.
paul@113 312
    """
paul@113 313
paul@113 314
    op = instruction[0]
paul@113 315
    args = instruction[1:]
paul@482 316
    substituted = set()
paul@113 317
paul@591 318
    # Encode the arguments.
paul@113 319
paul@591 320
    a = []
paul@591 321
    if args:
paul@153 322
        converting_op = op
paul@113 323
        for arg in args:
paul@989 324
            s, _substituted = encode_access_instruction_arg(arg, subs,
paul@989 325
                converting_op, accessor_index, context_index, attribute_ref_index)
paul@482 326
            substituted.update(_substituted)
paul@482 327
            a.append(s)
paul@153 328
            converting_op = None
paul@113 329
paul@591 330
    # Modify certain arguments.
paul@113 331
paul@624 332
    # Convert type name arguments.
paul@113 333
paul@624 334
    if op in typename_ops:
paul@624 335
        a[1] = encode_path(encode_type_attribute(args[1]))
paul@113 336
paul@591 337
    # Obtain addresses of type arguments.
paul@591 338
paul@591 339
    elif op in type_ops:
paul@591 340
        a[1] = "&%s" % a[1]
paul@113 341
paul@591 342
    # Obtain addresses of static objects.
paul@591 343
paul@591 344
    elif op in static_ops:
paul@591 345
        a[-1] = "&%s" % a[-1]
paul@385 346
paul@858 347
    # Add accessor storage information to certain operations.
paul@858 348
paul@858 349
    if op in accessor_ops:
paul@858 350
        a.insert(0, accessor_index)
paul@858 351
paul@989 352
    # Add attribute reference storage information to certain operations.
paul@989 353
paul@989 354
    if op in attribute_ref_ops:
paul@989 355
        a.insert(0, attribute_ref_index)
paul@989 356
paul@591 357
    # Add context storage information to certain operations.
paul@385 358
paul@595 359
    if op in context_ops:
paul@591 360
        a.insert(0, context_index)
paul@141 361
paul@602 362
    # Add the local context array to certain operations.
paul@602 363
paul@602 364
    if op in context_op_functions:
paul@602 365
        a.append("__tmp_contexts")
paul@602 366
paul@591 367
    # Define any argument string.
paul@141 368
paul@591 369
    if a:
paul@491 370
        argstr = "(%s)" % ", ".join(map(str, a))
paul@591 371
    else:
paul@591 372
        argstr = ""
paul@113 373
paul@113 374
    # Substitute the first element of the instruction, which may not be an
paul@113 375
    # operation at all.
paul@113 376
paul@144 377
    if subs.has_key(op):
paul@482 378
        substituted.add(op)
paul@498 379
paul@498 380
        # Break accessor initialisation into initialisation and value-yielding
paul@498 381
        # parts:
paul@498 382
paul@498 383
        if op == "<set_accessor>" and isinstance(a[0], InstructionSequence):
paul@498 384
            ops = []
paul@498 385
            ops += a[0].get_init_instructions()
paul@498 386
            ops.append("%s(%s)" % (subs[op], a[0].get_value_instruction()))
paul@498 387
            return ", ".join(map(str, ops)), substituted
paul@498 388
paul@144 389
        op = subs[op]
paul@498 390
paul@144 391
    elif not args:
paul@144 392
        op = "&%s" % encode_path(op)
paul@144 393
paul@482 394
    return "%s%s" % (op, argstr), substituted
paul@113 395
paul@989 396
def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index, attribute_ref_index):
paul@113 397
paul@482 398
    """
paul@591 399
    Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the
paul@858 400
    operation to which the argument belongs, and with 'accessor_index' and
paul@858 401
    'context_index' indicating any affected accessor and context storage.
paul@591 402
paul@591 403
    Return a tuple containing the encoded form of 'arg' along with a collection
paul@591 404
    of any substituted values.
paul@482 405
    """
paul@113 406
paul@113 407
    if isinstance(arg, tuple):
paul@989 408
        encoded, substituted = encode_access_instruction(arg, subs,
paul@989 409
            accessor_index, context_index, attribute_ref_index)
paul@757 410
        return attribute_to_reference(op, arg[0], encoded, substituted)
paul@113 411
paul@113 412
    # Special values only need replacing, not encoding.
paul@113 413
paul@113 414
    elif subs.has_key(arg):
paul@591 415
paul@591 416
        # Handle values modified by storage details.
paul@591 417
paul@858 418
        if arg in accessor_values or arg in context_values:
paul@757 419
            encoded = "%s(%s)" % (subs.get(arg), context_index)
paul@989 420
        elif arg in attribute_ref_values:
paul@989 421
            encoded = "%s(%s)" % (subs.get(arg), attribute_ref_index)
paul@591 422
        else:
paul@757 423
            encoded = subs.get(arg)
paul@757 424
paul@757 425
        substituted = set([arg])
paul@757 426
        return attribute_to_reference(op, arg, encoded, substituted)
paul@113 427
paul@258 428
    # Convert static references to the appropriate type.
paul@258 429
paul@757 430
    elif op and op in reference_acting_ops and \
paul@757 431
         arg not in attribute_producing_variables:
paul@757 432
paul@482 433
        return "&%s" % encode_path(arg), set()
paul@258 434
paul@113 435
    # Other values may need encoding.
paul@113 436
paul@113 437
    else:
paul@482 438
        return encode_path(arg), set()
paul@113 439
paul@757 440
def attribute_to_reference(op, arg, encoded, substituted):
paul@757 441
paul@757 442
    # Convert attribute results to references where required.
paul@757 443
paul@757 444
    if op and op in reference_acting_ops and (
paul@757 445
       arg in attribute_producing_ops or
paul@757 446
       arg in attribute_producing_variables):
paul@757 447
paul@757 448
        return "__VALUE(%s)" % encoded, substituted
paul@757 449
    else:
paul@757 450
        return encoded, substituted
paul@757 451
paul@0 452
def encode_function_pointer(path):
paul@0 453
paul@0 454
    "Encode 'path' as a reference to an output program function."
paul@0 455
paul@0 456
    return "__fn_%s" % encode_path(path)
paul@0 457
paul@0 458
def encode_instantiator_pointer(path):
paul@0 459
paul@0 460
    "Encode 'path' as a reference to an output program instantiator."
paul@0 461
paul@0 462
    return "__new_%s" % encode_path(path)
paul@0 463
paul@491 464
def encode_instructions(instructions):
paul@491 465
paul@491 466
    "Encode 'instructions' as a sequence."
paul@491 467
paul@491 468
    if len(instructions) == 1:
paul@491 469
        return instructions[0]
paul@491 470
    else:
paul@491 471
        return "(\n%s\n)" % ",\n".join(instructions)
paul@491 472
paul@136 473
def encode_literal_constant(n):
paul@136 474
paul@136 475
    "Encode a name for the literal constant with the number 'n'."
paul@136 476
paul@609 477
    return "__const%s" % n
paul@136 478
paul@378 479
def encode_literal_constant_size(value):
paul@378 480
paul@378 481
    "Encode a size for the literal constant with the given 'value'."
paul@378 482
paul@378 483
    if isinstance(value, basestring):
paul@378 484
        return len(value)
paul@378 485
    else:
paul@378 486
        return 0
paul@378 487
paul@136 488
def encode_literal_constant_member(value):
paul@136 489
paul@136 490
    "Encode the member name for the 'value' in the final program."
paul@136 491
paul@136 492
    return "%svalue" % value.__class__.__name__
paul@136 493
paul@136 494
def encode_literal_constant_value(value):
paul@136 495
paul@136 496
    "Encode the given 'value' in the final program."
paul@136 497
paul@136 498
    if isinstance(value, (int, float)):
paul@136 499
        return str(value)
paul@136 500
    else:
paul@451 501
        l = []
paul@451 502
paul@451 503
        # Encode characters including non-ASCII ones.
paul@451 504
paul@451 505
        for c in str(value):
paul@451 506
            if c == '"': l.append('\\"')
paul@451 507
            elif c == '\n': l.append('\\n')
paul@451 508
            elif c == '\t': l.append('\\t')
paul@451 509
            elif c == '\r': l.append('\\r')
paul@512 510
            elif c == '\\': l.append('\\\\')
paul@451 511
            elif 0x20 <= ord(c) < 0x80: l.append(c)
paul@451 512
            else: l.append("\\x%02x" % ord(c))
paul@451 513
paul@451 514
        return '"%s"' % "".join(l)
paul@136 515
paul@283 516
def encode_literal_data_initialiser(style):
paul@283 517
paul@283 518
    """
paul@283 519
    Encode a reference to a function populating the data for a literal having
paul@283 520
    the given 'style' ("mapping" or "sequence").
paul@283 521
    """
paul@283 522
paul@283 523
    return "__newdata_%s" % style
paul@283 524
paul@159 525
def encode_literal_instantiator(path):
paul@159 526
paul@159 527
    """
paul@159 528
    Encode a reference to an instantiator for a literal having the given 'path'.
paul@159 529
    """
paul@159 530
paul@159 531
    return "__newliteral_%s" % encode_path(path)
paul@159 532
paul@136 533
def encode_literal_reference(n):
paul@136 534
paul@136 535
    "Encode a reference to a literal constant with the number 'n'."
paul@136 536
paul@609 537
    return "__constvalue%s" % n
paul@136 538
paul@850 539
def encode_trailing_area(path):
paul@850 540
paul@850 541
    """
paul@850 542
    Encode any reference to trailing data members for instances of the type
paul@850 543
    given by 'path'.
paul@850 544
    """
paul@850 545
paul@850 546
    return "__TRAILING_%s" % encode_path(path)
paul@850 547
paul@512 548
paul@512 549
paul@340 550
# Track all encoded paths, detecting and avoiding conflicts.
paul@340 551
paul@340 552
all_encoded_paths = {}
paul@340 553
paul@0 554
def encode_path(path):
paul@0 555
paul@0 556
    "Encode 'path' as an output program object, translating special symbols."
paul@0 557
paul@0 558
    if path in reserved_words:
paul@0 559
        return "__%s" % path
paul@0 560
    else:
paul@340 561
        part_encoded = path.replace("#", "__").replace("$", "__")
paul@349 562
paul@349 563
        if "." not in path:
paul@349 564
            return part_encoded
paul@349 565
paul@340 566
        encoded = part_encoded.replace(".", "_")
paul@340 567
paul@340 568
        # Test for a conflict with the encoding of a different path, re-encoding
paul@340 569
        # if necessary.
paul@340 570
paul@340 571
        previous = all_encoded_paths.get(encoded)
paul@340 572
        replacement = "_"
paul@340 573
paul@340 574
        while previous:
paul@340 575
            if path == previous:
paul@340 576
                return encoded
paul@340 577
            replacement += "_"
paul@340 578
            encoded = part_encoded.replace(".", replacement)
paul@340 579
            previous = all_encoded_paths.get(encoded)
paul@340 580
paul@340 581
        # Store any new or re-encoded path.
paul@340 582
paul@340 583
        all_encoded_paths[encoded] = path
paul@340 584
        return encoded
paul@0 585
paul@623 586
def encode_code(name):
paul@623 587
paul@623 588
    "Encode 'name' as an attribute code indicator."
paul@623 589
paul@623 590
    return "__ATTRCODE(%s)" % encode_path(name)
paul@623 591
paul@623 592
def encode_pcode(name):
paul@623 593
paul@623 594
    "Encode 'name' as an parameter code indicator."
paul@623 595
paul@623 596
    return "__PARAMCODE(%s)" % encode_path(name)
paul@623 597
paul@623 598
def encode_pos(name):
paul@623 599
paul@623 600
    "Encode 'name' as an attribute position indicator."
paul@623 601
paul@623 602
    return "__ATTRPOS(%s)" % encode_path(name)
paul@623 603
paul@623 604
def encode_ppos(name):
paul@623 605
paul@623 606
    "Encode 'name' as an parameter position indicator."
paul@623 607
paul@623 608
    return "__PARAMPOS(%s)" % encode_path(name)
paul@623 609
paul@136 610
def encode_predefined_reference(path):
paul@136 611
paul@136 612
    "Encode a reference to a predefined constant value for 'path'."
paul@136 613
paul@136 614
    return "__predefined_%s" % encode_path(path)
paul@136 615
paul@150 616
def encode_size(kind, path=None):
paul@150 617
paul@150 618
    """
paul@150 619
    Encode a structure size reference for the given 'kind' of structure, with
paul@150 620
    'path' indicating a specific structure name.
paul@150 621
    """
paul@150 622
paul@150 623
    return "__%ssize%s" % (structure_size_prefixes.get(kind, kind), path and "_%s" % encode_path(path) or "")
paul@150 624
paul@0 625
def encode_symbol(symbol_type, path=None):
paul@0 626
paul@0 627
    "Encode a symbol with the given 'symbol_type' and optional 'path'."
paul@0 628
paul@0 629
    return "__%s%s" % (symbol_type, path and "_%s" % encode_path(path) or "")
paul@0 630
paul@150 631
def encode_tablename(kind, path):
paul@150 632
paul@150 633
    """
paul@150 634
    Encode a table reference for the given 'kind' of table structure, indicating
paul@150 635
    a 'path' for the specific object concerned.
paul@150 636
    """
paul@150 637
paul@150 638
    return "__%sTable_%s" % (table_name_prefixes[kind], encode_path(path))
paul@150 639
paul@131 640
def encode_type_attribute(path):
paul@131 641
paul@131 642
    "Encode the special type attribute for 'path'."
paul@131 643
paul@131 644
    return "#%s" % path
paul@131 645
paul@318 646
def decode_type_attribute(s):
paul@318 647
paul@318 648
    "Decode the special type attribute 's'."
paul@318 649
paul@318 650
    return s[1:]
paul@318 651
paul@318 652
def is_type_attribute(s):
paul@318 653
paul@318 654
    "Return whether 's' is a type attribute name."
paul@318 655
paul@318 656
    return s.startswith("#")
paul@318 657
paul@56 658
paul@56 659
paul@150 660
# A mapping from kinds to structure size reference prefixes.
paul@150 661
paul@150 662
structure_size_prefixes = {
paul@150 663
    "<class>" : "c",
paul@150 664
    "<module>" : "m",
paul@150 665
    "<instance>" : "i"
paul@150 666
    }
paul@150 667
paul@150 668
# A mapping from kinds to table name prefixes.
paul@150 669
paul@150 670
table_name_prefixes = {
paul@150 671
    "<class>" : "Class",
paul@150 672
    "<function>" : "Function",
paul@150 673
    "<module>" : "Module",
paul@150 674
    "<instance>" : "Instance"
paul@150 675
    }
paul@150 676
paul@150 677
paul@150 678
paul@0 679
# Output language reserved words.
paul@0 680
paul@0 681
reserved_words = [
paul@0 682
    "break", "char", "const", "continue",
paul@0 683
    "default", "double", "else",
paul@0 684
    "float", "for",
paul@0 685
    "if", "int", "long",
paul@0 686
    "NULL",
paul@0 687
    "return", "struct",
paul@0 688
    "typedef",
paul@0 689
    "void", "while",
paul@0 690
    ]
paul@0 691
paul@0 692
# vim: tabstop=4 expandtab shiftwidth=4