mirror of https://github.com/x64dbg/btparser
1216 lines
27 KiB
C++
1216 lines
27 KiB
C++
#include "types.h"
|
|
#include "helpers.h"
|
|
|
|
using namespace Types;
|
|
|
|
#include "lexer.h"
|
|
|
|
#define dprintf printf
|
|
#define QT_TRANSLATE_NOOP(ctx, s) s
|
|
|
|
struct Parser
|
|
{
|
|
Lexer lexer;
|
|
std::string owner;
|
|
std::vector<std::string>& errors;
|
|
|
|
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)
|
|
{
|
|
lexer.SetInputData(code);
|
|
}
|
|
|
|
Lexer::TokenState& getToken(size_t i)
|
|
{
|
|
if (index >= tokens.size() - 1)
|
|
i = tokens.size() - 1;
|
|
return tokens[i];
|
|
}
|
|
|
|
Lexer::TokenState& curToken()
|
|
{
|
|
return getToken(index);
|
|
}
|
|
|
|
bool isToken(Lexer::Token token)
|
|
{
|
|
return getToken(index).Token == token;
|
|
}
|
|
|
|
bool isStructLike()
|
|
{
|
|
auto tok = getToken(index).Token;
|
|
switch (tok)
|
|
{
|
|
case Lexer::tok_struct:
|
|
case Lexer::tok_union:
|
|
case Lexer::tok_enum:
|
|
case Lexer::tok_class:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool isTokenList(std::initializer_list<Lexer::Token> il)
|
|
{
|
|
size_t i = 0;
|
|
for (auto l : il)
|
|
if (getToken(index + i++).Token != l)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void errLine(const Lexer::TokenState& token, const std::string& message)
|
|
{
|
|
auto error = StringUtils::sprintf("[line %zu:%zu] %s", token.CurLine + 1, token.LineIndex, message.c_str());
|
|
errors.push_back(std::move(error));
|
|
}
|
|
|
|
void eatSemic()
|
|
{
|
|
while (curToken().Token == Lexer::tok_semic)
|
|
index++;
|
|
}
|
|
|
|
bool parseVariable(const std::vector<Lexer::TokenState>& tlist, QualifiedType& type, std::string& name, Lexer::Token kind)
|
|
{
|
|
std::string stype; // TODO: get rid of this variable
|
|
type = QualifiedType();
|
|
switch (kind)
|
|
{
|
|
case Lexer::tok_struct:
|
|
type.kind = "struct";
|
|
break;
|
|
case Lexer::tok_class:
|
|
type.kind = "class";
|
|
break;
|
|
case Lexer::tok_union:
|
|
type.kind = "union";
|
|
break;
|
|
case Lexer::tok_enum:
|
|
type.kind = "enum";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
name.clear();
|
|
|
|
bool sawPointer = false;
|
|
bool isKeyword = true;
|
|
size_t i = 0;
|
|
for (; i < tlist.size(); i++)
|
|
{
|
|
const auto& t = tlist[i];
|
|
if (t.Is(Lexer::tok_const))
|
|
{
|
|
if (i == 0 || type.pointers.empty())
|
|
type.isConst = true;
|
|
else
|
|
type.pointers.back().isConst = true;
|
|
continue;
|
|
}
|
|
|
|
auto isType = t.IsType();
|
|
if (!isType)
|
|
{
|
|
isKeyword = false;
|
|
}
|
|
|
|
if (isType)
|
|
{
|
|
if (isKeyword)
|
|
{
|
|
if (!stype.empty())
|
|
stype += ' ';
|
|
stype += lexer.TokString(t);
|
|
}
|
|
else
|
|
{
|
|
errLine(t, "invalid keyword in type");
|
|
return false;
|
|
}
|
|
}
|
|
else if (t.Is(Lexer::tok_identifier))
|
|
{
|
|
if (stype.empty())
|
|
{
|
|
stype = t.IdentifierStr;
|
|
}
|
|
else if (i + 1 == tlist.size())
|
|
{
|
|
name = t.IdentifierStr;
|
|
}
|
|
else
|
|
{
|
|
errLine(t, "invalid identifier in type");
|
|
return false;
|
|
}
|
|
}
|
|
else if (t.Is(Lexer::tok_op_mul) || t.Is(Lexer::tok_op_and))
|
|
{
|
|
if (stype.empty())
|
|
{
|
|
errLine(t, "unexpected * in type");
|
|
return false;
|
|
}
|
|
|
|
if (sawPointer && stype.back() != '*')
|
|
{
|
|
errLine(t, "unexpected * in type");
|
|
return false;
|
|
}
|
|
|
|
if (!sawPointer)
|
|
type.name = stype;
|
|
|
|
type.pointers.emplace_back();
|
|
|
|
// Apply the pointer to the type on the left
|
|
stype += '*';
|
|
sawPointer = true;
|
|
}
|
|
else
|
|
{
|
|
errLine(t, "invalid token in type");
|
|
return false;
|
|
}
|
|
}
|
|
if (stype.empty())
|
|
__debugbreak();
|
|
if (!sawPointer)
|
|
type.name = stype;
|
|
return true;
|
|
}
|
|
|
|
bool parseFunction(Lexer::Token retkind, std::vector<Lexer::TokenState>& rettypes, Function& fn, bool ptr)
|
|
{
|
|
if (rettypes.empty())
|
|
{
|
|
errLine(curToken(), "expected return type before function pointer type");
|
|
return false;
|
|
}
|
|
|
|
// TODO: calling conventions
|
|
|
|
std::string retname;
|
|
if (!parseVariable(rettypes, fn.rettype, retname, retkind))
|
|
return false;
|
|
|
|
if (ptr)
|
|
{
|
|
if (!retname.empty())
|
|
{
|
|
errLine(rettypes.back(), "invalid return type in function pointer");
|
|
return false;
|
|
}
|
|
|
|
if (!isToken(Lexer::tok_op_mul) && !isToken(Lexer::tok_op_and))
|
|
{
|
|
errLine(curToken(), "expected * in function pointer type");
|
|
return false;
|
|
}
|
|
index++;
|
|
|
|
while (isToken(Lexer::tok_const) || isToken(Lexer::tok_volatile))
|
|
index++;
|
|
|
|
if (!isToken(Lexer::tok_identifier))
|
|
{
|
|
if (isToken(Lexer::tok_parclose))
|
|
{
|
|
// unnamed function pointer
|
|
fn.name = "";
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "expected identifier in function pointer type");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fn.name = lexer.TokString(curToken());
|
|
index++;
|
|
}
|
|
|
|
if (!isToken(Lexer::tok_parclose))
|
|
{
|
|
errLine(curToken(), "expected ) after function pointer type name");
|
|
return false;
|
|
}
|
|
index++;
|
|
|
|
if (!isToken(Lexer::tok_paropen))
|
|
{
|
|
errLine(curToken(), "expected ( for start of parameter list in function pointer type");
|
|
return false;
|
|
}
|
|
index++;
|
|
}
|
|
else if (retname.empty())
|
|
{
|
|
errLine(rettypes.back(), "function name cannot be empty");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fn.name = retname;
|
|
}
|
|
|
|
auto kind = Lexer::tok_eof;
|
|
std::vector<Lexer::TokenState> tlist;
|
|
auto startToken = curToken();
|
|
auto finalizeArgument = [&]()
|
|
{
|
|
Member am;
|
|
if (!parseVariable(tlist, am.type, am.name, kind))
|
|
return false;
|
|
fn.args.push_back(am);
|
|
kind = Lexer::tok_eof;
|
|
tlist.clear();
|
|
startToken = curToken();
|
|
return true;
|
|
};
|
|
while (!isToken(Lexer::tok_parclose))
|
|
{
|
|
if (isToken(Lexer::tok_comma))
|
|
{
|
|
index++;
|
|
if (!finalizeArgument())
|
|
return false;
|
|
}
|
|
|
|
if (isStructLike())
|
|
{
|
|
if (tlist.empty() && getToken(index + 1).Token == Lexer::tok_identifier)
|
|
{
|
|
kind = curToken().Token;
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "unsupported struct/union/enum in function argument");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const auto& t = curToken();
|
|
if (t.IsType() || t.Is(Lexer::tok_identifier) || t.Is(Lexer::tok_const))
|
|
{
|
|
index++;
|
|
|
|
// Primitive type
|
|
tlist.push_back(t);
|
|
}
|
|
else if (t.Is(Lexer::tok_op_mul) || t.Is(Lexer::tok_op_and))
|
|
{
|
|
// Pointer to the type on the left
|
|
if (tlist.empty())
|
|
{
|
|
errLine(curToken(), "unexpected * in function type argument list");
|
|
return false;
|
|
}
|
|
index++;
|
|
|
|
tlist.push_back(t);
|
|
}
|
|
else if (isTokenList({ Lexer::tok_subopen, Lexer::tok_subclose }))
|
|
{
|
|
if (tlist.empty())
|
|
{
|
|
errLine(curToken(), "unexpected [ in function type argument list");
|
|
return false;
|
|
}
|
|
index += 2;
|
|
|
|
Lexer::TokenState fakePtr;
|
|
fakePtr.Token = Lexer::tok_op_mul;
|
|
fakePtr.CurLine = t.CurLine;
|
|
fakePtr.LineIndex = t.LineIndex;
|
|
if (tlist.size() > 1 && tlist.back().Is(Lexer::tok_identifier))
|
|
{
|
|
tlist.insert(tlist.end() - 1, fakePtr);
|
|
}
|
|
else
|
|
{
|
|
tlist.push_back(fakePtr);
|
|
}
|
|
}
|
|
else if (t.Is(Lexer::tok_varargs))
|
|
{
|
|
if (!tlist.empty())
|
|
{
|
|
errLine(t, "unexpected ... in function type argument list");
|
|
return false;
|
|
}
|
|
|
|
index++;
|
|
if (!isToken(Lexer::tok_parclose))
|
|
{
|
|
errLine(curToken(), "expected ) after ... in function type argument list");
|
|
return false;
|
|
}
|
|
|
|
Member am;
|
|
am.type = QualifiedType("...");
|
|
fn.args.push_back(am);
|
|
break;
|
|
}
|
|
else if (t.Is(Lexer::tok_paropen))
|
|
{
|
|
auto startToken = curToken();
|
|
index++;
|
|
|
|
// Function pointer argument to a function
|
|
Function subfn;
|
|
subfn.typeonly = true;
|
|
if (!parseFunction(kind, tlist, subfn, true))
|
|
{
|
|
return false;
|
|
}
|
|
kind = Lexer::tok_eof;
|
|
|
|
// Create fake tokens
|
|
auto typeToken = tlist.back();
|
|
typeToken.Token = Lexer::tok_identifier;
|
|
typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr";
|
|
|
|
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);
|
|
|
|
// Add the function to the model
|
|
subfn.name = typeToken.IdentifierStr;
|
|
model.functions.push_back(subfn);
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "unsupported token in function type argument list");
|
|
return false;
|
|
}
|
|
}
|
|
index++;
|
|
|
|
if (tlist.empty())
|
|
{
|
|
// Do nothing
|
|
}
|
|
else if (tlist.size() == 1 && tlist[0].Token == Lexer::tok_void)
|
|
{
|
|
if (!fn.args.empty())
|
|
{
|
|
errLine(tlist[0], "invalid argument type: void");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else if (!finalizeArgument())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool parseMember(StructUnion& su)
|
|
{
|
|
Member m;
|
|
bool sawPointer = false;
|
|
auto kind = Lexer::tok_eof;
|
|
std::vector<Lexer::TokenState> tlist;
|
|
auto startToken = curToken();
|
|
|
|
auto finalizeMember = [&]()
|
|
{
|
|
if (tlist.size() < 2)
|
|
{
|
|
errLine(startToken, "not enough tokens in member");
|
|
return false;
|
|
}
|
|
|
|
if (!parseVariable(tlist, m.type, m.name, kind))
|
|
return false;
|
|
kind = Lexer::tok_eof;
|
|
|
|
if (m.type.name == "void" && !m.type.isPointer())
|
|
{
|
|
errLine(startToken, "void is not a valid member type");
|
|
return false;
|
|
}
|
|
|
|
if (m.type.empty() || m.name.empty())
|
|
__debugbreak();
|
|
|
|
su.members.push_back(m);
|
|
return true;
|
|
};
|
|
|
|
while (!isToken(Lexer::tok_semic))
|
|
{
|
|
if (isToken(Lexer::tok_eof))
|
|
{
|
|
errLine(curToken(), "unexpected eof in member");
|
|
return false;
|
|
}
|
|
|
|
if (isStructLike())
|
|
{
|
|
if (tlist.empty() && getToken(index + 1).Token == Lexer::tok_identifier)
|
|
{
|
|
kind = curToken().Token;
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "unsupported struct/union/enum in member");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const auto& t = curToken();
|
|
if (t.IsType() || t.Is(Lexer::tok_identifier) || t.Is(Lexer::tok_const))
|
|
{
|
|
index++;
|
|
// Primitive type / name
|
|
tlist.push_back(t);
|
|
}
|
|
else if (t.Is(Lexer::tok_op_mul) || t.Is(Lexer::tok_op_and))
|
|
{
|
|
// Pointer to the type on the left
|
|
if (tlist.empty())
|
|
{
|
|
errLine(curToken(), "unexpected * in member");
|
|
return false;
|
|
}
|
|
|
|
if (sawPointer && tlist.back().Token != Lexer::tok_op_mul && tlist.back().Token != Lexer::tok_op_and)
|
|
{
|
|
errLine(curToken(), "unexpected * in member");
|
|
return false;
|
|
}
|
|
|
|
index++;
|
|
|
|
tlist.push_back(t);
|
|
sawPointer = true;
|
|
}
|
|
else if (t.Is(Lexer::tok_subopen))
|
|
{
|
|
index++;
|
|
|
|
// Array
|
|
if (!isToken(Lexer::tok_number))
|
|
{
|
|
errLine(curToken(), "expected number token after array");
|
|
return false;
|
|
}
|
|
m.arrsize = (int)curToken().NumberVal;
|
|
index++;
|
|
|
|
if (!isToken(Lexer::tok_subclose))
|
|
{
|
|
errLine(curToken(), "expected ] after array size");
|
|
return false;
|
|
}
|
|
index++;
|
|
|
|
break;
|
|
}
|
|
else if (t.Is(Lexer::tok_paropen))
|
|
{
|
|
index++;
|
|
|
|
// Function pointer type
|
|
Function subfn;
|
|
subfn.typeonly = true;
|
|
if (!parseFunction(kind, tlist, subfn, true))
|
|
{
|
|
return false;
|
|
}
|
|
kind = Lexer::tok_eof;
|
|
|
|
if (!isToken(Lexer::tok_semic))
|
|
{
|
|
errLine(curToken(), "expected ; after function type");
|
|
return false;
|
|
}
|
|
eatSemic();
|
|
|
|
// Create fake tokens
|
|
auto typeToken = tlist.back();
|
|
typeToken.Token = Lexer::tok_identifier;
|
|
typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr";
|
|
|
|
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);
|
|
|
|
// Add the function to the model
|
|
subfn.name = typeToken.IdentifierStr;
|
|
model.functions.push_back(subfn);
|
|
|
|
return true;
|
|
}
|
|
else if (t.Is(Lexer::tok_comma))
|
|
{
|
|
// Comma-separated members
|
|
index++;
|
|
|
|
if (!finalizeMember())
|
|
return false;
|
|
|
|
// Remove the name from the type
|
|
if (tlist.back().Token != Lexer::tok_identifier)
|
|
__debugbreak();
|
|
tlist.pop_back();
|
|
|
|
// Remove the pointer from the type
|
|
while (!tlist.empty() && (tlist.back().Token == Lexer::tok_op_mul || tlist.back().Token == Lexer::tok_op_and))
|
|
tlist.pop_back();
|
|
sawPointer = false;
|
|
|
|
m = Member();
|
|
}
|
|
else if (t.Is(Lexer::tok_virtual))
|
|
{
|
|
// Parse a virtual function declaration
|
|
index++;
|
|
|
|
// Parse the function declaration
|
|
Function vfunction;
|
|
if (!parseFunctionTop(vfunction, true))
|
|
return false;
|
|
su.vtable.push_back(vfunction);
|
|
}
|
|
else if (t.Is(Lexer::tok_brclose) && tlist.empty())
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
__debugbreak();
|
|
}
|
|
}
|
|
|
|
if (!isToken(Lexer::tok_semic))
|
|
{
|
|
errLine(curToken(), "expected ; after member");
|
|
return false;
|
|
}
|
|
eatSemic();
|
|
|
|
if (!finalizeMember())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool parseStructUnion()
|
|
{
|
|
auto startToken = curToken();
|
|
if (isToken(Lexer::tok_struct) || isToken(Lexer::tok_class) || isToken(Lexer::tok_union))
|
|
{
|
|
StructUnion su;
|
|
su.isunion = isToken(Lexer::tok_union);
|
|
index++;
|
|
if (!isToken(Lexer::tok_identifier))
|
|
{
|
|
errLine(curToken(), "expected identifier after struct");
|
|
return false;
|
|
}
|
|
su.name = lexer.TokString(curToken());
|
|
index++;
|
|
if (isToken(Lexer::tok_bropen))
|
|
{
|
|
index++;
|
|
while (!isToken(Lexer::tok_brclose))
|
|
{
|
|
if (isToken(Lexer::tok_eof))
|
|
{
|
|
errLine(curToken(), StringUtils::sprintf("unexpected eof in %s", su.isunion ? "union" : "struct"));
|
|
return false;
|
|
}
|
|
if (isToken(Lexer::tok_bropen))
|
|
{
|
|
errLine(curToken(), "nested blocks are not allowed!");
|
|
return false;
|
|
}
|
|
if (!parseMember(su))
|
|
return false;
|
|
}
|
|
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!");
|
|
return false;
|
|
}
|
|
eatSemic();
|
|
return true;
|
|
}
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "invalid struct token sequence!");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool parseEnum()
|
|
{
|
|
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))
|
|
{
|
|
errLine(curToken(), "unexpected eof in enum");
|
|
return false;
|
|
}
|
|
if (isToken(Lexer::tok_bropen))
|
|
{
|
|
errLine(curToken(), "nested blocks are not allowed!");
|
|
return false;
|
|
}
|
|
|
|
if (!e.values.empty())
|
|
{
|
|
if (isToken(Lexer::tok_comma))
|
|
{
|
|
index++;
|
|
if (isToken(Lexer::tok_brclose))
|
|
{
|
|
// Support final comma
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "expected comma in enum");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!isToken(Lexer::tok_identifier))
|
|
{
|
|
errLine(curToken(), StringUtils::sprintf("expected identifier in enum, got '%s'", lexer.TokString(curToken()).c_str()));
|
|
return false;
|
|
}
|
|
|
|
EnumValue v;
|
|
v.name = lexer.TokString(curToken());
|
|
index++;
|
|
|
|
if (isToken(Lexer::tok_assign))
|
|
{
|
|
bool negative = false;
|
|
index++;
|
|
if (isToken(Lexer::tok_op_min))
|
|
{
|
|
index++;
|
|
negative = true;
|
|
}
|
|
if (!isToken(Lexer::tok_number))
|
|
{
|
|
errLine(curToken(), "expected number after = in enum");
|
|
return false;
|
|
}
|
|
v.value = curToken().NumberVal;
|
|
if (negative)
|
|
{
|
|
v.value = -(int64_t)v.value;
|
|
}
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
v.value = e.values.empty() ? 0 : e.values.back().value + 1;
|
|
}
|
|
e.values.push_back(v);
|
|
}
|
|
index++; //eat tok_brclose
|
|
|
|
model.enums.emplace_back(e, etype);
|
|
if (!isToken(Lexer::tok_semic))
|
|
{
|
|
errLine(curToken(), "expected semicolon!");
|
|
return false;
|
|
}
|
|
eatSemic();
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "invalid enum token sequence!");
|
|
return false;
|
|
}
|
|
__debugbreak();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool parseTypedef()
|
|
{
|
|
// TODO: support "typedef struct foo { members... };"
|
|
// TODO: support "typedef enum foo { members... };"
|
|
|
|
if (isToken(Lexer::tok_typedef))
|
|
{
|
|
index++;
|
|
|
|
auto startToken = curToken();
|
|
|
|
bool sawPointer = false;
|
|
std::vector<Lexer::TokenState> tlist;
|
|
auto kind = Lexer::tok_eof;
|
|
while (!isToken(Lexer::tok_semic))
|
|
{
|
|
if (isToken(Lexer::tok_eof))
|
|
{
|
|
errLine(curToken(), "unexpected eof in typedef");
|
|
return false;
|
|
}
|
|
|
|
if (isStructLike())
|
|
{
|
|
if (tlist.empty() && getToken(index + 1).Is(Lexer::tok_identifier))
|
|
{
|
|
kind = curToken().Token;
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "unsupported struct/union/enum in typedef");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const auto& t = curToken();
|
|
if (t.IsType() || t.Token == Lexer::tok_identifier || t.Token == Lexer::tok_const)
|
|
{
|
|
// Primitive type
|
|
index++;
|
|
tlist.push_back(t);
|
|
}
|
|
else if (t.Is(Lexer::tok_op_mul) || t.Is(Lexer::tok_op_and))
|
|
{
|
|
// Pointer to the type on the left
|
|
if (tlist.empty())
|
|
{
|
|
errLine(curToken(), "unexpected * in member");
|
|
return false;
|
|
}
|
|
|
|
if (sawPointer && tlist.back().Token != Lexer::tok_op_mul && tlist.back().Token != Lexer::tok_op_and)
|
|
{
|
|
errLine(curToken(), "unexpected * in member");
|
|
return false;
|
|
}
|
|
|
|
tlist.push_back(t);
|
|
sawPointer = true;
|
|
|
|
index++;
|
|
}
|
|
else if (t.Token == Lexer::tok_paropen)
|
|
{
|
|
// Function pointer type
|
|
|
|
index++;
|
|
|
|
Function fn;
|
|
fn.typeonly = true;
|
|
if (!parseFunction(kind, tlist, fn, true))
|
|
{
|
|
return false;
|
|
}
|
|
kind = Lexer::tok_eof;
|
|
|
|
if (!isToken(Lexer::tok_semic))
|
|
{
|
|
errLine(curToken(), "expected ; after function type");
|
|
return false;
|
|
}
|
|
eatSemic();
|
|
|
|
model.functions.push_back(fn);
|
|
|
|
// TODO: handle pointer stuff correctly?
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
t.Throw("unsupported token in typedef");
|
|
}
|
|
}
|
|
eatSemic();
|
|
|
|
if (tlist.size() < 2)
|
|
{
|
|
errLine(startToken, "not enough tokens in typedef");
|
|
return false;
|
|
}
|
|
|
|
Member tm;
|
|
if (!parseVariable(tlist, tm.type, tm.name, kind))
|
|
return false;
|
|
model.types.push_back(tm);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool parseFunctionTop(Function& fn, bool isVirtual)
|
|
{
|
|
fn = {};
|
|
|
|
bool sawPointer = false;
|
|
auto kind = Lexer::tok_eof;
|
|
std::vector<Lexer::TokenState> tlist;
|
|
while (!isToken(Lexer::tok_semic))
|
|
{
|
|
if (isToken(Lexer::tok_eof))
|
|
{
|
|
errLine(curToken(), "unexpected eof in function");
|
|
return false;
|
|
}
|
|
|
|
if (isStructLike())
|
|
{
|
|
if (tlist.empty() && getToken(index + 1).Token == Lexer::tok_identifier)
|
|
{
|
|
kind = curToken().Token;
|
|
index++;
|
|
}
|
|
else
|
|
{
|
|
errLine(curToken(), "unexpected struct/union/enum in function");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const auto& t = curToken();
|
|
if (t.IsType() || t.Is(Lexer::tok_identifier) || t.Is(Lexer::tok_const))
|
|
{
|
|
index++;
|
|
// Primitive type / name
|
|
tlist.push_back(t);
|
|
}
|
|
else if (isTokenList({ Lexer::tok_op_neg, Lexer::tok_identifier }))
|
|
{
|
|
index++;
|
|
auto td = curToken();
|
|
index++;
|
|
// Destructor name
|
|
td.IdentifierStr = "~" + td.IdentifierStr;
|
|
auto tvoid = t;
|
|
tvoid.Token = Lexer::tok_void;
|
|
tlist.push_back(std::move(tvoid));
|
|
tlist.push_back(std::move(td));
|
|
}
|
|
else if (t.Is(Lexer::tok_op_mul) || t.Is(Lexer::tok_op_and))
|
|
{
|
|
// Pointer to the type on the left
|
|
if (tlist.empty())
|
|
{
|
|
errLine(curToken(), "unexpected * in function");
|
|
return false;
|
|
}
|
|
|
|
if (sawPointer && tlist.back().Token != Lexer::tok_op_mul && tlist.back().Token != Lexer::tok_op_and)
|
|
{
|
|
errLine(curToken(), "unexpected * in function");
|
|
return false;
|
|
}
|
|
|
|
index++;
|
|
|
|
tlist.push_back(t);
|
|
sawPointer = true;
|
|
}
|
|
else if (t.Is(Lexer::tok_paropen))
|
|
{
|
|
index++;
|
|
|
|
// Function pointer type
|
|
if (!parseFunction(kind, tlist, fn, false))
|
|
{
|
|
return false;
|
|
}
|
|
kind = Lexer::tok_eof;
|
|
|
|
if (!isToken(Lexer::tok_semic))
|
|
{
|
|
if (isVirtual)
|
|
{
|
|
auto endToken = curToken();
|
|
while (true)
|
|
{
|
|
auto tEnd = curToken();
|
|
if (tEnd.Is(Lexer::tok_eof))
|
|
{
|
|
errLine(curToken(), "unexpected eof after virtual function");
|
|
return false;
|
|
}
|
|
|
|
if (tEnd.Is(Lexer::tok_const) || tEnd.Is(Lexer::tok_override))
|
|
{
|
|
index++;
|
|
continue;
|
|
}
|
|
|
|
if (tEnd.Is(Lexer::tok_semic))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (tEnd.Is(Lexer::tok_assign))
|
|
{
|
|
index++;
|
|
tEnd = curToken();
|
|
if (!tEnd.Is(Lexer::tok_number) || tEnd.NumberVal != 0)
|
|
{
|
|
errLine(endToken, "expected = 0 after virtual function type");
|
|
return false;
|
|
}
|
|
index++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
errLine(endToken, "expected = 0 after virtual function type");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (!isToken(Lexer::tok_semic))
|
|
{
|
|
errLine(curToken(), "expected ; after function type");
|
|
return false;
|
|
}
|
|
}
|
|
eatSemic();
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
__debugbreak();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LoadModel(TypeManager& typeManager)
|
|
{
|
|
//Add all base struct/union types first to avoid errors later
|
|
for (auto& su : model.structUnions)
|
|
{
|
|
auto success = su.isunion ? typeManager.AddUnion(owner, su.name) : typeManager.AddStruct(owner, su.name);
|
|
if (!success)
|
|
{
|
|
//TODO properly handle errors
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add %s %s;\n"), su.isunion ? "union" : "struct", su.name.c_str());
|
|
su.name.clear(); //signal error
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//Add simple typedefs
|
|
for (auto& type : model.types)
|
|
{
|
|
// TODO: support pointers
|
|
if (type.type.isPointer() || type.type.isConst)
|
|
__debugbreak();
|
|
auto success = typeManager.AddType(owner, type.type.name, type.name);
|
|
if (!success)
|
|
{
|
|
//TODO properly handle errors
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add typedef %s %s;\n"), type.type.pretty().c_str(), type.name.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//Add enums
|
|
for (auto& kv : model.enums)
|
|
{
|
|
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
|
|
return false;
|
|
}
|
|
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);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//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);
|
|
if (!success)
|
|
{
|
|
//TODO properly handle errors
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add function %s %s()\n"), function.rettype.pretty().c_str(), function.name.c_str());
|
|
function.name.clear(); //signal error
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//Add struct/union members
|
|
for (auto& su : model.structUnions)
|
|
{
|
|
if (su.name.empty()) //skip error-signalled structs/unions
|
|
continue;
|
|
for (auto& member : su.members)
|
|
{
|
|
auto success = typeManager.AddMember(su.name, member.type, member.name, member.arrsize, member.offset);
|
|
if (!success)
|
|
{
|
|
//TODO properly handle errors
|
|
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add member %s %s.%s;\n"), member.type.pretty().c_str(), su.name.c_str(), member.name.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Add function arguments
|
|
for (auto& function : model.functions)
|
|
{
|
|
if (function.name.empty()) //skip error-signalled functions
|
|
continue;
|
|
for (size_t i = 0; i < function.args.size(); i++)
|
|
{
|
|
auto& arg = function.args[i];
|
|
if (arg.name.empty())
|
|
arg.name = "__unnamed_arg_" + 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[%zu]: %s %s;\n"), function.name.c_str(), i, arg.type.pretty().c_str(), arg.name.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Parse()
|
|
{
|
|
std::string error;
|
|
if (!lexer.DoLexing(tokens, error))
|
|
{
|
|
errors.push_back(error);
|
|
return false;
|
|
}
|
|
|
|
while (!isToken(Lexer::tok_eof))
|
|
{
|
|
auto curIndex = index;
|
|
if (!parseTypedef())
|
|
return false;
|
|
if (!parseStructUnion())
|
|
return false;
|
|
if (!parseEnum())
|
|
return false;
|
|
eatSemic();
|
|
if (curIndex == index)
|
|
{
|
|
Function fn;
|
|
if (!parseFunctionTop(fn, false))
|
|
return false;
|
|
model.functions.push_back(fn);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
bool TypeManager::ParseTypes(const std::string& code, const std::string& owner, std::vector<std::string>& errors)
|
|
{
|
|
Parser p(code, owner, errors);
|
|
return p.Parse() && p.LoadModel(*this);
|
|
}
|
|
|
|
bool Types::ParseModel(const std::string& code, const std::string& owner, std::vector<std::string>& errors, Model& model)
|
|
{
|
|
Parser p(code, owner, errors);
|
|
if (!p.Parse())
|
|
return false;
|
|
model = std::move(p.model);
|
|
return true;
|
|
} |