Lichen

Annotated encoders.py

1027:dd0745ab8b8a
5 months ago Paul Boddie Reordered GCC arguments to prevent linking failures. Someone decided to change the GCC invocation or linking semantics at some point, meaning that libraries specified "too early" in the argument list no longer provide the symbols required by the program objects, whereas specifying them at the end of the argument list allows those symbols to be found and obtained.
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@153 236
    )
paul@153 237
paul@153 238
attribute_ops = attribute_loading_ops + (
paul@815 239
    "__store_via_class", "__store_via_object",
paul@113 240
    )
paul@113 241
paul@153 242
checked_loading_ops = (
paul@113 243
    "__check_and_load_via_class", "__check_and_load_via_object", "__check_and_load_via_any",
paul@153 244
    )
paul@153 245
paul@153 246
checked_ops = checked_loading_ops + (
paul@113 247
    "__check_and_store_via_class", "__check_and_store_via_object", "__check_and_store_via_any",
paul@113 248
    )
paul@113 249
paul@113 250
typename_ops = (
paul@144 251
    "__test_common_instance", "__test_common_object", "__test_common_type",
paul@113 252
    )
paul@113 253
paul@385 254
type_ops = (
paul@385 255
    "__test_specific_instance", "__test_specific_object", "__test_specific_type",
paul@385 256
    )
paul@385 257
paul@141 258
static_ops = (
paul@595 259
    "__load_static_ignore", "__load_static_replace", "__load_static_test", "<test_context_static>",
paul@141 260
    )
paul@141 261
paul@858 262
accessor_values = (
paul@858 263
    "<accessor>",
paul@858 264
    )
paul@858 265
paul@858 266
accessor_ops = (
paul@858 267
    "<accessor>", "<set_accessor>",
paul@858 268
    )
paul@858 269
paul@591 270
context_values = (
paul@591 271
    "<context>",
paul@591 272
    )
paul@591 273
paul@591 274
context_ops = (
paul@601 275
    "<context>", "<set_context>", "<test_context_revert>", "<test_context_static>",
paul@591 276
    )
paul@591 277
paul@602 278
context_op_functions = (
paul@602 279
    "<test_context_revert>", "<test_context_static>",
paul@602 280
    )
paul@602 281
paul@757 282
reference_acting_ops = attribute_ops + checked_ops + type_ops + typename_ops
paul@153 283
attribute_producing_ops = attribute_loading_ops + checked_loading_ops
paul@153 284
paul@757 285
attribute_producing_variables = (
paul@757 286
    "<accessor>", "<context>", "<name>", "<private_context>", "<target_accessor>"
paul@757 287
    )
paul@757 288
paul@858 289
def encode_access_instruction(instruction, subs, accessor_index, context_index):
paul@113 290
paul@113 291
    """
paul@113 292
    Encode the 'instruction' - a sequence starting with an operation and
paul@113 293
    followed by arguments, each of which may be an instruction sequence or a
paul@113 294
    plain value - to produce a function call string representation.
paul@113 295
paul@113 296
    The 'subs' parameter defines a mapping of substitutions for special values
paul@113 297
    used in instructions.
paul@482 298
paul@858 299
    The 'accessor_index' parameter defines the position in local accessor
paul@858 300
    storage for the referenced accessor or affected by an accessor operation.
paul@858 301
paul@591 302
    The 'context_index' parameter defines the position in local context storage
paul@591 303
    for the referenced context or affected by a context operation.
paul@591 304
paul@482 305
    Return both the encoded instruction and a collection of substituted names.
paul@113 306
    """
paul@113 307
paul@113 308
    op = instruction[0]
paul@113 309
    args = instruction[1:]
paul@482 310
    substituted = set()
paul@113 311
paul@591 312
    # Encode the arguments.
paul@113 313
paul@591 314
    a = []
paul@591 315
    if args:
paul@153 316
        converting_op = op
paul@113 317
        for arg in args:
paul@858 318
            s, _substituted = encode_access_instruction_arg(arg, subs, converting_op, accessor_index, context_index)
paul@482 319
            substituted.update(_substituted)
paul@482 320
            a.append(s)
paul@153 321
            converting_op = None
paul@113 322
paul@591 323
    # Modify certain arguments.
paul@113 324
paul@624 325
    # Convert type name arguments.
paul@113 326
paul@624 327
    if op in typename_ops:
paul@624 328
        a[1] = encode_path(encode_type_attribute(args[1]))
paul@113 329
paul@591 330
    # Obtain addresses of type arguments.
paul@591 331
paul@591 332
    elif op in type_ops:
paul@591 333
        a[1] = "&%s" % a[1]
paul@113 334
paul@591 335
    # Obtain addresses of static objects.
paul@591 336
paul@591 337
    elif op in static_ops:
paul@591 338
        a[-1] = "&%s" % a[-1]
paul@385 339
paul@858 340
    # Add accessor storage information to certain operations.
paul@858 341
paul@858 342
    if op in accessor_ops:
paul@858 343
        a.insert(0, accessor_index)
paul@858 344
paul@591 345
    # Add context storage information to certain operations.
paul@385 346
paul@595 347
    if op in context_ops:
paul@591 348
        a.insert(0, context_index)
paul@141 349
paul@602 350
    # Add the local context array to certain operations.
paul@602 351
paul@602 352
    if op in context_op_functions:
paul@602 353
        a.append("__tmp_contexts")
paul@602 354
paul@591 355
    # Define any argument string.
paul@141 356
paul@591 357
    if a:
paul@491 358
        argstr = "(%s)" % ", ".join(map(str, a))
paul@591 359
    else:
paul@591 360
        argstr = ""
paul@113 361
paul@113 362
    # Substitute the first element of the instruction, which may not be an
paul@113 363
    # operation at all.
paul@113 364
paul@144 365
    if subs.has_key(op):
paul@482 366
        substituted.add(op)
paul@498 367
paul@498 368
        # Break accessor initialisation into initialisation and value-yielding
paul@498 369
        # parts:
paul@498 370
paul@498 371
        if op == "<set_accessor>" and isinstance(a[0], InstructionSequence):
paul@498 372
            ops = []
paul@498 373
            ops += a[0].get_init_instructions()
paul@498 374
            ops.append("%s(%s)" % (subs[op], a[0].get_value_instruction()))
paul@498 375
            return ", ".join(map(str, ops)), substituted
paul@498 376
paul@144 377
        op = subs[op]
paul@498 378
paul@144 379
    elif not args:
paul@144 380
        op = "&%s" % encode_path(op)
paul@144 381
paul@482 382
    return "%s%s" % (op, argstr), substituted
paul@113 383
paul@858 384
def encode_access_instruction_arg(arg, subs, op, accessor_index, context_index):
paul@113 385
paul@482 386
    """
paul@591 387
    Encode 'arg' using 'subs' to define substitutions, 'op' to indicate the
paul@858 388
    operation to which the argument belongs, and with 'accessor_index' and
paul@858 389
    'context_index' indicating any affected accessor and context storage.
paul@591 390
paul@591 391
    Return a tuple containing the encoded form of 'arg' along with a collection
paul@591 392
    of any substituted values.
paul@482 393
    """
paul@113 394
paul@113 395
    if isinstance(arg, tuple):
paul@858 396
        encoded, substituted = encode_access_instruction(arg, subs, accessor_index, context_index)
paul@757 397
        return attribute_to_reference(op, arg[0], encoded, substituted)
paul@113 398
paul@113 399
    # Special values only need replacing, not encoding.
paul@113 400
paul@113 401
    elif subs.has_key(arg):
paul@591 402
paul@591 403
        # Handle values modified by storage details.
paul@591 404
paul@858 405
        if arg in accessor_values or arg in context_values:
paul@757 406
            encoded = "%s(%s)" % (subs.get(arg), context_index)
paul@591 407
        else:
paul@757 408
            encoded = subs.get(arg)
paul@757 409
paul@757 410
        substituted = set([arg])
paul@757 411
        return attribute_to_reference(op, arg, encoded, substituted)
paul@113 412
paul@258 413
    # Convert static references to the appropriate type.
paul@258 414
paul@757 415
    elif op and op in reference_acting_ops and \
paul@757 416
         arg not in attribute_producing_variables:
paul@757 417
paul@482 418
        return "&%s" % encode_path(arg), set()
paul@258 419
paul@113 420
    # Other values may need encoding.
paul@113 421
paul@113 422
    else:
paul@482 423
        return encode_path(arg), set()
paul@113 424
paul@757 425
def attribute_to_reference(op, arg, encoded, substituted):
paul@757 426
paul@757 427
    # Convert attribute results to references where required.
paul@757 428
paul@757 429
    if op and op in reference_acting_ops and (
paul@757 430
       arg in attribute_producing_ops or
paul@757 431
       arg in attribute_producing_variables):
paul@757 432
paul@757 433
        return "__VALUE(%s)" % encoded, substituted
paul@757 434
    else:
paul@757 435
        return encoded, substituted
paul@757 436
paul@0 437
def encode_function_pointer(path):
paul@0 438
paul@0 439
    "Encode 'path' as a reference to an output program function."
paul@0 440
paul@0 441
    return "__fn_%s" % encode_path(path)
paul@0 442
paul@0 443
def encode_instantiator_pointer(path):
paul@0 444
paul@0 445
    "Encode 'path' as a reference to an output program instantiator."
paul@0 446
paul@0 447
    return "__new_%s" % encode_path(path)
paul@0 448
paul@491 449
def encode_instructions(instructions):
paul@491 450
paul@491 451
    "Encode 'instructions' as a sequence."
paul@491 452
paul@491 453
    if len(instructions) == 1:
paul@491 454
        return instructions[0]
paul@491 455
    else:
paul@491 456
        return "(\n%s\n)" % ",\n".join(instructions)
paul@491 457
paul@136 458
def encode_literal_constant(n):
paul@136 459
paul@136 460
    "Encode a name for the literal constant with the number 'n'."
paul@136 461
paul@609 462
    return "__const%s" % n
paul@136 463
paul@378 464
def encode_literal_constant_size(value):
paul@378 465
paul@378 466
    "Encode a size for the literal constant with the given 'value'."
paul@378 467
paul@378 468
    if isinstance(value, basestring):
paul@378 469
        return len(value)
paul@378 470
    else:
paul@378 471
        return 0
paul@378 472
paul@136 473
def encode_literal_constant_member(value):
paul@136 474
paul@136 475
    "Encode the member name for the 'value' in the final program."
paul@136 476
paul@136 477
    return "%svalue" % value.__class__.__name__
paul@136 478
paul@136 479
def encode_literal_constant_value(value):
paul@136 480
paul@136 481
    "Encode the given 'value' in the final program."
paul@136 482
paul@136 483
    if isinstance(value, (int, float)):
paul@136 484
        return str(value)
paul@136 485
    else:
paul@451 486
        l = []
paul@451 487
paul@451 488
        # Encode characters including non-ASCII ones.
paul@451 489
paul@451 490
        for c in str(value):
paul@451 491
            if c == '"': l.append('\\"')
paul@451 492
            elif c == '\n': l.append('\\n')
paul@451 493
            elif c == '\t': l.append('\\t')
paul@451 494
            elif c == '\r': l.append('\\r')
paul@512 495
            elif c == '\\': l.append('\\\\')
paul@451 496
            elif 0x20 <= ord(c) < 0x80: l.append(c)
paul@451 497
            else: l.append("\\x%02x" % ord(c))
paul@451 498
paul@451 499
        return '"%s"' % "".join(l)
paul@136 500
paul@283 501
def encode_literal_data_initialiser(style):
paul@283 502
paul@283 503
    """
paul@283 504
    Encode a reference to a function populating the data for a literal having
paul@283 505
    the given 'style' ("mapping" or "sequence").
paul@283 506
    """
paul@283 507
paul@283 508
    return "__newdata_%s" % style
paul@283 509
paul@159 510
def encode_literal_instantiator(path):
paul@159 511
paul@159 512
    """
paul@159 513
    Encode a reference to an instantiator for a literal having the given 'path'.
paul@159 514
    """
paul@159 515
paul@159 516
    return "__newliteral_%s" % encode_path(path)
paul@159 517
paul@136 518
def encode_literal_reference(n):
paul@136 519
paul@136 520
    "Encode a reference to a literal constant with the number 'n'."
paul@136 521
paul@609 522
    return "__constvalue%s" % n
paul@136 523
paul@512 524
paul@512 525
paul@340 526
# Track all encoded paths, detecting and avoiding conflicts.
paul@340 527
paul@340 528
all_encoded_paths = {}
paul@340 529
paul@0 530
def encode_path(path):
paul@0 531
paul@0 532
    "Encode 'path' as an output program object, translating special symbols."
paul@0 533
paul@0 534
    if path in reserved_words:
paul@0 535
        return "__%s" % path
paul@0 536
    else:
paul@340 537
        part_encoded = path.replace("#", "__").replace("$", "__")
paul@349 538
paul@349 539
        if "." not in path:
paul@349 540
            return part_encoded
paul@349 541
paul@340 542
        encoded = part_encoded.replace(".", "_")
paul@340 543
paul@340 544
        # Test for a conflict with the encoding of a different path, re-encoding
paul@340 545
        # if necessary.
paul@340 546
paul@340 547
        previous = all_encoded_paths.get(encoded)
paul@340 548
        replacement = "_"
paul@340 549
paul@340 550
        while previous:
paul@340 551
            if path == previous:
paul@340 552
                return encoded
paul@340 553
            replacement += "_"
paul@340 554
            encoded = part_encoded.replace(".", replacement)
paul@340 555
            previous = all_encoded_paths.get(encoded)
paul@340 556
paul@340 557
        # Store any new or re-encoded path.
paul@340 558
paul@340 559
        all_encoded_paths[encoded] = path
paul@340 560
        return encoded
paul@0 561
paul@623 562
def encode_code(name):
paul@623 563
paul@623 564
    "Encode 'name' as an attribute code indicator."
paul@623 565
paul@623 566
    return "__ATTRCODE(%s)" % encode_path(name)
paul@623 567
paul@623 568
def encode_pcode(name):
paul@623 569
paul@623 570
    "Encode 'name' as an parameter code indicator."
paul@623 571
paul@623 572
    return "__PARAMCODE(%s)" % encode_path(name)
paul@623 573
paul@623 574
def encode_pos(name):
paul@623 575
paul@623 576
    "Encode 'name' as an attribute position indicator."
paul@623 577
paul@623 578
    return "__ATTRPOS(%s)" % encode_path(name)
paul@623 579
paul@623 580
def encode_ppos(name):
paul@623 581
paul@623 582
    "Encode 'name' as an parameter position indicator."
paul@623 583
paul@623 584
    return "__PARAMPOS(%s)" % encode_path(name)
paul@623 585
paul@136 586
def encode_predefined_reference(path):
paul@136 587
paul@136 588
    "Encode a reference to a predefined constant value for 'path'."
paul@136 589
paul@136 590
    return "__predefined_%s" % encode_path(path)
paul@136 591
paul@150 592
def encode_size(kind, path=None):
paul@150 593
paul@150 594
    """
paul@150 595
    Encode a structure size reference for the given 'kind' of structure, with
paul@150 596
    'path' indicating a specific structure name.
paul@150 597
    """
paul@150 598
paul@150 599
    return "__%ssize%s" % (structure_size_prefixes.get(kind, kind), path and "_%s" % encode_path(path) or "")
paul@150 600
paul@0 601
def encode_symbol(symbol_type, path=None):
paul@0 602
paul@0 603
    "Encode a symbol with the given 'symbol_type' and optional 'path'."
paul@0 604
paul@0 605
    return "__%s%s" % (symbol_type, path and "_%s" % encode_path(path) or "")
paul@0 606
paul@150 607
def encode_tablename(kind, path):
paul@150 608
paul@150 609
    """
paul@150 610
    Encode a table reference for the given 'kind' of table structure, indicating
paul@150 611
    a 'path' for the specific object concerned.
paul@150 612
    """
paul@150 613
paul@150 614
    return "__%sTable_%s" % (table_name_prefixes[kind], encode_path(path))
paul@150 615
paul@131 616
def encode_type_attribute(path):
paul@131 617
paul@131 618
    "Encode the special type attribute for 'path'."
paul@131 619
paul@131 620
    return "#%s" % path
paul@131 621
paul@318 622
def decode_type_attribute(s):
paul@318 623
paul@318 624
    "Decode the special type attribute 's'."
paul@318 625
paul@318 626
    return s[1:]
paul@318 627
paul@318 628
def is_type_attribute(s):
paul@318 629
paul@318 630
    "Return whether 's' is a type attribute name."
paul@318 631
paul@318 632
    return s.startswith("#")
paul@318 633
paul@56 634
paul@56 635
paul@150 636
# A mapping from kinds to structure size reference prefixes.
paul@150 637
paul@150 638
structure_size_prefixes = {
paul@150 639
    "<class>" : "c",
paul@150 640
    "<module>" : "m",
paul@150 641
    "<instance>" : "i"
paul@150 642
    }
paul@150 643
paul@150 644
# A mapping from kinds to table name prefixes.
paul@150 645
paul@150 646
table_name_prefixes = {
paul@150 647
    "<class>" : "Class",
paul@150 648
    "<function>" : "Function",
paul@150 649
    "<module>" : "Module",
paul@150 650
    "<instance>" : "Instance"
paul@150 651
    }
paul@150 652
paul@150 653
paul@150 654
paul@0 655
# Output language reserved words.
paul@0 656
paul@0 657
reserved_words = [
paul@0 658
    "break", "char", "const", "continue",
paul@0 659
    "default", "double", "else",
paul@0 660
    "float", "for",
paul@0 661
    "if", "int", "long",
paul@0 662
    "NULL",
paul@0 663
    "return", "struct",
paul@0 664
    "typedef",
paul@0 665
    "void", "while",
paul@0 666
    ]
paul@0 667
paul@0 668
# vim: tabstop=4 expandtab shiftwidth=4