(Somewhat) correctly handle function declarations and function pointer types

This commit is contained in:
Duncan Ogilvie 2023-02-10 14:00:44 +01:00
parent c1c257ce7a
commit a87d84cc5b
3 changed files with 173 additions and 46 deletions

View File

@ -16,7 +16,10 @@ TypeManager::TypeManager()
primitivesizes[p] = size;
auto splits = StringUtils::Split(n, ',');
for (const auto& split : splits)
{
primitivenames.emplace(p, split);
addType("", p, split);
}
};
p("int8_t,int8,char,byte,bool,signed char", Int8, sizeof(char));
p("uint8_t,uint8,uchar,unsigned char,ubyte", Uint8, sizeof(unsigned char));
@ -27,14 +30,20 @@ TypeManager::TypeManager()
p("int64_t,int64,long long", Int64, sizeof(long long));
p("uint64_t,uint64,unsigned long long", Uint64, sizeof(unsigned long long));
p("dsint", Dsint, sizeof(void*));
p("duint,size_t", Duint, sizeof(void*));
p("size_t,duint", Duint, sizeof(void*));
p("float", Float, sizeof(float));
p("double", Double, sizeof(double));
p("ptr,void*", Pointer, sizeof(void*));
p("void*,ptr", Pointer, sizeof(void*));
p("char*,const char*", PtrString, sizeof(char*));
p("wchar_t*,const wchar_t*", PtrWString, sizeof(wchar_t*));
}
std::string Types::TypeManager::PrimitiveName(Primitive primitive)
{
auto found = primitivenames.find(primitive);
return found == primitivenames.end() ? "" : found->second;
}
bool TypeManager::AddType(const std::string & owner, const std::string & type, const std::string & name)
{
if(owner.empty())
@ -46,6 +55,32 @@ bool TypeManager::AddType(const std::string & owner, const std::string & type, c
return addType(owner, found->second.primitive, name);
}
bool Types::TypeManager::AddEnum(const std::string& owner, const std::string& name, const std::string& type)
{
Enum e;
e.name = name;
e.owner = owner;
auto found = types.find(type);
if (found == types.end())
return false;
if (!found->second.pointto.empty())
return false;
switch (found->second.primitive)
{
case Void:
case Float:
case Double:
case Pointer:
case PtrString:
case PtrWString:
return false;
default:
break;
}
e.type = found->second.primitive;
return addEnum(e);
}
bool TypeManager::AddStruct(const std::string & owner, const std::string & name)
{
StructUnion s;
@ -122,7 +157,25 @@ bool TypeManager::AppendMember(const std::string & type, const std::string & nam
return AddMember(laststruct, type, name, arrsize, offset);
}
bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn)
bool Types::TypeManager::AddEnumerator(const std::string& enumType, const std::string& name, uint64_t value)
{
auto found = enums.find(enumType);
if (found == enums.end())
return false;
auto& e = found->second;
for (const auto& v : e.values)
{
if (v.name == name || v.value == value)
return false;
}
EnumValue v;
v.name = name;
v.value = value;
e.values.push_back(v);
return true;
}
bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn, bool typeonly)
{
auto found = functions.find(name);
if(found != functions.end() || name.empty() || owner.empty())
@ -136,16 +189,16 @@ bool TypeManager::AddFunction(const std::string & owner, const std::string & nam
f.rettype = rettype;
f.callconv = callconv;
f.noreturn = noreturn;
functions.insert({f.name, f});
functions.emplace(f.name, f);
return true;
}
bool TypeManager::AddArg(const std::string & function, const std::string & type, const std::string & name)
{
if(!isDefined(type) && !validPtr(type))
return false;
auto found = functions.find(function);
if(found == functions.end() || function.empty() || name.empty() || !isDefined(type))
if (found == functions.end() || name.empty())
return false;
if(type != "..." && !isDefined(type) && !validPtr(type))
return false;
lastfunction = function;
Member arg;
@ -255,7 +308,7 @@ static void enumType(const std::unordered_map<K, V> & map, std::vector<TypeManag
}
}
void TypeManager::Enum(std::vector<Summary> & typeList) const
void TypeManager::Enumerate(std::vector<Summary> & typeList) const
{
typeList.clear();
enumType(types, typeList);
@ -310,7 +363,7 @@ static bool mapContains(const std::unordered_map<K, V> & map, const K & k)
bool TypeManager::isDefined(const std::string & id) const
{
return mapContains(types, id) || mapContains(structs, id);
return mapContains(types, id) || mapContains(structs, id) || mapContains(enums, id) || mapContains(functions, id);
}
bool TypeManager::validPtr(const std::string & id)
@ -337,16 +390,22 @@ bool TypeManager::addStructUnion(const StructUnion & s)
laststruct = s.name;
if(s.owner.empty() || s.name.empty() || isDefined(s.name))
return false;
structs.insert({s.name, s});
return true;
return structs.emplace(s.name, s).second;
}
bool Types::TypeManager::addEnum(const Enum& e)
{
lastenum = e.name;
if (e.owner.empty() || e.name.empty() || isDefined(e.name))
return false;
return enums.emplace(e.name, e).second;
}
bool TypeManager::addType(const Type & t)
{
if(t.name.empty() || isDefined(t.name))
return false;
types.insert({t.name, t});
return true;
return types.emplace(t.name, t).second;
}
bool TypeManager::addType(const std::string & owner, Primitive primitive, const std::string & name, const std::string & pointto)
@ -483,7 +542,7 @@ bool RemoveType(const std::string & type)
void EnumTypes(std::vector<Types::TypeManager::Summary> & typeList)
{
SHARED_ACQUIRE(LockTypeManager);
return typeManager.Enum(typeList);
return typeManager.Enumerate(typeList);
}
#if 0
@ -602,7 +661,6 @@ static void loadFunctions(const JSON froot, std::vector<Function> & functions)
void LoadModel(const std::string & owner, Model & model)
{
//Add all base struct/union types first to avoid errors later
// TODO: handle forward declarations
for(auto & su : model.structUnions)
{
auto success = su.isunion ? typeManager.AddUnion(owner, su.name) : typeManager.AddStruct(owner, su.name);
@ -626,15 +684,32 @@ void LoadModel(const std::string & owner, Model & model)
}
//Add enums
for (auto& enumm : model.enums)
for (auto& kv : model.enums)
{
__debugbreak();
auto& e = kv.first;
const auto& etype = kv.second;
auto success = typeManager.AddEnum(owner, e.name, etype);
if (!success)
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add enum %s;\n"), e.name.c_str());
e.name.clear(); // signal error
}
else
{
for (const auto& v : e.values)
{
if (!typeManager.AddEnumerator(e.name, v.name, v.value))
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add enum member %s.%s = %llu;\n"), e.name.c_str(), v.name.c_str(), v.value);
}
}
}
}
//Add base function types to avoid errors later
for(auto & function : model.functions)
{
auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn);
auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn, function.typeonly);
if(!success)
{
//TODO properly handle errors
@ -664,13 +739,16 @@ void LoadModel(const std::string & owner, Model & model)
{
if(function.name.empty()) //skip error-signalled functions
continue;
for(auto & arg : function.args)
for (size_t i = 0; i < function.args.size(); i++)
{
auto& arg = function.args[i];
if (arg.name.empty())
arg.name = "__unnamed" + std::to_string(i);
auto success = typeManager.AddArg(function.name, arg.type, arg.name);
if(!success)
{
//TODO properly handle errors
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add argument %s %s.%s;\n"), arg.type.c_str(), function.name.c_str(), arg.name.c_str());
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add argument %s[%zu]: %s %s;\n"), function.name.c_str(), i, arg.type.c_str(), arg.name.c_str());
}
}
}

View File

@ -23,14 +23,15 @@ namespace Types
Double,
Pointer,
PtrString, //char* (null-terminated)
PtrWString //wchar_t* (null-terminated)
PtrWString, //wchar_t* (null-terminated)
Varargs,
};
struct Type
{
std::string owner; //Type owner
std::string name; //Type identifier.
std::string pointto; //Type identifier of *Type
std::string pointto; //Type identifier of *Type (empty when the type is a primitive type)
Primitive primitive = Void; //Primitive type.
int size = 0; //Size in bytes.
};
@ -64,7 +65,7 @@ namespace Types
std::string owner; // Enum owner
std::string name; // Enum name
std::vector<EnumValue> values; // Enum values
Type type; // Enum value type
Primitive type; // Enum value type
};
enum CallingConvention
@ -83,6 +84,7 @@ namespace Types
std::string rettype; //Function return type
CallingConvention callconv = DefaultDecl; //Function calling convention
bool noreturn = false; //Function does not return (ExitProcess, _exit)
bool typeonly = false; //Function is only used as a type (the name is based on where it's used)
std::vector<Member> args; //Function arguments
};
@ -107,32 +109,39 @@ namespace Types
};
explicit TypeManager();
std::string PrimitiveName(Primitive primitive);
bool AddType(const std::string & owner, const std::string & type, const std::string & name);
bool AddEnum(const std::string& owner, const std::string& name, const std::string & type);
bool AddStruct(const std::string & owner, const std::string & name);
bool AddUnion(const std::string & owner, const std::string & name);
bool AddMember(const std::string & parent, const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
bool AppendMember(const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false);
bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false, bool typeonly = false);
bool AddEnumerator(const std::string& enumType, const std::string& name, uint64_t value);
bool AddArg(const std::string & function, const std::string & type, const std::string & name);
bool AppendArg(const std::string & type, const std::string & name);
int Sizeof(const std::string & type) const;
bool Visit(const std::string & type, const std::string & name, Visitor & visitor) const;
void Clear(const std::string & owner = "");
bool RemoveType(const std::string & type);
void Enum(std::vector<Summary> & typeList) const;
void Enumerate(std::vector<Summary> & typeList) const;
std::string StructUnionPtrType(const std::string & pointto) const;
private:
std::unordered_map<Primitive, int> primitivesizes;
std::unordered_map<Primitive, std::string> primitivenames;
std::unordered_map<std::string, Type> types;
std::unordered_map<std::string, Types::Enum> enums;
std::unordered_map<std::string, StructUnion> structs;
std::unordered_map<std::string, Function> functions;
std::string lastenum;
std::string laststruct;
std::string lastfunction;
bool isDefined(const std::string & id) const;
bool validPtr(const std::string & id);
bool addStructUnion(const StructUnion & s);
bool addEnum(const Enum& e);
bool addType(const std::string & owner, Primitive primitive, const std::string & name, const std::string & pointto = "");
bool addType(const Type & t);
bool visitMember(const Member & root, Visitor & visitor) const;
@ -141,7 +150,7 @@ namespace Types
struct Model
{
std::vector<Member> types;
std::vector<Enum> enums;
std::vector<std::pair<Enum, std::string>> enums;
std::vector<StructUnion> structUnions;
std::vector<Function> functions;
};

View File

@ -16,6 +16,7 @@ struct Parser
std::vector<Lexer::TokenState> tokens;
size_t index = 0;
Model model;
std::unordered_map<std::string, size_t> structUnions;
Parser(const std::string& code, const std::string& owner, std::vector<std::string>& errors)
: owner(owner), errors(errors)
@ -296,20 +297,17 @@ struct Parser
// Function pointer argument to a function
Function subfn;
subfn.typeonly = true;
if(!parseFunction(tlist, subfn, true))
return false;
// TODO: store this somewhere
// Create fake tokens
// TODO: handle this properly somehow
auto typeToken = tlist.back();
typeToken.Token = Lexer::tok_identifier;
typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr";
printf("TODO function pointer: %s %s\n", typeToken.IdentifierStr.c_str(), subfn.name.c_str());
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
auto nameToken = startToken;
nameToken.Token = Lexer::tok_identifier;
@ -455,8 +453,9 @@ struct Parser
index++;
// Function pointer type
Function fn;
if (!parseFunction(tlist, fn, true))
Function subfn;
subfn.typeonly = true;
if (!parseFunction(tlist, subfn, true))
{
return false;
}
@ -468,9 +467,22 @@ struct Parser
}
eatSemic();
// TODO: put the function somewhere
// Create fake tokens
auto typeToken = tlist.back();
typeToken.Token = Lexer::tok_identifier;
typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr";
printf("TODO function pointer: %s\n", fn.name.c_str());
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
auto nameToken = startToken;
nameToken.Token = Lexer::tok_identifier;
nameToken.IdentifierStr = subfn.name;
// Replace the return type with the fake tokens
tlist.clear();
tlist.push_back(typeToken);
tlist.push_back(nameToken);
return true;
}
@ -515,6 +527,7 @@ struct Parser
bool parseStructUnion()
{
auto startToken = curToken();
if (isToken(Lexer::tok_struct) || isToken(Lexer::tok_union))
{
StructUnion su;
@ -547,7 +560,25 @@ struct Parser
}
index++;
// Handle forward declarations
auto found = structUnions.find(su.name);
if (found != structUnions.end())
{
auto& oldSu = model.structUnions[found->second];
if (oldSu.size != -1)
{
errLine(startToken, "cannot redeclare type");
return false;
}
// Replace the forward declared type with the full type
oldSu = su;
}
else
{
structUnions.emplace(su.name, model.structUnions.size());
model.structUnions.push_back(su);
}
if (!isToken(Lexer::tok_semic))
{
errLine(curToken(), "expected semicolon!");
@ -559,7 +590,13 @@ struct Parser
else if (isToken(Lexer::tok_semic))
{
// Forward declaration
su.size = -1;
auto found = structUnions.find(su.name);
if (found == structUnions.end())
{
structUnions.emplace(su.name, model.structUnions.size());
model.structUnions.push_back(su);
}
eatSemic();
return true;
}
@ -577,12 +614,16 @@ struct Parser
if (isToken(Lexer::tok_enum))
{
Enum e;
std::string etype;
index++;
if (isTokenList({ Lexer::tok_identifier, Lexer::tok_bropen }))
{
e.name = lexer.TokString(curToken());
index += 2;
// TODO: support custom enum types (: type)
etype = "int";
while (!isToken(Lexer::tok_brclose))
{
if (isToken(Lexer::tok_eof))
@ -653,7 +694,7 @@ struct Parser
}
index++; //eat tok_brclose
model.enums.push_back(e);
model.enums.emplace_back(e, etype);
if (!isToken(Lexer::tok_semic))
{
errLine(curToken(), "expected semicolon!");
@ -740,6 +781,7 @@ struct Parser
index++;
Function fn;
fn.typeonly = true;
if (!parseFunction(tlist, fn, true))
{
return false;
@ -752,9 +794,9 @@ struct Parser
}
eatSemic();
// TODO: put the function somewhere
model.functions.push_back(fn);
printf("TODO function pointer: %s\n", fn.name.c_str());
// TODO: handle pointer stuff correctly?
return true;
}
@ -849,9 +891,7 @@ struct Parser
}
eatSemic();
// TODO: put the function somewhere
printf("TODO function: %s\n", fn.name.c_str());
model.functions.push_back(fn);
return true;
}