VPPAPIGEN: Add union and enum support and IP4/IP6 address type.

Note: The Python, Java and C/C++ bindings must be updated before ip/ip_types.api can be used.

ip_types.api:

typedef ip4_address {
  u8 address[4];
};

typedef ip6_address {
  u8 address[16];
};

enum address_family {
  ADDRESS_IP4 = 0,
  ADDRESS_IP6,
};

union address_union {
  vl_api_ip4_address_t ip4;
  vl_api_ip6_address_t ip6;
};

typedef address {
  vl_api_address_family_t af;
  vl_api_address_union_t un;
};

Change-Id: I22f67092f24db5bd650a03c6f446a84cd9fd1074
Signed-off-by: Ole Troan <ot@cisco.com>
This commit is contained in:
Ole Troan
2018-04-24 00:02:37 -04:00
committed by Florin Coras
parent 886bb1ae9b
commit 2c2feab7d8
4 changed files with 160 additions and 85 deletions
+41 -35
View File
@@ -42,7 +42,7 @@ def msg_ids(s):
#ifdef vl_msg_id
'''
for t in s['defines']:
for t in s['Define']:
output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
(t.name.upper(), t.name)
output += "#endif"
@@ -58,7 +58,7 @@ def msg_names(s):
#ifdef vl_msg_name
'''
for t in s['defines']:
for t in s['Define']:
dont_trace = 0 if t.dont_trace else 1
output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
output += "#endif"
@@ -75,7 +75,7 @@ def msg_name_crc_list(s, suffix):
'''
output += "#define foreach_vl_msg_name_crc_%s " % suffix
for t in s['defines']:
for t in s['Define']:
output += "\\\n_(VL_API_%s, %s, %08x) " % \
(t.name.upper(), t.name, t.crc)
output += "\n#endif"
@@ -93,7 +93,7 @@ def duplicate_wrapper_tail():
return '#endif\n\n'
def typedefs(s, filename):
def typedefs(objs, filename):
name = filename.replace('.', '_')
output = '''\
@@ -105,30 +105,32 @@ def typedefs(s, filename):
#define included_{module}
'''
output = output.format(module=name)
for e in s['enums']:
output += duplicate_wrapper_head(e.name)
output += "typedef enum {\n"
for b in e.block:
output += " %s = %s,\n" % (b[0], b[1])
output += '} vl_api_%s_t;\n' % e.name
output += duplicate_wrapper_tail()
for t in s['typedefs'] + s['defines']:
output += duplicate_wrapper_head(t.name)
output += "typedef VL_API_PACKED(struct _vl_api_%s {\n" % t.name
for b in t.block:
if b.type == 'Field':
output += " %s %s;\n" % (b.fieldtype, b.fieldname)
elif b.type == 'Array':
if b.lengthfield:
output += " %s %s[0];\n" % (b.fieldtype, b.fieldname)
else:
output += " %s %s[%s];\n" % (b.fieldtype, b.fieldname,
b.length)
for o in objs:
tname = o.__class__.__name__
output += duplicate_wrapper_head(o.name)
if tname == 'Enum':
output += "typedef enum {\n"
for b in o.block:
output += " %s = %s,\n" % (b[0], b[1])
output += '} vl_api_%s_t;\n' % o.name
else:
if tname == 'Union':
output += "typedef VL_API_PACKED(union _vl_api_%s {\n" % o.name
else:
raise ValueError("Error in processing array type %s" % b)
output += "typedef VL_API_PACKED(struct _vl_api_%s {\n" % o.name
for b in o.block:
if b.type == 'Field':
output += " %s %s;\n" % (b.fieldtype, b.fieldname)
elif b.type == 'Array':
if b.lengthfield:
output += " %s %s[0];\n" % (b.fieldtype, b.fieldname)
else:
output += " %s %s[%s];\n" % (b.fieldtype, b.fieldname,
b.length)
else:
raise ValueError("Error in processing array type %s" % b)
output += '}) vl_api_%s_t;\n' % t.name
output += '}) vl_api_%s_t;\n' % o.name
output += duplicate_wrapper_tail()
output += "\n#endif"
@@ -148,7 +150,7 @@ format_strings = {'u8': '%u',
'f64': '%.2f', }
def printfun(s):
def printfun(objs):
output = '''\
/****** Print functions *****/
#ifdef vl_printfun
@@ -162,7 +164,9 @@ def printfun(s):
#endif
'''
for t in s['typedefs'] + s['defines']:
for t in objs:
if t.__class__.__name__ == 'Enum':
continue
if t.manual_print:
output += "/***** manual: vl_api_%s_t_print *****/\n\n" % t.name
continue
@@ -199,7 +203,7 @@ endian_strings = {
}
def endianfun(s):
def endianfun(objs):
output = '''\
/****** Endian swap functions *****/\n\
@@ -214,7 +218,9 @@ def endianfun(s):
'''
for t in s['typedefs'] + s['defines']:
for t in objs:
if t.__class__.__name__ == 'Enum':
continue
if t.manual_endian:
output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name
continue
@@ -247,8 +253,8 @@ def version_tuple(s, module):
#ifdef vl_api_version_tuple
'''
if 'version' in s['options']:
v = s['options']['version']
if 'version' in s['Option']:
v = s['Option']['version']
(major, minor, patch) = v.split('.')
output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
(module, major, minor, patch)
@@ -269,9 +275,9 @@ def run(input_filename, s, file_crc):
output += msg_ids(s)
output += msg_names(s)
output += msg_name_crc_list(s, filename)
output += typedefs(s, filename + file_extension)
output += printfun(s)
output += endianfun(s)
output += typedefs(s['types'] + s['Define'], filename + file_extension)
output += printfun(s['types'] + s['Define'])
output += endianfun(s['types'] + s['Define'])
output += version_tuple(s, basename)
output += bottom_boilerplate.format(input_filename=basename,
file_crc=file_crc)
+7 -4
View File
@@ -40,6 +40,8 @@ def walk_defs(s):
f = [b.fieldtype, b.fieldname, b.length, b.lengthfield]
else:
f = [b.fieldtype, b.fieldname, b.length]
elif b.type == 'Union':
print('UNION')
else:
raise ValueError("Error in processing array type %s" % b)
d.append(f)
@@ -58,9 +60,10 @@ def walk_defs(s):
def run(filename, s, file_crc):
j = {}
j['types'] = walk_defs(s['typedefs'])
j['messages'] = walk_defs(s['defines'])
j['enums'] = walk_enums(s['enums'])
j['services'] = walk_services(s['services'])
j['types'] = walk_defs([o for o in s['types'] if o.__class__.__name__ == 'Typedef'])
j['messages'] = walk_defs(s['Define'])
j['unions'] = walk_defs([o for o in s['types'] if o.__class__.__name__ == 'Union'])
j['enums'] = walk_enums([o for o in s['types'] if o.__class__.__name__ == 'Enum'])
j['services'] = walk_services(s['Service'])
j['vl_api_version'] = hex(file_crc)
return json.dumps(j, indent=4, separators=(',', ': '))
+75 -46
View File
@@ -71,6 +71,7 @@ class VPPAPILexer(object):
'import': 'IMPORT',
'true': 'TRUE',
'false': 'FALSE',
'union': 'UNION',
}
tokens = ['STRING_LITERAL',
@@ -137,12 +138,35 @@ class Typedef():
self.block = block
self.crc = binascii.crc32(str(block)) & 0xffffffff
global_crc = binascii.crc32(str(block), global_crc)
self.manual_print = False
self.manual_endian = False
for f in flags:
if f == 'manual_print':
self.manual_print = True
elif f == 'manual_endian':
self.manual_endian = True
global_type_add(name)
def __repr__(self):
return self.name + str(self.flags) + str(self.block)
class Union():
def __init__(self, name, block):
self.type = 'Union'
self.manual_print = False
self.manual_endian = False
global global_crc
self.name = name
self.block = block
self.crc = binascii.crc32(str(block)) & 0xffffffff
global_crc = binascii.crc32(str(block), global_crc)
global_type_add(name)
def __repr__(self):
return str(self.block)
class Define():
def __init__(self, name, flags, block):
global global_crc
@@ -151,17 +175,13 @@ class Define():
self.block = block
self.crc = binascii.crc32(str(block)) & 0xffffffff
global_crc = binascii.crc32(str(block), global_crc)
self.typeonly = False
self.dont_trace = False
self.manual_print = False
self.manual_endian = False
self.autoreply = False
self.singular = False
for f in flags:
if f == 'typeonly':
self.typeonly = True
global_type_add(name)
elif f == 'dont_trace':
if f == 'dont_trace':
self.dont_trace = True
elif f == 'manual_print':
self.manual_print = True
@@ -185,6 +205,7 @@ class Enum():
global global_crc
self.name = name
self.enumtype = enumtype
count = 0
for i, b in enumerate(block):
if type(b) is list:
@@ -334,6 +355,7 @@ class VPPAPIParser(object):
| option
| import
| enum
| union
| service'''
p[0] = p[1]
@@ -407,7 +429,11 @@ class VPPAPIParser(object):
def p_define_flist(self, p):
'''define : flist DEFINE ID '{' block_statements_opt '}' ';' '''
p[0] = Define(p[3], p[1], p[5])
# Legacy typedef
if 'typeonly' in p[1]:
p[0] = Typedef(p[3], p[1], p[5])
else:
p[0] = Define(p[3], p[1], p[5])
def p_flist(self, p):
'''flist : flag
@@ -432,7 +458,7 @@ class VPPAPIParser(object):
p[0] = Typedef(p[2], [], p[4])
def p_block_statements_opt(self, p):
'''block_statements_opt : block_statements'''
'''block_statements_opt : block_statements '''
p[0] = p[1]
def p_block_statements(self, p):
@@ -526,6 +552,10 @@ class VPPAPIParser(object):
self._token_coord(p, 1))
p[0] = p[1]
def p_union(self, p):
'''union : UNION ID '{' block_statements_opt '}' ';' '''
p[0] = Union(p[2], p[4])
# Error rule for syntax errors
def p_error(self, p):
if p:
@@ -559,35 +589,33 @@ class VPPAPI(object):
def process(self, objs):
s = {}
s['defines'] = []
s['typedefs'] = []
s['imports'] = []
s['options'] = {}
s['enums'] = []
s['services'] = []
s['Option'] = {}
s['Define'] = []
s['Service'] = []
s['types'] = []
s['Import'] = []
for o in objs:
tname = o.__class__.__name__
if isinstance(o, Define):
if o.typeonly:
s['typedefs'].append(o)
else:
s['defines'].append(o)
if o.autoreply:
s['defines'].append(self.autoreply_block(o.name))
s[tname].append(o)
if o.autoreply:
s[tname].append(self.autoreply_block(o.name))
elif isinstance(o, Option):
s['options'][o[1]] = o[2]
elif isinstance(o, Enum):
s['enums'].append(o)
elif isinstance(o, Typedef):
s['typedefs'].append(o)
s[tname][o[1]] = o[2]
elif type(o) is list:
for o2 in o:
if isinstance(o2, Service):
s['services'].append(o2)
s['Service'].append(o2)
elif isinstance(o, Enum) or isinstance(o, Typedef) or isinstance(o, Union):
s['types'].append(o)
else:
if tname not in s:
raise ValueError('Unknown class type: {} {}'.format(tname, o))
s[tname].append(o)
msgs = {d.name: d for d in s['defines']}
svcs = {s.caller: s for s in s['services']}
replies = {s.reply: s for s in s['services']}
msgs = {d.name: d for d in s['Define']}
svcs = {s.caller: s for s in s['Service']}
replies = {s.reply: s for s in s['Service']}
seen_services = {}
for service in svcs:
@@ -627,7 +655,7 @@ class VPPAPI(object):
if d in svcs:
continue
if d[:-5]+'_details' in msgs:
s['services'].append(Service(d, d[:-5]+'_details',
s['Service'].append(Service(d, d[:-5]+'_details',
stream=True))
else:
raise ValueError('{} missing details message'
@@ -643,7 +671,7 @@ class VPPAPI(object):
if d in svcs:
continue
if d+'_reply' in msgs:
s['services'].append(Service(d, d+'_reply'))
s['Service'].append(Service(d, d+'_reply'))
else:
raise ValueError(
'{} missing reply message ({}) or service definition'
@@ -651,18 +679,18 @@ class VPPAPI(object):
return s
def process_imports(self, objs, in_import):
def process_imports(self, objs, in_import, result):
imported_objs = []
for o in objs:
if isinstance(o, Import):
return self.process_imports(o.result, True) + objs
if in_import:
if isinstance(o, Define) and o.typeonly:
imported_objs.append(o)
if in_import:
return imported_objs
return objs
# Only allow the following object types from imported file
if in_import and not (isinstance(o, Enum) or
isinstance(o, Union) or
isinstance(o, Typedef)):
continue
result.append(o)
if isinstance(o, Import):
self.process_imports(o.result, True, result)
# Add message ids to each message.
def add_msg_id(s):
@@ -720,14 +748,15 @@ def main():
log = logging.getLogger('vppapigen')
parser = VPPAPI(debug=args.debug, filename=filename, logger=log)
result = parser.parse_file(args.input, log)
parsed_objects = parser.parse_file(args.input, log)
# Build a list of objects. Hash of lists.
result = parser.process_imports(result, False)
result = []
parser.process_imports(parsed_objects, False, result)
s = parser.process(result)
# Add msg_id field
s['defines'] = add_msg_id(s['defines'])
s['Define'] = add_msg_id(s['Define'])
file_crc = global_crc & 0xffffffff
@@ -736,10 +765,10 @@ def main():
if args.debug:
import pprint
pp = pprint.PrettyPrinter(indent=4)
for t in s['defines']:
pp.pprint([t.name, t.flags, t.block])
for t in s['typedefs']:
for t in s['Define']:
pp.pprint([t.name, t.flags, t.block])
for t in s['types']:
pp.pprint([t.name, t.block])
#
# Generate representation
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
typedef ip4_address {
u8 address[4];
};
typedef ip6_address {
u8 address[16];
};
enum address_family {
ADDRESS_IP4 = 0,
ADDRESS_IP6,
};
union address_union {
vl_api_ip4_address_t ip4;
vl_api_ip6_address_t ip6;
};
typedef address {
vl_api_address_family_t af;
vl_api_address_union_t un;
};