Pattern Language rewrite (#111)
* Initial parser rewrite effort Lexer and Token cleanup, Parser started over * Greatly improved parser syntax * Reimplemented using declarations and variable placement parsing * Added back unions and structs * Added enums as well as mathematical expressions (+, -, *, /, <<, >>, &, |, ^) * Code style improvement * Implemented arrays and fixed memory issues * Fixed more memory issues in parser, reimplemented validator, evaluator and patterns * Fixed builtin types, arrays and reimplemented strings * Improved error messages * Made character a distinct type, used for chars and strings * Implemented padding, fixed arrays * Added bitfields * Added rvalue parsing, no evaluating yet * Added .idea folder to gitignore * Fixed build on MacOS * Added custom implementation of integral concept if not available * Rebased onto master * Fixed array variable decl crash * Added rvalues and dot syntax * Lower case all pattern language error messages * Fixed typo in variable name * Fixed bug where preprocessor would not ignore commented out directives * Reimplemented pointers * Fixed rebase issues
This commit is contained in:
parent
d510f8c7cf
commit
78ef07cf0f
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
.vscode/
|
||||
|
||||
.idea/
|
||||
|
||||
cmake-build-debug/
|
||||
|
||||
|
2
.idea/imhex.iml
generated
Normal file
2
.idea/imhex.iml
generated
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/imhex.iml" filepath="$PROJECT_DIR$/.idea/imhex.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -4,130 +4,351 @@
|
||||
|
||||
#include <bit>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
enum class Type {
|
||||
VariableDecl,
|
||||
TypeDecl,
|
||||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Bitfield,
|
||||
Scope,
|
||||
};
|
||||
constexpr ASTNode() = default;
|
||||
constexpr virtual ~ASTNode() = default;
|
||||
constexpr ASTNode(const ASTNode &) = default;
|
||||
|
||||
explicit ASTNode(Type type, u32 lineNumber) : m_type(type), m_lineNumber(lineNumber) {}
|
||||
virtual ~ASTNode() = default;
|
||||
[[nodiscard]] constexpr u32 getLineNumber() const { return this->m_lineNumber; }
|
||||
constexpr void setLineNumber(u32 lineNumber) { this->m_lineNumber = lineNumber; }
|
||||
|
||||
Type getType() { return this->m_type; }
|
||||
u32 getLineNumber() { return this->m_lineNumber; }
|
||||
virtual ASTNode* clone() = 0;
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
u32 m_lineNumber;
|
||||
u32 m_lineNumber = 1;
|
||||
};
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode {
|
||||
class ASTNodeIntegerLiteral : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeVariableDecl(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "", std::optional<u64> offset = { }, size_t arraySize = 1, std::optional<std::string> arraySizeVariable = { }, std::optional<u8> pointerSize = { }, std::optional<std::endian> endianess = { })
|
||||
: ASTNode(Type::VariableDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize), m_arraySizeVariable(arraySizeVariable), m_pointerSize(pointerSize), m_endianess(endianess) { }
|
||||
ASTNodeIntegerLiteral(std::variant<u128, s128> value, Token::ValueType type)
|
||||
: ASTNode(), m_value(value), m_type(type) {
|
||||
|
||||
const Token::TypeToken::Type& getVariableType() const { return this->m_type; }
|
||||
const std::string& getCustomVariableTypeName() const { return this->m_customTypeName; }
|
||||
const std::string& getVariableName() const { return this->m_name; };
|
||||
std::optional<u64> getOffset() const { return this->m_offset; }
|
||||
size_t getArraySize() const { return this->m_arraySize; }
|
||||
std::optional<std::string> getArraySizeVariable() const { return this->m_arraySizeVariable; }
|
||||
std::optional<u8> getPointerSize() const { return this->m_pointerSize; }
|
||||
std::optional<std::endian> getEndianess() const { return this->m_endianess; }
|
||||
}
|
||||
ASTNodeIntegerLiteral(const ASTNodeIntegerLiteral&) = default;
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeIntegerLiteral(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto& getValue() const {
|
||||
return this->m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] Token::ValueType getType() const {
|
||||
return this->m_type;
|
||||
}
|
||||
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
std::optional<u64> m_offset;
|
||||
size_t m_arraySize;
|
||||
std::optional<std::string> m_arraySizeVariable;
|
||||
std::optional<u8> m_pointerSize;
|
||||
std::optional<std::endian> m_endianess = { };
|
||||
std::variant<u128, s128> m_value;
|
||||
Token::ValueType m_type;
|
||||
};
|
||||
|
||||
class ASTNodeScope : public ASTNode {
|
||||
class ASTNodeNumericExpression : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeScope(u32 lineNumber, std::vector<ASTNode*> nodes) : ASTNode(Type::Scope, lineNumber), m_nodes(nodes) { }
|
||||
ASTNodeNumericExpression(ASTNode *left, ASTNode *right, Token::Operator op)
|
||||
: ASTNode(), m_left(left), m_right(right), m_operator(op) { }
|
||||
|
||||
~ASTNodeNumericExpression() override {
|
||||
delete this->m_left;
|
||||
delete this->m_right;
|
||||
}
|
||||
|
||||
ASTNodeNumericExpression(const ASTNodeNumericExpression &other) : ASTNode(other) {
|
||||
this->m_operator = other.m_operator;
|
||||
this->m_left = other.m_left->clone();
|
||||
this->m_right = other.m_right->clone();
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeNumericExpression(*this);
|
||||
}
|
||||
|
||||
ASTNode *getLeftOperand() { return this->m_left; }
|
||||
ASTNode *getRightOperand() { return this->m_right; }
|
||||
Token::Operator getOperator() { return this->m_operator; }
|
||||
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
ASTNode *m_left, *m_right;
|
||||
Token::Operator m_operator;
|
||||
};
|
||||
|
||||
class ASTNodeStruct : public ASTNode {
|
||||
class ASTNodeBuiltinType : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeStruct(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Struct, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||
constexpr explicit ASTNodeBuiltinType(Token::ValueType type)
|
||||
: ASTNode(), m_type(type) { }
|
||||
|
||||
[[nodiscard]] constexpr const auto& getType() const { return this->m_type; }
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeBuiltinType(*this);
|
||||
}
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeUnion : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeUnion(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Union, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeBitField : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeBitField(u32 lineNumber, std::string name, std::vector<std::pair<std::string, size_t>> fields)
|
||||
: ASTNode(Type::Bitfield, lineNumber), m_name(name), m_fields(fields) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<std::pair<std::string, size_t>> &getFields() { return this->m_fields; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::pair<std::string, size_t>> m_fields;
|
||||
const Token::ValueType m_type;
|
||||
};
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeTypeDecl(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
||||
: ASTNode(Type::TypeDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
|
||||
ASTNodeTypeDecl(std::string_view name, ASTNode *type, std::optional<std::endian> endian = { })
|
||||
: ASTNode(), m_name(name), m_type(type), m_endian(endian) { }
|
||||
|
||||
const std::string& getTypeName() const { return this->m_name; };
|
||||
ASTNodeTypeDecl(const ASTNodeTypeDecl& other) : ASTNode(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_endian = other.m_endian;
|
||||
}
|
||||
|
||||
~ASTNodeTypeDecl() override {
|
||||
delete this->m_type;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeTypeDecl(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view getName() const { return this->m_name; }
|
||||
[[nodiscard]] ASTNode* getType() { return this->m_type; }
|
||||
[[nodiscard]] std::optional<std::endian> getEndian() const { return this->m_endian; }
|
||||
|
||||
const Token::TypeToken::Type& getAssignedType() const { return this->m_type; }
|
||||
const std::string& getAssignedCustomTypeName() const { return this->m_customTypeName; }
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
std::string m_name;
|
||||
ASTNode *m_type;
|
||||
std::optional<std::endian> m_endian;
|
||||
};
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode {
|
||||
public:
|
||||
ASTNodeVariableDecl(std::string_view name, ASTNode *type, ASTNode *placementOffset = nullptr)
|
||||
: ASTNode(), m_name(name), m_type(type), m_placementOffset(placementOffset) { }
|
||||
|
||||
ASTNodeVariableDecl(const ASTNodeVariableDecl &other) : ASTNode(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
~ASTNodeVariableDecl() override {
|
||||
delete this->m_type;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeVariableDecl(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr ASTNode* getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr auto getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
ASTNode *m_type;
|
||||
ASTNode *m_placementOffset;
|
||||
};
|
||||
|
||||
class ASTNodeArrayVariableDecl : public ASTNode {
|
||||
public:
|
||||
ASTNodeArrayVariableDecl(std::string_view name, ASTNode *type, ASTNode *size, ASTNode *placementOffset = nullptr)
|
||||
: ASTNode(), m_name(name), m_type(type), m_size(size), m_placementOffset(placementOffset) { }
|
||||
|
||||
ASTNodeArrayVariableDecl(const ASTNodeArrayVariableDecl &other) : ASTNode(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_size = other.m_size->clone();
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
~ASTNodeArrayVariableDecl() override {
|
||||
delete this->m_type;
|
||||
delete this->m_size;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeArrayVariableDecl(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr ASTNode* getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr ASTNode* getSize() const { return this->m_size; }
|
||||
[[nodiscard]] constexpr auto getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
ASTNode *m_type;
|
||||
ASTNode *m_size;
|
||||
ASTNode *m_placementOffset;
|
||||
};
|
||||
|
||||
class ASTNodePointerVariableDecl : public ASTNode {
|
||||
public:
|
||||
ASTNodePointerVariableDecl(std::string_view name, ASTNode *type, ASTNode *sizeType, ASTNode *placementOffset = nullptr)
|
||||
: ASTNode(), m_name(name), m_type(type), m_sizeType(sizeType), m_placementOffset(placementOffset) { }
|
||||
|
||||
ASTNodePointerVariableDecl(const ASTNodePointerVariableDecl &other) : ASTNode(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_sizeType = other.m_sizeType->clone();
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
~ASTNodePointerVariableDecl() override {
|
||||
delete this->m_type;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodePointerVariableDecl(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr ASTNode* getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr ASTNode* getSizeType() const { return this->m_sizeType; }
|
||||
[[nodiscard]] constexpr auto getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
ASTNode *m_type;
|
||||
ASTNode *m_sizeType;
|
||||
ASTNode *m_placementOffset;
|
||||
};
|
||||
|
||||
class ASTNodeStruct : public ASTNode {
|
||||
public:
|
||||
ASTNodeStruct() : ASTNode() { }
|
||||
|
||||
ASTNodeStruct(const ASTNodeStruct &other) : ASTNode(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
}
|
||||
|
||||
~ASTNodeStruct() override {
|
||||
for (auto &member : this->m_members)
|
||||
delete member;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeStruct(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<ASTNode*>& getMembers() const { return this->m_members; }
|
||||
void addMember(ASTNode *node) { this->m_members.push_back(node); }
|
||||
|
||||
private:
|
||||
std::vector<ASTNode*> m_members;
|
||||
};
|
||||
|
||||
class ASTNodeUnion : public ASTNode {
|
||||
public:
|
||||
ASTNodeUnion() : ASTNode() { }
|
||||
|
||||
ASTNodeUnion(const ASTNodeUnion &other) : ASTNode(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
}
|
||||
|
||||
~ASTNodeUnion() override {
|
||||
for (auto &member : this->m_members)
|
||||
delete member;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeUnion(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<ASTNode*>& getMembers() const { return this->m_members; }
|
||||
void addMember(ASTNode *node) { this->m_members.push_back(node); }
|
||||
|
||||
private:
|
||||
std::vector<ASTNode*> m_members;
|
||||
};
|
||||
|
||||
class ASTNodeEnum : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeEnum(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name)
|
||||
: ASTNode(Type::Enum, lineNumber), m_type(type), m_name(name) { }
|
||||
explicit ASTNodeEnum(ASTNode *underlyingType) : ASTNode(), m_underlyingType(underlyingType) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; };
|
||||
ASTNodeEnum(const ASTNodeEnum &other) : ASTNode(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace_back(name, entry->clone());
|
||||
this->m_underlyingType = other.m_underlyingType->clone();
|
||||
}
|
||||
|
||||
~ASTNodeEnum() override {
|
||||
for (auto &[name, expr] : this->m_entries)
|
||||
delete expr;
|
||||
delete this->m_underlyingType;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeEnum(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::pair<std::string, ASTNode*>>& getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, ASTNode* expression) { this->m_entries.emplace_back(name, expression); }
|
||||
|
||||
[[nodiscard]] const ASTNode *getUnderlyingType() const { return this->m_underlyingType; }
|
||||
|
||||
const Token::TypeToken::Type& getUnderlyingType() const { return this->m_type; }
|
||||
std::vector<std::pair<u64, std::string>>& getValues() { return this->m_values; }
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name;
|
||||
std::vector<std::pair<u64, std::string>> m_values;
|
||||
std::vector<std::pair<std::string, ASTNode*>> m_entries;
|
||||
ASTNode *m_underlyingType;
|
||||
};
|
||||
|
||||
class ASTNodeBitfield : public ASTNode {
|
||||
public:
|
||||
ASTNodeBitfield() : ASTNode() { }
|
||||
|
||||
ASTNodeBitfield(const ASTNodeBitfield &other) : ASTNode(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace_back(name, entry->clone());
|
||||
}
|
||||
|
||||
~ASTNodeBitfield() override {
|
||||
for (auto &[name, expr] : this->m_entries)
|
||||
delete expr;
|
||||
}
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeBitfield(*this);
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::pair<std::string, ASTNode*>>& getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, ASTNode* size) { this->m_entries.emplace_back(name, size); }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, ASTNode*>> m_entries;
|
||||
};
|
||||
|
||||
class ASTNodeRValue : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeRValue(std::vector<std::string> path) : ASTNode(), m_path(std::move(path)) { }
|
||||
|
||||
ASTNodeRValue(const ASTNodeRValue&) = default;
|
||||
|
||||
ASTNode* clone() override {
|
||||
return new ASTNodeRValue(*this);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& getPath() {
|
||||
return this->m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_path;
|
||||
};
|
||||
|
||||
}
|
@ -15,7 +15,7 @@ namespace hex::lang {
|
||||
|
||||
class Evaluator {
|
||||
public:
|
||||
Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess);
|
||||
Evaluator(prv::Provider* &provider, std::endian defaultDataEndian);
|
||||
|
||||
std::optional<std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
|
||||
|
||||
@ -24,18 +24,36 @@ namespace hex::lang {
|
||||
private:
|
||||
std::unordered_map<std::string, ASTNode*> m_types;
|
||||
prv::Provider* &m_provider;
|
||||
std::endian m_defaultDataEndianess;
|
||||
std::endian m_defaultDataEndian;
|
||||
u64 m_currOffset = 0;
|
||||
std::optional<std::endian> m_currEndian;
|
||||
std::vector<PatternData*> *m_currMembers = nullptr;
|
||||
|
||||
std::pair<u32, std::string> m_error;
|
||||
|
||||
std::pair<PatternData*, size_t> createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createBuiltInTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
using EvaluateError = std::pair<u32, std::string>;
|
||||
|
||||
[[noreturn]] static void throwEvaluateError(std::string_view error, u32 lineNumber) {
|
||||
throw EvaluateError(lineNumber, "Evaluator: " + std::string(error));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::endian getCurrentEndian() const {
|
||||
return this->m_currEndian.value_or(this->m_defaultDataEndian);
|
||||
}
|
||||
|
||||
ASTNodeIntegerLiteral* evaluateRValue(ASTNodeRValue *node);
|
||||
ASTNodeIntegerLiteral* evaluateOperator(ASTNodeIntegerLiteral *left, ASTNodeIntegerLiteral *right, Token::Operator op);
|
||||
ASTNodeIntegerLiteral* evaluateMathematicalExpression(ASTNodeNumericExpression *node);
|
||||
|
||||
PatternData* evaluateBuiltinType(ASTNodeBuiltinType *node);
|
||||
PatternData* evaluateStruct(ASTNodeStruct *node);
|
||||
PatternData* evaluateUnion(ASTNodeUnion *node);
|
||||
PatternData* evaluateEnum(ASTNodeEnum *node);
|
||||
PatternData* evaluateBitfield(ASTNodeBitfield *node);
|
||||
PatternData* evaluateType(ASTNodeTypeDecl *node);
|
||||
PatternData* evaluateVariable(ASTNodeVariableDecl *node);
|
||||
PatternData* evaluateArray(ASTNodeArrayVariableDecl *node);
|
||||
PatternData* evaluatePointer(ASTNodePointerVariableDecl *node);
|
||||
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,12 @@ namespace hex::lang {
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
|
||||
using LexerError = std::pair<u32, std::string>;
|
||||
|
||||
[[noreturn]] void throwLexerError(std::string_view error, u32 lineNumber) const {
|
||||
throw LexerError(lineNumber, "Lexer: " + std::string(error));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -1,16 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "token.hpp"
|
||||
#include "ast_node.hpp"
|
||||
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser();
|
||||
Parser() = default;
|
||||
~Parser() = default;
|
||||
|
||||
using TokenIter = std::vector<Token>::const_iterator;
|
||||
|
||||
@ -19,31 +26,138 @@ namespace hex::lang {
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
using ParseError = std::pair<u32, std::string>;
|
||||
|
||||
ParseError m_error;
|
||||
TokenIter m_curr;
|
||||
TokenIter m_originalPosition;
|
||||
|
||||
ASTNode* parseBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseBuiltinPointerVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypePointerVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseBuiltinArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypeArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseBuiltinVariableArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseCustomTypeVariableArrayDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parsePaddingDecl(TokenIter &curr);
|
||||
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
ASTNode* parseFreeCustomTypeVariableDecl(TokenIter &curr, bool hasEndianDef);
|
||||
std::unordered_map<std::string, ASTNode*> m_types;
|
||||
std::vector<TokenIter> m_matchedOptionals;
|
||||
|
||||
ASTNode* parseStruct(TokenIter &curr);
|
||||
ASTNode* parseUnion(TokenIter &curr);
|
||||
ASTNode* parseEnum(TokenIter &curr);
|
||||
ASTNode *parseBitField(TokenIter &curr);
|
||||
ASTNode *parseScope(TokenIter &curr);
|
||||
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr);
|
||||
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr);
|
||||
u32 getLineNumber(s32 index) const {
|
||||
return this->m_curr[index].lineNumber;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& getValue(s32 index) const {
|
||||
auto value = std::get_if<T>(&this->m_curr[index].value);
|
||||
|
||||
if (value == nullptr)
|
||||
throwParseError("failed to decode token. Invalid type.", getLineNumber(index));
|
||||
|
||||
return *value;
|
||||
}
|
||||
|
||||
Token::Type getType(s32 index) const {
|
||||
return this->m_curr[index].type;
|
||||
}
|
||||
|
||||
ASTNode* parseRValue(std::vector<std::string> &path);
|
||||
ASTNode* parseFactor();
|
||||
ASTNode* parseMultiplicativeExpression();
|
||||
ASTNode* parseAdditiveExpression();
|
||||
ASTNode* parseShiftExpression();
|
||||
ASTNode* parseBinaryAndExpression();
|
||||
ASTNode* parseBinaryXorExpression();
|
||||
ASTNode* parseBinaryOrExpression();
|
||||
ASTNode* parseMathematicalExpression();
|
||||
|
||||
ASTNode* parseType(s32 startIndex);
|
||||
ASTNode* parseUsingDeclaration();
|
||||
ASTNode* parsePadding();
|
||||
ASTNode* parseMemberVariable();
|
||||
ASTNode* parseMemberArrayVariable();
|
||||
ASTNode* parseMemberPointerVariable();
|
||||
ASTNode* parseStruct();
|
||||
ASTNode* parseUnion();
|
||||
ASTNode* parseEnum();
|
||||
ASTNode* parseBitfield();
|
||||
ASTNode* parseVariablePlacement();
|
||||
ASTNode* parseArrayVariablePlacement();
|
||||
ASTNode* parsePointerVariablePlacement();
|
||||
ASTNode* parseStatement();
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(Token::Type endTokenType, const auto value) {
|
||||
std::vector<ASTNode*> program;
|
||||
ScopeExit guard([&]{ for (auto &node : program) delete node; });
|
||||
|
||||
while (this->m_curr->type != endTokenType || (*this->m_curr) != value) {
|
||||
program.push_back(parseStatement());
|
||||
}
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
guard.release();
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
[[noreturn]] void throwParseError(std::string_view error, s32 token = -1) const {
|
||||
throw ParseError(this->m_curr[token].lineNumber, "Parser: " + std::string(error));
|
||||
}
|
||||
|
||||
/* Token consuming */
|
||||
|
||||
bool begin() {
|
||||
this->m_originalPosition = this->m_curr;
|
||||
this->m_matchedOptionals.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sequence() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sequence(Token::Type type, auto value, auto ... args) {
|
||||
if (!peek(type, value)) {
|
||||
this->m_curr = this->m_originalPosition;
|
||||
return false;
|
||||
}
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
if (!sequence(args...)) {
|
||||
this->m_curr = this->m_originalPosition;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool variant(Token::Type type1, auto value1, Token::Type type2, auto value2) {
|
||||
if (!peek(type1, value1)) {
|
||||
if (!peek(type2, value2)) {
|
||||
this->m_curr = this->m_originalPosition;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool optional(Token::Type type, auto value) {
|
||||
if (peek(type, value)) {
|
||||
this->m_matchedOptionals.push_back(this->m_curr);
|
||||
this->m_curr++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool peek(Token::Type type, auto value, s32 index = 0) {
|
||||
return this->m_curr[index].type == type && this->m_curr[index] == value;
|
||||
}
|
||||
|
||||
bool peekOptional(Token::Type type, auto value, u32 index = 0) {
|
||||
if (index >= this->m_matchedOptionals.size())
|
||||
return false;
|
||||
return peek(type, value, std::distance(this->m_curr, this->m_matchedOptionals[index]));
|
||||
}
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
|
||||
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes);
|
||||
};
|
||||
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
@ -9,7 +8,9 @@
|
||||
#include "providers/provider.hpp"
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
@ -33,11 +34,9 @@ namespace hex::lang {
|
||||
|
||||
class PatternData {
|
||||
public:
|
||||
enum class Type { Padding, Unsigned, Signed, Float, Character, String, Struct, Union, Array, Enum };
|
||||
|
||||
PatternData(Type type, u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: m_type(type), m_offset(offset), m_size(size), m_name(name), m_endianess(endianess), m_color(color) {
|
||||
constexpr u32 Palette[] = { 0x50b4771f, 0x500e7fff, 0x502ca02c, 0x502827d6, 0x50bd6794, 0x504b568c, 0x50c277e3, 0x507f7f7f, 0x5022bdbc, 0x50cfbe17 };
|
||||
PatternData(u64 offset, size_t size, u32 color = 0)
|
||||
: m_offset(offset), m_size(size), m_color(color) {
|
||||
constexpr u32 Palette[] = { 0x70b4771f, 0x700e7fff, 0x702ca02c, 0x702827d6, 0x70bd6794, 0x704b568c, 0x70c277e3, 0x707f7f7f, 0x7022bdbc, 0x70cfbe17 };
|
||||
|
||||
if (color != 0)
|
||||
return;
|
||||
@ -49,21 +48,25 @@ namespace hex::lang {
|
||||
}
|
||||
virtual ~PatternData() = default;
|
||||
|
||||
[[nodiscard]] Type getPatternType() const { return this->m_type; }
|
||||
virtual PatternData* clone() = 0;
|
||||
|
||||
[[nodiscard]] u64 getOffset() const { return this->m_offset; }
|
||||
[[nodiscard]] size_t getSize() const { return this->m_size; }
|
||||
|
||||
[[nodiscard]] const std::string& getName() const { return this->m_name; }
|
||||
void setName(std::string name) { this->m_name = name; }
|
||||
[[nodiscard]] const std::string& getVariableName() const { return this->m_variableName; }
|
||||
void setVariableName(std::string name) { this->m_variableName = std::move(name); }
|
||||
|
||||
[[nodiscard]] const std::string& getTypeName() const { return this->m_typeName; }
|
||||
void setTypeName(std::string name) { this->m_typeName = std::move(name); }
|
||||
|
||||
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
||||
void setColor(u32 color) { this->m_color = color; }
|
||||
|
||||
[[nodiscard]] std::endian getEndianess() const { return this->m_endianess; }
|
||||
void setEndianess(std::endian endianess) { this->m_endianess = endianess; }
|
||||
[[nodiscard]] std::endian getEndian() const { return this->m_endian; }
|
||||
void setEndian(std::endian endian) { this->m_endian = endian; }
|
||||
|
||||
virtual void createEntry(prv::Provider* &provider) = 0;
|
||||
virtual std::string getTypeName() = 0;
|
||||
[[nodiscard]] virtual std::string getFormattedName() const = 0;
|
||||
|
||||
virtual std::optional<u32> highlightBytes(size_t offset) {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()))
|
||||
@ -77,9 +80,9 @@ namespace hex::lang {
|
||||
static bool sortPatternDataTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, lang::PatternData* left, lang::PatternData* right) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getName() > right->getName();
|
||||
return left->getVariableName() > right->getVariableName();
|
||||
else
|
||||
return left->getName() < right->getName();
|
||||
return left->getVariableName() < right->getVariableName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
@ -100,9 +103,9 @@ namespace hex::lang {
|
||||
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||
|
||||
if (left->m_endianess != std::endian::native)
|
||||
if (left->m_endian != std::endian::native)
|
||||
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
||||
if (right->m_endianess != std::endian::native)
|
||||
if (right->m_endian != std::endian::native)
|
||||
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
@ -129,38 +132,38 @@ namespace hex::lang {
|
||||
static void resetPalette() { PatternData::s_paletteOffset = 0; }
|
||||
|
||||
protected:
|
||||
void createDefaultEntry(std::string value) {
|
||||
void createDefaultEntry(std::string_view value) const {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
|
||||
ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
Region selectRegion = { this->getOffset(), this->getSize() };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", this->getName().c_str());
|
||||
ImGui::Text("%s", this->getVariableName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::Text("0x%04llx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getFormattedName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", value.c_str());
|
||||
ImGui::Text("%s", value.data());
|
||||
}
|
||||
|
||||
protected:
|
||||
std::endian m_endianess = std::endian::native;
|
||||
std::endian m_endian = std::endian::native;
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
u64 m_offset;
|
||||
size_t m_size;
|
||||
|
||||
u32 m_color;
|
||||
std::string m_name;
|
||||
std::string m_variableName;
|
||||
std::string m_typeName;
|
||||
|
||||
static inline u8 s_paletteOffset = 0;
|
||||
|
||||
@ -168,41 +171,49 @@ namespace hex::lang {
|
||||
|
||||
class PatternDataPadding : public PatternData {
|
||||
public:
|
||||
PatternDataPadding(u64 offset, size_t size) : PatternData(Type::Padding, offset, size, "", { }, 0x00FFFFFF) { }
|
||||
PatternDataPadding(u64 offset, size_t size) : PatternData(offset, size, 0x00FFFFFF) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataPadding(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataPointer : public PatternData {
|
||||
public:
|
||||
PatternDataPointer(u64 offset, size_t size, const std::string &name, PatternData *pointedAt, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Unsigned, offset, size, name, endianess, color), m_pointedAt(pointedAt) {
|
||||
this->m_pointedAt->setName("*" + this->m_pointedAt->getName());
|
||||
PatternDataPointer(u64 offset, size_t size, PatternData *pointedAt, u32 color = 0)
|
||||
: PatternData(offset, size, color), m_pointedAt(pointedAt) {
|
||||
this->m_pointedAt->setVariableName("*" + this->m_pointedAt->getVariableName());
|
||||
}
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataPointer(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::Text("0x%04llx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->getTypeName().c_str());
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s*", this->m_pointedAt->getFormattedName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("*(0x%0*llx)", this->getSize() * 2, data);
|
||||
ImGui::Text("*(0x%llx)", data);
|
||||
|
||||
if (open) {
|
||||
this->m_pointedAt->createEntry(provider);
|
||||
@ -211,7 +222,7 @@ namespace hex::lang {
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::optional<u32> highlightBytes(size_t offset) {
|
||||
std::optional<u32> highlightBytes(size_t offset) override {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()))
|
||||
return this->getColor();
|
||||
else if (auto color = this->m_pointedAt->highlightBytes(offset); color.has_value())
|
||||
@ -220,7 +231,7 @@ namespace hex::lang {
|
||||
return { };
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "Pointer";
|
||||
}
|
||||
|
||||
@ -230,18 +241,22 @@ namespace hex::lang {
|
||||
|
||||
class PatternDataUnsigned : public PatternData {
|
||||
public:
|
||||
PatternDataUnsigned(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Unsigned, offset, size, name, endianess, color) { }
|
||||
PatternDataUnsigned(u64 offset, size_t size, u32 color = 0)
|
||||
: PatternData(offset, size, color) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataUnsigned(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
this->createDefaultEntry(hex::format("%lu (0x%0*lx)", data, this->getSize() * 2, data));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "u8";
|
||||
case 2: return "u16";
|
||||
@ -255,20 +270,24 @@ namespace hex::lang {
|
||||
|
||||
class PatternDataSigned : public PatternData {
|
||||
public:
|
||||
PatternDataSigned(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Signed, offset, size, name, endianess, color) { }
|
||||
PatternDataSigned(u64 offset, size_t size, u32 color = 0)
|
||||
: PatternData(offset, size, color) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataSigned(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->m_endianess);
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
s64 signedData = signedData = hex::signExtend(data, this->getSize(), 64);
|
||||
s64 signedData = hex::signExtend(data, this->getSize(), 64);
|
||||
|
||||
this->createDefaultEntry(hex::format("%ld (0x%0*lx)", signedData, this->getSize() * 2, data));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "s8";
|
||||
case 2: return "s16";
|
||||
@ -282,21 +301,25 @@ namespace hex::lang {
|
||||
|
||||
class PatternDataFloat : public PatternData {
|
||||
public:
|
||||
PatternDataFloat(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Float, offset, size, name, endianess, color) { }
|
||||
PatternDataFloat(u64 offset, size_t size, u32 color = 0)
|
||||
: PatternData(offset, size, color) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataFloat(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
double formatData = 0;
|
||||
if (this->getSize() == 4) {
|
||||
float data = 0;
|
||||
provider->read(this->getOffset(), &data, 4);
|
||||
data = hex::changeEndianess(data, 4, this->m_endianess);
|
||||
data = hex::changeEndianess(data, 4, this->getEndian());
|
||||
|
||||
formatData = data;
|
||||
} else if (this->getSize() == 8) {
|
||||
double data = 0;
|
||||
provider->read(this->getOffset(), &data, 8);
|
||||
data = hex::changeEndianess(data, 8, this->m_endianess);
|
||||
data = hex::changeEndianess(data, 8, this->getEndian());
|
||||
|
||||
formatData = data;
|
||||
}
|
||||
@ -304,7 +327,7 @@ namespace hex::lang {
|
||||
this->createDefaultEntry(hex::format("%f (0x%0*lx)", formatData, this->getSize() * 2, formatData));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 4: return "float";
|
||||
case 8: return "double";
|
||||
@ -315,8 +338,12 @@ namespace hex::lang {
|
||||
|
||||
class PatternDataCharacter : public PatternData {
|
||||
public:
|
||||
PatternDataCharacter(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Character, offset, size, name, endianess, color) { }
|
||||
explicit PatternDataCharacter(u64 offset, u32 color = 0)
|
||||
: PatternData(offset, 1, color) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataCharacter(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
char character;
|
||||
@ -325,15 +352,19 @@ namespace hex::lang {
|
||||
this->createDefaultEntry(hex::format("'%c'", character));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "Character";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataString : public PatternData {
|
||||
public:
|
||||
PatternDataString(u64 offset, size_t size, const std::string &name, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::String, offset, size, name, endianess, color) { }
|
||||
PatternDataString(u64 offset, size_t size, u32 color = 0)
|
||||
: PatternData(offset, size, color) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataString(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
std::vector<u8> buffer(this->getSize() + 1, 0x00);
|
||||
@ -343,15 +374,24 @@ namespace hex::lang {
|
||||
this->createDefaultEntry(hex::format("\"%s\"", makeDisplayable(buffer.data(), this->getSize()).c_str()));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "String";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataArray : public PatternData {
|
||||
public:
|
||||
PatternDataArray(u64 offset, size_t size, const std::string &name, std::endian endianess, const std::vector<PatternData*> & entries, u32 color = 0)
|
||||
: PatternData(Type::Array, offset, size, name, endianess, color), m_entries(entries) { }
|
||||
PatternDataArray(u64 offset, size_t size, std::vector<PatternData*> entries, u32 color = 0)
|
||||
: PatternData(offset, size, color), m_entries(std::move(entries)) { }
|
||||
|
||||
PatternDataArray(const PatternDataArray &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) {
|
||||
for (const auto &entry : other.m_entries)
|
||||
this->m_entries.push_back(entry->clone());
|
||||
}
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataArray(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
if (this->m_entries.empty())
|
||||
@ -359,13 +399,13 @@ namespace hex::lang {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::Text("0x%04llx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), "%s", this->m_entries[0]->getTypeName().c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
@ -396,7 +436,7 @@ namespace hex::lang {
|
||||
return { };
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return this->m_entries[0]->getTypeName() + "[" + std::to_string(this->m_entries.size()) + "]";
|
||||
}
|
||||
|
||||
@ -406,20 +446,29 @@ namespace hex::lang {
|
||||
|
||||
class PatternDataStruct : public PatternData {
|
||||
public:
|
||||
PatternDataStruct(u64 offset, size_t size, const std::string &name, std::endian endianess, const std::string &structName, const std::vector<PatternData*> & members, u32 color = 0)
|
||||
: PatternData(Type::Struct, offset, size, name, endianess, color), m_structName(structName), m_members(members), m_sortedMembers(members) { }
|
||||
PatternDataStruct(u64 offset, size_t size, const std::vector<PatternData*> & members, u32 color = 0)
|
||||
: PatternData(offset, size, color), m_members(members), m_sortedMembers(members) { }
|
||||
|
||||
PatternDataStruct(const PatternDataStruct &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) {
|
||||
for (const auto &member : other.m_members)
|
||||
this->m_members.push_back(member->clone());
|
||||
}
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataStruct(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::Text("0x%04llx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "struct"); ImGui::SameLine(); ImGui::Text("%s", this->m_structName.c_str());
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "struct"); ImGui::SameLine(); ImGui::Text("%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
@ -452,32 +501,44 @@ namespace hex::lang {
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "struct " + this->m_structName;
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "struct " + PatternData::getTypeName();
|
||||
}
|
||||
|
||||
const auto& getMembers() const {
|
||||
return this->m_members;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_structName;
|
||||
std::vector<PatternData*> m_members;
|
||||
std::vector<PatternData*> m_sortedMembers;
|
||||
};
|
||||
|
||||
class PatternDataUnion : public PatternData {
|
||||
public:
|
||||
PatternDataUnion(u64 offset, size_t size, const std::string &name, const std::string &unionName, const std::vector<PatternData*> & members, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Union, offset, size, name, endianess, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { }
|
||||
PatternDataUnion(u64 offset, size_t size, const std::vector<PatternData*> & members, u32 color = 0)
|
||||
: PatternData(offset, size, color), m_members(members), m_sortedMembers(members) { }
|
||||
|
||||
PatternDataUnion(const PatternDataUnion &other) : PatternData(other.getOffset(), other.getSize(), other.getColor()) {
|
||||
for (const auto &member : other.m_members)
|
||||
this->m_members.push_back(member->clone());
|
||||
}
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataUnion(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::Text("0x%04llx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "union"); ImGui::SameLine(); ImGui::Text("%s", this->m_unionName.c_str());
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "union"); ImGui::SameLine(); ImGui::Text("%s", PatternData::getTypeName().c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
@ -511,27 +572,34 @@ namespace hex::lang {
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "union " + this->m_unionName;
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "union " + PatternData::getTypeName();;
|
||||
}
|
||||
|
||||
const auto& getMembers() const {
|
||||
return this->m_members;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_unionName;
|
||||
std::vector<PatternData*> m_members;
|
||||
std::vector<PatternData*> m_sortedMembers;
|
||||
};
|
||||
|
||||
class PatternDataEnum : public PatternData {
|
||||
public:
|
||||
PatternDataEnum(u64 offset, size_t size, const std::string &name, const std::string &enumName, std::vector<std::pair<u64, std::string>> enumValues, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Enum, offset, size, name, endianess, color), m_enumName(enumName), m_enumValues(enumValues) { }
|
||||
PatternDataEnum(u64 offset, size_t size, std::vector<std::pair<u64, std::string>> enumValues, u32 color = 0)
|
||||
: PatternData(offset, size, color), m_enumValues(std::move(enumValues)) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataEnum(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
|
||||
value = hex::changeEndianess(value, this->getSize(), this->getEndian());
|
||||
|
||||
std::string valueString = this->m_enumName + "::";
|
||||
std::string valueString = PatternData::getTypeName() + "::";
|
||||
|
||||
bool foundValue = false;
|
||||
for (auto &[entryValue, entryName] : this->m_enumValues) {
|
||||
@ -546,69 +614,84 @@ namespace hex::lang {
|
||||
valueString += "???";
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
Region selectRegion = { this->getOffset(), this->getSize() };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", this->getName().c_str());
|
||||
ImGui::Text("%s", this->getVariableName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::Text("0x%04llx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "enum"); ImGui::SameLine(); ImGui::Text("%s", this->m_enumName.c_str());
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "enum"); ImGui::SameLine(); ImGui::Text("%s", PatternData::getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", hex::format("%s (0x%0*lx)", valueString.c_str(), this->getSize() * 2, value).c_str());
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "enum " + this->m_enumName;
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "enum " + PatternData::getTypeName();
|
||||
}
|
||||
|
||||
const auto& getEnumValues() const {
|
||||
return this->m_enumValues;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_enumName;
|
||||
std::vector<std::pair<u64, std::string>> m_enumValues;
|
||||
};
|
||||
|
||||
class PatternDataBitfield : public PatternData {
|
||||
public:
|
||||
PatternDataBitfield(u64 offset, size_t size, const std::string &name, const std::string &bitfieldName, std::vector<std::pair<std::string, size_t>> fields, std::endian endianess, u32 color = 0)
|
||||
: PatternData(Type::Enum, offset, size, name, endianess, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
|
||||
PatternDataBitfield(u64 offset, size_t size, std::vector<std::pair<std::string, size_t>> fields, u32 color = 0)
|
||||
: PatternData(offset, size, color), m_fields(std::move(fields)) { }
|
||||
|
||||
PatternData* clone() override {
|
||||
return new PatternDataBitfield(*this);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
value = hex::changeEndianess(value, this->getSize(), this->m_endianess);
|
||||
std::vector<u8> value(this->getSize(), 0);
|
||||
provider->read(this->getOffset(), &value[0], value.size());
|
||||
|
||||
if (this->m_endian == std::endian::big)
|
||||
std::reverse(value.begin(), value.end());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
bool open = ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::Text("0x%04llx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "bitfield"); ImGui::SameLine(); ImGui::Text("%s", this->m_bitfieldName.c_str());
|
||||
ImGui::TextColored(ImColor(0xFFD69C56), "bitfield"); ImGui::SameLine(); ImGui::Text("%s", PatternData::getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("{ %llx }", value);
|
||||
|
||||
std::string valueString = "{ ";
|
||||
for (u64 i = 0; i < value.size(); i++)
|
||||
valueString += hex::format("%02x ", value[i]);
|
||||
valueString += "}";
|
||||
|
||||
ImGui::TextUnformatted(valueString.c_str());
|
||||
|
||||
if (open) {
|
||||
u16 bitOffset = 0;
|
||||
for (auto &[entryName, entrySize] : this->m_fields) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TreeNodeEx(this->getVariableName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", entryName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3) - 1);
|
||||
ImGui::Text("0x%08llx : 0x%08llx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3));
|
||||
ImGui::TableNextColumn();
|
||||
if (entrySize == 1)
|
||||
ImGui::Text("%llu bit", entrySize);
|
||||
@ -617,7 +700,11 @@ namespace hex::lang {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", entryName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llx", hex::extract((bitOffset + entrySize) - 1, bitOffset, value));
|
||||
{
|
||||
u128 fieldValue = 0;
|
||||
std::memcpy(&fieldValue, value.data() + (bitOffset / 8), (entrySize / 8) + 1);
|
||||
ImGui::Text("%llx", hex::extract((bitOffset + entrySize) - 1 - ((bitOffset / 8) * 8), bitOffset - ((bitOffset / 8) * 8), fieldValue));
|
||||
}
|
||||
bitOffset += entrySize;
|
||||
}
|
||||
|
||||
@ -626,12 +713,15 @@ namespace hex::lang {
|
||||
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "bitfield " + this->m_bitfieldName;
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "bitfield " + PatternData::getTypeName();
|
||||
}
|
||||
|
||||
const auto& getFields() const {
|
||||
return this->m_fields;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_bitfieldName;
|
||||
std::vector<std::pair<std::string, size_t>> m_fields;
|
||||
};
|
||||
|
||||
|
@ -18,12 +18,18 @@ namespace hex::lang {
|
||||
|
||||
std::optional<std::string> preprocess(const std::string& code, bool initialRun = true);
|
||||
|
||||
void addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
|
||||
void addPragmaHandler(const std::string &pragmaType, const std::function<bool(const std::string&)> &function);
|
||||
void addDefaultPragmaHandlers();
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
using PreprocessorError = std::pair<u32, std::string>;
|
||||
|
||||
[[noreturn]] void throwPreprocessorError(std::string_view error, u32 lineNumber) const {
|
||||
throw PreprocessorError(lineNumber, "Preprocessor: " + std::string(error));
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::function<bool(std::string)>> m_pragmaHandlers;
|
||||
|
||||
std::set<std::pair<std::string, std::string>> m_defines;
|
||||
|
@ -2,86 +2,209 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "helpers/utils.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
struct Token {
|
||||
class Token {
|
||||
public:
|
||||
enum class Type : u64 {
|
||||
Keyword,
|
||||
Type,
|
||||
ValueType,
|
||||
Operator,
|
||||
Integer,
|
||||
Identifier,
|
||||
Separator
|
||||
};
|
||||
|
||||
enum class Keyword {
|
||||
Struct,
|
||||
Union,
|
||||
Using,
|
||||
Enum,
|
||||
Bitfield,
|
||||
LittleEndian,
|
||||
BigEndian,
|
||||
};
|
||||
|
||||
enum class Operator {
|
||||
AtDeclaration,
|
||||
Assignment,
|
||||
Inherit,
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
ShiftLeft,
|
||||
ShiftRight,
|
||||
BitOr,
|
||||
BitAnd,
|
||||
BitXor
|
||||
};
|
||||
|
||||
enum class ValueType {
|
||||
Unsigned8Bit = 0x10,
|
||||
Signed8Bit = 0x11,
|
||||
Unsigned16Bit = 0x20,
|
||||
Signed16Bit = 0x21,
|
||||
Unsigned32Bit = 0x40,
|
||||
Signed32Bit = 0x41,
|
||||
Unsigned64Bit = 0x80,
|
||||
Signed64Bit = 0x81,
|
||||
Unsigned128Bit = 0x100,
|
||||
Signed128Bit = 0x101,
|
||||
Character = 0x13,
|
||||
Float = 0x42,
|
||||
Double = 0x82,
|
||||
CustomType = 0x00,
|
||||
Padding = 0x1F,
|
||||
|
||||
Unsigned = 0xFF00,
|
||||
Signed = 0xFF01,
|
||||
FloatingPoint = 0xFF02,
|
||||
Integer = 0xFF03,
|
||||
Any = 0xFFFF
|
||||
};
|
||||
|
||||
enum class Separator {
|
||||
RoundBracketOpen,
|
||||
RoundBracketClose,
|
||||
CurlyBracketOpen,
|
||||
CurlyBracketClose,
|
||||
SquareBracketOpen,
|
||||
SquareBracketClose,
|
||||
Comma,
|
||||
Dot,
|
||||
EndOfExpression,
|
||||
ScopeOpen,
|
||||
ScopeClose,
|
||||
ArrayOpen,
|
||||
ArrayClose,
|
||||
Separator,
|
||||
EndOfProgram
|
||||
} type;
|
||||
};
|
||||
|
||||
struct KeywordToken {
|
||||
enum class Keyword {
|
||||
Struct,
|
||||
Union,
|
||||
Using,
|
||||
Enum,
|
||||
Bitfield,
|
||||
LittleEndian,
|
||||
BigEndian
|
||||
} keyword;
|
||||
} keywordToken;
|
||||
struct IdentifierToken {
|
||||
std::string identifier;
|
||||
} identifierToken;
|
||||
struct OperatorToken {
|
||||
enum class Operator {
|
||||
AtDeclaration,
|
||||
Assignment,
|
||||
Inherit,
|
||||
Star
|
||||
} op;
|
||||
} operatorToken;
|
||||
struct IntegerToken {
|
||||
s128 integer;
|
||||
} integerToken;
|
||||
struct TypeToken {
|
||||
enum class Type {
|
||||
Unsigned8Bit = 0x10,
|
||||
Signed8Bit = 0x11,
|
||||
Unsigned16Bit = 0x20,
|
||||
Signed16Bit = 0x21,
|
||||
Unsigned32Bit = 0x40,
|
||||
Signed32Bit = 0x41,
|
||||
Unsigned64Bit = 0x80,
|
||||
Signed64Bit = 0x81,
|
||||
Unsigned128Bit = 0x100,
|
||||
Signed128Bit = 0x101,
|
||||
Float = 0x42,
|
||||
Double = 0x82,
|
||||
CustomType = 0x00,
|
||||
Padding = 0x1F
|
||||
} type;
|
||||
} typeToken;
|
||||
using ValueTypes = std::variant<Keyword, std::string, Operator, s128, ValueType, Separator>;
|
||||
|
||||
u32 lineNumber;
|
||||
Token(Type type, auto value, u32 lineNumber) : type(type), value(value), lineNumber(lineNumber) {
|
||||
|
||||
[[nodiscard]] constexpr static inline bool isUnsigned(const TypeToken::Type type) {
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline bool isUnsigned(const ValueType type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x00;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline bool isSigned(const TypeToken::Type type) {
|
||||
[[nodiscard]] constexpr static inline bool isSigned(const ValueType type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x01;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline bool isFloatingPoint(const TypeToken::Type type) {
|
||||
[[nodiscard]] constexpr static inline bool isFloatingPoint(const ValueType type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x02;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static inline u32 getTypeSize(const TypeToken::Type type) {
|
||||
[[nodiscard]] constexpr static inline u32 getTypeSize(const ValueType type) {
|
||||
return static_cast<u32>(type) >> 4;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr static auto getTypeName(const lang::Token::ValueType type) {
|
||||
switch (type) {
|
||||
case ValueType::Signed8Bit: return "s8";
|
||||
case ValueType::Signed16Bit: return "s16";
|
||||
case ValueType::Signed32Bit: return "s32";
|
||||
case ValueType::Signed64Bit: return "s64";
|
||||
case ValueType::Signed128Bit: return "s128";
|
||||
case ValueType::Unsigned8Bit: return "u8";
|
||||
case ValueType::Unsigned16Bit: return "u16";
|
||||
case ValueType::Unsigned32Bit: return "u32";
|
||||
case ValueType::Unsigned64Bit: return "u64";
|
||||
case ValueType::Unsigned128Bit: return "u128";
|
||||
case ValueType::Float: return "float";
|
||||
case ValueType::Double: return "double";
|
||||
case ValueType::Character: return "char";
|
||||
default: return "< ??? >";
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const ValueTypes &other) const {
|
||||
if (this->type == Type::Integer || this->type == Type::Identifier)
|
||||
return true;
|
||||
else if (this->type == Type::ValueType) {
|
||||
auto otherValueType = std::get_if<ValueType>(&other);
|
||||
auto valueType = std::get_if<ValueType>(&this->value);
|
||||
|
||||
if (otherValueType == nullptr) return false;
|
||||
if (valueType == nullptr) return false;
|
||||
|
||||
if (*otherValueType == *valueType)
|
||||
return true;
|
||||
else if (*otherValueType == ValueType::Any)
|
||||
return *valueType != ValueType::CustomType && *valueType != ValueType::Padding;
|
||||
else if (*otherValueType == ValueType::Unsigned)
|
||||
return isUnsigned(*valueType);
|
||||
else if (*otherValueType == ValueType::Signed)
|
||||
return isSigned(*valueType);
|
||||
else if (*otherValueType == ValueType::FloatingPoint)
|
||||
return isFloatingPoint(*valueType);
|
||||
else if (*otherValueType == ValueType::Integer)
|
||||
return isUnsigned(*valueType) || isSigned(*valueType);
|
||||
}
|
||||
else
|
||||
return other == this->value;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(const ValueTypes &other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
Type type;
|
||||
ValueTypes value;
|
||||
u32 lineNumber;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define COMPONENT(type, value) hex::lang::Token::Type::type, hex::lang::Token::type::value
|
||||
|
||||
#define KEYWORD_STRUCT COMPONENT(Keyword, Struct)
|
||||
#define KEYWORD_UNION COMPONENT(Keyword, Union)
|
||||
#define KEYWORD_USING COMPONENT(Keyword, Using)
|
||||
#define KEYWORD_ENUM COMPONENT(Keyword, Enum)
|
||||
#define KEYWORD_BITFIELD COMPONENT(Keyword, Bitfield)
|
||||
#define KEYWORD_LE COMPONENT(Keyword, LittleEndian)
|
||||
#define KEYWORD_BE COMPONENT(Keyword, BigEndian)
|
||||
|
||||
#define INTEGER hex::lang::Token::Type::Integer, 0xFFFF'FFFF'FFFF'FFFF
|
||||
#define IDENTIFIER hex::lang::Token::Type::Identifier, ""
|
||||
|
||||
#define OPERATOR_AT COMPONENT(Operator, AtDeclaration)
|
||||
#define OPERATOR_ASSIGNMENT COMPONENT(Operator, Assignment)
|
||||
#define OPERATOR_INHERIT COMPONENT(Operator, Inherit)
|
||||
#define OPERATOR_PLUS COMPONENT(Operator, Plus)
|
||||
#define OPERATOR_MINUS COMPONENT(Operator, Minus)
|
||||
#define OPERATOR_STAR COMPONENT(Operator, Star)
|
||||
#define OPERATOR_SLASH COMPONENT(Operator, Slash)
|
||||
#define OPERATOR_SHIFTLEFT COMPONENT(Operator, ShiftLeft)
|
||||
#define OPERATOR_SHIFTRIGHT COMPONENT(Operator, ShiftRight)
|
||||
#define OPERATOR_BITOR COMPONENT(Operator, BitOr)
|
||||
#define OPERATOR_BITAND COMPONENT(Operator, BitAnd)
|
||||
#define OPERATOR_BITXOR COMPONENT(Operator, BitXor)
|
||||
|
||||
#define VALUETYPE_CUSTOMTYPE COMPONENT(ValueType, CustomType)
|
||||
#define VALUETYPE_PADDING COMPONENT(ValueType, Padding)
|
||||
#define VALUETYPE_UNSIGNED COMPONENT(ValueType, Unsigned)
|
||||
#define VALUETYPE_SIGNED COMPONENT(ValueType, Signed)
|
||||
#define VALUETYPE_FLOATINGPOINT COMPONENT(ValueType, FloatingPoint)
|
||||
#define VALUETYPE_INTEGER COMPONENT(ValueType, Integer)
|
||||
#define VALUETYPE_ANY COMPONENT(ValueType, Any)
|
||||
|
||||
#define SEPARATOR_ROUNDBRACKETOPEN COMPONENT(Separator, RoundBracketOpen)
|
||||
#define SEPARATOR_ROUNDBRACKETCLOSE COMPONENT(Separator, RoundBracketClose)
|
||||
#define SEPARATOR_CURLYBRACKETOPEN COMPONENT(Separator, CurlyBracketOpen)
|
||||
#define SEPARATOR_CURLYBRACKETCLOSE COMPONENT(Separator, CurlyBracketClose)
|
||||
#define SEPARATOR_SQUAREBRACKETOPEN COMPONENT(Separator, SquareBracketOpen)
|
||||
#define SEPARATOR_SQUAREBRACKETCLOSE COMPONENT(Separator, SquareBracketClose)
|
||||
#define SEPARATOR_COMMA COMPONENT(Separator, Comma)
|
||||
#define SEPARATOR_DOT COMPONENT(Separator, Dot)
|
||||
#define SEPARATOR_ENDOFEXPRESSION COMPONENT(Separator, EndOfExpression)
|
||||
#define SEPARATOR_ENDOFPROGRAM COMPONENT(Separator, EndOfProgram)
|
@ -15,11 +15,19 @@ namespace hex::lang {
|
||||
Validator();
|
||||
|
||||
bool validate(const std::vector<ASTNode*>& ast);
|
||||
void printAST(const std::vector<ASTNode*>& ast);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
|
||||
using ValidatorError = std::pair<u32, std::string>;
|
||||
|
||||
[[noreturn]] void throwValidateError(std::string_view error, u32 lineNumber) const {
|
||||
throw ValidatorError(lineNumber, error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include "ImGuiFileBrowser.h"
|
||||
#include "TextEditor.h"
|
||||
|
@ -28,21 +28,44 @@
|
||||
// Make sure we break when derived_from is implemented in libc++. Then we can fix a compatibility version above
|
||||
#include <concepts>
|
||||
#endif
|
||||
// libcxx 12 still doesn't have derived_from implemented, as a result we need to define it ourself using clang built-ins.
|
||||
// libcxx 12 still doesn't have many default concepts implemented, as a result we need to define it ourself using clang built-ins.
|
||||
// [concept.derived] (patch from https://reviews.llvm.org/D74292)
|
||||
namespace hex {
|
||||
template<class _Dp, class _Bp>
|
||||
concept derived_from =
|
||||
__is_base_of(_Bp, _Dp) && __is_convertible_to(const volatile _Dp*, const volatile _Bp*);
|
||||
concept derived_from =
|
||||
__is_base_of(_Bp, _Dp) && __is_convertible_to(const volatile _Dp*, const volatile _Bp*);
|
||||
}
|
||||
|
||||
// [concepts.arithmetic]
|
||||
namespace hex {
|
||||
template<class _Tp>
|
||||
concept integral = __is_integral(_Tp);
|
||||
|
||||
template<class _Tp>
|
||||
concept signed_integral = integral<_Tp> && __is_signed(_Tp);
|
||||
|
||||
template<class _Tp>
|
||||
concept unsigned_integral = integral<_Tp> && !signed_integral<_Tp>;
|
||||
|
||||
template<class _Tp>
|
||||
concept floating_point = __is_floating_point(_Tp);
|
||||
}
|
||||
#else
|
||||
// Assume supported
|
||||
#include <concepts>
|
||||
namespace hex {
|
||||
using std::derived_from;
|
||||
|
||||
using std::integral;
|
||||
using std::signed_integral;
|
||||
using std::unsigned_integral;
|
||||
using std::floating_point;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define TOKEN_CONCAT_IMPL(x, y) x ## y
|
||||
#define TOKEN_CONCAT(x, y) TOKEN_CONCAT_IMPL(x, y)
|
||||
|
||||
namespace hex {
|
||||
|
||||
template<typename ... Args>
|
||||
@ -58,14 +81,15 @@ namespace hex {
|
||||
return std::string(buffer.data(), buffer.data() + size);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
|
||||
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
|
||||
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const hex::unsigned_integral auto &value) {
|
||||
std::remove_cvref_t<decltype(value)> mask = (std::numeric_limits<std::remove_cvref_t<decltype(value)>>::max() >> (((sizeof(value) * 8) - 1) - (from - to))) << to;
|
||||
return (value & mask) >> to;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
|
||||
u64 mask = 1LLU << (currWidth - 1);
|
||||
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
|
||||
template<std::integral T>
|
||||
[[nodiscard]] constexpr inline T signExtend(T value, u8 currWidth, u8 targetWidth) {
|
||||
T mask = 1LLU << (currWidth - 1);
|
||||
return (((value ^ mask) - mask) << ((sizeof(T) * 8) - targetWidth)) >> ((sizeof(T) * 8) - targetWidth);
|
||||
}
|
||||
|
||||
std::string toByteString(u64 bytes);
|
||||
@ -123,6 +147,7 @@ namespace hex {
|
||||
|
||||
std::vector<u8> readFile(std::string_view path);
|
||||
|
||||
#define SCOPE_EXIT(func) ScopeExit TOKEN_CONCAT(scopeGuard, __COUNTER__)([&] { func })
|
||||
class ScopeExit {
|
||||
public:
|
||||
ScopeExit(std::function<void()> func) : m_func(func) {}
|
||||
|
@ -23,4 +23,52 @@ extern char **mainArgv;
|
||||
#define MAGIC_PATH_SEPARATOR ";"
|
||||
#else
|
||||
#define MAGIC_PATH_SEPARATOR ":"
|
||||
#endif
|
||||
|
||||
template<>
|
||||
struct std::is_integral<u128> : public std::true_type { };
|
||||
template<>
|
||||
struct std::is_integral<s128> : public std::true_type { };
|
||||
template<>
|
||||
struct std::is_signed<s128> : public std::true_type { };
|
||||
|
||||
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION <= 12000
|
||||
#if __has_include(<concepts>)
|
||||
// Make sure we break when derived_from is implemented in libc++. Then we can fix a compatibility version above
|
||||
#include <concepts>
|
||||
#endif
|
||||
// libcxx 12 still doesn't have many default concepts implemented, as a result we need to define it ourself using clang built-ins.
|
||||
// [concept.derived] (patch from https://reviews.llvm.org/D74292)
|
||||
namespace hex {
|
||||
template<class _Dp, class _Bp>
|
||||
concept derived_from =
|
||||
__is_base_of(_Bp, _Dp) && __is_convertible_to(const volatile _Dp*, const volatile _Bp*);
|
||||
}
|
||||
|
||||
// [concepts.arithmetic] (patch from https://reviews.llvm.org/D88131)
|
||||
namespace hex {
|
||||
template<class _Tp>
|
||||
concept integral = __is_integral(_Tp);
|
||||
|
||||
template<class _Tp>
|
||||
concept signed_integral = integral<_Tp> && __is_signed(_Tp);
|
||||
|
||||
template<class _Tp>
|
||||
concept unsigned_integral = integral<_Tp> && !signed_integral<_Tp>;
|
||||
|
||||
template<class _Tp>
|
||||
concept floating_point = __is_floating_point(_Tp);
|
||||
}
|
||||
#else
|
||||
// Assume supported
|
||||
#include <concepts>
|
||||
|
||||
namespace hex {
|
||||
using std::derived_from;
|
||||
|
||||
using std::integral;
|
||||
using std::signed_integral;
|
||||
using std::unsigned_integral;
|
||||
using std::floating_point;
|
||||
}
|
||||
#endif
|
@ -3,428 +3,435 @@
|
||||
#include "lang/token.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Evaluator::Evaluator(prv::Provider* &provider, std::endian defaultDataEndianess) : m_provider(provider), m_defaultDataEndianess(defaultDataEndianess) {
|
||||
|
||||
Evaluator::Evaluator(prv::Provider* &provider, std::endian defaultDataEndian)
|
||||
: m_provider(provider), m_defaultDataEndian(defaultDataEndian) {
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> members;
|
||||
ASTNodeIntegerLiteral* Evaluator::evaluateRValue(ASTNodeRValue *node) {
|
||||
|
||||
auto structNode = static_cast<ASTNodeStruct*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
const std::vector<PatternData*>* currMembers = this->m_currMembers;
|
||||
|
||||
if (structNode == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
PatternData *currPattern = nullptr;
|
||||
for (const auto &identifier : node->getPath()) {
|
||||
if (auto structPattern = dynamic_cast<PatternDataStruct*>(currPattern); structPattern != nullptr)
|
||||
currMembers = &structPattern->getMembers();
|
||||
else if (auto unionPattern = dynamic_cast<PatternDataUnion*>(currPattern); unionPattern != nullptr)
|
||||
currMembers = &unionPattern->getMembers();
|
||||
else if (currPattern != nullptr)
|
||||
throwEvaluateError("tried to access member of a non-struct/union type", node->getLineNumber());
|
||||
|
||||
size_t structSize = 0;
|
||||
for (const auto &node : structNode->getNodes()) {
|
||||
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
|
||||
auto candidate = std::find_if(currMembers->begin(), currMembers->end(), [&](auto member) {
|
||||
return member->getVariableName() == identifier;
|
||||
});
|
||||
|
||||
u64 memberOffset = 0;
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
this->m_provider->read(offset + structSize, &memberOffset, member->getPointerSize().value());
|
||||
|
||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
}
|
||||
if (candidate != currMembers->end())
|
||||
currPattern = *candidate;
|
||||
else
|
||||
memberOffset = offset + structSize;
|
||||
|
||||
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
|
||||
|
||||
PatternData *pattern = nullptr;
|
||||
size_t memberSize = 0;
|
||||
|
||||
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
|
||||
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
|
||||
&& member->getArraySize() > 1) {
|
||||
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySize() > 1 || member->getVariableType() == Token::TypeToken::Type::Padding) {
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySizeVariable().has_value()) {
|
||||
std::optional<size_t> arraySize;
|
||||
|
||||
|
||||
for (auto &prevMember : members) {
|
||||
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
|
||||
u64 value = 0;
|
||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||
|
||||
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
|
||||
|
||||
arraySize = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arraySize.has_value()) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
if (arraySize.value() == 0)
|
||||
continue;
|
||||
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
||||
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||
}
|
||||
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
members.push_back(new PatternDataPointer(offset + structSize, member->getPointerSize().value(), member->getVariableName(), pattern, member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess))));
|
||||
structSize += member->getPointerSize().value();
|
||||
}
|
||||
else {
|
||||
members.push_back(pattern);
|
||||
structSize += memberSize;
|
||||
}
|
||||
throwEvaluateError(hex::format("could not find identifier '%s'", identifier.c_str()), node->getLineNumber());
|
||||
}
|
||||
|
||||
return { new PatternDataStruct(offset, structSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess), structNode->getName(), members, 0x00FFFFFF), structSize };
|
||||
if (auto unsignedPattern = dynamic_cast<PatternDataUnsigned*>(currPattern); unsignedPattern != nullptr) {
|
||||
s128 value = 0;
|
||||
this->m_provider->read(unsignedPattern->getOffset(), &value, unsignedPattern->getSize());
|
||||
return new ASTNodeIntegerLiteral(value, Token::ValueType::Signed128Bit);
|
||||
} else if (auto signedPattern = dynamic_cast<PatternDataSigned*>(currPattern); signedPattern != nullptr) {
|
||||
s128 value = 0;
|
||||
this->m_provider->read(signedPattern->getOffset(), &value, signedPattern->getSize());
|
||||
return new ASTNodeIntegerLiteral(signExtend(value, signedPattern->getSize() * 8, 128), Token::ValueType::Signed128Bit);
|
||||
} else
|
||||
throwEvaluateError("tried to use non-integer value in numeric expression", node->getLineNumber());
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> members;
|
||||
ASTNodeIntegerLiteral* Evaluator::evaluateOperator(ASTNodeIntegerLiteral *left, ASTNodeIntegerLiteral *right, Token::Operator op) {
|
||||
return std::visit([&](auto &&leftValue, auto &&rightValue) -> ASTNodeIntegerLiteral* {
|
||||
|
||||
auto unionNode = static_cast<ASTNodeUnion*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
auto newType = [&] {
|
||||
#define CHECK_TYPE(type) if (left->getType() == (type) || right->getType() == (type)) return (type)
|
||||
#define DEFAULT_TYPE(type) return (type)
|
||||
|
||||
if (unionNode == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
CHECK_TYPE(Token::ValueType::Double);
|
||||
CHECK_TYPE(Token::ValueType::Float);
|
||||
CHECK_TYPE(Token::ValueType::Unsigned128Bit);
|
||||
CHECK_TYPE(Token::ValueType::Signed128Bit);
|
||||
CHECK_TYPE(Token::ValueType::Unsigned64Bit);
|
||||
CHECK_TYPE(Token::ValueType::Signed64Bit);
|
||||
CHECK_TYPE(Token::ValueType::Unsigned32Bit);
|
||||
CHECK_TYPE(Token::ValueType::Signed32Bit);
|
||||
CHECK_TYPE(Token::ValueType::Unsigned16Bit);
|
||||
CHECK_TYPE(Token::ValueType::Signed16Bit);
|
||||
CHECK_TYPE(Token::ValueType::Unsigned8Bit);
|
||||
CHECK_TYPE(Token::ValueType::Signed8Bit);
|
||||
CHECK_TYPE(Token::ValueType::Character);
|
||||
DEFAULT_TYPE(Token::ValueType::Signed32Bit);
|
||||
|
||||
size_t unionSize = 0;
|
||||
for (const auto &node : unionNode->getNodes()) {
|
||||
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
|
||||
#undef CHECK_TYPE
|
||||
#undef DEFAULT_TYPE
|
||||
}();
|
||||
|
||||
u64 memberOffset = 0;
|
||||
switch (op) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeIntegerLiteral(leftValue + rightValue, newType);
|
||||
case Token::Operator::Minus:
|
||||
return new ASTNodeIntegerLiteral(leftValue - rightValue, newType);
|
||||
case Token::Operator::Star:
|
||||
return new ASTNodeIntegerLiteral(leftValue * rightValue, newType);
|
||||
case Token::Operator::Slash:
|
||||
return new ASTNodeIntegerLiteral(leftValue / rightValue, newType);
|
||||
case Token::Operator::ShiftLeft:
|
||||
return new ASTNodeIntegerLiteral(leftValue << rightValue, newType);
|
||||
case Token::Operator::ShiftRight:
|
||||
return new ASTNodeIntegerLiteral(leftValue >> rightValue, newType);
|
||||
case Token::Operator::BitAnd:
|
||||
return new ASTNodeIntegerLiteral(leftValue & rightValue, newType);
|
||||
case Token::Operator::BitXor:
|
||||
return new ASTNodeIntegerLiteral(leftValue ^ rightValue, newType);
|
||||
case Token::Operator::BitOr:
|
||||
return new ASTNodeIntegerLiteral(leftValue | rightValue, newType);
|
||||
default: throwEvaluateError("invalid operator used in mathematical expression", left->getLineNumber());
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
this->m_provider->read(offset + unionSize, &memberOffset, member->getPointerSize().value());
|
||||
|
||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), member->getEndianess().value_or(this->m_defaultDataEndianess));
|
||||
}
|
||||
|
||||
}, left->getValue(), right->getValue());
|
||||
}
|
||||
|
||||
ASTNodeIntegerLiteral* Evaluator::evaluateMathematicalExpression(ASTNodeNumericExpression *node) {
|
||||
ASTNodeIntegerLiteral *leftInteger, *rightInteger;
|
||||
|
||||
if (auto leftExprLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(node->getLeftOperand()); leftExprLiteral != nullptr)
|
||||
leftInteger = leftExprLiteral;
|
||||
else if (auto leftExprExpression = dynamic_cast<ASTNodeNumericExpression*>(node->getLeftOperand()); leftExprExpression != nullptr)
|
||||
leftInteger = evaluateMathematicalExpression(leftExprExpression);
|
||||
else if (auto leftExprRvalue = dynamic_cast<ASTNodeRValue*>(node->getLeftOperand()); leftExprRvalue != nullptr)
|
||||
leftInteger = evaluateRValue(leftExprRvalue);
|
||||
else
|
||||
throwEvaluateError("invalid expression. Expected integer literal", node->getLineNumber());
|
||||
|
||||
if (auto rightExprLiteral = dynamic_cast<ASTNodeIntegerLiteral*>(node->getRightOperand()); rightExprLiteral != nullptr)
|
||||
rightInteger = rightExprLiteral;
|
||||
else if (auto rightExprExpression = dynamic_cast<ASTNodeNumericExpression*>(node->getRightOperand()); rightExprExpression != nullptr)
|
||||
rightInteger = evaluateMathematicalExpression(rightExprExpression);
|
||||
else if (auto rightExprRvalue = dynamic_cast<ASTNodeRValue*>(node->getRightOperand()); rightExprRvalue != nullptr)
|
||||
rightInteger = evaluateRValue(rightExprRvalue);
|
||||
else
|
||||
throwEvaluateError("invalid expression. Expected integer literal", node->getLineNumber());
|
||||
|
||||
return evaluateOperator(leftInteger, rightInteger, node->getOperator());
|
||||
}
|
||||
|
||||
PatternData* Evaluator::evaluateBuiltinType(ASTNodeBuiltinType *node) {
|
||||
auto &type = node->getType();
|
||||
auto typeSize = Token::getTypeSize(type);
|
||||
|
||||
PatternData *pattern;
|
||||
|
||||
if (type == Token::ValueType::Character)
|
||||
pattern = new PatternDataCharacter(this->m_currOffset);
|
||||
else if (Token::isUnsigned(type))
|
||||
pattern = new PatternDataUnsigned(this->m_currOffset, typeSize);
|
||||
else if (Token::isSigned(type))
|
||||
pattern = new PatternDataSigned(this->m_currOffset, typeSize);
|
||||
else if (Token::isFloatingPoint(type))
|
||||
pattern = new PatternDataFloat(this->m_currOffset, typeSize);
|
||||
else
|
||||
throwEvaluateError("invalid builtin type", node->getLineNumber());
|
||||
|
||||
this->m_currOffset += typeSize;
|
||||
|
||||
pattern->setTypeName(Token::getTypeName(type));
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
PatternData* Evaluator::evaluateStruct(ASTNodeStruct *node) {
|
||||
std::vector<PatternData*> memberPatterns;
|
||||
|
||||
ScopeExit currMemberReset([this] { this->m_currMembers = nullptr; });
|
||||
if (this->m_currMembers == nullptr)
|
||||
this->m_currMembers = &memberPatterns;
|
||||
else
|
||||
currMemberReset.release();
|
||||
|
||||
auto startOffset = this->m_currOffset;
|
||||
for (auto &member : node->getMembers()) {
|
||||
if (auto memberVariableNode = dynamic_cast<ASTNodeVariableDecl*>(member); memberVariableNode != nullptr)
|
||||
memberPatterns.emplace_back(this->evaluateVariable(memberVariableNode));
|
||||
else if (auto memberArrayNode = dynamic_cast<ASTNodeArrayVariableDecl*>(member); memberArrayNode != nullptr)
|
||||
memberPatterns.emplace_back(this->evaluateArray(memberArrayNode));
|
||||
else if (auto memberPointerNode = dynamic_cast<ASTNodePointerVariableDecl*>(member); memberPointerNode != nullptr)
|
||||
memberPatterns.emplace_back(this->evaluatePointer(memberPointerNode));
|
||||
else
|
||||
memberOffset = offset;
|
||||
throwEvaluateError("invalid struct member", member->getLineNumber());
|
||||
|
||||
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
|
||||
this->m_currEndian.reset();
|
||||
}
|
||||
|
||||
PatternData *pattern = nullptr;
|
||||
size_t memberSize = 0;
|
||||
return new PatternDataStruct(startOffset, this->m_currOffset - startOffset, memberPatterns);
|
||||
}
|
||||
|
||||
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
PatternData* Evaluator::evaluateUnion(ASTNodeUnion *node) {
|
||||
std::vector<PatternData*> memberPatterns;
|
||||
|
||||
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
|
||||
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
|
||||
&& member->getArraySize() > 1) {
|
||||
ScopeExit currMemberReset([this] { this->m_currMembers = nullptr; });
|
||||
if (this->m_currMembers == nullptr)
|
||||
this->m_currMembers = &memberPatterns;
|
||||
else
|
||||
currMemberReset.release();
|
||||
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
auto startOffset = this->m_currOffset;
|
||||
for (auto &member : node->getMembers()) {
|
||||
if (auto memberVariableNode = dynamic_cast<ASTNodeVariableDecl*>(member); memberVariableNode != nullptr)
|
||||
memberPatterns.emplace_back(this->evaluateVariable(memberVariableNode));
|
||||
else if (auto memberArrayNode = dynamic_cast<ASTNodeArrayVariableDecl*>(member); memberArrayNode != nullptr)
|
||||
memberPatterns.emplace_back(this->evaluateArray(memberArrayNode));
|
||||
else if (auto memberPointerNode = dynamic_cast<ASTNodePointerVariableDecl*>(member); memberPointerNode != nullptr)
|
||||
memberPatterns.emplace_back(this->evaluatePointer(memberPointerNode));
|
||||
else
|
||||
throwEvaluateError("invalid union member", member->getLineNumber());
|
||||
|
||||
}
|
||||
else if (member->getArraySize() > 1 || member->getVariableType() == Token::TypeToken::Type::Padding) {
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
this->m_currOffset = startOffset;
|
||||
this->m_currEndian.reset();
|
||||
}
|
||||
|
||||
}
|
||||
else if (member->getArraySizeVariable().has_value()) {
|
||||
std::optional<size_t> arraySize;
|
||||
return new PatternDataUnion(startOffset, this->m_currOffset - startOffset, memberPatterns);
|
||||
}
|
||||
|
||||
PatternData* Evaluator::evaluateEnum(ASTNodeEnum *node) {
|
||||
std::vector<std::pair<u64, std::string>> entryPatterns;
|
||||
|
||||
for (auto &prevMember : members) {
|
||||
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
|
||||
u64 value = 0;
|
||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||
auto startOffset = this->m_currOffset;
|
||||
for (auto &[name, value] : node->getEntries()) {
|
||||
auto expression = dynamic_cast<ASTNodeNumericExpression*>(value);
|
||||
if (expression == nullptr)
|
||||
throwEvaluateError("invalid expression in enum value", value->getLineNumber());
|
||||
|
||||
value = hex::changeEndianess(value, prevMember->getSize(), prevMember->getEndianess());
|
||||
auto valueNode = evaluateMathematicalExpression(expression);
|
||||
SCOPE_EXIT( delete valueNode; );
|
||||
|
||||
arraySize = value;
|
||||
}
|
||||
entryPatterns.emplace_back( std::get<s128>(valueNode->getValue()), name );
|
||||
}
|
||||
|
||||
size_t size;
|
||||
if (auto underlyingType = dynamic_cast<const ASTNodeBuiltinType*>(node->getUnderlyingType()); underlyingType != nullptr)
|
||||
size = Token::getTypeSize(underlyingType->getType());
|
||||
else
|
||||
throwEvaluateError("invalid enum underlying type", node->getLineNumber());
|
||||
|
||||
return new PatternDataEnum(startOffset, size, entryPatterns);
|
||||
}
|
||||
|
||||
PatternData* Evaluator::evaluateBitfield(ASTNodeBitfield *node) {
|
||||
std::vector<std::pair<std::string, size_t>> entryPatterns;
|
||||
|
||||
auto startOffset = this->m_currOffset;
|
||||
size_t bits = 0;
|
||||
for (auto &[name, value] : node->getEntries()) {
|
||||
auto expression = dynamic_cast<ASTNodeNumericExpression*>(value);
|
||||
if (expression == nullptr)
|
||||
throwEvaluateError("invalid expression in bitfield field size", value->getLineNumber());
|
||||
|
||||
auto valueNode = evaluateMathematicalExpression(expression);
|
||||
SCOPE_EXIT( delete valueNode; );
|
||||
|
||||
auto fieldBits = std::get<s128>(valueNode->getValue());
|
||||
if (fieldBits > 64)
|
||||
throwEvaluateError("bitfield entry must at most occupy 64 bits", value->getLineNumber());
|
||||
|
||||
bits += fieldBits;
|
||||
|
||||
entryPatterns.emplace_back(name, fieldBits);
|
||||
}
|
||||
|
||||
return new PatternDataBitfield(startOffset, (bits / 8) + 1, entryPatterns);
|
||||
}
|
||||
|
||||
PatternData* Evaluator::evaluateType(ASTNodeTypeDecl *node) {
|
||||
auto type = node->getType();
|
||||
|
||||
if (!this->m_currEndian.has_value())
|
||||
this->m_currEndian = node->getEndian();
|
||||
|
||||
PatternData *pattern;
|
||||
|
||||
if (auto builtinTypeNode = dynamic_cast<ASTNodeBuiltinType*>(type); builtinTypeNode != nullptr)
|
||||
return this->evaluateBuiltinType(builtinTypeNode);
|
||||
else if (auto typeDeclNode = dynamic_cast<ASTNodeTypeDecl*>(type); typeDeclNode != nullptr)
|
||||
pattern = this->evaluateType(typeDeclNode);
|
||||
else if (auto structNode = dynamic_cast<ASTNodeStruct*>(type); structNode != nullptr)
|
||||
pattern = this->evaluateStruct(structNode);
|
||||
else if (auto unionNode = dynamic_cast<ASTNodeUnion*>(type); unionNode != nullptr)
|
||||
pattern = this->evaluateUnion(unionNode);
|
||||
else if (auto enumNode = dynamic_cast<ASTNodeEnum*>(type); enumNode != nullptr)
|
||||
pattern = this->evaluateEnum(enumNode);
|
||||
else if (auto bitfieldNode = dynamic_cast<ASTNodeBitfield*>(type); bitfieldNode != nullptr)
|
||||
pattern = this->evaluateBitfield(bitfieldNode);
|
||||
else
|
||||
throwEvaluateError("type could not be evaluated", node->getLineNumber());
|
||||
|
||||
if (!node->getName().empty())
|
||||
pattern->setTypeName(node->getName().data());
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
PatternData* Evaluator::evaluateVariable(ASTNodeVariableDecl *node) {
|
||||
|
||||
if (auto offset = dynamic_cast<ASTNodeNumericExpression*>(node->getPlacementOffset()); offset != nullptr) {
|
||||
auto valueNode = evaluateMathematicalExpression(offset);
|
||||
SCOPE_EXIT( delete valueNode; );
|
||||
|
||||
this->m_currOffset = std::get<s128>(valueNode->getValue());
|
||||
}
|
||||
if (this->m_currOffset >= this->m_provider->getActualSize())
|
||||
throwEvaluateError("array exceeds size of file", node->getLineNumber());
|
||||
|
||||
PatternData *pattern;
|
||||
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr)
|
||||
pattern = this->evaluateType(typeDecl);
|
||||
else if (auto builtinTypeDecl = dynamic_cast<ASTNodeBuiltinType*>(node->getType()); builtinTypeDecl != nullptr)
|
||||
pattern = this->evaluateBuiltinType(builtinTypeDecl);
|
||||
else
|
||||
throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!", 1);
|
||||
|
||||
pattern->setVariableName(node->getName().data());
|
||||
pattern->setEndian(this->getCurrentEndian());
|
||||
this->m_currEndian.reset();
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
PatternData* Evaluator::evaluateArray(ASTNodeArrayVariableDecl *node) {
|
||||
|
||||
if (auto offset = dynamic_cast<ASTNodeNumericExpression*>(node->getPlacementOffset()); offset != nullptr) {
|
||||
auto valueNode = evaluateMathematicalExpression(offset);
|
||||
SCOPE_EXIT( delete valueNode; );
|
||||
|
||||
this->m_currOffset = std::get<s128>(valueNode->getValue());
|
||||
}
|
||||
|
||||
auto startOffset = this->m_currOffset;
|
||||
|
||||
auto sizeNode = dynamic_cast<ASTNodeNumericExpression*>(node->getSize());
|
||||
if (sizeNode == nullptr)
|
||||
throwEvaluateError("array size not a numeric expression", node->getLineNumber());
|
||||
|
||||
auto valueNode = evaluateMathematicalExpression(sizeNode);
|
||||
SCOPE_EXIT( delete valueNode; );
|
||||
|
||||
auto arraySize = std::get<s128>(valueNode->getValue());
|
||||
|
||||
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr) {
|
||||
if (auto builtinType = dynamic_cast<ASTNodeBuiltinType*>(typeDecl->getType()); builtinType != nullptr) {
|
||||
if (builtinType->getType() == Token::ValueType::Padding) {
|
||||
this->m_currOffset += arraySize;
|
||||
return new PatternDataPadding(startOffset, arraySize);
|
||||
}
|
||||
|
||||
if (!arraySize.has_value()) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
if (arraySize.value() == 0)
|
||||
continue;
|
||||
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
||||
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||
}
|
||||
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
pattern->setEndianess(member->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
members.push_back(new PatternDataPointer(offset, member->getPointerSize().value(), member->getVariableName(), pattern, member->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
unionSize = std::max(size_t(member->getPointerSize().value()), unionSize);
|
||||
}
|
||||
else {
|
||||
members.push_back(pattern);
|
||||
unionSize = std::max(memberSize, unionSize);
|
||||
}
|
||||
}
|
||||
|
||||
return { new PatternDataUnion(offset, unionSize, varDeclNode->getVariableName(), unionNode->getName(), members, varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess), 0x00FFFFFF), unionSize };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto *enumType = static_cast<ASTNodeEnum*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (enumType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t size = Token::getTypeSize(enumType->getUnderlyingType());
|
||||
|
||||
return { new PatternDataEnum(offset, size, varDeclNode->getVariableName(), enumType->getName(), enumType->getValues(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), size };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
|
||||
auto *bitfieldType = static_cast<ASTNodeBitField*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (bitfieldType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
|
||||
size += fieldSize;
|
||||
|
||||
size = bit_ceil(size) / 8;
|
||||
|
||||
return { new PatternDataBitfield(offset, size, varDeclNode->getVariableName(), bitfieldType->getName(), bitfieldType->getFields(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), size };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> entries;
|
||||
|
||||
size_t arrayOffset = 0;
|
||||
std::optional<u32> arrayColor;
|
||||
for (u32 i = 0; i < varDeclNode->getArraySize(); i++) {
|
||||
ASTNodeVariableDecl *nonArrayVarDeclNode = new ASTNodeVariableDecl(varDeclNode->getLineNumber(), varDeclNode->getVariableType(), "[" + std::to_string(i) + "]", varDeclNode->getCustomVariableTypeName(), varDeclNode->getOffset(), 1);
|
||||
|
||||
|
||||
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Padding) {
|
||||
return { new PatternDataPadding(offset, varDeclNode->getArraySize()), varDeclNode->getArraySize() };
|
||||
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
const auto& [pattern, size] = this->createBuiltInTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
||||
|
||||
if (pattern == nullptr) {
|
||||
delete nonArrayVarDeclNode;
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (!arrayColor.has_value())
|
||||
arrayColor = pattern->getColor();
|
||||
|
||||
pattern->setColor(arrayColor.value());
|
||||
|
||||
entries.push_back(pattern);
|
||||
arrayOffset += size;
|
||||
} else {
|
||||
const auto &[pattern, size] = this->createCustomTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
||||
|
||||
if (pattern == nullptr) {
|
||||
delete nonArrayVarDeclNode;
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
pattern->setEndianess(varDeclNode->getEndianess().value_or(varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)));
|
||||
|
||||
if (!arrayColor.has_value())
|
||||
arrayColor = pattern->getColor();
|
||||
|
||||
pattern->setColor(arrayColor.value());
|
||||
|
||||
entries.push_back(pattern);
|
||||
arrayOffset += size;
|
||||
std::optional<u32> color;
|
||||
for (s128 i = 0; i < arraySize; i++) {
|
||||
PatternData *entry;
|
||||
if (auto typeDecl = dynamic_cast<ASTNodeTypeDecl*>(node->getType()); typeDecl != nullptr)
|
||||
entry = this->evaluateType(typeDecl);
|
||||
else if (auto builtinTypeDecl = dynamic_cast<ASTNodeBuiltinType*>(node->getType()); builtinTypeDecl != nullptr) {
|
||||
entry = this->evaluateBuiltinType(builtinTypeDecl);
|
||||
}
|
||||
else
|
||||
throwEvaluateError("ASTNodeVariableDecl had an invalid type. This is a bug!", 1);
|
||||
|
||||
delete nonArrayVarDeclNode;
|
||||
entry->setVariableName(hex::format("[%llu]", (u64)i));
|
||||
entry->setEndian(this->getCurrentEndian());
|
||||
|
||||
if (!color.has_value())
|
||||
color = entry->getColor();
|
||||
entry->setColor(color.value_or(0));
|
||||
|
||||
entries.push_back(entry);
|
||||
|
||||
if (this->m_currOffset >= this->m_provider->getActualSize())
|
||||
throwEvaluateError("array exceeds size of file", node->getLineNumber());
|
||||
}
|
||||
|
||||
return { new PatternDataArray(offset, arrayOffset, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess), entries, arrayColor.value_or(0xFF000000)), arrayOffset };
|
||||
this->m_currEndian.reset();
|
||||
|
||||
if (entries.empty())
|
||||
throwEvaluateError("array size must be greater than zero", node->getLineNumber());
|
||||
|
||||
|
||||
PatternData *pattern;
|
||||
if (dynamic_cast<PatternDataCharacter*>(entries[0]))
|
||||
pattern = new PatternDataString(startOffset, (this->m_currOffset - startOffset), color.value_or(0));
|
||||
else
|
||||
pattern = new PatternDataArray(startOffset, (this->m_currOffset - startOffset), entries, color.value_or(0));
|
||||
|
||||
pattern->setVariableName(node->getName().data());
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
size_t arraySize = varDeclNode->getArraySize();
|
||||
PatternData* Evaluator::evaluatePointer(ASTNodePointerVariableDecl *node) {
|
||||
s128 pointerOffset;
|
||||
if (auto offset = dynamic_cast<ASTNodeNumericExpression*>(node->getPlacementOffset()); offset != nullptr) {
|
||||
auto valueNode = evaluateMathematicalExpression(offset);
|
||||
SCOPE_EXIT( delete valueNode; );
|
||||
|
||||
return { new PatternDataString(offset, arraySize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), arraySize };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto &currType = this->m_types[varDeclNode->getCustomVariableTypeName()];
|
||||
|
||||
if (currType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
pointerOffset = std::get<s128>(valueNode->getValue());
|
||||
this->m_currOffset = pointerOffset;
|
||||
} else {
|
||||
pointerOffset = this->m_currOffset;
|
||||
}
|
||||
|
||||
switch (currType->getType()) {
|
||||
case ASTNode::Type::Struct:
|
||||
return this->createStructPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Union:
|
||||
return this->createUnionPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Enum:
|
||||
return this->createEnumPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Bitfield:
|
||||
return this->createBitfieldPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::TypeDecl:
|
||||
return this->createBuiltInTypePattern(varDeclNode, offset);
|
||||
}
|
||||
PatternData *sizeType;
|
||||
if (auto builtinTypeNode = dynamic_cast<ASTNodeBuiltinType*>(node->getSizeType()); builtinTypeNode != nullptr) {
|
||||
sizeType = evaluateBuiltinType(builtinTypeNode);
|
||||
} else
|
||||
throwEvaluateError("Pointer size is not a builtin type", node->getLineNumber());
|
||||
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
size_t pointerSize = sizeType->getSize();
|
||||
delete sizeType;
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createBuiltInTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto type = varDeclNode->getVariableType();
|
||||
if (type == Token::TypeToken::Type::CustomType) {
|
||||
const auto &currType = static_cast<ASTNodeTypeDecl*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
if (currType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
u128 pointedAtOffset = 0;
|
||||
this->m_provider->read(pointerOffset, &pointedAtOffset, pointerSize);
|
||||
|
||||
type = currType->getAssignedType();
|
||||
}
|
||||
this->m_currOffset = pointedAtOffset;
|
||||
auto pointedAt = evaluateType(dynamic_cast<ASTNodeTypeDecl*>(node->getType()));
|
||||
this->m_currOffset = pointerOffset + pointerSize;
|
||||
|
||||
size_t typeSize = Token::getTypeSize(type);
|
||||
size_t arraySize = varDeclNode->getArraySize();
|
||||
|
||||
if (Token::isSigned(type)) {
|
||||
if (typeSize == 1 && arraySize == 1)
|
||||
return { new PatternDataCharacter(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), 1 };
|
||||
else if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataSigned(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
|
||||
} else if (Token::isUnsigned(varDeclNode->getVariableType())) {
|
||||
if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataUnsigned(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
|
||||
} else if (Token::isFloatingPoint(varDeclNode->getVariableType())) {
|
||||
if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataFloat(offset, typeSize, varDeclNode->getVariableName(), varDeclNode->getEndianess().value_or(this->m_defaultDataEndianess)), typeSize * arraySize };
|
||||
}
|
||||
|
||||
return { nullptr, 0 };
|
||||
return new PatternDataPointer(pointerOffset, pointerSize, pointedAt);
|
||||
}
|
||||
|
||||
std::optional<std::vector<PatternData*>> Evaluator::evaluate(const std::vector<ASTNode *> &ast) {
|
||||
|
||||
// Evaluate types
|
||||
for (const auto &node : ast) {
|
||||
std::vector<PatternData*> patterns;
|
||||
|
||||
switch(node->getType()) {
|
||||
case ASTNode::Type::Struct:
|
||||
{
|
||||
auto *structNode = static_cast<ASTNodeStruct*>(node);
|
||||
this->m_types.emplace(structNode->getName(), structNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Union:
|
||||
{
|
||||
auto *unionNode = static_cast<ASTNodeUnion*>(node);
|
||||
this->m_types.emplace(unionNode->getName(), unionNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Enum:
|
||||
{
|
||||
auto *enumNode = static_cast<ASTNodeEnum*>(node);
|
||||
this->m_types.emplace(enumNode->getName(), enumNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Bitfield:
|
||||
{
|
||||
auto *bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
||||
this->m_types.emplace(bitfieldNode->getName(), bitfieldNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::TypeDecl:
|
||||
{
|
||||
auto *typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
|
||||
try {
|
||||
for (const auto& node : ast) {
|
||||
this->m_currEndian.reset();
|
||||
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType)
|
||||
this->m_types.emplace(typeDeclNode->getTypeName(), this->m_types[typeDeclNode->getAssignedCustomTypeName()]);
|
||||
else
|
||||
this->m_types.emplace(typeDeclNode->getTypeName(), typeDeclNode);
|
||||
if (auto variableDeclNode = dynamic_cast<ASTNodeVariableDecl*>(node); variableDeclNode != nullptr) {
|
||||
patterns.push_back(this->evaluateVariable(variableDeclNode));
|
||||
} else if (auto arrayDeclNode = dynamic_cast<ASTNodeArrayVariableDecl*>(node); arrayDeclNode != nullptr) {
|
||||
patterns.push_back(this->evaluateArray(arrayDeclNode));
|
||||
} else if (auto pointerDeclNode = dynamic_cast<ASTNodePointerVariableDecl*>(node); pointerDeclNode != nullptr) {
|
||||
patterns.push_back(this->evaluatePointer(pointerDeclNode));
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::VariableDecl: break;
|
||||
case ASTNode::Type::Scope: break;
|
||||
|
||||
}
|
||||
} catch (EvaluateError &e) {
|
||||
this->m_error = e;
|
||||
return { };
|
||||
}
|
||||
|
||||
// Evaluate variable declarations
|
||||
|
||||
std::vector<PatternData*> variables;
|
||||
for (const auto &node : ast) {
|
||||
if (node->getType() != ASTNode::Type::VariableDecl)
|
||||
continue;
|
||||
|
||||
auto *varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
||||
|
||||
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Signed8Bit && varDeclNode->getArraySize() > 1) {
|
||||
const auto &[pattern, _] = createStringPattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
}
|
||||
else if (varDeclNode->getArraySize() > 1) {
|
||||
const auto &[pattern, _] = this->createArrayPattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
|
||||
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
const auto &[pattern, _] = this->createBuiltInTypePattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
} else {
|
||||
const auto &[pattern, _] = this->createCustomTypePattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &var : variables)
|
||||
if (var == nullptr)
|
||||
return { };
|
||||
|
||||
return variables;
|
||||
return patterns;
|
||||
}
|
||||
|
||||
}
|
@ -6,6 +6,9 @@
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
#define TOKEN(type, value) Token::Type::type, Token::type::value, lineNumber
|
||||
#define VALUE_TOKEN(type, value) Token::Type::type, value, lineNumber
|
||||
|
||||
Lexer::Lexer() { }
|
||||
|
||||
std::string matchTillInvalid(const char* characters, std::function<bool(char)> predicate) {
|
||||
@ -72,186 +75,192 @@ namespace hex::lang {
|
||||
|
||||
u32 lineNumber = 1;
|
||||
|
||||
while (offset < code.length()) {
|
||||
try {
|
||||
|
||||
// Handle comments
|
||||
if (code[offset] == '/') {
|
||||
offset++;
|
||||
while (offset < code.length()) {
|
||||
const char& c = code[offset];
|
||||
|
||||
if (offset < code.length() && code[offset] == '/') {
|
||||
offset++;
|
||||
while (offset < code.length()) {
|
||||
if (code[offset] == '\n' || code[offset] == '\r')
|
||||
break;
|
||||
offset++;
|
||||
}
|
||||
} else if (offset < code.length() && code[offset] == '*') {
|
||||
offset++;
|
||||
while (offset < (code.length() - 1)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
|
||||
if (code[offset] == '*' && code[offset + 1] == '/')
|
||||
break;
|
||||
offset++;
|
||||
}
|
||||
if (c == 0x00)
|
||||
break;
|
||||
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.emplace_back(TOKEN(Separator, EndOfExpression));
|
||||
offset += 1;
|
||||
} else if (c == '(') {
|
||||
tokens.emplace_back(TOKEN(Separator, RoundBracketOpen));
|
||||
offset += 1;
|
||||
} else if (c == ')') {
|
||||
tokens.emplace_back(TOKEN(Separator, RoundBracketClose));
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.emplace_back(TOKEN(Separator, CurlyBracketOpen));
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.emplace_back(TOKEN(Separator, CurlyBracketClose));
|
||||
offset += 1;
|
||||
} else if (c == '[') {
|
||||
tokens.emplace_back(TOKEN(Separator, SquareBracketOpen));
|
||||
offset += 1;
|
||||
} else if (c == ']') {
|
||||
tokens.emplace_back(TOKEN(Separator, SquareBracketClose));
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.emplace_back(TOKEN(Separator, Comma));
|
||||
offset += 1;
|
||||
} else if (c == '.') {
|
||||
tokens.emplace_back(TOKEN(Separator, Dot));
|
||||
offset += 1;
|
||||
} else if (c == '@') {
|
||||
tokens.emplace_back(TOKEN(Operator, AtDeclaration));
|
||||
offset += 1;
|
||||
} else if (c == '=') {
|
||||
tokens.emplace_back(TOKEN(Operator, Assignment));
|
||||
offset += 1;
|
||||
} else if (c == ':') {
|
||||
tokens.emplace_back(TOKEN(Operator, Inherit));
|
||||
offset += 1;
|
||||
} else if (c == '+') {
|
||||
tokens.emplace_back(TOKEN(Operator, Plus));
|
||||
offset += 1;
|
||||
} else if (c == '-') {
|
||||
tokens.emplace_back(TOKEN(Operator, Minus));
|
||||
offset += 1;
|
||||
} else if (c == '*') {
|
||||
tokens.emplace_back(TOKEN(Operator, Star));
|
||||
offset += 1;
|
||||
} else if (c == '/') {
|
||||
tokens.emplace_back(TOKEN(Operator, Slash));
|
||||
offset += 1;
|
||||
} else if (offset + 1 <= code.length() && code[offset] == '<' && code[offset + 1] == '<') {
|
||||
tokens.emplace_back(TOKEN(Operator, ShiftLeft));
|
||||
offset += 2;
|
||||
} else offset--;
|
||||
}
|
||||
|
||||
const char& c = code[offset];
|
||||
|
||||
if (c == 0x00)
|
||||
break;
|
||||
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.push_back({ .type = Token::Type::EndOfExpression, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.push_back({ .type = Token::Type::ScopeOpen, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.push_back({ .type = Token::Type::ScopeClose, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '[') {
|
||||
tokens.push_back({ .type = Token::Type::ArrayOpen, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ']') {
|
||||
tokens.push_back({.type = Token::Type::ArrayClose, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.push_back({ .type = Token::Type::Separator, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '@') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '=') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ':') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '*') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Star }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '\'') {
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length()) {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { };
|
||||
}
|
||||
|
||||
char character = code[offset];
|
||||
|
||||
if (character == '\\') {
|
||||
} else if (offset + 1 <= code.length() && code[offset] == '>' && code[offset + 1] == '>') {
|
||||
tokens.emplace_back(TOKEN(Operator, ShiftRight));
|
||||
offset += 2;
|
||||
} else if (c == '|') {
|
||||
tokens.emplace_back(TOKEN(Operator, BitOr));
|
||||
offset += 1;
|
||||
} else if (c == '&') {
|
||||
tokens.emplace_back(TOKEN(Operator, BitAnd));
|
||||
offset += 1;
|
||||
} else if (c == '^') {
|
||||
tokens.emplace_back(TOKEN(Operator, BitXor));
|
||||
offset += 1;
|
||||
} else if (c == '\'') {
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length()) {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { };
|
||||
if (offset >= code.length())
|
||||
throwLexerError("invalid character literal", lineNumber);
|
||||
|
||||
char character = code[offset];
|
||||
|
||||
if (character == '\\') {
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length())
|
||||
throwLexerError("invalid character literal", lineNumber);
|
||||
|
||||
if (code[offset] != '\\' && code[offset] != '\'')
|
||||
throwLexerError("invalid escape sequence", lineNumber);
|
||||
|
||||
|
||||
character = code[offset];
|
||||
} else {
|
||||
if (code[offset] == '\\' || code[offset] == '\'' || character == '\n' || character == '\r')
|
||||
throwLexerError("invalid character literal", lineNumber);
|
||||
|
||||
}
|
||||
|
||||
if (code[offset] != '\\' && code[offset] != '\'') {
|
||||
this->m_error = { lineNumber, "Invalid escape sequence" };
|
||||
return { };
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
character = code[offset];
|
||||
} else {
|
||||
if (code[offset] == '\\' || code[offset] == '\'' || character == '\n' || character == '\r') {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { };
|
||||
}
|
||||
}
|
||||
if (offset >= code.length() || code[offset] != '\'')
|
||||
throwLexerError("missing terminating ' after character literal", lineNumber);
|
||||
|
||||
offset += 1;
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, character));
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length() || code[offset] != '\'') {
|
||||
this->m_error = { lineNumber, "Missing terminating ' after character literal" };
|
||||
return { };
|
||||
}
|
||||
} else if (std::isalpha(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = character }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
// Check for reserved keywords
|
||||
|
||||
} else if (std::isalpha(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||
if (identifier == "struct")
|
||||
tokens.emplace_back(TOKEN(Keyword, Struct));
|
||||
else if (identifier == "union")
|
||||
tokens.emplace_back(TOKEN(Keyword, Union));
|
||||
else if (identifier == "using")
|
||||
tokens.emplace_back(TOKEN(Keyword, Using));
|
||||
else if (identifier == "enum")
|
||||
tokens.emplace_back(TOKEN(Keyword, Enum));
|
||||
else if (identifier == "bitfield")
|
||||
tokens.emplace_back(TOKEN(Keyword, Bitfield));
|
||||
else if (identifier == "be")
|
||||
tokens.emplace_back(TOKEN(Keyword, BigEndian));
|
||||
else if (identifier == "le")
|
||||
tokens.emplace_back(TOKEN(Keyword, LittleEndian));
|
||||
|
||||
// Check for reserved keywords
|
||||
// Check for built-in types
|
||||
else if (identifier == "u8")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned8Bit));
|
||||
else if (identifier == "s8")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed8Bit));
|
||||
else if (identifier == "u16")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned16Bit));
|
||||
else if (identifier == "s16")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed16Bit));
|
||||
else if (identifier == "u32")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned32Bit));
|
||||
else if (identifier == "s32")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed32Bit));
|
||||
else if (identifier == "u64")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned64Bit));
|
||||
else if (identifier == "s64")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed64Bit));
|
||||
else if (identifier == "u128")
|
||||
tokens.emplace_back(TOKEN(ValueType, Unsigned128Bit));
|
||||
else if (identifier == "s128")
|
||||
tokens.emplace_back(TOKEN(ValueType, Signed128Bit));
|
||||
else if (identifier == "float")
|
||||
tokens.emplace_back(TOKEN(ValueType, Float));
|
||||
else if (identifier == "double")
|
||||
tokens.emplace_back(TOKEN(ValueType, Double));
|
||||
else if (identifier == "char")
|
||||
tokens.emplace_back(TOKEN(ValueType, Character));
|
||||
else if (identifier == "padding")
|
||||
tokens.emplace_back(TOKEN(ValueType, Padding));
|
||||
|
||||
if (identifier == "struct")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct }, .lineNumber = lineNumber });
|
||||
else if (identifier == "union")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union }, .lineNumber = lineNumber });
|
||||
else if (identifier == "using")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using }, .lineNumber = lineNumber });
|
||||
else if (identifier == "enum")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum }, .lineNumber = lineNumber });
|
||||
else if (identifier == "bitfield")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield }, .lineNumber = lineNumber });
|
||||
else if (identifier == "be")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::BigEndian }, .lineNumber = lineNumber });
|
||||
else if (identifier == "le")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::LittleEndian }, .lineNumber = lineNumber });
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
|
||||
// Check for built-in types
|
||||
else if (identifier == "u8")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s8")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u16")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s16")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u32")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s32")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u64")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s64")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "u128")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "s128")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit }, .lineNumber = lineNumber });
|
||||
else if (identifier == "float")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float }, .lineNumber = lineNumber });
|
||||
else if (identifier == "double")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double }, .lineNumber = lineNumber });
|
||||
else if (identifier == "padding")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Padding }, .lineNumber = lineNumber });
|
||||
else
|
||||
tokens.emplace_back(VALUE_TOKEN(Identifier, identifier));
|
||||
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
char *end = nullptr;
|
||||
std::strtoull(&code[offset], &end, 0);
|
||||
|
||||
else
|
||||
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier }, .lineNumber = lineNumber });
|
||||
auto integer = parseInt(std::string_view(&code[offset], end - &code[offset]));
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
char *end = nullptr;
|
||||
std::strtoull(&code[offset], &end, 0);
|
||||
if (!integer.has_value())
|
||||
throwLexerError("invalid integer literal", lineNumber);
|
||||
|
||||
auto integer = parseInt(std::string_view(&code[offset], end - &code[offset]));
|
||||
|
||||
if (!integer.has_value()) {
|
||||
this->m_error = { lineNumber, "Invalid integer literal" };
|
||||
return { };
|
||||
}
|
||||
tokens.emplace_back(VALUE_TOKEN(Integer, integer.value()));
|
||||
offset += (end - &code[offset]);
|
||||
} else
|
||||
throwLexerError("unknown token", lineNumber);
|
||||
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() }, .lineNumber = lineNumber });
|
||||
offset += (end - &code[offset]);
|
||||
} else {
|
||||
this->m_error = { lineNumber, "Unknown token" };
|
||||
return { };
|
||||
}
|
||||
|
||||
tokens.emplace_back(TOKEN(Separator, EndOfProgram));
|
||||
} catch (LexerError &e) {
|
||||
this->m_error = e;
|
||||
return { };
|
||||
}
|
||||
|
||||
tokens.push_back({ .type = Token::Type::EndOfProgram, .lineNumber = lineNumber });
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,165 +18,188 @@ namespace hex::lang {
|
||||
std::string output;
|
||||
output.reserve(code.length());
|
||||
|
||||
while (offset < code.length()) {
|
||||
if (code[offset] == '#') {
|
||||
try {
|
||||
while (offset < code.length()) {
|
||||
if (code[offset] == '#') {
|
||||
offset += 1;
|
||||
|
||||
if (code.substr(offset, 7) == "include") {
|
||||
offset += 7;
|
||||
|
||||
while (std::isblank(code[offset]) || std::isspace(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
if (code[offset] != '<' && code[offset] != '"')
|
||||
throwPreprocessorError("expected '<' or '\"' before file name", lineNumber);
|
||||
|
||||
char endChar = code[offset];
|
||||
if (endChar == '<') endChar = '>';
|
||||
|
||||
offset += 1;
|
||||
|
||||
std::string includeFile;
|
||||
while (code[offset] != endChar) {
|
||||
includeFile += code[offset];
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError(hex::format("missing terminating '%c' character", endChar), lineNumber);
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
if (includeFile[0] != '/')
|
||||
includeFile = "include/" + includeFile;
|
||||
|
||||
FILE *file = fopen(includeFile.c_str(), "r");
|
||||
if (file == nullptr)
|
||||
throwPreprocessorError(hex::format("%s: No such file or directory", includeFile.c_str()), lineNumber);
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
char *buffer = new char[size + 1];
|
||||
rewind(file);
|
||||
|
||||
fread(buffer, size, 1, file);
|
||||
buffer[size] = 0x00;
|
||||
|
||||
auto preprocessedInclude = this->preprocess(buffer, false);
|
||||
if (!preprocessedInclude.has_value())
|
||||
throw this->m_error;
|
||||
|
||||
output += preprocessedInclude.value();
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
fclose(file);
|
||||
} else if (code.substr(offset, 6) == "define") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset])) {
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
std::string defineName;
|
||||
while (!std::isblank(code[offset])) {
|
||||
defineName += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
throwPreprocessorError("no value given in #define directive", lineNumber);
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset])) {
|
||||
offset += 1;
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("no value given in #define directive", lineNumber);
|
||||
}
|
||||
|
||||
std::string replaceValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("missing new line after #define directive", lineNumber);
|
||||
|
||||
replaceValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (replaceValue.empty())
|
||||
throwPreprocessorError("no value given in #define directive", lineNumber);
|
||||
|
||||
this->m_defines.emplace(defineName, replaceValue);
|
||||
} else if (code.substr(offset, 6) == "pragma") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaKey;
|
||||
while (!std::isblank(code[offset])) {
|
||||
pragmaKey += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
throwPreprocessorError("no instruction given in #pragma directive", lineNumber);
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("missing new line after #pragma directive", lineNumber);
|
||||
|
||||
pragmaValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (pragmaValue.empty())
|
||||
throwPreprocessorError("missing value in #pragma directive", lineNumber);
|
||||
|
||||
this->m_pragmas.emplace(pragmaKey, pragmaValue);
|
||||
} else
|
||||
throwPreprocessorError("unknown preprocessor directive", lineNumber);
|
||||
} else if (code.substr(offset, 2) == "//") {
|
||||
while (code[offset] != '\n' && offset < code.length())
|
||||
offset += 1;
|
||||
} else if (code.substr(offset, 2) == "/*") {
|
||||
while (code.substr(offset, 2) != "*/" && offset < code.length()) {
|
||||
if (code[offset] == '\n')
|
||||
lineNumber++;
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
offset += 2;
|
||||
if (offset >= code.length())
|
||||
throwPreprocessorError("unterminated comment", lineNumber);
|
||||
}
|
||||
|
||||
if (code[offset] == '\n')
|
||||
lineNumber++;
|
||||
|
||||
output += code[offset];
|
||||
offset += 1;
|
||||
|
||||
if (code.substr(offset, 7) == "include") {
|
||||
offset += 7;
|
||||
|
||||
while (std::isblank(code[offset]) || std::isspace(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
if (code[offset] != '<' && code[offset] != '"')
|
||||
return { };
|
||||
|
||||
char endChar = code[offset];
|
||||
if (endChar == '<') endChar = '>';
|
||||
|
||||
offset += 1;
|
||||
|
||||
std::string includeFile;
|
||||
while (code[offset] != endChar) {
|
||||
includeFile += code[offset];
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length())
|
||||
return { };
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
if (includeFile[0] != '/')
|
||||
includeFile = "include/" + includeFile;
|
||||
|
||||
FILE *file = fopen(includeFile.c_str(), "r");
|
||||
if (file == nullptr)
|
||||
return { };
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
char *buffer = new char[size + 1];
|
||||
rewind(file);
|
||||
|
||||
fread(buffer, size, 1, file);
|
||||
buffer[size] = 0x00;
|
||||
|
||||
auto preprocessedInclude = this->preprocess(buffer, false);
|
||||
if (!preprocessedInclude.has_value())
|
||||
return { };
|
||||
|
||||
output += preprocessedInclude.value();
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
fclose(file);
|
||||
} else if (code.substr(offset, 6) == "define") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string defineName;
|
||||
while (!std::isblank(code[offset])) {
|
||||
defineName += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
return { };
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string replaceValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
return { };
|
||||
|
||||
replaceValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (replaceValue.empty())
|
||||
return { };
|
||||
|
||||
this->m_defines.emplace(defineName, replaceValue);
|
||||
} else if (code.substr(offset, 6) == "pragma") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaKey;
|
||||
while (!std::isblank(code[offset])) {
|
||||
pragmaKey += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
return { };
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
return { };
|
||||
|
||||
pragmaValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (pragmaValue.empty())
|
||||
return { };
|
||||
|
||||
this->m_pragmas.emplace(pragmaKey, pragmaValue);
|
||||
} else
|
||||
return { };
|
||||
}
|
||||
|
||||
if (code[offset] == '\n')
|
||||
lineNumber++;
|
||||
if (initialRun) {
|
||||
// Apply defines
|
||||
for (const auto &[define, value] : this->m_defines) {
|
||||
s32 index = 0;
|
||||
while((index = output.find(define, index)) != std::string::npos) {
|
||||
output.replace(index, define.length(), value);
|
||||
index += value.length();
|
||||
}
|
||||
}
|
||||
|
||||
output += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (initialRun) {
|
||||
// Apply defines
|
||||
for (const auto &[define, value] : this->m_defines) {
|
||||
s32 index = 0;
|
||||
while((index = output.find(define, index)) != std::string::npos) {
|
||||
output.replace(index, define.length(), value);
|
||||
index += value.length();
|
||||
// Handle pragmas
|
||||
for (const auto &[type, value] : this->m_pragmas) {
|
||||
if (this->m_pragmaHandlers.contains(type)) {
|
||||
if (!this->m_pragmaHandlers[type](value))
|
||||
throwPreprocessorError(hex::format("invalid value provided to '%s' #pragma directive", type.c_str()), lineNumber);
|
||||
} else
|
||||
throwPreprocessorError(hex::format("no #pragma handler registered for type %s", type.c_str()), lineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle pragmas
|
||||
for (const auto &[type, value] : this->m_pragmas) {
|
||||
if (this->m_pragmaHandlers.contains(type)) {
|
||||
if (!this->m_pragmaHandlers[type](value))
|
||||
return { };
|
||||
} else
|
||||
return { };
|
||||
}
|
||||
} catch (PreprocessorError &e) {
|
||||
this->m_error = e;
|
||||
return { };
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Preprocessor::addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function) {
|
||||
void Preprocessor::addPragmaHandler(const std::string &pragmaType, const std::function<bool(const std::string&)> &function) {
|
||||
if (!this->m_pragmaHandlers.contains(pragmaType))
|
||||
this->m_pragmaHandlers.emplace(pragmaType, function);
|
||||
}
|
||||
|
||||
void Preprocessor::addDefaultPragmaHandlers() {
|
||||
this->addPragmaHandler("MIME", [](std::string value) {
|
||||
this->addPragmaHandler("MIME", [](const std::string &value) {
|
||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||
});
|
||||
this->addPragmaHandler("endian", [](std::string value) {
|
||||
this->addPragmaHandler("endian", [](const std::string &value) {
|
||||
return value == "big" || value == "little" || value == "native";
|
||||
});
|
||||
}
|
||||
|
@ -12,116 +12,152 @@ namespace hex::lang {
|
||||
}
|
||||
|
||||
bool Validator::validate(const std::vector<ASTNode*>& ast) {
|
||||
std::unordered_set<std::string> identifiers;
|
||||
|
||||
std::unordered_set<std::string> typeNames;
|
||||
try {
|
||||
|
||||
for (const auto &node : ast) {
|
||||
if (node == nullptr) {
|
||||
this->m_error = { 1, "Empty AST" };
|
||||
return false;
|
||||
for (const auto &node : ast) {
|
||||
if (node == nullptr)
|
||||
throwValidateError("nullptr in AST. This is a bug!", 1);
|
||||
|
||||
if (auto variableDeclNode = dynamic_cast<ASTNodeVariableDecl*>(node); variableDeclNode != nullptr) {
|
||||
if (!identifiers.insert(variableDeclNode->getName().data()).second)
|
||||
throwValidateError(hex::format("redefinition of identifier '%s'", variableDeclNode->getName().data()), variableDeclNode->getLineNumber());
|
||||
|
||||
this->validate({ variableDeclNode->getType() });
|
||||
} else if (auto typeDeclNode = dynamic_cast<ASTNodeTypeDecl*>(node); typeDeclNode != nullptr) {
|
||||
if (!identifiers.insert(typeDeclNode->getName().data()).second)
|
||||
throwValidateError(hex::format("redefinition of identifier '%s'", typeDeclNode->getName().data()), typeDeclNode->getLineNumber());
|
||||
|
||||
this->validate({ typeDeclNode->getType() });
|
||||
} else if (auto structNode = dynamic_cast<ASTNodeStruct*>(node); structNode != nullptr) {
|
||||
this->validate(structNode->getMembers());
|
||||
} else if (auto unionNode = dynamic_cast<ASTNodeUnion*>(node); unionNode != nullptr) {
|
||||
this->validate(unionNode->getMembers());
|
||||
} else if (auto enumNode = dynamic_cast<ASTNodeEnum*>(node); enumNode != nullptr) {
|
||||
std::unordered_set<std::string> enumIdentifiers;
|
||||
for (auto &[name, value] : enumNode->getEntries()) {
|
||||
if (!enumIdentifiers.insert(name).second)
|
||||
throwValidateError(hex::format("redefinition of enum constant '%s'", name.c_str()), value->getLineNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (node->getType()) {
|
||||
case ASTNode::Type::VariableDecl:
|
||||
{
|
||||
// Check for duplicate variable names
|
||||
auto varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
||||
if (!typeNames.insert(varDeclNode->getVariableName()).second) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("Redefinition of variable '%s'", varDeclNode->getVariableName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (varDeclNode->getArraySize() == 0 && !varDeclNode->getArraySizeVariable().has_value() ||
|
||||
varDeclNode->getArraySize() != 0 && varDeclNode->getArraySizeVariable().has_value()) {
|
||||
|
||||
this->m_error = { varDeclNode->getLineNumber(), "Invalid array size" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::TypeDecl:
|
||||
{
|
||||
// Check for duplicate type names
|
||||
auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
|
||||
if (!typeNames.insert(typeDeclNode->getTypeName()).second) {
|
||||
this->m_error = { typeDeclNode->getLineNumber(), hex::format("Redefinition of type '%s'", typeDeclNode->getTypeName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType && !typeNames.contains(typeDeclNode->getAssignedCustomTypeName())) {
|
||||
this->m_error = { typeDeclNode->getLineNumber(), "Type declaration without a name" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Struct:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto structNode = static_cast<ASTNodeStruct*>(node);
|
||||
if (!typeNames.insert(structNode->getName()).second) {
|
||||
this->m_error = { structNode->getLineNumber(), hex::format("Redeclaration of type '%s'", structNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate member names
|
||||
std::unordered_set<std::string> memberNames;
|
||||
for (const auto &member : structNode->getNodes())
|
||||
if (!memberNames.insert(static_cast<ASTNodeVariableDecl*>(member)->getVariableName()).second) {
|
||||
this->m_error = { member->getLineNumber(), hex::format("Redeclaration of member '%s'", static_cast<ASTNodeVariableDecl*>(member)->getVariableName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Enum:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto enumNode = static_cast<ASTNodeEnum*>(node);
|
||||
if (!typeNames.insert(enumNode->getName()).second) {
|
||||
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of type '%s'", enumNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate constant names
|
||||
std::unordered_set<std::string> constantNames;
|
||||
for (const auto &[value, name] : enumNode->getValues())
|
||||
if (!constantNames.insert(name).second) {
|
||||
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of enum constant '%s'", name.c_str()) };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Bitfield:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
||||
if (!typeNames.insert(bitfieldNode->getName()).second) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of type '%s'", bitfieldNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t bitfieldSize = 0;
|
||||
|
||||
// Check for duplicate constant names
|
||||
std::unordered_set<std::string> flagNames;
|
||||
for (const auto &[name, size] : bitfieldNode->getFields()) {
|
||||
if (!flagNames.insert(name).second) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of member '%s'", name.c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
bitfieldSize += size;
|
||||
}
|
||||
|
||||
if (bitfieldSize > 64) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), "Bitfield exceeds maximum size of 64 bits" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (ValidatorError &e) {
|
||||
this->m_error = e;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Validator::printAST(const std::vector<ASTNode*>& ast){
|
||||
#if DEBUG
|
||||
#define INDENT_VALUE indent, ' '
|
||||
static s32 indent = -2;
|
||||
|
||||
indent += 2;
|
||||
for (const auto &node : ast) {
|
||||
if (auto variableDeclNode = dynamic_cast<ASTNodeVariableDecl*>(node); variableDeclNode != nullptr) {
|
||||
if (auto offset = dynamic_cast<ASTNodeNumericExpression*>(variableDeclNode->getPlacementOffset()); offset != nullptr) {
|
||||
printf("%*c ASTNodeVariableDecl (%s) @\n", INDENT_VALUE, variableDeclNode->getName().data());
|
||||
printAST({ offset });
|
||||
}
|
||||
else
|
||||
printf("%*c ASTNodeVariableDecl (%s)\n", INDENT_VALUE, variableDeclNode->getName().data());
|
||||
printAST({ variableDeclNode->getType() });
|
||||
} else if (auto pointerDeclNode = dynamic_cast<ASTNodePointerVariableDecl*>(node); pointerDeclNode != nullptr) {
|
||||
if (auto offset = dynamic_cast<ASTNodeNumericExpression*>(pointerDeclNode->getPlacementOffset()); offset != nullptr) {
|
||||
printf("%*c ASTNodePointerVariableDecl (*%s) @\n", INDENT_VALUE, pointerDeclNode->getName().data());
|
||||
printAST({ offset });
|
||||
}
|
||||
else
|
||||
printf("%*c ASTNodePointerVariableDecl (*%s)\n", INDENT_VALUE, pointerDeclNode->getName().data());
|
||||
printAST({ pointerDeclNode->getType() });
|
||||
printAST({ pointerDeclNode->getSizeType() });
|
||||
} else if (auto arrayDeclNode = dynamic_cast<ASTNodeArrayVariableDecl*>(node); arrayDeclNode != nullptr) {
|
||||
auto sizeExpr = dynamic_cast<ASTNodeNumericExpression*>(arrayDeclNode->getSize());
|
||||
if (sizeExpr == nullptr) {
|
||||
printf("%*c Invalid size!\n", INDENT_VALUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto offset = dynamic_cast<ASTNodeNumericExpression*>(arrayDeclNode->getPlacementOffset()); offset != nullptr) {
|
||||
printf("%*c ASTNodeArrayVariableDecl (%s[]) @\n", INDENT_VALUE, arrayDeclNode->getName().data());
|
||||
printAST({ sizeExpr });
|
||||
printAST({ offset });
|
||||
}
|
||||
else {
|
||||
printf("%*c ASTNodeArrayVariableDecl (%s[])\n", INDENT_VALUE, arrayDeclNode->getName().data());
|
||||
printAST({ sizeExpr });
|
||||
}
|
||||
|
||||
printAST({ arrayDeclNode->getType() });
|
||||
printAST({ arrayDeclNode->getSize() });
|
||||
} else if (auto typeDeclNode = dynamic_cast<ASTNodeTypeDecl*>(node); typeDeclNode != nullptr) {
|
||||
printf("%*c ASTNodeTypeDecl (%s %s)\n", INDENT_VALUE, typeDeclNode->getEndian().value_or(std::endian::native) == std::endian::little ? "le" : "be", typeDeclNode->getName().empty() ? "<unnamed>" : typeDeclNode->getName().data());
|
||||
printAST({ typeDeclNode->getType() });
|
||||
} else if (auto builtinTypeNode = dynamic_cast<ASTNodeBuiltinType*>(node); builtinTypeNode != nullptr) {
|
||||
std::string typeName = Token::getTypeName(builtinTypeNode->getType());
|
||||
printf("%*c ASTNodeTypeDecl (%s)\n", INDENT_VALUE, typeName.c_str());
|
||||
} else if (auto integerLiteralNode = dynamic_cast<ASTNodeIntegerLiteral*>(node); integerLiteralNode != nullptr) {
|
||||
printf("%*c ASTNodeIntegerLiteral %lld\n", INDENT_VALUE, (s64)std::get<s128>(integerLiteralNode->getValue()));
|
||||
} else if (auto numericExpressionNode = dynamic_cast<ASTNodeNumericExpression*>(node); numericExpressionNode != nullptr) {
|
||||
std::string op;
|
||||
switch (numericExpressionNode->getOperator()) {
|
||||
case Token::Operator::Plus: op = "+"; break;
|
||||
case Token::Operator::Minus: op = "-"; break;
|
||||
case Token::Operator::Star: op = "*"; break;
|
||||
case Token::Operator::Slash: op = "/"; break;
|
||||
|
||||
case Token::Operator::ShiftLeft: op = ">>"; break;
|
||||
case Token::Operator::ShiftRight: op = "<<"; break;
|
||||
|
||||
case Token::Operator::BitAnd: op = "&"; break;
|
||||
case Token::Operator::BitOr: op = "|"; break;
|
||||
case Token::Operator::BitXor: op = "^"; break;
|
||||
default: op = "???";
|
||||
}
|
||||
printf("%*c ASTNodeNumericExpression %s\n", INDENT_VALUE, op.c_str());
|
||||
printf("%*c Left:\n", INDENT_VALUE);
|
||||
printAST({ numericExpressionNode->getLeftOperand() });
|
||||
printf("%*c Right:\n", INDENT_VALUE);
|
||||
printAST({ numericExpressionNode->getRightOperand() });
|
||||
} else if (auto structNode = dynamic_cast<ASTNodeStruct*>(node); structNode != nullptr) {
|
||||
printf("%*c ASTNodeStruct\n", INDENT_VALUE);
|
||||
printAST(structNode->getMembers());
|
||||
} else if (auto unionNode = dynamic_cast<ASTNodeUnion*>(node); unionNode != nullptr) {
|
||||
printf("%*c ASTNodeUnion\n", INDENT_VALUE);
|
||||
printAST(unionNode->getMembers());
|
||||
} else if (auto enumNode = dynamic_cast<ASTNodeEnum*>(node); enumNode != nullptr) {
|
||||
printf("%*c ASTNodeEnum\n", INDENT_VALUE);
|
||||
|
||||
for (const auto &[name, entry] : enumNode->getEntries()) {
|
||||
printf("%*c ::%s\n", INDENT_VALUE, name.c_str());
|
||||
printAST({ entry });
|
||||
}
|
||||
} else if (auto bitfieldNode = dynamic_cast<ASTNodeBitfield*>(node); bitfieldNode != nullptr) {
|
||||
printf("%*c ASTNodeBitfield\n", INDENT_VALUE);
|
||||
|
||||
for (const auto &[name, entry] : bitfieldNode->getEntries()) {
|
||||
printf("%*c %s : \n", INDENT_VALUE, name.c_str());
|
||||
printAST({ entry });
|
||||
}
|
||||
} else if (auto rvalueNode = dynamic_cast<ASTNodeRValue*>(node); rvalueNode != nullptr) {
|
||||
printf("%*c ASTNodeRValue\n", INDENT_VALUE);
|
||||
|
||||
printf("%*c ", INDENT_VALUE);
|
||||
for (const auto &path : rvalueNode->getPath())
|
||||
printf("%s.", path.c_str());
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("%*c Invalid AST node!\n", INDENT_VALUE);
|
||||
}
|
||||
}
|
||||
indent -= 2;
|
||||
|
||||
#undef INDENT_VALUE
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
#include "helpers/utils.hpp"
|
||||
#include "window.hpp"
|
||||
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
@ -26,7 +26,7 @@ namespace hex {
|
||||
static std::pair<const char* const, size_t> builtInTypes[] = {
|
||||
{ "u8", 1 }, { "u16", 2 }, { "u32", 4 }, { "u64", 8 }, { "u128", 16 },
|
||||
{ "s8", 1 }, { "s16", 2 }, { "s32", 4 }, { "s64", 8 }, { "s128", 16 },
|
||||
{ "float", 4 }, { "double", 8 }, { "padding", 1 }
|
||||
{ "float", 4 }, { "double", 8 }, { "char", 1 }, { "padding", 1 }
|
||||
};
|
||||
for (const auto &[name, size] : builtInTypes) {
|
||||
TextEditor::Identifier id;
|
||||
@ -251,17 +251,6 @@ namespace hex {
|
||||
lang::PatternData::resetPalette();
|
||||
}
|
||||
|
||||
template<derived_from<lang::ASTNode> T>
|
||||
static std::vector<T*> findNodes(const lang::ASTNode::Type type, const std::vector<lang::ASTNode*> &nodes) {
|
||||
std::vector<T*> result;
|
||||
|
||||
for (const auto & node : nodes)
|
||||
if (node->getType() == type)
|
||||
result.push_back(static_cast<T*>(node));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ViewPattern::parsePattern(char *buffer) {
|
||||
this->clearPatternData();
|
||||
this->m_textEditor.SetErrorMarkers({ });
|
||||
@ -285,14 +274,14 @@ namespace hex {
|
||||
});
|
||||
preprocessor.addDefaultPragmaHandlers();
|
||||
|
||||
auto preprocesedCode = preprocessor.preprocess(buffer);
|
||||
if (!preprocesedCode.has_value()) {
|
||||
auto preprocessedCode = preprocessor.preprocess(buffer);
|
||||
if (!preprocessedCode.has_value()) {
|
||||
this->m_textEditor.SetErrorMarkers({ preprocessor.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
hex::lang::Lexer lexer;
|
||||
auto tokens = lexer.lex(preprocesedCode.value());
|
||||
auto tokens = lexer.lex(preprocessedCode.value());
|
||||
if (!tokens.has_value()) {
|
||||
this->m_textEditor.SetErrorMarkers({ lexer.getError() });
|
||||
return;
|
||||
@ -302,11 +291,10 @@ namespace hex {
|
||||
auto ast = parser.parse(tokens.value());
|
||||
if (!ast.has_value()) {
|
||||
this->m_textEditor.SetErrorMarkers({ parser.getError() });
|
||||
printf("%d %s\n", parser.getError().first, parser.getError().second.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
hex::ScopeExit deleteAst([&ast]{ for(auto &node : ast.value()) delete node; });
|
||||
SCOPE_EXIT( for(auto &node : ast.value()) delete node; );
|
||||
|
||||
hex::lang::Validator validator;
|
||||
auto validatorResult = validator.validate(ast.value());
|
||||
@ -322,8 +310,8 @@ namespace hex {
|
||||
this->m_textEditor.SetErrorMarkers({ evaluator.getError() });
|
||||
return;
|
||||
}
|
||||
this->m_patternData = patternData.value();
|
||||
|
||||
this->m_patternData = patternData.value();
|
||||
this->postEvent(Events::PatternChanged);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user