Add qualified types and improve parsing support

This commit is contained in:
Duncan Ogilvie 2023-02-28 21:33:56 +01:00
parent a87d84cc5b
commit 6d3f1f75c4
5 changed files with 258 additions and 48 deletions

3
.gitignore vendored
View File

@ -7,3 +7,6 @@ My Amplifier XE Results */
actual.out
expected.out
.vs/
cmake-build*/
build*/
.idea/

View File

@ -287,23 +287,23 @@ std::string preprocess(const std::string& input, std::string& error, const std::
t.skip_spaces();
auto directive = t.identifier();
line.print();
//line.print();
if (directive == "ifndef")
{
t.skip_spaces(true);
auto identifier = t.identifier();
printf("#ifndef(%s)\n", identifier.c_str());
//printf("#ifndef(%s)\n", identifier.c_str());
stack.push_back({ i, "!defined(" + identifier + ")", state.count(identifier) == 0 });
printf("emitting: %d\n", emitting());
//printf("emitting: %d\n", emitting());
}
else if (directive == "ifdef")
{
t.skip_spaces(true);
auto identifier = t.identifier();
printf("#ifdef(%s)\n", identifier.c_str());
//printf("#ifdef(%s)\n", identifier.c_str());
stack.push_back({ i, identifier, state.count(identifier) != 0 });
printf("emitting: %d\n", emitting());
//printf("emitting: %d\n", emitting());
}
else if (directive == "else")
{
@ -313,16 +313,16 @@ std::string preprocess(const std::string& input, std::string& error, const std::
{
stack.back().value = true;
}
printf("#else (%s)\n", stack.back().condition.c_str());
printf("emitting: %d\n", emitting());
//printf("#else (%s)\n", stack.back().condition.c_str());
//printf("emitting: %d\n", emitting());
}
else if (directive == "endif")
{
if (stack.empty())
throw std::runtime_error("no matching #if for #endif");
printf("#endif (%s)\n", stack.back().condition.c_str());
//("#endif (%s)\n", stack.back().condition.c_str());
stack.pop_back();
printf("emitting: %d\n", emitting());
//printf("emitting: %d\n", emitting());
}
else if (directive == "define")
{
@ -367,7 +367,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
pretty += parameters[i];
}
printf("#define %s('%s' = '%s')\n", identifier.c_str(), pretty.c_str(), token.c_str());
//printf("#define %s('%s' = '%s')\n", identifier.c_str(), pretty.c_str(), token.c_str());
}
else
@ -376,11 +376,11 @@ std::string preprocess(const std::string& input, std::string& error, const std::
auto token = t.remainder();
if (token.empty())
{
printf("#define(%s)\n", identifier.c_str());
//printf("#define(%s)\n", identifier.c_str());
}
else
{
printf("#define('%s' = '%s')\n", identifier.c_str(), token.c_str());
//printf("#define('%s' = '%s')\n", identifier.c_str(), token.c_str());
}
if (emitting())
{
@ -396,13 +396,13 @@ std::string preprocess(const std::string& input, std::string& error, const std::
{
t.consume();
auto file = t.until('\"');
printf("#include \"%s\"\n", file.c_str());
//printf("#include \"%s\"\n", file.c_str());
}
else if (type == '<')
{
t.consume();
auto file = t.until('>');
printf("#include <%s>\n", file.c_str());
//printf("#include <%s>\n", file.c_str());
}
else
{
@ -411,7 +411,7 @@ std::string preprocess(const std::string& input, std::string& error, const std::
}
else
{
printf("directive: '%s'\n", directive.c_str());
//printf("directive: '%s'\n", directive.c_str());
throw std::runtime_error("unknown directive '" + directive + "'");
}
}

View File

@ -1,6 +1,8 @@
#include "types.h"
#include "helpers.h"
#include <algorithm>
#include <unordered_set>
#include <ctime>
using namespace Types;
@ -175,7 +177,7 @@ bool Types::TypeManager::AddEnumerator(const std::string& enumType, const std::s
return true;
}
bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn, bool typeonly)
bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn, bool typeonly, const QualifiedType& retqtype)
{
auto found = functions.find(name);
if(found != functions.end() || name.empty() || owner.empty())
@ -189,11 +191,13 @@ bool TypeManager::AddFunction(const std::string & owner, const std::string & nam
f.rettype = rettype;
f.callconv = callconv;
f.noreturn = noreturn;
f.typeonly = typeonly;
f.retqtype = retqtype;
functions.emplace(f.name, f);
return true;
}
bool TypeManager::AddArg(const std::string & function, const std::string & type, const std::string & name)
bool TypeManager::AddArg(const std::string & function, const std::string & type, const std::string & name, const QualifiedType& qtype)
{
auto found = functions.find(function);
if (found == functions.end() || name.empty())
@ -204,13 +208,14 @@ bool TypeManager::AddArg(const std::string & function, const std::string & type,
Member arg;
arg.name = name;
arg.type = type;
arg.qtype = qtype;
found->second.args.push_back(arg);
return true;
}
bool TypeManager::AppendArg(const std::string & type, const std::string & name)
bool TypeManager::AppendArg(const std::string & type, const std::string & name, const QualifiedType& qtype)
{
return AddArg(lastfunction, type, name);
return AddArg(lastfunction, type, name, qtype);
}
int TypeManager::Sizeof(const std::string & type) const
@ -467,6 +472,153 @@ bool TypeManager::visitMember(const Member & root, Visitor & visitor) const
return false;
}
bool TypeManager::GenerateStubs() const
{
std::unordered_map<std::string, const Function*> fnptrs;
std::unordered_set<std::string> declared;
auto formatFunctionPointer = [this](const std::string& name, const Function& fn)
{
std::string r = fn.retqtype.pretty();
r += " (*";
r += name;
r += ")(";
for(size_t i = 0; i < fn.args.size(); i++)
{
const auto& arg = fn.args[i];
if(functions.count(arg.type) != 0)
__debugbreak();
r += arg.qtype.pretty();
if(i + 1 < fn.args.size())
{
r += ", ";
}
}
r += ")";
return r;
};
printf("// THIS WAS AUTOGENERATED YOU MORONS %ju\n", time(nullptr));
puts("#include \"hooks.h\"");
puts("");
std::vector<std::string> hooks;
for(const auto& itr : functions)
{
const auto &fn = itr.second;
// Skip non-declarations
if (fn.typeonly)
{
fnptrs.emplace(fn.name, &fn);
continue;
}
auto variadic = false;
for (const auto &arg: fn.args)
{
if (arg.type == "...")
{
variadic = true;
break;
}
}
if (variadic)
{
printf("// Skipping variadic function %s\n", fn.name.c_str());
continue;
}
hooks.push_back(fn.name);
}
puts("");
for(const auto& hook : hooks)
{
printf("static decltype(&%s) orig_%s;\n", hook.c_str(), hook.c_str());
}
for(const auto& hook : hooks)
{
const auto& fn = functions.at(hook);
puts("");
printf("static ");
printf("%s", fn.retqtype.pretty().c_str());
printf(" hook_%s(\n", fn.name.c_str());
std::vector<std::string> argtypes;
for(size_t i = 0; i < fn.args.size(); i++)
{
const auto& arg = fn.args[i];
printf(" ");
auto argPtr = functions.find(arg.type);
if(argPtr != functions.end())
{
argtypes.push_back(formatFunctionPointer(arg.name, argPtr->second));
printf("%s", argtypes.back().c_str());
if(arg.qtype.isConst)
__debugbreak();
}
else
{
argtypes.push_back(arg.type);
printf("%s", arg.qtype.pretty().c_str());
printf(" ");
printf("%s", arg.name.c_str());
}
if(i + 1 < fn.args.size())
{
printf(",");
}
puts("");
}
puts(")");
puts("{");
printf(" LOG_CALL(\"%s\");\n", fn.name.c_str());
for(size_t i = 0; i < fn.args.size(); i++)
{
printf(" LOG_ARGUMENT(\"%s\", %s);\n", argtypes[i].c_str(), fn.args[i].name.c_str());
}
if(fn.rettype == "void")
{
printf(" orig_%s(", fn.name.c_str());
}
else
{
printf(" auto _hook_result = orig_%s(", fn.name.c_str());
}
for(size_t i = 0; i < fn.args.size(); i++)
{
const auto& arg = fn.args[i];
printf("%s", arg.name.c_str());
if(i + 1 < fn.args.size())
{
printf(", ");
}
}
puts(");");
if(fn.rettype == "void")
{
puts(" LOG_RETURN_VOID();");
}
else
{
puts(" LOG_RETURN(_hook_result);");
puts(" return _hook_result;");
}
puts("}");
}
puts("");
puts("void InstallHooks()");
puts("{");
for(const auto& hook : hooks)
{
printf(" HOOK(%s);\n", hook.c_str(), hook.c_str());
}
puts("}");
return false;
}
bool AddType(const std::string & owner, const std::string & type, const std::string & name)
{
EXCLUSIVE_ACQUIRE(LockTypeManager);
@ -503,16 +655,16 @@ bool AddFunction(const std::string & owner, const std::string & name, const std:
return typeManager.AddFunction(owner, name, rettype, callconv, noreturn);
}
bool AddArg(const std::string & function, const std::string & type, const std::string & name)
bool AddArg(const std::string & function, const std::string & type, const std::string & name, const QualifiedType& qtype)
{
EXCLUSIVE_ACQUIRE(LockTypeManager);
return typeManager.AddArg(function, type, name);
return typeManager.AddArg(function, type, name, qtype);
}
bool AppendArg(const std::string & type, const std::string & name)
bool AppendArg(const std::string & type, const std::string & name, const QualifiedType& qtype)
{
EXCLUSIVE_ACQUIRE(LockTypeManager);
return typeManager.AppendArg(type, name);
return typeManager.AppendArg(type, name, qtype);
}
int SizeofType(const std::string & type)
@ -709,7 +861,7 @@ void LoadModel(const std::string & owner, Model & model)
//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, function.typeonly);
auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn, function.typeonly, function.retqtype);
if(!success)
{
//TODO properly handle errors
@ -743,8 +895,8 @@ void LoadModel(const std::string & owner, Model & model)
{
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);
arg.name = "__unnamed_arg_" + std::to_string(i);
auto success = typeManager.AddArg(function.name, arg.type, arg.name, arg.qtype);
if(!success)
{
//TODO properly handle errors
@ -794,3 +946,8 @@ std::string StructUnionPtrType(const std::string & pointto)
{
return typeManager.StructUnionPtrType(pointto);
}
bool GenerateStubs()
{
return typeManager.GenerateStubs();
}

View File

@ -36,12 +36,48 @@ namespace Types
int size = 0; //Size in bytes.
};
struct QualifiedType
{
std::string name; // base name of the type
bool isConst = false; // whether the base type is const
struct Ptr
{
bool isConst = false;
};
std::vector<Ptr> pointers; // arbitrary amount of pointers
std::string pretty() const
{
std::string r;
if(isConst)
r += "const ";
r += name;
for(const auto& ptr : pointers)
{
r += '*';
if(ptr.isConst)
r += " const";
}
return r;
}
std::string noconst() const
{
auto r = name;
for(const auto& ptr : pointers)
r += '*';
return r;
}
};
struct Member
{
std::string name; //Member identifier
std::string type; //Type.name
bool isConst = false; //Whether the member is marked as const
int arrsize = 0; //Number of elements if Member is an array
QualifiedType qtype; // Qualified Type
int arrsize = 0; //Number of elements if Member is an array (unused for function arguments)
int offset = -1; //Member offset (only stored for reference)
};
@ -82,6 +118,7 @@ namespace Types
std::string owner; //Function owner
std::string name; //Function identifier
std::string rettype; //Function return type
QualifiedType retqtype; // Function return qualified 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)
@ -116,16 +153,17 @@ namespace Types
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 typeonly = false);
bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false, bool typeonly = false, const QualifiedType& retqtype = {});
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);
bool AddArg(const std::string & function, const std::string & type, const std::string & name, const QualifiedType& qtype);
bool AppendArg(const std::string & type, const std::string & name, const QualifiedType& qtype);
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 Enumerate(std::vector<Summary> & typeList) const;
std::string StructUnionPtrType(const std::string & pointto) const;
bool GenerateStubs() const;
private:
std::unordered_map<Primitive, int> primitivesizes;
@ -162,8 +200,8 @@ 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, Types::CallingConvention callconv = Types::Cdecl, bool noreturn = false);
bool AddArg(const std::string & function, const std::string & type, const std::string & name);
bool AppendArg(const std::string & type, const std::string & name);
bool AddArg(const std::string & function, const std::string & type, const std::string & name, const Types::QualifiedType& qtype);
bool AppendArg(const std::string & type, const std::string & name, const Types::QualifiedType& qtype);
int SizeofType(const std::string & type);
bool VisitType(const std::string & type, const std::string & name, Types::TypeManager::Visitor & visitor);
void ClearTypes(const std::string & owner = "");
@ -173,3 +211,4 @@ bool LoadTypesJson(const std::string & json, const std::string & owner);
bool LoadTypesFile(const std::string & path, const std::string & owner);
bool ParseTypes(const std::string & code, const std::string & owner, std::vector<std::string> & errors);
std::string StructUnionPtrType(const std::string & pointto);
bool GenerateStubs();

View File

@ -62,10 +62,10 @@ struct Parser
index++;
}
bool parseVariable(const std::vector<Lexer::TokenState>& tlist, std::string& type, bool& isConst, std::string& name)
bool parseVariable(const std::vector<Lexer::TokenState>& tlist, std::string& type, QualifiedType& qtype, std::string& name)
{
type.clear();
isConst = false;
qtype = QualifiedType();
name.clear();
bool sawPointer = false;
@ -76,7 +76,10 @@ struct Parser
const auto& t = tlist[i];
if (t.Is(Lexer::tok_const))
{
isConst = true;
if(i == 0 || qtype.pointers.empty())
qtype.isConst = true;
else
qtype.pointers.back().isConst = true;
continue;
}
@ -130,6 +133,11 @@ struct Parser
return false;
}
if(!sawPointer)
qtype.name = type;
qtype.pointers.emplace_back();
// Apply the pointer to the type on the left
type += '*';
sawPointer = true;
@ -142,6 +150,8 @@ struct Parser
}
if (type.empty())
__debugbreak();
if(!sawPointer)
qtype.name = type;
return true;
}
@ -156,8 +166,7 @@ struct Parser
// TODO: calling conventions
std::string retname;
bool retconst = false;
if (!parseVariable(rettypes, fn.rettype, retconst, retname))
if (!parseVariable(rettypes, fn.rettype, fn.retqtype, retname))
return false;
if (ptr)
@ -212,7 +221,7 @@ struct Parser
auto finalizeArgument = [&]()
{
Member am;
if (!parseVariable(tlist, am.type, am.isConst, am.name))
if (!parseVariable(tlist, am.type, am.qtype, am.name))
return false;
fn.args.push_back(am);
tlist.clear();
@ -306,9 +315,6 @@ struct Parser
typeToken.Token = Lexer::tok_identifier;
typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr";
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
auto nameToken = startToken;
nameToken.Token = Lexer::tok_identifier;
nameToken.IdentifierStr = subfn.name;
@ -317,6 +323,10 @@ struct Parser
tlist.clear();
tlist.push_back(typeToken);
tlist.push_back(nameToken);
// Add the function to the model
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
}
else
{
@ -362,7 +372,7 @@ struct Parser
return false;
}
if (!parseVariable(tlist, m.type, m.isConst, m.name))
if (!parseVariable(tlist, m.type, m.qtype, m.name))
return false;
if (m.type == "void")
@ -472,9 +482,6 @@ struct Parser
typeToken.Token = Lexer::tok_identifier;
typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr";
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
auto nameToken = startToken;
nameToken.Token = Lexer::tok_identifier;
nameToken.IdentifierStr = subfn.name;
@ -484,6 +491,10 @@ struct Parser
tlist.push_back(typeToken);
tlist.push_back(nameToken);
// Add the function to the model
subfn.name = typeToken.IdentifierStr;
model.functions.push_back(subfn);
return true;
}
else if (t.Is(Lexer::tok_comma))
@ -814,7 +825,7 @@ struct Parser
}
Member tm;
if (!parseVariable(tlist, tm.type, tm.isConst, tm.name))
if (!parseVariable(tlist, tm.type, tm.qtype, tm.name))
return false;
model.types.push_back(tm);
}