283 lines
9.3 KiB
Python
Executable File
283 lines
9.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# coding=utf-8
|
|
# Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2021-2022. All rights reserved.
|
|
|
|
"""
|
|
* Description: NV binary create.
|
|
* Create: 2020-3-10
|
|
"""
|
|
import pycparser
|
|
import ctypes
|
|
|
|
binary_operators = {
|
|
"&": lambda x, y: x & y,
|
|
"|": lambda x, y: x | y,
|
|
"*": lambda x, y: x * y,
|
|
"+": lambda x, y: x + y,
|
|
"-": lambda x, y: x - y,
|
|
"/": lambda x, y: x // y,
|
|
"<<": lambda x, y: x << y,
|
|
">>": lambda x, y: x >> y,
|
|
"<=": lambda x, y: int(x <= y),
|
|
">=": lambda x, y: int(x >= y),
|
|
"==": lambda x, y: int(x == y),
|
|
"&&": lambda x, y: int(bool(x and y)),
|
|
"||": lambda x, y: int(bool(x or y)),
|
|
}
|
|
|
|
unary_operators = {
|
|
"-": lambda x: -x,
|
|
"!": lambda x: 1 if x else 0,
|
|
"sizeof": lambda x: ctypes.sizeof(x),
|
|
}
|
|
|
|
fundamental_types = {
|
|
"": ctypes.c_int,
|
|
"_Bool": ctypes.c_bool,
|
|
"char unsigned": ctypes.c_ubyte,
|
|
"char": ctypes.c_byte,
|
|
"double": ctypes.c_double,
|
|
"double long": ctypes.c_longdouble,
|
|
"float": ctypes.c_float,
|
|
"long long unsigned": ctypes.c_ulonglong,
|
|
"long long": ctypes.c_longlong,
|
|
"long unsigned": ctypes.c_ulong,
|
|
"long": ctypes.c_long,
|
|
"short unsigned": ctypes.c_ushort,
|
|
"short": ctypes.c_short,
|
|
"unsigned": ctypes.c_uint,
|
|
"void": None,
|
|
}
|
|
|
|
def get_typename(names):
|
|
specifiers = []
|
|
for name in sorted(names):
|
|
if name in ["signed", "int"]:
|
|
continue
|
|
specifiers.append(name)
|
|
return " ".join(specifiers)
|
|
|
|
def create_enum_type(enumname, enums):
|
|
# enum size fixed to 4Bytes
|
|
return type(enumname, (ctypes.c_uint,), {"members": enums})
|
|
|
|
# enum size depends on max enum value
|
|
smallest = min(enums.values())
|
|
largest = max(enums.values())
|
|
if smallest < 0:
|
|
if -128 <= smallest and largest <= 127:
|
|
base = ctypes.c_byte
|
|
elif -32768 <= smallest and largest <= 32767:
|
|
base = ctypes.c_short
|
|
else:
|
|
base = ctypes.c_int
|
|
else:
|
|
if largest < 256:
|
|
base = ctypes.c_ubyte
|
|
elif largest < 65536:
|
|
base = ctypes.c_ushort
|
|
else:
|
|
base = ctypes.c_uint
|
|
return type(enumname, (base,), {"members": enums})
|
|
|
|
|
|
class Visitor(pycparser.c_ast.NodeVisitor):
|
|
# Parse all typedefs and expand identifiers.
|
|
def __init__(self):
|
|
self.enums = {}
|
|
self.typedefs = dict(fundamental_types)
|
|
|
|
# The context in which the ID names exist.
|
|
self.id_context = self.enums
|
|
self.anon_struct_count = 1
|
|
self.anon_enum_count = 1
|
|
self.anon_union_count = 1
|
|
|
|
def generic_visit(self, node):
|
|
# Dump ast to see what is not implemented.
|
|
raise SystemExit("Unhandled ast element at %s: %s" % (node.coord, node))
|
|
|
|
def visit_Decl(self, node):
|
|
return
|
|
|
|
def visit_FuncDef(self, node):
|
|
return
|
|
|
|
def visit_FuncDecl(self, node):
|
|
return
|
|
|
|
def visit_FileAST(self, node):
|
|
for c in node:
|
|
self.visit(c)
|
|
|
|
def visit_ID(self, node):
|
|
try:
|
|
return self.id_context[node.name]
|
|
except KeyError:
|
|
raise SystemExit("Failed to resolve identifier '%s' at %s" % (node.name, node.coord))
|
|
|
|
def visit_Typename(self, node):
|
|
return self.visit(node.type)
|
|
|
|
def visit_TypeDecl(self, node):
|
|
return self.visit(node.type)
|
|
|
|
def visit_CompoundLiteral(self, node):
|
|
return self.visit(node.type)
|
|
|
|
def visit_PtrDecl(self, node):
|
|
return ctypes.POINTER(self.visit(node.type))
|
|
|
|
def visit_Typedef(self, node):
|
|
if node.name in self.typedefs:
|
|
raise SystemExit("Duplicate typedef '%s' at %s" % (node.name, node.coord))
|
|
value = self.visit(node.type)
|
|
self.typedefs[node.name] = value
|
|
|
|
def visit_ArrayRef(self, node):
|
|
# For accessing type of an array element
|
|
array = self.visit(node.name)
|
|
#index = self.visit(node.subscript)
|
|
return array._type_
|
|
|
|
def visit_StructRef(self, node):
|
|
# This is needed to get access to types inside a struct.
|
|
struct = self.visit(node.name)
|
|
self.id_context = dict(struct._fields_)
|
|
try:
|
|
return self.visit(node.field)
|
|
finally:
|
|
self.id_context = self.enums
|
|
|
|
def visit_BinaryOp(self, node):
|
|
try:
|
|
op = binary_operators[node.op]
|
|
except KeyError:
|
|
raise SystemExit("Unhandled binary operator '%s' at %s" % (node.op, node.coord))
|
|
leftval = self.visit(node.left)
|
|
rightval = self.visit(node.right)
|
|
return op(leftval, rightval)
|
|
|
|
def visit_UnaryOp(self, node):
|
|
value = self.visit(node.expr)
|
|
try:
|
|
op = unary_operators[node.op]
|
|
except KeyError:
|
|
raise SystemExit("Unhandled unary operator '%s' at %s" % (node.op, node.coord))
|
|
return op(value)
|
|
|
|
def visit_Enum(self, node):
|
|
# Mapping of enum names to enum values from all parsed enums.
|
|
value = -1
|
|
enums = {}
|
|
for enum in node.values:
|
|
if enum.value is None:
|
|
value += 1
|
|
else:
|
|
value = self.visit(enum.value)
|
|
self.enums[enum.name] = enums[enum.name] = value
|
|
|
|
if node.name is None:
|
|
enumname = "enum_anon_%d" % self.anon_enum_count
|
|
self.anon_enum_count += 1
|
|
else:
|
|
enumname = "enum_%s" % str(node.name)
|
|
return create_enum_type(enumname, enums)
|
|
|
|
def visit_Constant(self, node):
|
|
if node.type not in ["int", "unsigned int"]:
|
|
raise SystemExit("Unhandled Constant node type '%s' at %s" % (node.type, node.coord))
|
|
value = node.value.rstrip("LlUu")
|
|
if value.startswith(("0x", "0X")):
|
|
return int(value, 16)
|
|
return int(value)
|
|
|
|
def visit_IdentifierType(self, node):
|
|
name = get_typename(node.names)
|
|
try:
|
|
return self.typedefs[name]
|
|
except KeyError:
|
|
raise SystemExit("Invalid type specifier '%s' at %s" % (name, node.coord))
|
|
|
|
def visit_Struct(self, node):
|
|
fields = []
|
|
# node.decls can be None when the struct declaration is outside the typedef.
|
|
if node.decls is not None:
|
|
for decl in node.decls:
|
|
if decl.bitsize is not None:
|
|
temp = self.visit(decl.type)
|
|
value = type(decl.name, (temp,),{"bitsize": decl.bitsize.value})
|
|
else:
|
|
value = self.visit(decl.type)
|
|
if value is None:
|
|
# This is the void type - indicates an invalid message definition type.
|
|
return None
|
|
fields.append((decl.name, value))
|
|
|
|
if node.name is None:
|
|
structname = "struct_anon_%d" % self.anon_struct_count
|
|
self.anon_struct_count += 1
|
|
else:
|
|
structname = "struct_%s" % str(node.name)
|
|
return type(structname, (ctypes.Structure,), {"_fields_": fields})
|
|
|
|
def visit_Union(self, node):
|
|
fields = []
|
|
for decl in node.decls:
|
|
value = self.visit(decl.type)
|
|
if value is None:
|
|
return None
|
|
fields.append((decl.name, value))
|
|
|
|
if node.name is None:
|
|
unionname = "union_anon_%d" % self.anon_union_count
|
|
self.anon_struct_count += 1
|
|
else:
|
|
unionname = "union_%s" % str(node.name)
|
|
return type(unionname, (ctypes.Union,), {"_fields_": fields})
|
|
|
|
def visit_ArrayDecl(self, node):
|
|
length = self.visit(node.dim)
|
|
type = self.visit(node.type)
|
|
if length is None or length < 0:
|
|
raise SystemExit("Invalid array len %s at %s" % (length, node.dim.coord))
|
|
if type is None:
|
|
raise SystemExit("Invalid array type %s at %s" % (type, node.type.coord))
|
|
return type * length
|
|
|
|
def message_enum_name(elemName):
|
|
if elemName.endswith("_t"):
|
|
return elemName[:-2].upper()
|
|
if elemName.endswith("_s"):
|
|
# NAS messages, already uppercase
|
|
return elemName[:-2]
|
|
if elemName.endswith("_STRUCT"):
|
|
return elemName[:-7]
|
|
if elemName.find("LOG_MESSAGE") != -1:
|
|
return elemName +'_ID'
|
|
|
|
def parse_preprocessed_headers(source):
|
|
# Public function that returns the required data for producing messagexml
|
|
try:
|
|
node = pycparser.parse_file(source)
|
|
except pycparser.plyparser.ParseError as e:
|
|
raise SystemExit("ERROR parsing msgdefs %s: %s" % (source, e))
|
|
v = Visitor()
|
|
v.visit(node)
|
|
|
|
messages = []
|
|
for structname, fields in sorted(v.typedefs.items()):
|
|
messageEnumName = message_enum_name(structname)
|
|
if messageEnumName is None:
|
|
# Not a valid message definition name.
|
|
continue
|
|
try:
|
|
messageId = v.enums[messageEnumName]
|
|
except KeyError:
|
|
# No associated message id, so not a message definition.
|
|
continue
|
|
if fields is None:
|
|
raise SystemExit("Message definition contains undefined type: %s" % structname)
|
|
messages.append((messageEnumName, structname, messageId, fields))
|
|
return messages
|