fib: fib api updates
[vpp.git] / src / tools / vppapigen / vppapigen.py
index 431a9dc..576fa54 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
 
 from __future__ import print_function
 import ply.lex as lex
@@ -18,15 +18,12 @@ sys.dont_write_bytecode = True
 
 # Global dictionary of new types (including enums)
 global_types = {}
-global_crc = 0
 
 
-def global_type_add(name):
+def global_type_add(name, obj):
     '''Add new type to the dictionary of types '''
     type_name = 'vl_api_' + name + '_t'
-    if type_name in global_types:
-        raise KeyError('Type is already defined: {}'.format(name))
-    global_types[type_name] = True
+    global_types[type_name] = obj
 
 
 # All your trace are belong to us!
@@ -122,15 +119,9 @@ class VPPAPILexer(object):
     t_ignore = ' \t'
 
 
-#
-# Side-effect: Sets global_crc
-#
-def crc_block(block):
-    global global_crc
+def crc_block_combine(block, crc):
     s = str(block).encode()
-    global_crc = binascii.crc32(s, global_crc)
-    return binascii.crc32(s) & 0xffffffff
-
+    return binascii.crc32(s, crc) & 0xffffffff
 
 class Service():
     def __init__(self, caller, reply, events=None, stream=False):
@@ -145,7 +136,7 @@ class Typedef():
         self.name = name
         self.flags = flags
         self.block = block
-        self.crc = crc_block(block)
+        self.crc = str(block).encode()
         self.manual_print = False
         self.manual_endian = False
         for f in flags:
@@ -153,7 +144,7 @@ class Typedef():
                 self.manual_print = True
             elif f == 'manual_endian':
                 self.manual_endian = True
-        global_type_add(name)
+        global_type_add(name, self)
 
     def __repr__(self):
         return self.name + str(self.flags) + str(self.block)
@@ -161,18 +152,16 @@ class Typedef():
 
 class Using():
     def __init__(self, name, alias):
-        global global_crc
         self.name = name
 
         if isinstance(alias, Array):
-            a = { 'type': alias.fieldtype,
-                  'length': alias.length }
+            a = { 'type': alias.fieldtype,  # noqa: E201
+                  'length': alias.length }  # noqa: E202
         else:
-            a = { 'type': alias.fieldtype }
+            a = { 'type': alias.fieldtype }  # noqa: E201,E202
         self.alias = a
-        self.crc = binascii.crc32(str(alias).encode()) & 0xffffffff
-        global_crc = binascii.crc32(str(alias).encode(), global_crc)
-        global_type_add(name)
+        self.crc = str(alias).encode()
+        global_type_add(name, self)
 
     def __repr__(self):
         return self.name + str(self.alias)
@@ -183,11 +172,10 @@ class Union():
         self.type = 'Union'
         self.manual_print = False
         self.manual_endian = False
-        global global_crc
         self.name = name
         self.block = block
-        self.crc = crc_block(block)
-        global_type_add(name)
+        self.crc = str(block).encode()
+        global_type_add(name, self)
 
     def __repr__(self):
         return str(self.block)
@@ -198,7 +186,7 @@ class Define():
         self.name = name
         self.flags = flags
         self.block = block
-        self.crc = crc_block(block)
+        self.crc = str(block).encode()
         self.dont_trace = False
         self.manual_print = False
         self.manual_endian = False
@@ -238,8 +226,8 @@ class Enum():
                 block[i] = [b, count]
 
         self.block = block
-        self.crc = crc_block(block)
-        global_type_add(name)
+        self.crc = str(block).encode()
+        global_type_add(name, self)
 
     def __repr__(self):
         return self.name + str(self.block)
@@ -271,7 +259,7 @@ class Import():
 class Option():
     def __init__(self, option):
         self.option = option
-        self.crc = crc_block(option)
+        self.crc = str(option).encode()
 
     def __repr__(self):
         return str(self.option)
@@ -298,10 +286,11 @@ class Array():
 
 
 class Field():
-    def __init__(self, fieldtype, name):
+    def __init__(self, fieldtype, name, limit=None):
         self.type = 'Field'
         self.fieldtype = fieldtype
         self.fieldname = name
+        self.limit = limit
 
     def __repr__(self):
         return str([self.fieldtype, self.fieldname])
@@ -504,7 +493,7 @@ class VPPAPIParser(object):
 
     def p_enum_statements(self, p):
         '''enum_statements : enum_statement
-                            | enum_statements enum_statement'''
+                           | enum_statements enum_statement'''
         if len(p) == 2:
             p[0] = [p[1]]
         else:
@@ -518,12 +507,30 @@ class VPPAPIParser(object):
         else:
             p[0] = p[1]
 
+    def p_field_options(self, p):
+        '''field_options : field_option
+                           | field_options field_option'''
+        if len(p) == 2:
+            p[0] = p[1]
+        else:
+            p[0] = { **p[1], **p[2] }
+
+    def p_field_option(self, p):
+        '''field_option : ID '=' assignee ','
+                        | ID '=' assignee
+        '''
+        p[0] = { p[1]: p[3] }
+
     def p_declaration(self, p):
-        '''declaration : type_specifier ID ';' '''
-        if len(p) != 4:
+        '''declaration : type_specifier ID ';'
+                       | type_specifier ID '[' field_options ']' ';' '''
+        if len(p) == 7:
+            p[0] = Field(p[1], p[2], p[4])
+        elif len(p) == 4:
+            p[0] = Field(p[1], p[2])
+        else:
             self._parse_error('ERROR')
         self.fields.append(p[2])
-        p[0] = Field(p[1], p[2])
 
     def p_declaration_array(self, p):
         '''declaration : type_specifier ID '[' NUM ']' ';'
@@ -623,8 +630,13 @@ class VPPAPI(object):
         s['types'] = []
         s['Import'] = []
         s['Alias'] = {}
+        crc = 0
         for o in objs:
             tname = o.__class__.__name__
+            try:
+                crc = binascii.crc32(o.crc, crc)
+            except AttributeError:
+                pass
             if isinstance(o, Define):
                 s[tname].append(o)
                 if o.autoreply:
@@ -652,6 +664,8 @@ class VPPAPI(object):
         replies = {s.reply: s for s in s['Service']}
         seen_services = {}
 
+        s['file_crc'] = crc
+
         for service in svcs:
             if service not in msgs:
                 raise ValueError(
@@ -748,6 +762,23 @@ def dirlist_add(dirs):
 def dirlist_get():
     return dirlist
 
+def foldup_blocks(block, crc):
+    for b in block:
+        # Look up CRC in user defined types
+        if b.fieldtype.startswith('vl_api_'):
+            # Recursively
+            t = global_types[b.fieldtype]
+            try:
+                crc = crc_block_combine(t.block, crc)
+                return foldup_blocks(t.block, crc)
+            except:
+                pass
+    return crc
+
+def foldup_crcs(s):
+    for f in s:
+        f.crc = foldup_blocks(f.block,
+                              binascii.crc32(f.crc))
 
 #
 # Main
@@ -805,7 +836,8 @@ def main():
     # Add msg_id field
     s['Define'] = add_msg_id(s['Define'])
 
-    file_crc = global_crc & 0xffffffff
+    # Fold up CRCs
+    foldup_crcs(s['Define'])
 
     #
     # Debug
@@ -820,7 +852,7 @@ def main():
     #
     # Generate representation
     #
-    import imp
+    from importlib.machinery import SourceFileLoader
 
     # Default path
     pluginpath = ''
@@ -843,12 +875,13 @@ def main():
                                              args.output_module.lower())
 
     try:
-        plugin = imp.load_source(args.output_module, module_path)
+        plugin = SourceFileLoader(args.output_module,
+                                  module_path).load_module()
     except Exception as err:
         raise Exception('Error importing output plugin: {}, {}'
                         .format(module_path, err))
 
-    result = plugin.run(filename, s, file_crc)
+    result = plugin.run(filename, s)
     if result:
         print(result, file=args.output)
     else: