hop-2012/specgen.py

230 lines
7.1 KiB
Python

import xml.dom.minidom
def constify(s):
s = s.replace('-', '_')
s = s.replace(' ', '_')
s = s.upper()
return s
def cify(s):
s = constify(s)
s = s.lower()
return s
def camelify(s):
s = constify(s)
s = s.split('_')
s = [s[0].lower()] + [w.capitalize() for w in s[1:]]
s = ''.join(s)
return s
ctypemap = {
'shortstr': 'cmsg_bytes_t',
'longstr': 'cmsg_bytes_t',
'table': 'cmsg_bytes_t', # TODO fix
'octet': 'uint8_t',
'short': 'uint16_t',
'long': 'uint32_t',
'longlong': 'uint64_t',
'bit': 'uint8_t',
'timestamp': 'uint64_t',
}
class Constant:
def __init__(self, e):
self.name = e.getAttribute('name')
self.value = e.getAttribute('value')
self.class_ = e.getAttribute('class') or None
def getName(self):
if self.class_:
return 'CMSG_AMQP_ERROR_' + constify(self.name)
else:
return 'CMSG_AMQP_' + constify(self.name)
def getValue(self):
return self.value
class Field:
def __init__(self, e):
self.name = e.getAttribute('name')
self.domain = e.getAttribute('domain') or e.getAttribute('type')
# self.reserved = bool(e.getAttribute('reserved'))
self.type = resolveDomain(self.domain)
def getName(self):
return cify(self.name)
def ctype(self):
return ctypemap[str(self.type)]
class Entity:
def __init__(self, parent, e):
self.parent = parent
self.name = e.getAttribute('name')
self.index = int(e.getAttribute('index'))
self.fields = [Field(ee) \
for ee in e.getElementsByTagName('field') \
if ee.parentNode is e]
def getName(self):
if self.parent:
return self.parent.getName() + '_' + cify(self.name)
else:
return cify(self.name)
def printStructDefExtras(self):
pass
def printStructDef(self, suffix):
if self.fields:
print
print 'typedef struct cmsg_amqp_%s_%s_t_ {' % (self.getName(), suffix)
self.printStructDefExtras()
for f in self.fields:
print ' %s %s;' % (f.ctype(), f.getName())
print '} cmsg_amqp_%s_%s_t;' % (self.getName(), suffix)
class BitWriter:
def __init__(self):
self.bit_offset = 0
def flush(self):
if self.bit_offset:
print ' write_amqp_octet(bit_buffer);'
self.bit_offset = 0
def emit(self, valueExpr):
if self.bit_offset == 0:
print ' bit_buffer = 0;'
print ' if (%s) bit_buffer |= 0x%02x;' % (valueExpr, 1 << self.bit_offset)
self.bit_offset += 1
if self.bit_offset == 8:
self.flush()
class Method(Entity):
def __init__(self, parent, e):
Entity.__init__(self, parent, e)
self.has_content = bool(e.getAttribute('content'))
self.synchronous = bool(e.getAttribute('synchronous'))
self.responses = [ee.getAttribute('name') for ee in e.getElementsByTagName('response')]
def methodId(self):
return self.parent.index << 16 | self.index
def printParseClause(self):
bit_offset = 0
print ' case 0x%08x: /* %s */ ' % (self.methodId(), self.getName())
for f in self.fields:
if f.type == 'bit':
if bit_offset == 0:
print ' if (!parse_amqp_octet(&bit_buffer, &input, &offset))'
print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;'
print ' output->body.%s.%s = (bit_buffer & 0x%02x) != 0;' % \
(self.getName(), f.getName(), 1 << bit_offset)
bit_offset += 1
if bit_offset == 8: bit_offset = 0
else:
print ' if (!parse_amqp_%s(&output->body.%s.%s, &input, &offset))' % \
(f.type, self.getName(), f.getName())
print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;'
print ' return 0;'
def printWriteClause(self):
bw = BitWriter()
print ' case 0x%08x: /* %s */ ' % (self.methodId(), self.getName())
for f in self.fields:
if f.type == 'bit':
bw.emit('output->body.%s.%s' % (self.getName(), f.getName()))
else:
bw.flush()
print ' write_amqp_%s(output->body.%s.%s);' % \
(f.type, self.getName(), f.getName())
bw.flush()
print ' break;'
class Class(Entity):
def __init__(self, e):
Entity.__init__(self, None, e)
self.methods = [Method(self, ee) for ee in e.getElementsByTagName('method')]
def printStructDefExtras(self):
print ' uint32_t _flags;'
def resolveDomain(n):
while n in domainmap and domainmap[n] != n:
n = domainmap[n]
return n
specxml = xml.dom.minidom.parse("amqp0-9-1.stripped.xml")
constants = [Constant(e) for e in specxml.getElementsByTagName('constant')]
domainmap = dict((e.getAttribute('name'), e.getAttribute('type')) \
for e in specxml.getElementsByTagName('domain'))
classes = [Class(e) for e in specxml.getElementsByTagName('class')]
classmap = dict((c.name, c) for c in classes)
def header():
print '/* TODO: put copyright etc */'
print '/* Generated from AMQP spec version %s.%s.%s */' % \
(specxml.documentElement.getAttribute('major'),
specxml.documentElement.getAttribute('minor'),
specxml.documentElement.getAttribute('revision'))
print
for c in constants:
print '#define %s %s' % (c.getName(), c.getValue())
for c in classes:
c.printStructDef('properties')
for c in classes:
for m in c.methods:
m.printStructDef('method')
print
print 'typedef struct cmsg_amqp_method_t_ {'
print ' uint32_t id;'
print ' union {'
for c in classes:
for m in c.methods:
if m.fields:
print ' cmsg_amqp_%s_method_t %s;' % (m.getName(), m.getName())
print ' } body;'
print '} cmsg_amqp_method_t;'
print
print 'int parse_amqp_method('
print ' cmsg_bytes_t input,'
print ' cmsg_amqp_method_t *output) {'
print ' size_t offset = 0;'
print ' uint8_t bit_buffer = 0;'
print ' if (!parse_amqp_long(&output->id, &input, &offset))'
print ' return -CMSG_AMQP_ERROR_FRAME_ERROR;'
print ' switch (output->id) {'
for c in classes:
for m in c.methods:
m.printParseClause()
print ' default:'
print ' warn("Invalid AMQP method number 0x%%08x", output->id);'
print ' return -CMSG_AMQP_ERROR_NOT_IMPLEMENTED;'
print ' }'
print '}'
print
print 'void write_amqp_method('
print ' IOHandle *h,'
print ' cmsg_amqp_method_t *m) {'
print ' uint8_t bit_buffer = 0;'
print ' write_amqp_long(&m->id);'
print ' switch (m->id) {'
for c in classes:
for m in c.methods:
m.printWriteClause()
print ' default:'
print ' die("Invalid AMQP method number 0x%%08x", m->id);'
print ' }'
print '}'
header()