mirror of https://github.com/x64dbg/btparser
Add qualified types and improve parsing support
This commit is contained in:
parent
a87d84cc5b
commit
6d3f1f75c4
|
@ -7,3 +7,6 @@ My Amplifier XE Results */
|
|||
actual.out
|
||||
expected.out
|
||||
.vs/
|
||||
cmake-build*/
|
||||
build*/
|
||||
.idea/
|
||||
|
|
|
@ -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 + "'");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 = "");
|
||||
|
@ -172,4 +210,5 @@ void EnumTypes(std::vector<Types::TypeManager::Summary> & typeList);
|
|||
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);
|
||||
std::string StructUnionPtrType(const std::string & pointto);
|
||||
bool GenerateStubs();
|
|
@ -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();
|
||||
|
@ -305,9 +314,6 @@ struct Parser
|
|||
auto typeToken = tlist.back();
|
||||
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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue