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()