vppapigen: fix incorrect comments in json
[vpp.git] / src / tools / vppapigen / vppapigen.py
index 944bf5e..2b0ce99 100755 (executable)
@@ -89,7 +89,7 @@ class VPPAPILexer:
         "description": "DESCRIPTION",
     }
 
-    tokens = ["STRING_LITERAL", "ID", "NUM"] + list(reserved.values())
+    tokens = ["STRING_LITERAL", "COMMENT", "ID", "NUM"] + list(reserved.values())
 
     t_ignore_LINE_COMMENT = "//.*"
 
@@ -125,9 +125,10 @@ class VPPAPILexer:
         return t
 
     # C or C++ comment (ignore)
-    def t_comment(self, t):
+    def t_COMMENT(self, t):
         r"(/\*(.|\n)*?\*/)|(//.*)"
         t.lexer.lineno += t.value.count("\n")
+        return t
 
     # Error handling rule
     def t_error(self, t):
@@ -301,7 +302,7 @@ class Union(Processable):
 class Define(Processable):
     type = "Define"
 
-    def __init__(self, name, flags, block):
+    def __init__(self, name, flags, block, comment=None):
         self.name = name
         self.flags = flags
         self.block = block
@@ -311,6 +312,7 @@ class Define(Processable):
         self.autoreply = False
         self.autoendian = 0
         self.options = {}
+        self.comment = comment
         for f in flags:
             if f == "dont_trace":
                 self.dont_trace = True
@@ -560,6 +562,7 @@ class VPPAPIParser:
         self.logger = logger
         self.fields = []
         self.revision = revision
+        self.last_comment = None
 
     def _parse_error(self, msg, coord):
         raise ParseError("%s: %s" % (coord, msg))
@@ -600,6 +603,7 @@ class VPPAPIParser:
         | union
         | service
         | paths
+        | comment
         | counters"""
         p[0] = p[1]
 
@@ -747,7 +751,8 @@ class VPPAPIParser:
     def p_define(self, p):
         """define : DEFINE ID '{' block_statements_opt '}' ';'"""
         self.fields = []
-        p[0] = Define(p[2], [], p[4])
+        p[0] = Define(p[2], [], p[4], self.last_comment)
+        self.last_comment = None
 
     def p_define_flist(self, p):
         """define : flist DEFINE ID '{' block_statements_opt '}' ';'"""
@@ -758,7 +763,8 @@ class VPPAPIParser:
                 self._token_coord(p, 1),
             )
         else:
-            p[0] = Define(p[3], p[1], p[5])
+            p[0] = Define(p[3], p[1], p[5], self.last_comment)
+            self.last_comment = None
 
     def p_flist(self, p):
         """flist : flag
@@ -865,6 +871,11 @@ class VPPAPIParser:
         """
         p[0] = p[1]
 
+    def p_comment(self, p):
+        """comment : COMMENT"""
+        self.last_comment = p[1]
+        p[0] = []
+
     def p_declaration(self, p):
         """declaration : type_specifier variable_name ';'
         | type_specifier variable_name '[' field_options ']' ';'
@@ -955,26 +966,29 @@ class VPPAPIParser:
     # Error rule for syntax errors
     def p_error(self, p):
         if p:
+            if p.type == "COMMENT":
+                self.parser.errok()
+                return
             self._parse_error("before: %s" % p.value, self._coord(lineno=p.lineno))
         else:
             self._parse_error("At end of input", self.filename)
 
+    def build(self, **kwargs):
+        self.parser = yacc.yacc(module=self, **kwargs)
+
 
 class VPPAPI:
     def __init__(self, debug=False, filename="", logger=None, revision=None):
         self.lexer = lex.lex(module=VPPAPILexer(filename), debug=debug)
-        self.parser = yacc.yacc(
-            module=VPPAPIParser(filename, logger, revision=revision),
-            write_tables=False,
-            debug=debug,
-        )
+        self.parser = VPPAPIParser(filename, logger, revision=revision)
+        self.parser.build(write_tables=False, debug=debug)
         self.logger = logger
         self.revision = revision
         self.filename = filename
 
     def parse_string(self, code, debug=0, lineno=1):
         self.lexer.lineno = lineno
-        return self.parser.parse(code, lexer=self.lexer, debug=debug)
+        return self.parser.parser.parse(code, lexer=self.lexer, debug=debug)
 
     def parse_fd(self, fd, debug=0):
         data = fd.read()
@@ -1152,55 +1166,35 @@ def foldup_crcs(s):
         f.crc = foldup_blocks(f.block, binascii.crc32(f.crc) & 0xFFFFFFFF)
 
 
-#
-# Main
-#
-def main():
-    if sys.version_info < (
-        3,
-        5,
-    ):
-        log.exception(
-            "vppapigen requires a supported version of python. "
-            "Please use version 3.5 or greater. "
-            "Using %s",
-            sys.version,
-        )
-        return 1
-
-    cliparser = argparse.ArgumentParser(description="VPP API generator")
-    cliparser.add_argument("--pluginpath", default="")
-    cliparser.add_argument("--includedir", action="append")
-    cliparser.add_argument("--outputdir", action="store")
-    cliparser.add_argument("--input")
-    cliparser.add_argument(
-        "--output",
-        nargs="?",
-        type=argparse.FileType("w", encoding="UTF-8"),
-        default=sys.stdout,
-    )
-
-    cliparser.add_argument("output_module", nargs="?", default="C")
-    cliparser.add_argument("--debug", action="store_true")
-    cliparser.add_argument("--show-name", nargs=1)
-    cliparser.add_argument(
-        "--git-revision", help="Git revision to use for opening files"
-    )
-    args = cliparser.parse_args()
-
-    dirlist_add(args.includedir)
-    if not args.debug:
+def run_vppapigen(
+    input_file=None,
+    output=sys.stdout,
+    includedir=None,
+    debug=False,
+    show_name=None,
+    output_module="C",
+    outputdir=None,
+    pluginpath="",
+    git_revision=None,
+):
+    # reset globals
+    dirlist.clear()
+    global_types.clear()
+    seen_imports.clear()
+
+    dirlist_add(includedir)
+    if not debug:
         sys.excepthook = exception_handler
 
     # Filename
-    if args.show_name:
-        filename = args.show_name[0]
-    elif args.input:
-        filename = args.input
+    if show_name:
+        filename = show_name[0]
+    elif input_file:
+        filename = input_file
     else:
         filename = ""
 
-    if args.debug:
+    if debug:
         logging.basicConfig(stream=sys.stdout, level=logging.WARNING)
     else:
         logging.basicConfig()
@@ -1212,39 +1206,35 @@ def main():
 
     # Default path
     pluginpath = ""
-    if not args.pluginpath:
+    if not pluginpath:
         cand = []
         cand.append(os.path.dirname(os.path.realpath(__file__)))
         cand.append(os.path.dirname(os.path.realpath(__file__)) + "/../share/vpp/")
         for c in cand:
             c += "/"
-            if os.path.isfile(
-                "{}vppapigen_{}.py".format(c, args.output_module.lower())
-            ):
+            if os.path.isfile("{}vppapigen_{}.py".format(c, output_module.lower())):
                 pluginpath = c
                 break
     else:
-        pluginpath = args.pluginpath + "/"
+        pluginpath = pluginpath + "/"
     if pluginpath == "":
         log.exception("Output plugin not found")
         return 1
-    module_path = "{}vppapigen_{}.py".format(pluginpath, args.output_module.lower())
+    module_path = "{}vppapigen_{}.py".format(pluginpath, output_module.lower())
 
     try:
-        plugin = SourceFileLoader(args.output_module, module_path).load_module()
+        plugin = SourceFileLoader(output_module, module_path).load_module()
     except Exception as err:
         log.exception("Error importing output plugin: %s, %s", module_path, err)
         return 1
 
-    parser = VPPAPI(
-        debug=args.debug, filename=filename, logger=log, revision=args.git_revision
-    )
+    parser = VPPAPI(debug=debug, filename=filename, logger=log, revision=git_revision)
 
     try:
-        if not args.input:
+        if not input_file:
             parsed_objects = parser.parse_fd(sys.stdin, log)
         else:
-            parsed_objects = parser.parse_filename(args.input, log)
+            parsed_objects = parser.parse_filename(input_file, log)
     except ParseError as e:
         print("Parse error: ", e, file=sys.stderr)
         sys.exit(1)
@@ -1274,7 +1264,7 @@ def main():
 
     #
     # Debug
-    if args.debug:
+    if debug:
         import pprint
 
         pp = pprint.PrettyPrinter(indent=4, stream=sys.stderr)
@@ -1283,14 +1273,71 @@ def main():
         for t in s["types"]:
             pp.pprint([t.name, t.block])
 
-    result = plugin.run(args, filename, s)
+    result = plugin.run(outputdir, filename, s)
     if result:
-        print(result, file=args.output)
+        if isinstance(output, str):
+            with open(output, "w", encoding="UTF-8") as f:
+                print(result, file=f)
+        else:
+            print(result, file=output)
     else:
         log.exception("Running plugin failed: %s %s", filename, result)
         return 1
     return 0
 
 
+def run_kw_vppapigen(kwargs):
+    return run_vppapigen(**kwargs)
+
+
+#
+# Main
+#
+def main():
+    if sys.version_info < (
+        3,
+        5,
+    ):
+        log.exception(
+            "vppapigen requires a supported version of python. "
+            "Please use version 3.5 or greater. "
+            "Using %s",
+            sys.version,
+        )
+        return 1
+
+    cliparser = argparse.ArgumentParser(description="VPP API generator")
+    cliparser.add_argument("--pluginpath", default="")
+    cliparser.add_argument("--includedir", action="append")
+    cliparser.add_argument("--outputdir", action="store")
+    cliparser.add_argument("--input")
+    cliparser.add_argument(
+        "--output",
+        nargs="?",
+        type=argparse.FileType("w", encoding="UTF-8"),
+        default=sys.stdout,
+    )
+
+    cliparser.add_argument("output_module", nargs="?", default="C")
+    cliparser.add_argument("--debug", action="store_true")
+    cliparser.add_argument("--show-name", nargs=1)
+    cliparser.add_argument(
+        "--git-revision", help="Git revision to use for opening files"
+    )
+    args = cliparser.parse_args()
+
+    return run_vppapigen(
+        includedir=args.includedir,
+        debug=args.debug,
+        outputdir=args.outputdir,
+        show_name=args.show_name,
+        input_file=args.input,
+        output_module=args.output_module,
+        pluginpath=args.pluginpath,
+        git_revision=args.git_revision,
+        output=args.output,
+    )
+
+
 if __name__ == "__main__":
     sys.exit(main())