blob: 0bed578eb9318dd4cbc042733694da4e89afc8a7 [file] [log] [blame]
Nathan Skrzypczak5c2f9642019-09-09 16:45:06 +02001#!/usr/bin/env python3
Ole Troan9d420872017-10-12 13:06:35 +02002
Ole Troan9d420872017-10-12 13:06:35 +02003import sys
4import argparse
Paul Vinciguerraff47fb62019-08-06 19:58:24 -04005import keyword
Ole Troan9d420872017-10-12 13:06:35 +02006import logging
7import binascii
8import os
Ole Troan5c318c72020-05-05 12:23:47 +02009from subprocess import Popen, PIPE
Ole Troandf87f802020-11-18 19:17:48 +010010import ply.lex as lex
11import ply.yacc as yacc
Ole Troaned61b202024-06-19 11:31:30 +020012from io import TextIOWrapper
Ole Troan9d420872017-10-12 13:06:35 +020013
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020014assert sys.version_info >= (3, 5), "Not supported Python version: {}".format(
15 sys.version
16)
17log = logging.getLogger("vppapigen")
Paul Vinciguerra2cd3cc82019-08-06 22:02:45 -040018
Ole Troand6743b12018-03-07 08:40:58 +010019# Ensure we don't leave temporary files around
20sys.dont_write_bytecode = True
21
Ole Troan9d420872017-10-12 13:06:35 +020022#
23# VPP API language
24#
25
26# Global dictionary of new types (including enums)
27global_types = {}
28
Paul Vinciguerra4bf84902019-07-31 00:34:05 -040029seen_imports = {}
30
Ole Troan9d420872017-10-12 13:06:35 +020031
Ole Troan8dbfb432019-04-24 14:31:18 +020032def global_type_add(name, obj):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020033 """Add new type to the dictionary of types"""
34 type_name = "vl_api_" + name + "_t"
Paul Vinciguerra4bf84902019-07-31 00:34:05 -040035 if type_name in global_types:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020036 raise KeyError("Attempted redefinition of {!r} with {!r}.".format(name, obj))
Ole Troan8dbfb432019-04-24 14:31:18 +020037 global_types[type_name] = obj
Ole Troan9d420872017-10-12 13:06:35 +020038
39
40# All your trace are belong to us!
41def exception_handler(exception_type, exception, traceback):
Ole Troan17225df2018-04-11 09:50:03 +020042 print("%s: %s" % (exception_type.__name__, exception))
Ole Troan9d420872017-10-12 13:06:35 +020043
44
45#
46# Lexer
47#
Paul Vinciguerrab00c49c2020-12-04 15:01:53 -050048class VPPAPILexer:
Ole Troan9d420872017-10-12 13:06:35 +020049 def __init__(self, filename):
50 self.filename = filename
51
52 reserved = {
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020053 "service": "SERVICE",
54 "rpc": "RPC",
55 "returns": "RETURNS",
56 "null": "NULL",
57 "stream": "STREAM",
58 "events": "EVENTS",
59 "define": "DEFINE",
60 "typedef": "TYPEDEF",
61 "enum": "ENUM",
62 "enumflag": "ENUMFLAG",
63 "typeonly": "TYPEONLY",
64 "manual_print": "MANUAL_PRINT",
65 "manual_endian": "MANUAL_ENDIAN",
66 "dont_trace": "DONT_TRACE",
67 "autoreply": "AUTOREPLY",
68 "autoendian": "AUTOENDIAN",
69 "option": "OPTION",
70 "u8": "U8",
71 "u16": "U16",
72 "u32": "U32",
73 "u64": "U64",
74 "i8": "I8",
75 "i16": "I16",
76 "i32": "I32",
77 "i64": "I64",
78 "f64": "F64",
79 "bool": "BOOL",
80 "string": "STRING",
81 "import": "IMPORT",
82 "true": "TRUE",
83 "false": "FALSE",
84 "union": "UNION",
85 "counters": "COUNTERS",
86 "paths": "PATHS",
87 "units": "UNITS",
88 "severity": "SEVERITY",
89 "type": "TYPE",
90 "description": "DESCRIPTION",
Ole Troan9d420872017-10-12 13:06:35 +020091 }
92
Ole Troan5d234682021-05-05 23:00:58 +020093 tokens = ["STRING_LITERAL", "COMMENT", "ID", "NUM"] + list(reserved.values())
Ole Troan9d420872017-10-12 13:06:35 +020094
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020095 t_ignore_LINE_COMMENT = "//.*"
Ole Troan9d420872017-10-12 13:06:35 +020096
Ole Troan33a58172019-09-04 09:12:29 +020097 def t_FALSE(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +020098 r"false"
Ole Troan33a58172019-09-04 09:12:29 +020099 t.value = False
100 return t
101
102 def t_TRUE(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200103 r"false"
Ole Troan33a58172019-09-04 09:12:29 +0200104 t.value = True
105 return t
106
Ole Troan9d420872017-10-12 13:06:35 +0200107 def t_NUM(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200108 r"0[xX][0-9a-fA-F]+|-?\d+\.?\d*"
109 base = 16 if t.value.startswith("0x") else 10
110 if "." in t.value:
Paul Vinciguerra063f3742019-07-02 13:00:58 -0400111 t.value = float(t.value)
112 else:
113 t.value = int(t.value, base)
Ole Troan9d420872017-10-12 13:06:35 +0200114 return t
115
116 def t_ID(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200117 r"[a-zA-Z_][a-zA-Z_0-9]*"
Ole Troan9d420872017-10-12 13:06:35 +0200118 # Check for reserved words
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200119 t.type = VPPAPILexer.reserved.get(t.value, "ID")
Ole Troan9d420872017-10-12 13:06:35 +0200120 return t
121
122 # C string
123 def t_STRING_LITERAL(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200124 r"\"([^\\\n]|(\\.))*?\" "
125 t.value = str(t.value).replace('"', "")
Ole Troan9d420872017-10-12 13:06:35 +0200126 return t
127
128 # C or C++ comment (ignore)
Ole Troan5d234682021-05-05 23:00:58 +0200129 def t_COMMENT(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200130 r"(/\*(.|\n)*?\*/)|(//.*)"
131 t.lexer.lineno += t.value.count("\n")
Ole Troan5d234682021-05-05 23:00:58 +0200132 return t
Ole Troan9d420872017-10-12 13:06:35 +0200133
134 # Error handling rule
135 def t_error(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200136 raise ParseError(
137 "Illegal character '{}' ({})"
138 "in {}: line {}".format(
139 t.value[0], hex(ord(t.value[0])), self.filename, t.lexer.lineno
140 )
141 )
Ole Troan9d420872017-10-12 13:06:35 +0200142
143 # Define a rule so we can track line numbers
144 def t_newline(self, t):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200145 r"\n+"
Ole Troan9d420872017-10-12 13:06:35 +0200146 t.lexer.lineno += len(t.value)
147
148 literals = ":{}[];=.,"
149
150 # A string containing ignored characters (spaces and tabs)
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200151 t_ignore = " \t"
Ole Troan9d420872017-10-12 13:06:35 +0200152
Ole Troan17225df2018-04-11 09:50:03 +0200153
Ole Troandf87f802020-11-18 19:17:48 +0100154def vla_mark_length_field(block):
155 if isinstance(block[-1], Array):
156 lengthfield = block[-1].lengthfield
157 for b in block:
158 if b.fieldname == lengthfield:
159 b.is_lengthfield = True
160
161
Ole Troand5a78a52019-09-18 12:12:47 +0200162def vla_is_last_check(name, block):
163 vla = False
164 for i, b in enumerate(block):
165 if isinstance(b, Array) and b.vla:
166 vla = True
167 if i + 1 < len(block):
168 raise ValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200169 'VLA field "{}" must be the last field in message "{}"'.format(
170 b.fieldname, name
171 )
172 )
173 elif b.fieldtype.startswith("vl_api_"):
Ole Troand5a78a52019-09-18 12:12:47 +0200174 if global_types[b.fieldtype].vla:
175 vla = True
176 if i + 1 < len(block):
177 raise ValueError(
178 'VLA field "{}" must be the last '
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200179 'field in message "{}"'.format(b.fieldname, name)
180 )
181 elif b.fieldtype == "string" and b.length == 0:
Ole Troand5a78a52019-09-18 12:12:47 +0200182 vla = True
183 if i + 1 < len(block):
184 raise ValueError(
185 'VLA field "{}" must be the last '
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200186 'field in message "{}"'.format(b.fieldname, name)
187 )
Ole Troand5a78a52019-09-18 12:12:47 +0200188 return vla
189
190
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500191class Processable:
192 type = "<Invalid>"
193
194 def process(self, result): # -> Dict
195 result[self.type].append(self)
196
197
198class Service(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200199 type = "Service"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500200
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200201 def __init__(self, caller, reply, events=None, stream_message=None, stream=False):
Ole Troan9d420872017-10-12 13:06:35 +0200202 self.caller = caller
203 self.reply = reply
204 self.stream = stream
Ole Troanf5db3712020-05-20 15:47:06 +0200205 self.stream_message = stream_message
Paul Vinciguerra7e0c48e2019-02-01 19:37:45 -0800206 self.events = [] if events is None else events
Ole Troan9d420872017-10-12 13:06:35 +0200207
208
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500209class Typedef(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200210 type = "Typedef"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500211
Ole Troan9d420872017-10-12 13:06:35 +0200212 def __init__(self, name, flags, block):
213 self.name = name
214 self.flags = flags
215 self.block = block
Ole Troan8dbfb432019-04-24 14:31:18 +0200216 self.crc = str(block).encode()
Ole Troan2c2feab2018-04-24 00:02:37 -0400217 self.manual_print = False
218 self.manual_endian = False
219 for f in flags:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200220 if f == "manual_print":
Ole Troan2c2feab2018-04-24 00:02:37 -0400221 self.manual_print = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200222 elif f == "manual_endian":
Ole Troan2c2feab2018-04-24 00:02:37 -0400223 self.manual_endian = True
Ole Troan8dbfb432019-04-24 14:31:18 +0200224 global_type_add(name, self)
Ole Troan9d420872017-10-12 13:06:35 +0200225
Ole Troand5a78a52019-09-18 12:12:47 +0200226 self.vla = vla_is_last_check(name, block)
Ole Troandf87f802020-11-18 19:17:48 +0100227 vla_mark_length_field(self.block)
Ole Troane5ff5a32019-08-23 22:55:18 +0200228
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500229 def process(self, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200230 result["types"].append(self)
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500231
Ole Troan9d420872017-10-12 13:06:35 +0200232 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200233 return self.name + str(self.flags) + str(self.block)
Ole Troan9d420872017-10-12 13:06:35 +0200234
235
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500236class Using(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200237 type = "Using"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500238
Ole Troan33a58172019-09-04 09:12:29 +0200239 def __init__(self, name, flags, alias):
Ole Troan53fffa12018-11-13 12:36:56 +0100240 self.name = name
Ole Troane5ff5a32019-08-23 22:55:18 +0200241 self.vla = False
Ole Troan75761b92019-09-11 17:49:08 +0200242 self.block = []
243 self.manual_print = True
244 self.manual_endian = True
Ole Troan53fffa12018-11-13 12:36:56 +0100245
Ole Troan33a58172019-09-04 09:12:29 +0200246 self.manual_print = False
247 self.manual_endian = False
248 for f in flags:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200249 if f == "manual_print":
Ole Troan33a58172019-09-04 09:12:29 +0200250 self.manual_print = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200251 elif f == "manual_endian":
Ole Troan33a58172019-09-04 09:12:29 +0200252 self.manual_endian = True
253
Ole Troan53fffa12018-11-13 12:36:56 +0100254 if isinstance(alias, Array):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200255 a = {"type": alias.fieldtype, "length": alias.length}
Ole Troan53fffa12018-11-13 12:36:56 +0100256 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200257 a = {"type": alias.fieldtype}
Ole Troan53fffa12018-11-13 12:36:56 +0100258 self.alias = a
Ole Troandf87f802020-11-18 19:17:48 +0100259 self.using = alias
260
Ole Troan6006ca82020-08-31 13:54:47 +0200261 #
262 # Should have been:
263 # self.crc = str(alias).encode()
264 # but to be backwards compatible use the block ([])
265 #
266 self.crc = str(self.block).encode()
Ole Troan8dbfb432019-04-24 14:31:18 +0200267 global_type_add(name, self)
Ole Troan53fffa12018-11-13 12:36:56 +0100268
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500269 def process(self, result): # -> Dict
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200270 result["types"].append(self)
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500271
Ole Troan53fffa12018-11-13 12:36:56 +0100272 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200273 return self.name + str(self.alias)
Ole Troan53fffa12018-11-13 12:36:56 +0100274
275
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500276class Union(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200277 type = "Union"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500278
Ole Troan33a58172019-09-04 09:12:29 +0200279 def __init__(self, name, flags, block):
Ole Troan2c2feab2018-04-24 00:02:37 -0400280 self.manual_print = False
281 self.manual_endian = False
Ole Troan2c2feab2018-04-24 00:02:37 -0400282 self.name = name
Ole Troan33a58172019-09-04 09:12:29 +0200283
Ole Troan33a58172019-09-04 09:12:29 +0200284 for f in flags:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200285 if f == "manual_print":
Ole Troan33a58172019-09-04 09:12:29 +0200286 self.manual_print = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200287 elif f == "manual_endian":
Ole Troan33a58172019-09-04 09:12:29 +0200288 self.manual_endian = True
289
Ole Troan2c2feab2018-04-24 00:02:37 -0400290 self.block = block
Ole Troan8dbfb432019-04-24 14:31:18 +0200291 self.crc = str(block).encode()
Ole Troand5a78a52019-09-18 12:12:47 +0200292 self.vla = vla_is_last_check(name, block)
293
Ole Troan8dbfb432019-04-24 14:31:18 +0200294 global_type_add(name, self)
Ole Troan2c2feab2018-04-24 00:02:37 -0400295
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500296 def process(self, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200297 result["types"].append(self)
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500298
Ole Troan2c2feab2018-04-24 00:02:37 -0400299 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200300 return str(self.block)
Ole Troan2c2feab2018-04-24 00:02:37 -0400301
302
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500303class Define(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200304 type = "Define"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500305
Ole Troan5d234682021-05-05 23:00:58 +0200306 def __init__(self, name, flags, block, comment=None):
Ole Troan9d420872017-10-12 13:06:35 +0200307 self.name = name
308 self.flags = flags
309 self.block = block
Ole Troan9d420872017-10-12 13:06:35 +0200310 self.dont_trace = False
311 self.manual_print = False
312 self.manual_endian = False
313 self.autoreply = False
Neale Ranns9302cfe2021-02-02 09:21:52 +0000314 self.autoendian = 0
Ole Troan2a1ca782019-09-19 01:08:30 +0200315 self.options = {}
Ole Troan5d234682021-05-05 23:00:58 +0200316 self.comment = comment
Ole Troan9d420872017-10-12 13:06:35 +0200317 for f in flags:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200318 if f == "dont_trace":
Ole Troan9d420872017-10-12 13:06:35 +0200319 self.dont_trace = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200320 elif f == "manual_print":
Ole Troan9d420872017-10-12 13:06:35 +0200321 self.manual_print = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200322 elif f == "manual_endian":
Ole Troan9d420872017-10-12 13:06:35 +0200323 self.manual_endian = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200324 elif f == "autoreply":
Ole Troan9d420872017-10-12 13:06:35 +0200325 self.autoreply = True
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200326 elif f == "autoendian":
Neale Ranns9302cfe2021-02-02 09:21:52 +0000327 self.autoendian = 1
Ole Troan9d420872017-10-12 13:06:35 +0200328
Ole Troan5c318c72020-05-05 12:23:47 +0200329 remove = []
Ole Troand5a78a52019-09-18 12:12:47 +0200330 for b in block:
Ole Troan9d420872017-10-12 13:06:35 +0200331 if isinstance(b, Option):
Ole Troan791c2062020-12-08 20:35:32 +0100332 self.options[b.option] = b.value
Ole Troan5c318c72020-05-05 12:23:47 +0200333 remove.append(b)
Ole Troan2a1ca782019-09-19 01:08:30 +0200334
Ole Troan14a6c0e2020-05-13 11:47:43 +0200335 block = [x for x in block if x not in remove]
Ole Troan5c318c72020-05-05 12:23:47 +0200336 self.block = block
Ole Troand5a78a52019-09-18 12:12:47 +0200337 self.vla = vla_is_last_check(name, block)
Ole Troandf87f802020-11-18 19:17:48 +0100338 vla_mark_length_field(self.block)
339
Ole Troan2a1ca782019-09-19 01:08:30 +0200340 self.crc = str(block).encode()
Ole Troane5ff5a32019-08-23 22:55:18 +0200341
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500342 def autoreply_block(self, name, parent):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200343 block = [Field("u32", "context"), Field("i32", "retval")]
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500344 # inherit the parent's options
345 for k, v in parent.options.items():
346 block.append(Option(k, v))
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200347 return Define(name + "_reply", [], block)
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500348
349 def process(self, result): # -> Dict
350 tname = self.__class__.__name__
351 result[tname].append(self)
352 if self.autoreply:
353 result[tname].append(self.autoreply_block(self.name, self))
354
Ole Troan9d420872017-10-12 13:06:35 +0200355 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200356 return self.name + str(self.flags) + str(self.block)
Ole Troan9d420872017-10-12 13:06:35 +0200357
358
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500359class Enum(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200360 type = "Enum"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500361
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200362 def __init__(self, name, block, enumtype="u32"):
Ole Troan9d420872017-10-12 13:06:35 +0200363 self.name = name
364 self.enumtype = enumtype
Ole Troane5ff5a32019-08-23 22:55:18 +0200365 self.vla = False
Ole Troandf87f802020-11-18 19:17:48 +0100366 self.manual_print = False
Ole Troan2c2feab2018-04-24 00:02:37 -0400367
Filip Varga9a8d3d72022-05-23 21:18:38 +0200368 count = -1
Ole Troan6006ca82020-08-31 13:54:47 +0200369 block2 = []
370 block3 = []
371 bc_set = False
372
373 for b in block:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200374 if "value" in b:
375 count = b["value"]
Ole Troan9d420872017-10-12 13:06:35 +0200376 else:
377 count += 1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200378 block2.append([b["id"], count])
Ole Troan6006ca82020-08-31 13:54:47 +0200379 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200380 if b["option"]["backwards_compatible"]:
Ole Troan6006ca82020-08-31 13:54:47 +0200381 pass
382 bc_set = True
383 except KeyError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200384 block3.append([b["id"], count])
Ole Troan6006ca82020-08-31 13:54:47 +0200385 if bc_set:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200386 raise ValueError(
387 "Backward compatible enum must "
388 "be last {!r} {!r}".format(name, b["id"])
389 )
Ole Troan6006ca82020-08-31 13:54:47 +0200390 self.block = block2
391 self.crc = str(block3).encode()
Ole Troan8dbfb432019-04-24 14:31:18 +0200392 global_type_add(name, self)
Ole Troan9d420872017-10-12 13:06:35 +0200393
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500394 def process(self, result):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200395 result["types"].append(self)
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500396
Ole Troan9d420872017-10-12 13:06:35 +0200397 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200398 return self.name + str(self.block)
Ole Troan9d420872017-10-12 13:06:35 +0200399
400
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500401class EnumFlag(Enum):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200402 type = "EnumFlag"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500403
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200404 def __init__(self, name, block, enumtype="u32"):
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500405 super(EnumFlag, self).__init__(name, block, enumtype)
406
407 for b in self.block:
408 if bin(b[1])[2:].count("1") > 1:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200409 raise TypeError(
410 "%s is not a flag enum. No element in a "
411 "flag enum may have more than a "
412 "single bit set." % self.name
413 )
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500414
415
416class Import(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200417 type = "Import"
Ole Troandf87f802020-11-18 19:17:48 +0100418 _initialized = False
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500419
Paul Vinciguerra4bf84902019-07-31 00:34:05 -0400420 def __new__(cls, *args, **kwargs):
421 if args[0] not in seen_imports:
422 instance = super().__new__(cls)
423 instance._initialized = False
424 seen_imports[args[0]] = instance
425
426 return seen_imports[args[0]]
427
Ole Troan5c318c72020-05-05 12:23:47 +0200428 def __init__(self, filename, revision):
Paul Vinciguerra4bf84902019-07-31 00:34:05 -0400429 if self._initialized:
430 return
Ole Troandf87f802020-11-18 19:17:48 +0100431 self.filename = filename
432 # Deal with imports
433 parser = VPPAPI(filename=filename, revision=revision)
434 dirlist = dirlist_get()
435 f = filename
436 for dir in dirlist:
437 f = os.path.join(dir, filename)
438 if os.path.exists(f):
439 break
440 self.result = parser.parse_filename(f, None)
441 self._initialized = True
Ole Troan9d420872017-10-12 13:06:35 +0200442
443 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200444 return self.filename
Ole Troan9d420872017-10-12 13:06:35 +0200445
446
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500447class Option(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200448 type = "Option"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500449
Ole Troan68ebcd52020-08-10 17:06:44 +0200450 def __init__(self, option, value=None):
Ole Troan9d420872017-10-12 13:06:35 +0200451 self.option = option
Ole Troan33a58172019-09-04 09:12:29 +0200452 self.value = value
Ole Troan8dbfb432019-04-24 14:31:18 +0200453 self.crc = str(option).encode()
Ole Troan9d420872017-10-12 13:06:35 +0200454
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500455 def process(self, result): # -> Dict
456 result[self.type][self.option] = self.value
457
Ole Troan9d420872017-10-12 13:06:35 +0200458 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200459 return str(self.option)
Ole Troan9d420872017-10-12 13:06:35 +0200460
461 def __getitem__(self, index):
462 return self.option[index]
463
464
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500465class Array(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200466 type = "Array"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500467
Ole Troane5ff5a32019-08-23 22:55:18 +0200468 def __init__(self, fieldtype, name, length, modern_vla=False):
Ole Troan9d420872017-10-12 13:06:35 +0200469 self.fieldtype = fieldtype
470 self.fieldname = name
Ole Troane5ff5a32019-08-23 22:55:18 +0200471 self.modern_vla = modern_vla
Ole Troan9d420872017-10-12 13:06:35 +0200472 if type(length) is str:
473 self.lengthfield = length
474 self.length = 0
Ole Troane5ff5a32019-08-23 22:55:18 +0200475 self.vla = True
Ole Troan9d420872017-10-12 13:06:35 +0200476 else:
477 self.length = length
478 self.lengthfield = None
Ole Troane5ff5a32019-08-23 22:55:18 +0200479 self.vla = False
Ole Troan9d420872017-10-12 13:06:35 +0200480
481 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200482 return str([self.fieldtype, self.fieldname, self.length, self.lengthfield])
Ole Troan9d420872017-10-12 13:06:35 +0200483
484
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500485class Field(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200486 type = "Field"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500487
Ole Troan9ac11382019-04-23 17:11:01 +0200488 def __init__(self, fieldtype, name, limit=None):
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500489 # limit field has been expanded to an options dict.
490
Ole Troan9d420872017-10-12 13:06:35 +0200491 self.fieldtype = fieldtype
Ole Troandf87f802020-11-18 19:17:48 +0100492 self.is_lengthfield = False
Ole Troane5ff5a32019-08-23 22:55:18 +0200493
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200494 if self.fieldtype == "string":
495 raise ValueError("The string type {!r} is an " "array type ".format(name))
Ole Troane5ff5a32019-08-23 22:55:18 +0200496
Paul Vinciguerraff47fb62019-08-06 19:58:24 -0400497 if name in keyword.kwlist:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200498 raise ValueError(
499 "Fieldname {!r} is a python keyword and is not "
500 "accessible via the python API. ".format(name)
501 )
Ole Troan9d420872017-10-12 13:06:35 +0200502 self.fieldname = name
Ole Troan9ac11382019-04-23 17:11:01 +0200503 self.limit = limit
Ole Troan9d420872017-10-12 13:06:35 +0200504
505 def __repr__(self):
Vratko Polak7520e172019-08-01 10:31:49 +0200506 return str([self.fieldtype, self.fieldname])
Ole Troan9d420872017-10-12 13:06:35 +0200507
508
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500509class Counter(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200510 type = "Counter"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500511
Ole Troan148c7b72020-10-07 18:05:37 +0200512 def __init__(self, path, counter):
Ole Troan148c7b72020-10-07 18:05:37 +0200513 self.name = path
514 self.block = counter
515
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500516 def process(self, result): # -> Dict
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200517 result["Counters"].append(self)
Ole Troan148c7b72020-10-07 18:05:37 +0200518
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500519
520class Paths(Processable):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200521 type = "Paths"
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500522
Ole Troan148c7b72020-10-07 18:05:37 +0200523 def __init__(self, pathset):
Ole Troan148c7b72020-10-07 18:05:37 +0200524 self.paths = pathset
525
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500526 def __repr__(self):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200527 return "%s(paths=%s)" % (self.__class__.__name__, self.paths)
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500528
Ole Troan148c7b72020-10-07 18:05:37 +0200529
Paul Vinciguerrab00c49c2020-12-04 15:01:53 -0500530class Coord:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200531 """Coordinates of a syntactic element. Consists of:
532 - File name
533 - Line number
534 - (optional) column number, for the Lexer
Ole Troan9d420872017-10-12 13:06:35 +0200535 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200536
537 __slots__ = ("file", "line", "column", "__weakref__")
Ole Troan9d420872017-10-12 13:06:35 +0200538
539 def __init__(self, file, line, column=None):
540 self.file = file
541 self.line = line
542 self.column = column
543
544 def __str__(self):
545 str = "%s:%s" % (self.file, self.line)
546 if self.column:
547 str += ":%s" % self.column
548 return str
549
550
551class ParseError(Exception):
552 pass
553
554
555#
556# Grammar rules
557#
Paul Vinciguerrab00c49c2020-12-04 15:01:53 -0500558class VPPAPIParser:
Ole Troan9d420872017-10-12 13:06:35 +0200559 tokens = VPPAPILexer.tokens
560
Ole Troan5c318c72020-05-05 12:23:47 +0200561 def __init__(self, filename, logger, revision=None):
Ole Troan9d420872017-10-12 13:06:35 +0200562 self.filename = filename
563 self.logger = logger
564 self.fields = []
Ole Troan5c318c72020-05-05 12:23:47 +0200565 self.revision = revision
Ole Troan5d234682021-05-05 23:00:58 +0200566 self.last_comment = None
Ole Troan9d420872017-10-12 13:06:35 +0200567
568 def _parse_error(self, msg, coord):
569 raise ParseError("%s: %s" % (coord, msg))
570
571 def _parse_warning(self, msg, coord):
572 if self.logger:
573 self.logger.warning("%s: %s" % (coord, msg))
574
575 def _coord(self, lineno, column=None):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200576 return Coord(file=self.filename, line=lineno, column=column)
Ole Troan9d420872017-10-12 13:06:35 +0200577
578 def _token_coord(self, p, token_idx):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200579 """Returns the coordinates for the YaccProduction object 'p' indexed
580 with 'token_idx'. The coordinate includes the 'lineno' and
581 'column'. Both follow the lex semantic, starting from 1.
Ole Troan9d420872017-10-12 13:06:35 +0200582 """
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200583 last_cr = p.lexer.lexdata.rfind("\n", 0, p.lexpos(token_idx))
Ole Troan9d420872017-10-12 13:06:35 +0200584 if last_cr < 0:
585 last_cr = -1
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200586 column = p.lexpos(token_idx) - (last_cr)
Ole Troan9d420872017-10-12 13:06:35 +0200587 return self._coord(p.lineno(token_idx), column)
588
589 def p_slist(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200590 """slist : stmt
591 | slist stmt"""
Ole Troan9d420872017-10-12 13:06:35 +0200592 if len(p) == 2:
593 p[0] = [p[1]]
594 else:
595 p[0] = p[1] + [p[2]]
596
597 def p_stmt(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200598 """stmt : define
599 | typedef
600 | option
601 | import
602 | enum
603 | enumflag
604 | union
605 | service
606 | paths
Ole Troan5d234682021-05-05 23:00:58 +0200607 | comment
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200608 | counters"""
Ole Troan9d420872017-10-12 13:06:35 +0200609 p[0] = p[1]
610
611 def p_import(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200612 """import : IMPORT STRING_LITERAL ';'"""
Ole Troan5c318c72020-05-05 12:23:47 +0200613 p[0] = Import(p[2], revision=self.revision)
Ole Troan9d420872017-10-12 13:06:35 +0200614
Ole Troan148c7b72020-10-07 18:05:37 +0200615 def p_path_elements(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200616 """path_elements : path_element
617 | path_elements path_element"""
Ole Troan148c7b72020-10-07 18:05:37 +0200618 if len(p) == 2:
619 p[0] = p[1]
620 else:
621 if type(p[1]) is dict:
622 p[0] = [p[1], p[2]]
623 else:
624 p[0] = p[1] + [p[2]]
625
626 def p_path_element(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200627 """path_element : STRING_LITERAL STRING_LITERAL ';'"""
628 p[0] = {"path": p[1], "counter": p[2]}
Ole Troan148c7b72020-10-07 18:05:37 +0200629
630 def p_paths(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200631 """paths : PATHS '{' path_elements '}' ';'"""
Ole Troan148c7b72020-10-07 18:05:37 +0200632 p[0] = Paths(p[3])
633
634 def p_counters(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200635 """counters : COUNTERS ID '{' counter_elements '}' ';'"""
Ole Troan148c7b72020-10-07 18:05:37 +0200636 p[0] = Counter(p[2], p[4])
637
638 def p_counter_elements(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200639 """counter_elements : counter_element
640 | counter_elements counter_element"""
Ole Troan148c7b72020-10-07 18:05:37 +0200641 if len(p) == 2:
Ole Troan18327be2021-01-12 21:49:38 +0100642 p[0] = [p[1]]
Ole Troan148c7b72020-10-07 18:05:37 +0200643 else:
644 if type(p[1]) is dict:
645 p[0] = [p[1], p[2]]
646 else:
647 p[0] = p[1] + [p[2]]
648
649 def p_counter_element(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200650 """counter_element : ID '{' counter_statements '}' ';'"""
651 p[0] = {**{"name": p[1]}, **p[3]}
Ole Troan148c7b72020-10-07 18:05:37 +0200652
653 def p_counter_statements(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200654 """counter_statements : counter_statement
655 | counter_statements counter_statement"""
Ole Troan148c7b72020-10-07 18:05:37 +0200656 if len(p) == 2:
657 p[0] = p[1]
658 else:
659 p[0] = {**p[1], **p[2]}
660
661 def p_counter_statement(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200662 """counter_statement : SEVERITY ID ';'
663 | UNITS STRING_LITERAL ';'
664 | DESCRIPTION STRING_LITERAL ';'
665 | TYPE ID ';'"""
Ole Troan148c7b72020-10-07 18:05:37 +0200666 p[0] = {p[1]: p[2]}
667
Ole Troan9d420872017-10-12 13:06:35 +0200668 def p_service(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200669 """service : SERVICE '{' service_statements '}' ';'"""
Ole Troan9d420872017-10-12 13:06:35 +0200670 p[0] = p[3]
671
672 def p_service_statements(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200673 """service_statements : service_statement
674 | service_statements service_statement"""
Ole Troan9d420872017-10-12 13:06:35 +0200675 if len(p) == 2:
676 p[0] = [p[1]]
677 else:
678 p[0] = p[1] + [p[2]]
679
680 def p_service_statement(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200681 """service_statement : RPC ID RETURNS NULL ';'
682 | RPC ID RETURNS ID ';'
683 | RPC ID RETURNS STREAM ID ';'
684 | RPC ID RETURNS ID EVENTS event_list ';'"""
Marek Gradzkifc70e3a2018-03-06 10:56:26 +0100685 if p[2] == p[4]:
686 # Verify that caller and reply differ
Ole Troan17225df2018-04-11 09:50:03 +0200687 self._parse_error(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200688 "Reply ID ({}) should not be equal to Caller ID".format(p[2]),
689 self._token_coord(p, 1),
690 )
Ole Troan9d420872017-10-12 13:06:35 +0200691 if len(p) == 8:
692 p[0] = Service(p[2], p[4], p[6])
693 elif len(p) == 7:
694 p[0] = Service(p[2], p[5], stream=True)
695 else:
696 p[0] = Service(p[2], p[4])
697
Ole Troanf5db3712020-05-20 15:47:06 +0200698 def p_service_statement2(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200699 """service_statement : RPC ID RETURNS ID STREAM ID ';'"""
Ole Troanf5db3712020-05-20 15:47:06 +0200700 p[0] = Service(p[2], p[4], stream_message=p[6], stream=True)
701
Ole Troan9d420872017-10-12 13:06:35 +0200702 def p_event_list(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200703 """event_list : events
704 | event_list events"""
Ole Troan9d420872017-10-12 13:06:35 +0200705 if len(p) == 2:
706 p[0] = [p[1]]
707 else:
708 p[0] = p[1] + [p[2]]
709
710 def p_event(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200711 """events : ID
712 | ID ','"""
Ole Troan9d420872017-10-12 13:06:35 +0200713 p[0] = p[1]
714
715 def p_enum(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200716 """enum : ENUM ID '{' enum_statements '}' ';'"""
Ole Troan9d420872017-10-12 13:06:35 +0200717 p[0] = Enum(p[2], p[4])
718
719 def p_enum_type(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200720 """enum : ENUM ID ':' enum_size '{' enum_statements '}' ';'"""
Ole Troan9d420872017-10-12 13:06:35 +0200721 if len(p) == 9:
722 p[0] = Enum(p[2], p[6], enumtype=p[4])
723 else:
724 p[0] = Enum(p[2], p[4])
725
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500726 def p_enumflag(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200727 """enumflag : ENUMFLAG ID '{' enum_statements '}' ';'"""
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500728 p[0] = EnumFlag(p[2], p[4])
729
730 def p_enumflag_type(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200731 """enumflag : ENUMFLAG ID ':' enumflag_size '{' enum_statements '}' ';'""" # noqa : E502
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -0500732 if len(p) == 9:
733 p[0] = EnumFlag(p[2], p[6], enumtype=p[4])
734 else:
735 p[0] = EnumFlag(p[2], p[4])
736
Ole Troan9d420872017-10-12 13:06:35 +0200737 def p_enum_size(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200738 """enum_size : U8
739 | U16
740 | U32
741 | I8
742 | I16
743 | I32"""
Paul Vinciguerra6d467b32020-12-13 04:12:55 +0000744 p[0] = p[1]
745
746 def p_enumflag_size(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200747 """enumflag_size : U8
748 | U16
749 | U32"""
Ole Troan9d420872017-10-12 13:06:35 +0200750 p[0] = p[1]
751
752 def p_define(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200753 """define : DEFINE ID '{' block_statements_opt '}' ';'"""
Ole Troan9d420872017-10-12 13:06:35 +0200754 self.fields = []
Ole Troan5d234682021-05-05 23:00:58 +0200755 p[0] = Define(p[2], [], p[4], self.last_comment)
Ondrej Fabrya4f994f2023-02-03 11:33:39 +0100756 self.last_comment = None
Ole Troan9d420872017-10-12 13:06:35 +0200757
758 def p_define_flist(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200759 """define : flist DEFINE ID '{' block_statements_opt '}' ';'"""
Ole Troan2c2feab2018-04-24 00:02:37 -0400760 # Legacy typedef
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200761 if "typeonly" in p[1]:
762 self._parse_error(
763 "legacy typedef. use typedef: {} {}[{}];".format(p[1], p[2], p[4]),
764 self._token_coord(p, 1),
765 )
Ole Troan2c2feab2018-04-24 00:02:37 -0400766 else:
Ole Troan5d234682021-05-05 23:00:58 +0200767 p[0] = Define(p[3], p[1], p[5], self.last_comment)
Ondrej Fabrya4f994f2023-02-03 11:33:39 +0100768 self.last_comment = None
Ole Troan9d420872017-10-12 13:06:35 +0200769
770 def p_flist(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200771 """flist : flag
772 | flist flag"""
Ole Troan9d420872017-10-12 13:06:35 +0200773 if len(p) == 2:
774 p[0] = [p[1]]
775 else:
776 p[0] = p[1] + [p[2]]
777
778 def p_flag(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200779 """flag : MANUAL_PRINT
780 | MANUAL_ENDIAN
781 | DONT_TRACE
782 | TYPEONLY
783 | AUTOENDIAN
784 | AUTOREPLY"""
Ole Troan9d420872017-10-12 13:06:35 +0200785 if len(p) == 1:
786 return
787 p[0] = p[1]
788
789 def p_typedef(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200790 """typedef : TYPEDEF ID '{' block_statements_opt '}' ';'"""
Ole Troan9d420872017-10-12 13:06:35 +0200791 p[0] = Typedef(p[2], [], p[4])
792
Ole Troan33a58172019-09-04 09:12:29 +0200793 def p_typedef_flist(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200794 """typedef : flist TYPEDEF ID '{' block_statements_opt '}' ';'"""
Ole Troan33a58172019-09-04 09:12:29 +0200795 p[0] = Typedef(p[3], p[1], p[5])
796
Ole Troan53fffa12018-11-13 12:36:56 +0100797 def p_typedef_alias(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200798 """typedef : TYPEDEF declaration"""
Ole Troan33a58172019-09-04 09:12:29 +0200799 p[0] = Using(p[2].fieldname, [], p[2])
800
801 def p_typedef_alias_flist(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200802 """typedef : flist TYPEDEF declaration"""
Ole Troan33a58172019-09-04 09:12:29 +0200803 p[0] = Using(p[3].fieldname, p[1], p[3])
Ole Troan53fffa12018-11-13 12:36:56 +0100804
Ole Troan9d420872017-10-12 13:06:35 +0200805 def p_block_statements_opt(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200806 """block_statements_opt : block_statements"""
Ole Troan9d420872017-10-12 13:06:35 +0200807 p[0] = p[1]
808
809 def p_block_statements(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200810 """block_statements : block_statement
811 | block_statements block_statement"""
Ole Troan9d420872017-10-12 13:06:35 +0200812 if len(p) == 2:
813 p[0] = [p[1]]
814 else:
815 p[0] = p[1] + [p[2]]
816
817 def p_block_statement(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200818 """block_statement : declaration
819 | option"""
Ole Troan9d420872017-10-12 13:06:35 +0200820 p[0] = p[1]
821
822 def p_enum_statements(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200823 """enum_statements : enum_statement
824 | enum_statements enum_statement"""
Ole Troan9d420872017-10-12 13:06:35 +0200825 if len(p) == 2:
826 p[0] = [p[1]]
827 else:
828 p[0] = p[1] + [p[2]]
829
830 def p_enum_statement(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200831 """enum_statement : ID '=' NUM ','
832 | ID ','
833 | ID '[' field_options ']' ','
834 | ID '=' NUM '[' field_options ']' ','"""
Ole Troan6006ca82020-08-31 13:54:47 +0200835 if len(p) == 3:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200836 p[0] = {"id": p[1]}
Ole Troan6006ca82020-08-31 13:54:47 +0200837 elif len(p) == 5:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200838 p[0] = {"id": p[1], "value": p[3]}
Ole Troan6006ca82020-08-31 13:54:47 +0200839 elif len(p) == 6:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200840 p[0] = {"id": p[1], "option": p[3]}
Ole Troan6006ca82020-08-31 13:54:47 +0200841 elif len(p) == 8:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200842 p[0] = {"id": p[1], "value": p[3], "option": p[5]}
Ole Troan9d420872017-10-12 13:06:35 +0200843 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200844 self._parse_error("ERROR", self._token_coord(p, 1))
Ole Troan9d420872017-10-12 13:06:35 +0200845
Ole Troan85465582019-04-30 10:04:36 +0200846 def p_field_options(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200847 """field_options : field_option
848 | field_options field_option"""
Ole Troan85465582019-04-30 10:04:36 +0200849 if len(p) == 2:
850 p[0] = p[1]
851 else:
Ole Troane5ff5a32019-08-23 22:55:18 +0200852 p[0] = {**p[1], **p[2]}
Ole Troan85465582019-04-30 10:04:36 +0200853
854 def p_field_option(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200855 """field_option : ID
856 | ID '=' assignee ','
857 | ID '=' assignee
Ole Troane5ff5a32019-08-23 22:55:18 +0200858
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200859 """
Ole Troane5ff5a32019-08-23 22:55:18 +0200860 if len(p) == 2:
861 p[0] = {p[1]: None}
862 else:
863 p[0] = {p[1]: p[3]}
Ole Troan85465582019-04-30 10:04:36 +0200864
Ole Troan148c7b72020-10-07 18:05:37 +0200865 def p_variable_name(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200866 """variable_name : ID
867 | TYPE
868 | SEVERITY
869 | DESCRIPTION
870 | COUNTERS
871 | PATHS
872 """
Ole Troan148c7b72020-10-07 18:05:37 +0200873 p[0] = p[1]
874
Ole Troan5d234682021-05-05 23:00:58 +0200875 def p_comment(self, p):
876 """comment : COMMENT"""
877 self.last_comment = p[1]
878 p[0] = []
879
Ole Troan9d420872017-10-12 13:06:35 +0200880 def p_declaration(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200881 """declaration : type_specifier variable_name ';'
882 | type_specifier variable_name '[' field_options ']' ';'
883 """
Ole Troan85465582019-04-30 10:04:36 +0200884 if len(p) == 7:
885 p[0] = Field(p[1], p[2], p[4])
Ole Troan9ac11382019-04-23 17:11:01 +0200886 elif len(p) == 4:
887 p[0] = Field(p[1], p[2])
888 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200889 self._parse_error("ERROR", self._token_coord(p, 1))
Ole Troan9d420872017-10-12 13:06:35 +0200890 self.fields.append(p[2])
Ole Troan9ac11382019-04-23 17:11:01 +0200891
Ole Troane5ff5a32019-08-23 22:55:18 +0200892 def p_declaration_array_vla(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200893 """declaration : type_specifier variable_name '[' ']' ';'"""
Ole Troane5ff5a32019-08-23 22:55:18 +0200894 p[0] = Array(p[1], p[2], 0, modern_vla=True)
895
Ole Troan9d420872017-10-12 13:06:35 +0200896 def p_declaration_array(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200897 """declaration : type_specifier variable_name '[' NUM ']' ';'
898 | type_specifier variable_name '[' ID ']' ';'"""
Ole Troane5ff5a32019-08-23 22:55:18 +0200899
Ole Troan9d420872017-10-12 13:06:35 +0200900 if len(p) != 7:
901 return self._parse_error(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200902 "array: %s" % p.value, self._coord(lineno=p.lineno)
903 )
Ole Troan9d420872017-10-12 13:06:35 +0200904
905 # Make this error later
906 if type(p[4]) is int and p[4] == 0:
907 # XXX: Line number is wrong
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200908 self._parse_warning(
909 "Old Style VLA: {} {}[{}];".format(p[1], p[2], p[4]),
910 self._token_coord(p, 1),
911 )
Ole Troan9d420872017-10-12 13:06:35 +0200912
913 if type(p[4]) is str and p[4] not in self.fields:
914 # Verify that length field exists
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200915 self._parse_error(
916 "Missing length field: {} {}[{}];".format(p[1], p[2], p[4]),
917 self._token_coord(p, 1),
918 )
Ole Troan9d420872017-10-12 13:06:35 +0200919 p[0] = Array(p[1], p[2], p[4])
920
921 def p_option(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200922 """option : OPTION ID '=' assignee ';'
923 | OPTION ID ';'"""
Ole Troan68ebcd52020-08-10 17:06:44 +0200924 if len(p) == 4:
925 p[0] = Option(p[2])
926 else:
927 p[0] = Option(p[2], p[4])
Ole Troan9d420872017-10-12 13:06:35 +0200928
929 def p_assignee(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200930 """assignee : NUM
931 | TRUE
932 | FALSE
933 | STRING_LITERAL"""
Ole Troan9d420872017-10-12 13:06:35 +0200934 p[0] = p[1]
935
936 def p_type_specifier(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200937 """type_specifier : U8
938 | U16
939 | U32
940 | U64
941 | I8
942 | I16
943 | I32
944 | I64
945 | F64
946 | BOOL
947 | STRING"""
Ole Troan9d420872017-10-12 13:06:35 +0200948 p[0] = p[1]
949
950 # Do a second pass later to verify that user defined types are defined
951 def p_typedef_specifier(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200952 """type_specifier : ID"""
Ole Troan9d420872017-10-12 13:06:35 +0200953 if p[1] not in global_types:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200954 self._parse_error(
955 "Undefined type: {}".format(p[1]), self._token_coord(p, 1)
956 )
Ole Troan9d420872017-10-12 13:06:35 +0200957 p[0] = p[1]
958
Ole Troan2c2feab2018-04-24 00:02:37 -0400959 def p_union(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200960 """union : UNION ID '{' block_statements_opt '}' ';'"""
Ole Troan33a58172019-09-04 09:12:29 +0200961 p[0] = Union(p[2], [], p[4])
962
963 def p_union_flist(self, p):
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200964 """union : flist UNION ID '{' block_statements_opt '}' ';'"""
Ole Troan33a58172019-09-04 09:12:29 +0200965 p[0] = Union(p[3], p[1], p[5])
Ole Troan2c2feab2018-04-24 00:02:37 -0400966
Ole Troan9d420872017-10-12 13:06:35 +0200967 # Error rule for syntax errors
968 def p_error(self, p):
969 if p:
Ole Troan5d234682021-05-05 23:00:58 +0200970 if p.type == "COMMENT":
971 self.parser.errok()
972 return
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200973 self._parse_error("before: %s" % p.value, self._coord(lineno=p.lineno))
Ole Troan9d420872017-10-12 13:06:35 +0200974 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200975 self._parse_error("At end of input", self.filename)
Ole Troan9d420872017-10-12 13:06:35 +0200976
Ole Troan5d234682021-05-05 23:00:58 +0200977 def build(self, **kwargs):
978 self.parser = yacc.yacc(module=self, **kwargs)
979
Ole Troan9d420872017-10-12 13:06:35 +0200980
Klement Sekerad9b0c6f2022-04-26 19:02:15 +0200981class VPPAPI:
982 def __init__(self, debug=False, filename="", logger=None, revision=None):
Ole Troan9d420872017-10-12 13:06:35 +0200983 self.lexer = lex.lex(module=VPPAPILexer(filename), debug=debug)
Ole Troan5d234682021-05-05 23:00:58 +0200984 self.parser = VPPAPIParser(filename, logger, revision=revision)
985 self.parser.build(write_tables=False, debug=debug)
Ole Troan9d420872017-10-12 13:06:35 +0200986 self.logger = logger
Ole Troan5c318c72020-05-05 12:23:47 +0200987 self.revision = revision
988 self.filename = filename
Ole Troan9d420872017-10-12 13:06:35 +0200989
990 def parse_string(self, code, debug=0, lineno=1):
991 self.lexer.lineno = lineno
Ole Troan5d234682021-05-05 23:00:58 +0200992 return self.parser.parser.parse(code, lexer=self.lexer, debug=debug)
Ole Troan9d420872017-10-12 13:06:35 +0200993
Ole Troan5c318c72020-05-05 12:23:47 +0200994 def parse_fd(self, fd, debug=0):
Ole Troan9d420872017-10-12 13:06:35 +0200995 data = fd.read()
996 return self.parse_string(data, debug=debug)
997
Ole Troan5c318c72020-05-05 12:23:47 +0200998 def parse_filename(self, filename, debug=0):
999 if self.revision:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001000 git_show = "git show {}:{}".format(self.revision, filename)
1001 proc = Popen(git_show.split(), stdout=PIPE, encoding="utf-8")
Ole Troandeecc932020-05-19 12:33:00 +02001002 try:
1003 data, errs = proc.communicate()
1004 if proc.returncode != 0:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001005 print(
1006 "File not found: {}:{}".format(self.revision, filename),
1007 file=sys.stderr,
1008 )
Ole Troandeecc932020-05-19 12:33:00 +02001009 sys.exit(2)
1010 return self.parse_string(data, debug=debug)
Ole Troandf87f802020-11-18 19:17:48 +01001011 except Exception:
Ole Troandeecc932020-05-19 12:33:00 +02001012 sys.exit(3)
Ole Troan5c318c72020-05-05 12:23:47 +02001013 else:
1014 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001015 with open(filename, encoding="utf-8") as fd:
Ole Troan5c318c72020-05-05 12:23:47 +02001016 return self.parse_fd(fd, None)
1017 except FileNotFoundError:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001018 print("File not found: {}".format(filename), file=sys.stderr)
Ole Troan5c318c72020-05-05 12:23:47 +02001019 sys.exit(2)
1020
Ole Troan9d420872017-10-12 13:06:35 +02001021 def process(self, objs):
1022 s = {}
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001023 s["Option"] = {}
1024 s["Define"] = []
1025 s["Service"] = []
1026 s["types"] = []
1027 s["Import"] = []
1028 s["Counters"] = []
1029 s["Paths"] = []
Ole Troan8dbfb432019-04-24 14:31:18 +02001030 crc = 0
Ole Troan9d420872017-10-12 13:06:35 +02001031 for o in objs:
Ole Troan8dbfb432019-04-24 14:31:18 +02001032 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001033 crc = binascii.crc32(o.crc, crc) & 0xFFFFFFFF
Ole Troan8dbfb432019-04-24 14:31:18 +02001034 except AttributeError:
1035 pass
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -05001036
1037 if type(o) is list:
Ole Troan9d420872017-10-12 13:06:35 +02001038 for o2 in o:
1039 if isinstance(o2, Service):
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -05001040 o2.process(s)
Ole Troan2c2feab2018-04-24 00:02:37 -04001041 else:
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -05001042 o.process(s)
Ole Troan9d420872017-10-12 13:06:35 +02001043
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001044 msgs = {d.name: d for d in s["Define"]}
1045 svcs = {s.caller: s for s in s["Service"]}
1046 replies = {s.reply: s for s in s["Service"]}
Marek Gradzki51e59682018-03-06 10:05:44 +01001047 seen_services = {}
Ole Troan9d420872017-10-12 13:06:35 +02001048
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001049 s["file_crc"] = crc
Ole Troan8dbfb432019-04-24 14:31:18 +02001050
Ole Troan9d420872017-10-12 13:06:35 +02001051 for service in svcs:
1052 if service not in msgs:
Ole Troan17225df2018-04-11 09:50:03 +02001053 raise ValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001054 "Service definition refers to unknown message"
1055 " definition: {}".format(service)
1056 )
1057 if svcs[service].reply != "null" and svcs[service].reply not in msgs:
1058 raise ValueError(
1059 "Service definition refers to unknown message"
1060 " definition in reply: {}".format(svcs[service].reply)
1061 )
Marek Gradzkib533f3f2018-03-06 11:10:56 +01001062 if service in replies:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001063 raise ValueError(
1064 "Service definition refers to message"
1065 " marked as reply: {}".format(service)
1066 )
Ole Troan9d420872017-10-12 13:06:35 +02001067 for event in svcs[service].events:
1068 if event not in msgs:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001069 raise ValueError(
1070 "Service definition refers to unknown "
1071 "event: {} in message: {}".format(event, service)
1072 )
Marek Gradzki51e59682018-03-06 10:05:44 +01001073 seen_services[event] = True
Ole Troan9d420872017-10-12 13:06:35 +02001074
Marek Gradzki51e59682018-03-06 10:05:44 +01001075 # Create services implicitly
Ole Troan9d420872017-10-12 13:06:35 +02001076 for d in msgs:
Marek Gradzki51e59682018-03-06 10:05:44 +01001077 if d in seen_services:
1078 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001079 if d.endswith("_reply"):
Ole Troan9d420872017-10-12 13:06:35 +02001080 if d[:-6] in svcs:
1081 continue
1082 if d[:-6] not in msgs:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001083 raise ValueError("{} missing calling message".format(d))
Ole Troan9d420872017-10-12 13:06:35 +02001084 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001085 if d.endswith("_dump"):
Ole Troan9d420872017-10-12 13:06:35 +02001086 if d in svcs:
1087 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001088 if d[:-5] + "_details" in msgs:
1089 s["Service"].append(Service(d, d[:-5] + "_details", stream=True))
Ole Troan9d420872017-10-12 13:06:35 +02001090 else:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001091 raise ValueError("{} missing details message".format(d))
Ole Troan9d420872017-10-12 13:06:35 +02001092 continue
1093
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001094 if d.endswith("_details"):
1095 if d[:-8] + "_get" in msgs:
1096 if d[:-8] + "_get" in svcs:
Jon Loeligerc0b19542020-05-11 08:43:51 -05001097 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001098 raise ValueError(
1099 "{} should be in a stream service".format(d[:-8] + "_get")
1100 )
1101 if d[:-8] + "_dump" in msgs:
Jon Loeligerc0b19542020-05-11 08:43:51 -05001102 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001103 raise ValueError("{} missing dump or get message".format(d))
Ole Troan9d420872017-10-12 13:06:35 +02001104
1105 if d in svcs:
1106 continue
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001107 if d + "_reply" in msgs:
1108 s["Service"].append(Service(d, d + "_reply"))
Ole Troan9d420872017-10-12 13:06:35 +02001109 else:
Ole Troan17225df2018-04-11 09:50:03 +02001110 raise ValueError(
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001111 "{} missing reply message ({}) or service definition".format(
1112 d, d + "_reply"
1113 )
1114 )
Ole Troan9d420872017-10-12 13:06:35 +02001115
1116 return s
1117
Paul Vinciguerraa51f9b32020-11-24 23:26:06 -05001118 def process_imports(self, objs, in_import, result): # -> List
Ole Troan9d420872017-10-12 13:06:35 +02001119 for o in objs:
Ole Troan2c2feab2018-04-24 00:02:37 -04001120 # Only allow the following object types from imported file
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001121 if in_import and not isinstance(o, (Enum, Import, Typedef, Union, Using)):
Ole Troan2c2feab2018-04-24 00:02:37 -04001122 continue
Ole Troan2c2feab2018-04-24 00:02:37 -04001123 if isinstance(o, Import):
Ole Troan33a58172019-09-04 09:12:29 +02001124 result.append(o)
Paul Vinciguerra4bf84902019-07-31 00:34:05 -04001125 result = self.process_imports(o.result, True, result)
Ole Troan10a09892018-06-29 11:32:33 +02001126 else:
1127 result.append(o)
Paul Vinciguerra4bf84902019-07-31 00:34:05 -04001128 return result
Ole Troan9d420872017-10-12 13:06:35 +02001129
Ole Troan58914252018-10-23 10:50:07 +02001130
Ole Troan9d420872017-10-12 13:06:35 +02001131# Add message ids to each message.
1132def add_msg_id(s):
1133 for o in s:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001134 o.block.insert(0, Field("u16", "_vl_msg_id"))
Ole Troan9d420872017-10-12 13:06:35 +02001135 return s
1136
1137
Ole Troan9d420872017-10-12 13:06:35 +02001138dirlist = []
1139
1140
1141def dirlist_add(dirs):
1142 global dirlist
1143 if dirs:
1144 dirlist = dirlist + dirs
1145
1146
1147def dirlist_get():
1148 return dirlist
1149
Paul Vinciguerra2cd3cc82019-08-06 22:02:45 -04001150
Ole Troan8dbfb432019-04-24 14:31:18 +02001151def foldup_blocks(block, crc):
1152 for b in block:
1153 # Look up CRC in user defined types
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001154 if b.fieldtype.startswith("vl_api_"):
Ole Troan8dbfb432019-04-24 14:31:18 +02001155 # Recursively
1156 t = global_types[b.fieldtype]
1157 try:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001158 crc = binascii.crc32(t.crc, crc) & 0xFFFFFFFF
Ole Troan9f84e702020-06-25 14:27:46 +02001159 crc = foldup_blocks(t.block, crc)
Ole Troane5ff5a32019-08-23 22:55:18 +02001160 except AttributeError:
Ole Troan8dbfb432019-04-24 14:31:18 +02001161 pass
1162 return crc
1163
Paul Vinciguerra2cd3cc82019-08-06 22:02:45 -04001164
Ole Troan8dbfb432019-04-24 14:31:18 +02001165def foldup_crcs(s):
1166 for f in s:
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001167 f.crc = foldup_blocks(f.block, binascii.crc32(f.crc) & 0xFFFFFFFF)
Ole Troan9d420872017-10-12 13:06:35 +02001168
Ole Troandf87f802020-11-18 19:17:48 +01001169
Ole Troaned61b202024-06-19 11:31:30 +02001170def write_dependencies(output_file, dependency_file, imports):
1171 r = []
1172 for i in imports:
1173 for d in dirlist:
1174 f = os.path.abspath(os.path.join(d, i.filename))
1175 if os.path.exists(f):
1176 r.append(f)
1177 with open(dependency_file, "w", encoding="utf8") as f:
1178 print(f"{output_file}: \\", file=f)
1179 for i in r[:-1]:
1180 print(f" {i} \\", file=f)
1181 if imports:
1182 print(f" {r[-1]}", file=f)
1183
1184
Nathan Skrzypczak1b299fa2022-06-16 17:00:02 +02001185def run_vppapigen(
1186 input_file=None,
1187 output=sys.stdout,
1188 includedir=None,
1189 debug=False,
1190 show_name=None,
1191 output_module="C",
1192 outputdir=None,
1193 pluginpath="",
1194 git_revision=None,
Ole Troaned61b202024-06-19 11:31:30 +02001195 dependency_file=None,
Nathan Skrzypczak1b299fa2022-06-16 17:00:02 +02001196):
1197 # reset globals
1198 dirlist.clear()
1199 global_types.clear()
1200 seen_imports.clear()
1201
1202 dirlist_add(includedir)
1203 if not debug:
1204 sys.excepthook = exception_handler
1205
1206 # Filename
1207 if show_name:
1208 filename = show_name[0]
1209 elif input_file:
1210 filename = input_file
1211 else:
1212 filename = ""
1213
1214 if debug:
1215 logging.basicConfig(stream=sys.stdout, level=logging.WARNING)
1216 else:
1217 logging.basicConfig()
1218
1219 #
1220 # Generate representation
1221 #
1222 from importlib.machinery import SourceFileLoader
1223
1224 # Default path
1225 pluginpath = ""
1226 if not pluginpath:
1227 cand = []
1228 cand.append(os.path.dirname(os.path.realpath(__file__)))
1229 cand.append(os.path.dirname(os.path.realpath(__file__)) + "/../share/vpp/")
1230 for c in cand:
1231 c += "/"
1232 if os.path.isfile("{}vppapigen_{}.py".format(c, output_module.lower())):
1233 pluginpath = c
1234 break
1235 else:
1236 pluginpath = pluginpath + "/"
1237 if pluginpath == "":
1238 log.exception("Output plugin not found")
1239 return 1
1240 module_path = "{}vppapigen_{}.py".format(pluginpath, output_module.lower())
1241
1242 try:
1243 plugin = SourceFileLoader(output_module, module_path).load_module()
1244 except Exception as err:
1245 log.exception("Error importing output plugin: %s, %s", module_path, err)
1246 return 1
1247
1248 parser = VPPAPI(debug=debug, filename=filename, logger=log, revision=git_revision)
1249
1250 try:
1251 if not input_file:
1252 parsed_objects = parser.parse_fd(sys.stdin, log)
1253 else:
1254 parsed_objects = parser.parse_filename(input_file, log)
1255 except ParseError as e:
1256 print("Parse error: ", e, file=sys.stderr)
1257 sys.exit(1)
1258
1259 # Build a list of objects. Hash of lists.
1260 result = []
1261
1262 # if the variable is not set in the plugin, assume it to be false.
1263 try:
1264 plugin.process_imports
1265 except AttributeError:
1266 plugin.process_imports = False
1267
1268 if plugin.process_imports:
1269 result = parser.process_imports(parsed_objects, False, result)
1270 s = parser.process(result)
1271 else:
1272 s = parser.process(parsed_objects)
1273 imports = parser.process_imports(parsed_objects, False, result)
1274 s["imported"] = parser.process(imports)
1275
Ole Troaned61b202024-06-19 11:31:30 +02001276 if dependency_file and isinstance(output, TextIOWrapper):
1277 write_dependencies(output.name, dependency_file[0], s["Import"])
1278
Nathan Skrzypczak1b299fa2022-06-16 17:00:02 +02001279 # Add msg_id field
1280 s["Define"] = add_msg_id(s["Define"])
1281
1282 # Fold up CRCs
1283 foldup_crcs(s["Define"])
1284
1285 #
1286 # Debug
1287 if debug:
1288 import pprint
1289
1290 pp = pprint.PrettyPrinter(indent=4, stream=sys.stderr)
1291 for t in s["Define"]:
1292 pp.pprint([t.name, t.flags, t.block])
1293 for t in s["types"]:
1294 pp.pprint([t.name, t.block])
1295
1296 result = plugin.run(outputdir, filename, s)
1297 if result:
1298 if isinstance(output, str):
1299 with open(output, "w", encoding="UTF-8") as f:
1300 print(result, file=f)
1301 else:
1302 print(result, file=output)
1303 else:
1304 log.exception("Running plugin failed: %s %s", filename, result)
1305 return 1
1306 return 0
1307
1308
1309def run_kw_vppapigen(kwargs):
1310 return run_vppapigen(**kwargs)
1311
1312
Ole Troan9d420872017-10-12 13:06:35 +02001313#
1314# Main
1315#
1316def main():
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001317 if sys.version_info < (
1318 3,
1319 5,
1320 ):
1321 log.exception(
1322 "vppapigen requires a supported version of python. "
1323 "Please use version 3.5 or greater. "
1324 "Using %s",
1325 sys.version,
1326 )
Paul Vinciguerra2cd3cc82019-08-06 22:02:45 -04001327 return 1
1328
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001329 cliparser = argparse.ArgumentParser(description="VPP API generator")
1330 cliparser.add_argument("--pluginpath", default="")
1331 cliparser.add_argument("--includedir", action="append")
1332 cliparser.add_argument("--outputdir", action="store")
1333 cliparser.add_argument("--input")
1334 cliparser.add_argument(
1335 "--output",
1336 nargs="?",
1337 type=argparse.FileType("w", encoding="UTF-8"),
1338 default=sys.stdout,
1339 )
Ole Troan9d420872017-10-12 13:06:35 +02001340
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001341 cliparser.add_argument("output_module", nargs="?", default="C")
1342 cliparser.add_argument("--debug", action="store_true")
1343 cliparser.add_argument("--show-name", nargs=1)
1344 cliparser.add_argument(
1345 "--git-revision", help="Git revision to use for opening files"
1346 )
Ole Troaned61b202024-06-19 11:31:30 +02001347 cliparser.add_argument("-MF", nargs=1, help="Dependency file")
Ole Troan9d420872017-10-12 13:06:35 +02001348 args = cliparser.parse_args()
1349
Nathan Skrzypczak1b299fa2022-06-16 17:00:02 +02001350 return run_vppapigen(
1351 includedir=args.includedir,
1352 debug=args.debug,
1353 outputdir=args.outputdir,
1354 show_name=args.show_name,
1355 input_file=args.input,
1356 output_module=args.output_module,
1357 pluginpath=args.pluginpath,
1358 git_revision=args.git_revision,
1359 output=args.output,
Ole Troaned61b202024-06-19 11:31:30 +02001360 dependency_file=args.MF,
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001361 )
Paul Vinciguerra9046e442020-11-20 23:10:09 -05001362
Ole Troan9d420872017-10-12 13:06:35 +02001363
Klement Sekerad9b0c6f2022-04-26 19:02:15 +02001364if __name__ == "__main__":
Paul Vinciguerra2cd3cc82019-08-06 22:02:45 -04001365 sys.exit(main())