# ====-- Function class for libc function headers -------------*- python -*--==# # # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. # See https://llvm.org/LICENSE.txt for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # # ==-------------------------------------------------------------------------==# import re from functools import total_ordering from hdrgen.type import Type # These are the keywords that appear in C type syntax but are not part of the # include file name. This is all of the modifiers, qualifiers, and base types, # but not "struct". KEYWORDS = [ "_Atomic", "_Complex", "_Float16", "_Noreturn", "__restrict", "accum", "char", "const", "double", "float", "fract", "int", "long", "short", "signed", "unsigned", "void", "volatile", ] NONIDENTIFIER = re.compile("[^a-zA-Z0-9_]+") @total_ordering class Function: def __init__( self, return_type, name, arguments, standards, guard=None, attributes=[] ): assert return_type self.return_type = return_type self.name = name self.arguments = [ arg if isinstance(arg, str) else arg["type"] for arg in arguments ] assert all(self.arguments) self.standards = standards self.guard = guard self.attributes = attributes or [] def __eq__(self, other): return self.name == other.name def __lt__(self, other): return self.name < other.name def __hash__(self): return self.name.__hash__() def signature_types(self): def collapse(type_string): assert type_string # Split into words at nonidentifier characters (`*`, `[`, etc.), # filter out keywords and numbers, and then rejoin with "_". return "_".join( word for word in NONIDENTIFIER.split(type_string) if word and not word.isdecimal() and word not in KEYWORDS ) all_types = [self.return_type] + self.arguments return { Type(string) for string in filter(None, (collapse(t) for t in all_types)) } def __str__(self): attrs_str = "".join(f"{attr} " for attr in self.attributes) arguments_str = ", ".join(self.arguments) if self.arguments else "void" # The rendering of the return type may look like `int` or it may look # like `int *` (and other examples). For `int`, a space is always # needed to separate the tokens. For `int *`, no whitespace matters to # the syntax one way or the other, but an extra space between `*` and # the function identifier is not the canonical style. type_str = str(self.return_type) if type_str[-1].isalnum() or type_str[-1] == "_": type_str += " " return attrs_str + type_str + self.name + "(" + arguments_str + ")"