vppapigen: add parser support for enumflags
Type: improvement
Change-Id: I0f15862cc8399a4f7c8a81fe44ba8b27d8772278
Signed-off-by: Paul Vinciguerra <pvinci@vinciconsulting.com>
Signed-off-by: Ole Troan <ot@cisco.com>
(cherry picked from commit e15523297bb3905f2e0eef4272fc69a8a92463cc)
diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py
index da00823..b80dd4d 100755
--- a/src/tools/vppapigen/vppapigen.py
+++ b/src/tools/vppapigen/vppapigen.py
@@ -58,6 +58,7 @@
'define': 'DEFINE',
'typedef': 'TYPEDEF',
'enum': 'ENUM',
+ 'enumflag': 'ENUMFLAG',
'typeonly': 'TYPEONLY',
'manual_print': 'MANUAL_PRINT',
'manual_endian': 'MANUAL_ENDIAN',
@@ -182,7 +183,16 @@
return vla
-class Service():
+class Processable:
+ type = "<Invalid>"
+
+ def process(self, result): # -> Dict
+ result[self.type].append(self)
+
+
+class Service(Processable):
+ type = 'Service'
+
def __init__(self, caller, reply, events=None, stream_message=None,
stream=False):
self.caller = caller
@@ -192,10 +202,11 @@
self.events = [] if events is None else events
-class Typedef():
+class Typedef(Processable):
+ type = 'Typedef'
+
def __init__(self, name, flags, block):
self.name = name
- self.type = 'Typedef'
self.flags = flags
self.block = block
self.crc = str(block).encode()
@@ -211,14 +222,18 @@
self.vla = vla_is_last_check(name, block)
vla_mark_length_field(self.block)
+ def process(self, result):
+ result['types'].append(self)
+
def __repr__(self):
return self.name + str(self.flags) + str(self.block)
-class Using():
+class Using(Processable):
+ type = 'Using'
+
def __init__(self, name, flags, alias):
self.name = name
- self.type = 'Using'
self.vla = False
self.block = []
self.manual_print = True
@@ -248,13 +263,17 @@
self.crc = str(self.block).encode()
global_type_add(name, self)
+ def process(self, result): # -> Dict
+ result['types'].append(self)
+
def __repr__(self):
return self.name + str(self.alias)
-class Union():
+class Union(Processable):
+ type = 'Union'
+
def __init__(self, name, flags, block):
- self.type = 'Union'
self.manual_print = False
self.manual_endian = False
self.name = name
@@ -271,14 +290,18 @@
global_type_add(name, self)
+ def process(self, result):
+ result['types'].append(self)
+
def __repr__(self):
return str(self.block)
-class Define():
+class Define(Processable):
+ type = 'Define'
+
def __init__(self, name, flags, block):
self.name = name
- self.type = 'Define'
self.flags = flags
self.block = block
self.dont_trace = False
@@ -313,16 +336,31 @@
self.crc = str(block).encode()
+ def autoreply_block(self, name, parent):
+ block = [Field('u32', 'context'),
+ Field('i32', 'retval')]
+ # inherit the parent's options
+ for k, v in parent.options.items():
+ block.append(Option(k, v))
+ return Define(name + '_reply', [], block)
+
+ def process(self, result): # -> Dict
+ tname = self.__class__.__name__
+ result[tname].append(self)
+ if self.autoreply:
+ result[tname].append(self.autoreply_block(self.name, self))
+
def __repr__(self):
return self.name + str(self.flags) + str(self.block)
-class Enum():
+class Enum(Processable):
+ type = 'Enum'
+
def __init__(self, name, block, enumtype='u32'):
self.name = name
self.enumtype = enumtype
self.vla = False
- self.type = 'Enum'
self.manual_print = False
count = 0
@@ -350,12 +388,30 @@
self.crc = str(block3).encode()
global_type_add(name, self)
+ def process(self, result):
+ result['types'].append(self)
+
def __repr__(self):
return self.name + str(self.block)
-class Import():
+class EnumFlag(Enum):
+ type = 'EnumFlag'
+
+ def __init__(self, name, block, enumtype='u32'):
+ super(EnumFlag, self).__init__(name, block, enumtype)
+
+ for b in self.block:
+ if bin(b[1])[2:].count("1") > 1:
+ raise TypeError("%s is not a flag enum. No element in a "
+ "flag enum may have more than a "
+ "single bit set." % self.name)
+
+
+class Import(Processable):
+ type = 'Import'
_initialized = False
+
def __new__(cls, *args, **kwargs):
if args[0] not in seen_imports:
instance = super().__new__(cls)
@@ -383,13 +439,17 @@
return self.filename
-class Option():
+class Option(Processable):
+ type = 'Option'
+
def __init__(self, option, value=None):
- self.type = 'Option'
self.option = option
self.value = value
self.crc = str(option).encode()
+ def process(self, result): # -> Dict
+ result[self.type][self.option] = self.value
+
def __repr__(self):
return str(self.option)
@@ -397,9 +457,10 @@
return self.option[index]
-class Array():
+class Array(Processable):
+ type = 'Array'
+
def __init__(self, fieldtype, name, length, modern_vla=False):
- self.type = 'Array'
self.fieldtype = fieldtype
self.fieldname = name
self.modern_vla = modern_vla
@@ -417,9 +478,12 @@
self.lengthfield])
-class Field():
+class Field(Processable):
+ type = 'Field'
+
def __init__(self, fieldtype, name, limit=None):
- self.type = 'Field'
+ # limit field has been expanded to an options dict.
+
self.fieldtype = fieldtype
self.is_lengthfield = False
@@ -437,18 +501,28 @@
return str([self.fieldtype, self.fieldname])
-class Counter():
+class Counter(Processable):
+ type = 'Counter'
+
def __init__(self, path, counter):
- self.type = 'Counter'
self.name = path
self.block = counter
+ def process(self, result): # -> Dict
+ result['Counters'].append(self)
-class Paths():
+
+class Paths(Processable):
+ type = 'Paths'
+
def __init__(self, pathset):
- self.type = 'Paths'
self.paths = pathset
+ def __repr__(self):
+ return "%s(paths=%s)" % (
+ self.__class__.__name__, self.paths
+ )
+
class Coord(object):
""" Coordinates of a syntactic element. Consists of:
@@ -523,6 +597,7 @@
| option
| import
| enum
+ | enumflag
| union
| service
| paths
@@ -643,6 +718,17 @@
else:
p[0] = Enum(p[2], p[4])
+ def p_enumflag(self, p):
+ '''enumflag : ENUMFLAG ID '{' enum_statements '}' ';' '''
+ p[0] = EnumFlag(p[2], p[4])
+
+ def p_enumflag_type(self, p):
+ ''' enumflag : ENUMFLAG ID ':' enum_size '{' enum_statements '}' ';' ''' # noqa : E502
+ if len(p) == 9:
+ p[0] = EnumFlag(p[2], p[6], enumtype=p[4])
+ else:
+ p[0] = EnumFlag(p[2], p[4])
+
def p_enum_size(self, p):
''' enum_size : U8
| U16
@@ -902,14 +988,6 @@
print('File not found: {}'.format(filename), file=sys.stderr)
sys.exit(2)
- def autoreply_block(self, name, parent):
- block = [Field('u32', 'context'),
- Field('i32', 'retval')]
- # inherhit the parent's options
- for k, v in parent.options.items():
- block.append(Option(k, v))
- return Define(name + '_reply', [], block)
-
def process(self, objs):
s = {}
s['Option'] = {}
@@ -921,32 +999,17 @@
s['Paths'] = []
crc = 0
for o in objs:
- tname = o.__class__.__name__
try:
crc = binascii.crc32(o.crc, crc) & 0xffffffff
except AttributeError:
pass
- if isinstance(o, Define):
- s[tname].append(o)
- if o.autoreply:
- s[tname].append(self.autoreply_block(o.name, o))
- elif isinstance(o, Option):
- s[tname][o.option] = o.value
- elif type(o) is list:
+
+ if type(o) is list:
for o2 in o:
if isinstance(o2, Service):
- s['Service'].append(o2)
- elif isinstance(o, (Enum, Typedef, Union, Using)):
- s['types'].append(o)
- elif isinstance(o, Counter):
- s['Counters'].append(o)
- elif isinstance(o, Paths):
- s['Paths'].append(o)
+ o2.process(s)
else:
- if tname not in s:
- raise ValueError('Unknown class type: {} {}'
- .format(tname, o))
- s[tname].append(o)
+ o.process(s)
msgs = {d.name: d for d in s['Define']}
svcs = {s.caller: s for s in s['Service']}
@@ -1021,7 +1084,7 @@
return s
- def process_imports(self, objs, in_import, result):
+ def process_imports(self, objs, in_import, result): # -> List
for o in objs:
# Only allow the following object types from imported file
if in_import and not isinstance(o, (Enum, Import, Typedef,