codegen.py
author Simon MacMullen <simon@rabbitmq.com>
Fri Feb 03 15:59:12 2012 +0000 (3 months ago)
changeset 8922 4f87837a40be
parent 8430 86988974d7fa
permissions -rw-r--r--
Merge bug24702
matthew@6074
     1
##  The contents of this file are subject to the Mozilla Public License
matthew@6074
     2
##  Version 1.1 (the "License"); you may not use this file except in
matthew@6074
     3
##  compliance with the License. You may obtain a copy of the License
matthew@6074
     4
##  at http://www.mozilla.org/MPL/
tonyg@0
     5
##
matthew@6074
     6
##  Software distributed under the License is distributed on an "AS IS"
matthew@6074
     7
##  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
matthew@6074
     8
##  the License for the specific language governing rights and
matthew@6074
     9
##  limitations under the License.
tonyg@0
    10
##
matthew@6074
    11
##  The Original Code is RabbitMQ.
tonyg@0
    12
##
matthew@6074
    13
##  The Initial Developer of the Original Code is VMware, Inc.
emile@8906
    14
##  Copyright (c) 2007-2012 VMware, Inc.  All rights reserved.
tonyg@0
    15
##
tonyg@0
    16
tonyg@0
    17
from __future__ import nested_scopes
tonyg@0
    18
tonyg@0
    19
import sys
tonyg@0
    20
sys.path.append("../rabbitmq-codegen")  # in case we're next to an experimental revision
tonyg@0
    21
sys.path.append("codegen")              # in case we're building from a distribution package
tonyg@0
    22
tonyg@0
    23
from amqp_codegen import *
tonyg@0
    24
import string
tonyg@0
    25
import re
tonyg@0
    26
tonyg@0
    27
erlangTypeMap = {
tonyg@0
    28
    'octet': 'octet',
tonyg@0
    29
    'shortstr': 'shortstr',
tonyg@0
    30
    'longstr': 'longstr',
tonyg@0
    31
    'short': 'shortint',
tonyg@0
    32
    'long': 'longint',
tonyg@0
    33
    'longlong': 'longlongint',
tonyg@0
    34
    'bit': 'bit',
tonyg@0
    35
    'table': 'table',
tonyg@0
    36
    'timestamp': 'timestamp',
tonyg@0
    37
}
tonyg@0
    38
matthias@108
    39
# Coming up with a proper encoding of AMQP tables in JSON is too much
matthias@108
    40
# hassle at this stage. Given that the only default value we are
matthias@108
    41
# interested in is for the empty table, we only support that.
matthias@108
    42
def convertTable(d):
matthias@108
    43
    if len(d) == 0:
matthias@108
    44
        return "[]"
alexandru@8423
    45
    else:
alexandru@8423
    46
        raise Exception('Non-empty table defaults not supported ' + d)
matthias@108
    47
karol@73
    48
erlangDefaultValueTypeConvMap = {
karol@73
    49
    bool : lambda x: str(x).lower(),
karol@106
    50
    str : lambda x: "<<\"" + x + "\">>",
karol@73
    51
    int : lambda x: str(x),
matthias@108
    52
    float : lambda x: str(x),
hubert@384
    53
    dict: convertTable,
hubert@384
    54
    unicode: lambda x: "<<\"" + x.encode("utf-8") + "\">>"
karol@73
    55
}
karol@73
    56
tonyg@0
    57
def erlangize(s):
tonyg@0
    58
    s = s.replace('-', '_')
tonyg@0
    59
    s = s.replace(' ', '_')
tonyg@0
    60
    return s
tonyg@0
    61
tonyg@0
    62
AmqpMethod.erlangName = lambda m: "'" + erlangize(m.klass.name) + '.' + erlangize(m.name) + "'"
tonyg@0
    63
vlad@2570
    64
AmqpClass.erlangName = lambda c: "'" + erlangize(c.name) + "'"
vlad@2570
    65
tonyg@0
    66
def erlangConstantName(s):
tonyg@0
    67
    return '_'.join(re.split('[- ]', s.upper()))
tonyg@0
    68
tonyg@0
    69
class PackedMethodBitField:
tonyg@0
    70
    def __init__(self, index):
tonyg@0
    71
        self.index = index
tonyg@0
    72
        self.domain = 'bit'
tonyg@0
    73
        self.contents = []
tonyg@0
    74
tonyg@0
    75
    def extend(self, f):
tonyg@0
    76
        self.contents.append(f)
tonyg@0
    77
tonyg@0
    78
    def count(self):
tonyg@0
    79
        return len(self.contents)
tonyg@0
    80
tonyg@0
    81
    def full(self):
tonyg@0
    82
        return self.count() == 8
tonyg@2503
    83
alexandru@3585
    84
def multiLineFormat(things, prologue, separator, lineSeparator, epilogue, thingsPerLine = 4):
alexandru@3585
    85
    r = [prologue]
alexandru@3585
    86
    i = 0
alexandru@3585
    87
    for t in things:
alexandru@3585
    88
        if i != 0:
alexandru@3585
    89
            if i % thingsPerLine == 0:
alexandru@3585
    90
                r += [lineSeparator]
alexandru@3585
    91
            else:
alexandru@3585
    92
                r += [separator]
alexandru@3585
    93
        r += [t]
alexandru@3585
    94
        i += 1
alexandru@3585
    95
    r += [epilogue]
alexandru@3585
    96
    return "".join(r)
alexandru@3585
    97
alexandru@3585
    98
def prettyType(typeName, subTypes, typesPerLine = 4):
alexandru@3585
    99
    """Pretty print a type signature made up of many alternative subtypes"""
alexandru@3585
   100
    sTs = multiLineFormat(subTypes,
alexandru@3586
   101
                          "( ", " | ", "\n       | ", " )",
alexandru@3585
   102
                          thingsPerLine = typesPerLine)
alexandru@3586
   103
    return "-type(%s ::\n       %s)." % (typeName, sTs)
alexandru@3585
   104
tonyg@2503
   105
def printFileHeader():
tonyg@2503
   106
    print """%%   Autogenerated code. Do not edit.
tonyg@2503
   107
%%
matthew@6074
   108
%%  The contents of this file are subject to the Mozilla Public License
matthew@6074
   109
%%  Version 1.1 (the "License"); you may not use this file except in
matthew@6074
   110
%%  compliance with the License. You may obtain a copy of the License
matthew@6074
   111
%%  at http://www.mozilla.org/MPL/
tonyg@2503
   112
%%
matthew@6074
   113
%%  Software distributed under the License is distributed on an "AS IS"
matthew@6074
   114
%%  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
matthew@6074
   115
%%  the License for the specific language governing rights and
matthew@6074
   116
%%  limitations under the License.
tonyg@2503
   117
%%
matthew@6074
   118
%%  The Original Code is RabbitMQ.
tonyg@2503
   119
%%
matthew@6074
   120
%%  The Initial Developer of the Original Code is VMware, Inc.
emile@8906
   121
%%  Copyright (c) 2007-2012 VMware, Inc.  All rights reserved.
tonyg@2503
   122
%%"""
matthew@2624
   123
tonyg@0
   124
def genErl(spec):
tonyg@0
   125
    def erlType(domain):
tonyg@0
   126
        return erlangTypeMap[spec.resolveDomain(domain)]
tonyg@0
   127
tonyg@0
   128
    def fieldTypeList(fields):
tonyg@0
   129
        return '[' + ', '.join([erlType(f.domain) for f in fields]) + ']'
tonyg@0
   130
tonyg@0
   131
    def fieldNameList(fields):
tonyg@0
   132
        return '[' + ', '.join([erlangize(f.name) for f in fields]) + ']'
tonyg@0
   133
tonyg@0
   134
    def fieldTempList(fields):
tonyg@0
   135
        return '[' + ', '.join(['F' + str(f.index) for f in fields]) + ']'
tonyg@0
   136
tonyg@0
   137
    def fieldMapList(fields):
tonyg@0
   138
        return ', '.join([erlangize(f.name) + " = F" + str(f.index) for f in fields])
tonyg@0
   139
tonyg@0
   140
    def genLookupMethodName(m):
tonyg@0
   141
        print "lookup_method_name({%d, %d}) -> %s;" % (m.klass.index, m.index, m.erlangName())
tonyg@0
   142
vlad@2570
   143
    def genLookupClassName(c):
vlad@2570
   144
        print "lookup_class_name(%d) -> %s;" % (c.index, c.erlangName())
vlad@2570
   145
tonyg@0
   146
    def genMethodId(m):
tonyg@0
   147
        print "method_id(%s) -> {%d, %d};" % (m.erlangName(), m.klass.index, m.index)
tonyg@0
   148
tonyg@0
   149
    def genMethodHasContent(m):
tonyg@0
   150
        print "method_has_content(%s) -> %s;" % (m.erlangName(), str(m.hasContent).lower())
matthew@2624
   151
paulj@1686
   152
    def genMethodIsSynchronous(m):
paulj@1687
   153
        hasNoWait = "nowait" in fieldNameList(m.arguments)
paulj@1687
   154
        if m.isSynchronous and hasNoWait:
paulj@1687
   155
          print "is_method_synchronous(#%s{nowait = NoWait}) -> not(NoWait);" % (m.erlangName())
paulj@1687
   156
        else:
paulj@1687
   157
          print "is_method_synchronous(#%s{}) -> %s;" % (m.erlangName(), str(m.isSynchronous).lower())
tonyg@0
   158
tonyg@0
   159
    def genMethodFieldTypes(m):
tonyg@0
   160
        """Not currently used - may be useful in future?"""
tonyg@0
   161
        print "method_fieldtypes(%s) -> %s;" % (m.erlangName(), fieldTypeList(m.arguments))
tonyg@0
   162
tonyg@0
   163
    def genMethodFieldNames(m):
tonyg@0
   164
        print "method_fieldnames(%s) -> %s;" % (m.erlangName(), fieldNameList(m.arguments))
tonyg@0
   165
tonyg@0
   166
    def packMethodFields(fields):
tonyg@0
   167
        packed = []
tonyg@0
   168
        bitfield = None
tonyg@0
   169
        for f in fields:
tonyg@0
   170
            if erlType(f.domain) == 'bit':
tonyg@0
   171
                if not(bitfield) or bitfield.full():
tonyg@0
   172
                    bitfield = PackedMethodBitField(f.index)
tonyg@0
   173
                    packed.append(bitfield)
tonyg@0
   174
                bitfield.extend(f)
tonyg@0
   175
            else:
tonyg@0
   176
                bitfield = None
tonyg@0
   177
                packed.append(f)
tonyg@0
   178
        return packed
tonyg@0
   179
tonyg@0
   180
    def methodFieldFragment(f):
tonyg@0
   181
        type = erlType(f.domain)
tonyg@0
   182
        p = 'F' + str(f.index)
tonyg@0
   183
        if type == 'shortstr':
tonyg@0
   184
            return p+'Len:8/unsigned, '+p+':'+p+'Len/binary'
tonyg@0
   185
        elif type == 'longstr':
tonyg@0
   186
            return p+'Len:32/unsigned, '+p+':'+p+'Len/binary'
tonyg@0
   187
        elif type == 'octet':
tonyg@0
   188
            return p+':8/unsigned'
tonyg@0
   189
        elif type == 'shortint':
tonyg@0
   190
            return p+':16/unsigned'
tonyg@0
   191
        elif type == 'longint':
tonyg@0
   192
            return p+':32/unsigned'
tonyg@0
   193
        elif type == 'longlongint':
tonyg@0
   194
            return p+':64/unsigned'
tonyg@0
   195
        elif type == 'timestamp':
tonyg@0
   196
            return p+':64/unsigned'
tonyg@0
   197
        elif type == 'bit':
tonyg@0
   198
            return p+'Bits:8'
tonyg@0
   199
        elif type == 'table':
tonyg@0
   200
            return p+'Len:32/unsigned, '+p+'Tab:'+p+'Len/binary'
tonyg@0
   201
tonyg@0
   202
    def genFieldPostprocessing(packed):
tonyg@0
   203
        for f in packed:
tonyg@0
   204
            type = erlType(f.domain)
tonyg@0
   205
            if type == 'bit':
tonyg@0
   206
                for index in range(f.count()):
tonyg@0
   207
                    print "  F%d = ((F%dBits band %d) /= 0)," % \
tonyg@0
   208
                          (f.index + index,
tonyg@0
   209
                           f.index,
tonyg@0
   210
                           1 << index)
tonyg@0
   211
            elif type == 'table':
tonyg@0
   212
                print "  F%d = rabbit_binary_parser:parse_table(F%dTab)," % \
tonyg@0
   213
                      (f.index, f.index)
tonyg@0
   214
            else:
tonyg@0
   215
                pass
tonyg@0
   216
matthew@2624
   217
    def genMethodRecord(m):
matthew@2624
   218
        print "method_record(%s) -> #%s{};" % (m.erlangName(), m.erlangName())
matthew@2624
   219
tonyg@0
   220
    def genDecodeMethodFields(m):
tonyg@0
   221
        packedFields = packMethodFields(m.arguments)
tonyg@0
   222
        binaryPattern = ', '.join([methodFieldFragment(f) for f in packedFields])
tonyg@0
   223
        if binaryPattern:
tonyg@0
   224
            restSeparator = ', '
tonyg@0
   225
        else:
tonyg@0
   226
            restSeparator = ''
tonyg@0
   227
        recordConstructorExpr = '#%s{%s}' % (m.erlangName(), fieldMapList(m.arguments))
tonyg@0
   228
        print "decode_method_fields(%s, <<%s>>) ->" % (m.erlangName(), binaryPattern)
tonyg@0
   229
        genFieldPostprocessing(packedFields)
tonyg@0
   230
        print "  %s;" % (recordConstructorExpr,)
tonyg@0
   231
tonyg@0
   232
    def genDecodeProperties(c):
alexandru@8423
   233
        def presentBin(fields):
alexandru@8430
   234
            ps = ', '.join(['P' + str(f.index) + ':1' for f in fields])
alexandru@8430
   235
            return '<<' + ps + ', _:%d, R0/binary>>' % (16 - len(fields),)
alexandru@8423
   236
        def mkMacroName(field):
alexandru@8423
   237
            return '?' + field.domain.upper() + '_PROP'
alexandru@8423
   238
        def writePropFieldLine(field, bin_next = None):
alexandru@8423
   239
            i = str(field.index)
alexandru@8423
   240
            if not bin_next:
alexandru@8430
   241
                bin_next = 'R' + str(field.index + 1)
alexandru@8423
   242
            if field.domain in ['octet', 'timestamp']:
alexandru@8430
   243
                print ("  {%s, %s} = %s(%s, %s, %s, %s)," %
alexandru@8430
   244
                       ('F' + i, bin_next, mkMacroName(field), 'P' + i,
alexandru@8430
   245
                        'R' + i, 'I' + i, 'X' + i))
alexandru@8423
   246
            else:
alexandru@8430
   247
                print ("  {%s, %s} = %s(%s, %s, %s, %s, %s)," %
alexandru@8430
   248
                       ('F' + i, bin_next, mkMacroName(field), 'P' + i,
alexandru@8430
   249
                        'R' + i, 'L' + i, 'S' + i, 'X' + i))
alexandru@8423
   250
alexandru@8423
   251
        if len(c.fields) == 0:
alexandru@8430
   252
            print "decode_properties(%d, _) ->" % (c.index,)
alexandru@8423
   253
        else:
alexandru@8430
   254
            print ("decode_properties(%d, %s) ->" %
alexandru@8430
   255
                   (c.index, presentBin(c.fields)))
alexandru@8423
   256
            for field in c.fields[:-1]:
alexandru@8423
   257
                writePropFieldLine(field)
alexandru@8423
   258
            writePropFieldLine(c.fields[-1], "<<>>")
tonyg@0
   259
        print "  #'P_%s'{%s};" % (erlangize(c.name), fieldMapList(c.fields))
tonyg@0
   260
tonyg@0
   261
    def genFieldPreprocessing(packed):
tonyg@0
   262
        for f in packed:
tonyg@0
   263
            type = erlType(f.domain)
tonyg@0
   264
            if type == 'bit':
tonyg@0
   265
                print "  F%dBits = (%s)," % \
tonyg@0
   266
                      (f.index,
tonyg@0
   267
                       ' bor '.join(['(bitvalue(F%d) bsl %d)' % (x.index, x.index - f.index)
tonyg@0
   268
                                     for x in f.contents]))
tonyg@0
   269
            elif type == 'table':
tonyg@0
   270
                print "  F%dTab = rabbit_binary_generator:generate_table(F%d)," % (f.index, f.index)
tonyg@0
   271
                print "  F%dLen = size(F%dTab)," % (f.index, f.index)
vlad@2656
   272
            elif type == 'shortstr':
matthias@5347
   273
                print "  F%dLen = shortstr_size(F%d)," % (f.index, f.index)
vlad@2656
   274
            elif type == 'longstr':
tonyg@0
   275
                print "  F%dLen = size(F%d)," % (f.index, f.index)
tonyg@0
   276
            else:
tonyg@0
   277
                pass
tonyg@0
   278
tonyg@0
   279
    def genEncodeMethodFields(m):
tonyg@0
   280
        packedFields = packMethodFields(m.arguments)
tonyg@0
   281
        print "encode_method_fields(#%s{%s}) ->" % (m.erlangName(), fieldMapList(m.arguments))
tonyg@0
   282
        genFieldPreprocessing(packedFields)
tonyg@0
   283
        print "  <<%s>>;" % (', '.join([methodFieldFragment(f) for f in packedFields]))
tonyg@0
   284
tonyg@0
   285
    def genEncodeProperties(c):
tonyg@0
   286
        print "encode_properties(#'P_%s'{%s}) ->" % (erlangize(c.name), fieldMapList(c.fields))
tonyg@0
   287
        print "  rabbit_binary_generator:encode_properties(%s, %s);" % \
tonyg@0
   288
              (fieldTypeList(c.fields), fieldTempList(c.fields))
tonyg@0
   289
vlad@2104
   290
    def messageConstantClass(cls):
tonyg@0
   291
        # We do this because 0.8 uses "soft error" and 8.1 uses "soft-error".
tonyg@0
   292
        return erlangConstantName(cls)
tonyg@0
   293
tonyg@0
   294
    def genLookupException(c,v,cls):
vlad@2104
   295
        mCls = messageConstantClass(cls)
tonyg@0
   296
        if mCls == 'SOFT_ERROR': genLookupException1(c,'false')
tonyg@0
   297
        elif mCls == 'HARD_ERROR': genLookupException1(c, 'true')
tonyg@0
   298
        elif mCls == '': pass
alexandru@8423
   299
        else: raise Exception('Unknown constant class' + cls)
tonyg@0
   300
tonyg@0
   301
    def genLookupException1(c,hardErrorBoolStr):
tonyg@0
   302
        n = erlangConstantName(c)
tonyg@0
   303
        print 'lookup_amqp_exception(%s) -> {%s, ?%s, <<"%s">>};' % \
tonyg@0
   304
              (n.lower(), hardErrorBoolStr, n, n)
tonyg@0
   305
vlad@2357
   306
    def genAmqpException(c,v,cls):
vlad@2104
   307
        n = erlangConstantName(c)
vlad@2357
   308
        print 'amqp_exception(?%s) -> %s;' % \
vlad@2357
   309
            (n, n.lower())
vlad@2104
   310
tonyg@0
   311
    methods = spec.allMethods()
tonyg@0
   312
tonyg@2503
   313
    printFileHeader()
simon@3768
   314
    module = "rabbit_framing_amqp_%d_%d" % (spec.major, spec.minor)
simon@3870
   315
    if spec.revision != 0:
simon@3768
   316
        module = "%s_%d" % (module, spec.revision)
simon@3768
   317
    if module == "rabbit_framing_amqp_8_0":
simon@3768
   318
        module = "rabbit_framing_amqp_0_8"
simon@3768
   319
    print "-module(%s)." % module
simon@3768
   320
    print """-include("rabbit_framing.hrl").
tonyg@0
   321
matthias@3989
   322
-export([version/0]).
tonyg@0
   323
-export([lookup_method_name/1]).
vlad@2570
   324
-export([lookup_class_name/1]).
tonyg@0
   325
tonyg@0
   326
-export([method_id/1]).
tonyg@0
   327
-export([method_has_content/1]).
paulj@1686
   328
-export([is_method_synchronous/1]).
matthew@2624
   329
-export([method_record/1]).
tonyg@0
   330
-export([method_fieldnames/1]).
tonyg@0
   331
-export([decode_method_fields/2]).
tonyg@0
   332
-export([decode_properties/2]).
tonyg@0
   333
-export([encode_method_fields/1]).
tonyg@0
   334
-export([encode_properties/1]).
tonyg@0
   335
-export([lookup_amqp_exception/1]).
vlad@2357
   336
-export([amqp_exception/1]).
tonyg@0
   337
alexandru@3901
   338
"""
alexandru@3901
   339
    print "%% Various types"
alexandru@3901
   340
    print "-ifdef(use_specs)."
alexandru@3901
   341
matthias@5327
   342
    print """-export_type([amqp_field_type/0, amqp_property_type/0,
matthias@5327
   343
              amqp_table/0, amqp_array/0, amqp_value/0,
matthias@5327
   344
              amqp_method_name/0, amqp_method/0, amqp_method_record/0,
matthias@5327
   345
              amqp_method_field_name/0, amqp_property_record/0,
matthias@5327
   346
              amqp_exception/0, amqp_exception_code/0, amqp_class_id/0]).
alexandru@3841
   347
alexandru@3841
   348
-type(amqp_field_type() ::
alexandru@3841
   349
      'longstr' | 'signedint' | 'decimal' | 'timestamp' |
alexandru@3841
   350
      'table' | 'byte' | 'double' | 'float' | 'long' |
emile@6070
   351
      'short' | 'bool' | 'binary' | 'void' | 'array').
alexandru@3841
   352
-type(amqp_property_type() ::
alexandru@3841
   353
      'shortstr' | 'longstr' | 'octet' | 'shortint' | 'longint' |
alexandru@3841
   354
      'longlongint' | 'timestamp' | 'bit' | 'table').
alexandru@3861
   355
alexandru@3861
   356
-type(amqp_table() :: [{binary(), amqp_field_type(), amqp_value()}]).
alexandru@3861
   357
-type(amqp_array() :: [{amqp_field_type(), amqp_value()}]).
alexandru@3861
   358
-type(amqp_value() :: binary() |    % longstr
alexandru@3861
   359
                      integer() |   % signedint
alexandru@3861
   360
                      {non_neg_integer(), non_neg_integer()} | % decimal
alexandru@3861
   361
                      amqp_table() |
alexandru@3861
   362
                      amqp_array() |
alexandru@3861
   363
                      byte() |      % byte
alexandru@3861
   364
                      float() |     % double
alexandru@3861
   365
                      integer() |   % long
alexandru@3861
   366
                      integer() |   % short
alexandru@3861
   367
                      boolean() |   % bool
alexandru@3861
   368
                      binary() |    % binary
alexandru@3861
   369
                      'undefined' | % void
alexandru@3861
   370
                      non_neg_integer() % timestamp
alexandru@3861
   371
     ).
tonyg@0
   372
"""
alexandru@3901
   373
alexandru@3841
   374
    print prettyType("amqp_method_name()",
alexandru@3841
   375
                     [m.erlangName() for m in methods])
alexandru@3841
   376
    print prettyType("amqp_method()",
alexandru@3841
   377
                     ["{%s, %s}" % (m.klass.index, m.index) for m in methods],
alexandru@3841
   378
                     6)
alexandru@3841
   379
    print prettyType("amqp_method_record()",
alexandru@3841
   380
                     ["#%s{}" % (m.erlangName()) for m in methods])
alexandru@3841
   381
    fieldNames = set()
alexandru@3841
   382
    for m in methods:
alexandru@3841
   383
        fieldNames.update(m.arguments)
alexandru@3841
   384
    fieldNames = [erlangize(f.name) for f in fieldNames]
alexandru@3841
   385
    print prettyType("amqp_method_field_name()",
alexandru@3841
   386
                     fieldNames)
alexandru@3841
   387
    print prettyType("amqp_property_record()",
alexandru@3841
   388
                     ["#'P_%s'{}" % erlangize(c.name) for c in spec.allClasses()])
alexandru@3841
   389
    print prettyType("amqp_exception()",
alexandru@3841
   390
                     ["'%s'" % erlangConstantName(c).lower() for (c, v, cls) in spec.constants])
alexandru@3841
   391
    print prettyType("amqp_exception_code()",
alexandru@3841
   392
                     ["%i" % v for (c, v, cls) in spec.constants])
alexandru@3841
   393
    classIds = set()
alexandru@3841
   394
    for m in spec.allMethods():
alexandru@3841
   395
        classIds.add(m.klass.index)
alexandru@3841
   396
    print prettyType("amqp_class_id()",
alexandru@3841
   397
                     ["%i" % ci for ci in classIds])
alexandru@7789
   398
    print prettyType("amqp_class_name()",
alexandru@7789
   399
                     ["%s" % c.erlangName() for c in spec.allClasses()])
alexandru@3841
   400
    print "-endif. % use_specs"
alexandru@3841
   401
alexandru@3901
   402
    print """
alexandru@3901
   403
%% Method signatures
alexandru@3901
   404
-ifdef(use_specs).
matthias@3989
   405
-spec(version/0 :: () -> {non_neg_integer(), non_neg_integer(), non_neg_integer()}).
alexandru@3901
   406
-spec(lookup_method_name/1 :: (amqp_method()) -> amqp_method_name()).
alexandru@7789
   407
-spec(lookup_class_name/1 :: (amqp_class_id()) -> amqp_class_name()).
alexandru@3901
   408
-spec(method_id/1 :: (amqp_method_name()) -> amqp_method()).
alexandru@3901
   409
-spec(method_has_content/1 :: (amqp_method_name()) -> boolean()).
alexandru@3901
   410
-spec(is_method_synchronous/1 :: (amqp_method_record()) -> boolean()).
alexandru@3901
   411
-spec(method_record/1 :: (amqp_method_name()) -> amqp_method_record()).
alexandru@3901
   412
-spec(method_fieldnames/1 :: (amqp_method_name()) -> [amqp_method_field_name()]).
alexandru@4210
   413
-spec(decode_method_fields/2 ::
alexandru@4210
   414
        (amqp_method_name(), binary()) -> amqp_method_record() | rabbit_types:connection_exit()).
alexandru@3901
   415
-spec(decode_properties/2 :: (non_neg_integer(), binary()) -> amqp_property_record()).
alexandru@3901
   416
-spec(encode_method_fields/1 :: (amqp_method_record()) -> binary()).
matthias@5338
   417
-spec(encode_properties/1 :: (amqp_property_record()) -> binary()).
alexandru@3901
   418
-spec(lookup_amqp_exception/1 :: (amqp_exception()) -> {boolean(), amqp_exception_code(), binary()}).
alexandru@3901
   419
-spec(amqp_exception/1 :: (amqp_exception_code()) -> amqp_exception()).
alexandru@3901
   420
-endif. % use_specs
alexandru@3901
   421
alexandru@3901
   422
bitvalue(true) -> 1;
alexandru@3901
   423
bitvalue(false) -> 0;
alexandru@3901
   424
bitvalue(undefined) -> 0.
matthias@5347
   425
matthias@5347
   426
shortstr_size(S) ->
matthias@5347
   427
    case size(S) of
matthias@5347
   428
        Len when Len =< 255 -> Len;
matthias@5347
   429
        _                   -> exit(method_field_shortstr_overflow)
matthias@5347
   430
    end.
alexandru@8423
   431
alexandru@8423
   432
-define(SHORTSTR_PROP(P, R, L, S, X),
alexandru@8423
   433
        if P =:= 0 -> {undefined, R};
alexandru@8423
   434
           true    -> <<L:8/unsigned, S:L/binary, X/binary>> = R,
alexandru@8423
   435
                      {S, X}
alexandru@8423
   436
        end).
alexandru@8423
   437
-define(TABLE_PROP(P, R, L, T, X),
alexandru@8423
   438
        if P =:= 0 -> {undefined, R};
alexandru@8423
   439
           true    -> <<L:32/unsigned, T:L/binary, X/binary>> = R,
alexandru@8423
   440
                      {rabbit_binary_parser:parse_table(T), X}
alexandru@8423
   441
        end).
alexandru@8423
   442
-define(OCTET_PROP(P, R, I, X),
alexandru@8423
   443
        if P =:= 0 -> {undefined, R};
alexandru@8423
   444
           true    -> <<I:8/unsigned, X/binary>> = R,
alexandru@8423
   445
                      {I, X}
alexandru@8423
   446
        end).
alexandru@8423
   447
-define(TIMESTAMP_PROP(P, R, I, X),
alexandru@8423
   448
        if P =:= 0 -> {undefined, R};
alexandru@8423
   449
           true    -> <<I:64/unsigned, X/binary>> = R,
alexandru@8423
   450
                      {I, X}
alexandru@8423
   451
        end).
alexandru@3901
   452
"""
matthias@3989
   453
    version = "{%d, %d, %d}" % (spec.major, spec.minor, spec.revision)
matthias@3989
   454
    if version == '{8, 0, 0}': version = '{0, 8, 0}'
matthias@3989
   455
    print "version() -> %s." % (version)
matthias@3989
   456
tonyg@0
   457
    for m in methods: genLookupMethodName(m)
tonyg@0
   458
    print "lookup_method_name({_ClassId, _MethodId} = Id) -> exit({unknown_method_id, Id})."
tonyg@0
   459
vlad@2570
   460
    for c in spec.allClasses(): genLookupClassName(c)
vlad@2570
   461
    print "lookup_class_name(ClassId) -> exit({unknown_class_id, ClassId})."
vlad@2570
   462
tonyg@0
   463
    for m in methods: genMethodId(m)
tonyg@0
   464
    print "method_id(Name) -> exit({unknown_method_name, Name})."
tonyg@0
   465
tonyg@0
   466
    for m in methods: genMethodHasContent(m)
tonyg@0
   467
    print "method_has_content(Name) -> exit({unknown_method_name, Name})."
tonyg@0
   468
paulj@1686
   469
    for m in methods: genMethodIsSynchronous(m)
paulj@1686
   470
    print "is_method_synchronous(Name) -> exit({unknown_method_name, Name})."
paulj@1686
   471
matthew@2624
   472
    for m in methods: genMethodRecord(m)
matthew@2624
   473
    print "method_record(Name) -> exit({unknown_method_name, Name})."
matthew@2624
   474
tonyg@0
   475
    for m in methods: genMethodFieldNames(m)
tonyg@0
   476
    print "method_fieldnames(Name) -> exit({unknown_method_name, Name})."
tonyg@0
   477
tonyg@0
   478
    for m in methods: genDecodeMethodFields(m)
tonyg@0
   479
    print "decode_method_fields(Name, BinaryFields) ->"
tonyg@0
   480
    print "  rabbit_misc:frame_error(Name, BinaryFields)."
tonyg@0
   481
tonyg@0
   482
    for c in spec.allClasses(): genDecodeProperties(c)
tonyg@0
   483
    print "decode_properties(ClassId, _BinaryFields) -> exit({unknown_class_id, ClassId})."
tonyg@0
   484
tonyg@0
   485
    for m in methods: genEncodeMethodFields(m)
tonyg@0
   486
    print "encode_method_fields(Record) -> exit({unknown_method_name, element(1, Record)})."
tonyg@0
   487
tonyg@0
   488
    for c in spec.allClasses(): genEncodeProperties(c)
tonyg@0
   489
    print "encode_properties(Record) -> exit({unknown_properties_record, Record})."
tonyg@0
   490
tonyg@0
   491
    for (c,v,cls) in spec.constants: genLookupException(c,v,cls)
tonyg@0
   492
    print "lookup_amqp_exception(Code) ->"
tonyg@0
   493
    print "  rabbit_log:warning(\"Unknown AMQP error code '~p'~n\", [Code]),"
vlad@2104
   494
    print "  {true, ?INTERNAL_ERROR, <<\"INTERNAL_ERROR\">>}."
tonyg@0
   495
vlad@2357
   496
    for(c,v,cls) in spec.constants: genAmqpException(c,v,cls)
vlad@2357
   497
    print "amqp_exception(_Code) -> undefined."
tonyg@0
   498
tonyg@0
   499
def genHrl(spec):
tonyg@0
   500
    def erlType(domain):
tonyg@0
   501
        return erlangTypeMap[spec.resolveDomain(domain)]
tonyg@0
   502
tonyg@0
   503
    def fieldNameList(fields):
tonyg@0
   504
        return ', '.join([erlangize(f.name) for f in fields])
tonyg@1
   505
tonyg@1
   506
    def fieldNameListDefaults(fields):
tonyg@1
   507
        def fillField(field):
tonyg@1
   508
            result = erlangize(f.name)
tonyg@1
   509
            if field.defaultvalue != None:
karol@73
   510
                conv_fn = erlangDefaultValueTypeConvMap[type(field.defaultvalue)]
karol@73
   511
                result += ' = ' + conv_fn(field.defaultvalue)
tonyg@1
   512
            return result
tonyg@1
   513
        return ', '.join([fillField(f) for f in fields])
matthew@2624
   514
tonyg@0
   515
    methods = spec.allMethods()
tonyg@0
   516
tonyg@2503
   517
    printFileHeader()
tonyg@0
   518
    print "-define(PROTOCOL_PORT, %d)." % (spec.port)
tonyg@0
   519
tonyg@0
   520
    for (c,v,cls) in spec.constants:
tonyg@0
   521
        print "-define(%s, %s)." % (erlangConstantName(c), v)
tonyg@0
   522
tonyg@0
   523
    print "%% Method field records."
tonyg@0
   524
    for m in methods:
tonyg@1
   525
        print "-record(%s, {%s})." % (m.erlangName(), fieldNameListDefaults(m.arguments))
tonyg@0
   526
tonyg@0
   527
    print "%% Class property records."
tonyg@0
   528
    for c in spec.allClasses():
tonyg@0
   529
        print "-record('P_%s', {%s})." % (erlangize(c.name), fieldNameList(c.fields))
tonyg@0
   530
scvalex@3545
   531
tonyg@0
   532
def generateErl(specPath):
tonyg@0
   533
    genErl(AmqpSpec(specPath))
tonyg@0
   534
tonyg@0
   535
def generateHrl(specPath):
tonyg@0
   536
    genHrl(AmqpSpec(specPath))
matthew@2624
   537
tonyg@0
   538
if __name__ == "__main__":
scvalex@3547
   539
    do_main_dict({"header": generateHrl,
scvalex@3547
   540
                  "body": generateErl})
tonyg@0
   541