Read object event gfx info by parsing its C file, rather than relying on regex
This commit is contained in:
parent
ed30115d2e
commit
0b0c588c64
16 changed files with 1599 additions and 5 deletions
21
include/lib/fex/LICENSE
Normal file
21
include/lib/fex/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Ashley Coleman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
include/lib/fex/README
Normal file
2
include/lib/fex/README
Normal file
|
@ -0,0 +1,2 @@
|
|||
This is a slightly-modified library for parsing C code originally written by Ashley Coleman
|
||||
(https://github.com/V-FEXrt).
|
56
include/lib/fex/array.h
Normal file
56
include/lib/fex/array.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef INCLUDE_CORE_ARRAY_H
|
||||
#define INCLUDE_CORE_ARRAY_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "array_value.h"
|
||||
|
||||
namespace fex
|
||||
{
|
||||
class Array
|
||||
{
|
||||
public:
|
||||
Array(std::string type, std::string name) : type_(type), name_(name) {}
|
||||
|
||||
void Add(ArrayValue value)
|
||||
{
|
||||
values_.push_back(std::move(value));
|
||||
}
|
||||
|
||||
const std::string &type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
const std::string &name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
const std::vector<ArrayValue> &values() const
|
||||
{
|
||||
return values_;
|
||||
}
|
||||
|
||||
std::vector<ArrayValue> release_values()
|
||||
{
|
||||
return std::move(values_);
|
||||
}
|
||||
|
||||
std::string ToString() const
|
||||
{
|
||||
std::string out = name_ + ":\n";
|
||||
for (const ArrayValue &v : values_)
|
||||
{
|
||||
out += v.ToString() + "\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string type_;
|
||||
std::string name_;
|
||||
std::vector<ArrayValue> values_;
|
||||
};
|
||||
} // namespace fex
|
||||
|
||||
#endif // INCLUDE_CORE_ARRAY_H
|
151
include/lib/fex/array_value.h
Normal file
151
include/lib/fex/array_value.h
Normal file
|
@ -0,0 +1,151 @@
|
|||
#ifndef INCLUDE_CORE_ARRAY_VALUE_H
|
||||
#define INCLUDE_CORE_ARRAY_VALUE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace fex
|
||||
{
|
||||
class ArrayValue
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
kNumber, // Number literal
|
||||
kString, // String literal
|
||||
kIdentifier, // Identifier
|
||||
|
||||
kValueList, // Value, Value, Value, Value
|
||||
kValuePair, // Identifier = ArrayValue
|
||||
|
||||
kEmpty,
|
||||
};
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
const std::vector<ArrayValue> &values() const
|
||||
{
|
||||
return values_;
|
||||
}
|
||||
|
||||
std::vector<ArrayValue> release_values()
|
||||
{
|
||||
return std::move(values_);
|
||||
}
|
||||
|
||||
const std::pair<std::string, std::unique_ptr<ArrayValue>> &pair() const
|
||||
{
|
||||
return pair_;
|
||||
}
|
||||
|
||||
const std::string &string_value() const
|
||||
{
|
||||
return string_value_;
|
||||
}
|
||||
|
||||
int int_value() const
|
||||
{
|
||||
return int_value_;
|
||||
}
|
||||
|
||||
void set_type(const Type& type)
|
||||
{
|
||||
type_ = type;
|
||||
}
|
||||
|
||||
void set_string_value(const std::string& value)
|
||||
{
|
||||
string_value_ = value;
|
||||
}
|
||||
|
||||
void set_int_value(int value)
|
||||
{
|
||||
int_value_ = value;
|
||||
}
|
||||
|
||||
void set_values(std::vector<ArrayValue> values)
|
||||
{
|
||||
values_ = std::move(values);
|
||||
}
|
||||
|
||||
std::string ToString() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case Type::kEmpty:
|
||||
return "kEmpty: {}";
|
||||
case Type::kNumber:
|
||||
return "kNumber: " + std::to_string(int_value_);
|
||||
case Type::kString:
|
||||
return "kString: \"" + string_value_ + "\"";
|
||||
case Type::kIdentifier:
|
||||
return "kIdentifier: " + string_value_;
|
||||
case Type::kValueList:
|
||||
{
|
||||
std::string out = "kValueList: {\n";
|
||||
for (const ArrayValue &v : values_)
|
||||
{
|
||||
out += "\t" + v.ToString() + ",\n";
|
||||
}
|
||||
return out + "}\n";
|
||||
}
|
||||
case Type::kValuePair:
|
||||
return "kValuePair: " + pair_.first + " = " + pair_.second->ToString() + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
static ArrayValue Empty()
|
||||
{
|
||||
return ArrayValue(ArrayValue::Type::kEmpty);
|
||||
}
|
||||
|
||||
static ArrayValue Number(int value)
|
||||
{
|
||||
return ArrayValue(ArrayValue::Type::kNumber, value);
|
||||
}
|
||||
|
||||
static ArrayValue String(std::string value)
|
||||
{
|
||||
return ArrayValue(ArrayValue::Type::kString, value);
|
||||
}
|
||||
|
||||
static ArrayValue Identifier(std::string value)
|
||||
{
|
||||
return ArrayValue(ArrayValue::Type::kIdentifier, value);
|
||||
}
|
||||
|
||||
static ArrayValue ValueList(std::vector<ArrayValue> values)
|
||||
{
|
||||
return ArrayValue(ArrayValue::Type::kValueList, std::move(values));
|
||||
}
|
||||
static ArrayValue ValuePair(std::pair<std::string, std::unique_ptr<ArrayValue>> value)
|
||||
{
|
||||
return ArrayValue(ArrayValue::Type::kValuePair, std::move(value));
|
||||
}
|
||||
|
||||
ArrayValue(Type type) : type_(type) {}
|
||||
ArrayValue(Type type, int value) : type_(type), int_value_(value) {}
|
||||
ArrayValue(Type type, std::string value) : type_(type), string_value_(value) {}
|
||||
ArrayValue(Type type, std::vector<ArrayValue> values) : type_(type), values_(std::move(values)) {}
|
||||
ArrayValue(Type type, std::pair<std::string, std::unique_ptr<ArrayValue>> pair) : type_(type), pair_(std::move(pair)) {}
|
||||
ArrayValue() {}
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
|
||||
// Number
|
||||
int int_value_;
|
||||
// String, Identifier
|
||||
std::string string_value_;
|
||||
// ValueList
|
||||
std::vector<ArrayValue> values_;
|
||||
// ValuePair
|
||||
std::pair<std::string, std::unique_ptr<ArrayValue>> pair_ = std::pair<std::string, std::unique_ptr<ArrayValue>>("", nullptr);
|
||||
};
|
||||
} // namespace fex
|
||||
|
||||
#endif // INCLUDE_CORE_ARRAY_VALUE_H
|
22
include/lib/fex/define_statement.h
Normal file
22
include/lib/fex/define_statement.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef INCLUDE_CORE_DEFINE_STATEMENT_H
|
||||
#define INCLUDE_CORE_DEFINE_STATEMENT_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace fex
|
||||
{
|
||||
class DefineStatement
|
||||
{
|
||||
public:
|
||||
DefineStatement(std::string name, int value) : name_(name), value_(value) {}
|
||||
|
||||
const std::string &name() const { return name_; }
|
||||
int value() const { return value_; }
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
int value_;
|
||||
};
|
||||
} // namespace fex
|
||||
|
||||
#endif // INCLUDE_CORE_DEFINE_STATEMENT_H
|
121
include/lib/fex/lexer.h
Normal file
121
include/lib/fex/lexer.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
#ifndef INCLUDE_CORE_LEXER_H
|
||||
#define INCLUDE_CORE_LEXER_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fex
|
||||
{
|
||||
class Token
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
// Macros
|
||||
kIfDef,
|
||||
kIfNDef,
|
||||
kDefine,
|
||||
kEndIf,
|
||||
kInclude,
|
||||
|
||||
// Identifiers
|
||||
kIdentifier,
|
||||
|
||||
// Keywords
|
||||
kExtern,
|
||||
kConst,
|
||||
kStruct,
|
||||
|
||||
// Literals
|
||||
kNumber,
|
||||
kString,
|
||||
|
||||
// Symbols
|
||||
kOpenParen,
|
||||
kCloseParen,
|
||||
kLessThan,
|
||||
kGreaterThan,
|
||||
kLessThanEqual,
|
||||
kGreaterThanEqual,
|
||||
kEqual,
|
||||
kLeftShift,
|
||||
kRightShift,
|
||||
kPlus,
|
||||
kMinus,
|
||||
kTimes,
|
||||
kDivide,
|
||||
kBitXor,
|
||||
kBitAnd,
|
||||
kBitOr,
|
||||
kLogicalAnd,
|
||||
kLogicalOr,
|
||||
kQuote,
|
||||
kComma,
|
||||
kSemicolon,
|
||||
kOpenSquare,
|
||||
kCloseSquare,
|
||||
kOpenCurly,
|
||||
kCloseCurly,
|
||||
kPeriod,
|
||||
kUnderscore,
|
||||
};
|
||||
|
||||
Token(Type type, std::string filename, int line_number) : type_(type), filename_(filename), line_number_(line_number) {}
|
||||
Token(Type type, std::string filename, int line_number, std::string string_value) : type_(type), string_value_(string_value) , filename_(filename), line_number_(line_number) {}
|
||||
Token(Type type, std::string filename, int line_number, int int_value) : type_(type), int_value_(int_value), filename_(filename), line_number_(line_number) {}
|
||||
|
||||
Type type() const { return type_; }
|
||||
const std::string &string_value() const { return string_value_; }
|
||||
int int_value() const { return int_value_; }
|
||||
|
||||
const std::string &filename() const { return filename_; }
|
||||
int line_number() const { return line_number_; }
|
||||
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
Type type_;
|
||||
std::string string_value_;
|
||||
int int_value_;
|
||||
|
||||
std::string filename_ = "";
|
||||
int line_number_ = 0;
|
||||
};
|
||||
|
||||
class Lexer
|
||||
{
|
||||
public:
|
||||
Lexer() = default;
|
||||
~Lexer() = default;
|
||||
|
||||
std::vector<Token> LexFile(const std::string &path);
|
||||
std::vector<Token> LexString(const std::string &data);
|
||||
void LexFileDumpTokens(const std::string &path, const std::string &out);
|
||||
|
||||
private:
|
||||
std::vector<Token> Lex();
|
||||
char Peek();
|
||||
char Next();
|
||||
bool IsNumber();
|
||||
bool IsAlpha();
|
||||
bool IsHexAlpha();
|
||||
bool IsAlphaNumber();
|
||||
bool IsWhitespace();
|
||||
|
||||
Token ConsumeIdentifier();
|
||||
Token ConsumeKeyword(Token identifier);
|
||||
Token ConsumeNumber();
|
||||
Token ConsumeString();
|
||||
Token ConsumeMacro();
|
||||
|
||||
std::string ReadIdentifier();
|
||||
|
||||
std::string data_ = "";
|
||||
uint32_t index_ = 0;
|
||||
|
||||
std::string filename_ = "";
|
||||
int line_number_ = 1;
|
||||
};
|
||||
} // namespace fex
|
||||
|
||||
#endif // INCLUDE_CORE_LEXER_H
|
48
include/lib/fex/parser.h
Normal file
48
include/lib/fex/parser.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef INCLUDE_CORE_PARSER_H
|
||||
#define INCLUDE_CORE_PARSER_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "array.h"
|
||||
#include "array_value.h"
|
||||
#include "define_statement.h"
|
||||
#include "lexer.h"
|
||||
|
||||
namespace fex
|
||||
{
|
||||
class Parser
|
||||
{
|
||||
public:
|
||||
Parser() = default;
|
||||
|
||||
std::vector<DefineStatement> Parse(std::vector<Token> tokens);
|
||||
std::vector<Array> ParseTopLevelArrays(std::vector<Token> tokens);
|
||||
std::map<std::string, ArrayValue> ParseTopLevelObjects(std::vector<Token> tokens);
|
||||
|
||||
std::map<std::string, int> ReadDefines(const std::string &filename, std::vector<std::string> matching);
|
||||
|
||||
private:
|
||||
int EvaluateExpression(std::vector<Token> tokens);
|
||||
int ResolveIdentifier();
|
||||
int ResolveIdentifier(const Token &token);
|
||||
int GetPrecedence(const Token &token);
|
||||
bool IsOperator(const Token &token);
|
||||
bool IsParamMacro();
|
||||
std::vector<Token> ToPostfix();
|
||||
DefineStatement ParseDefine();
|
||||
|
||||
ArrayValue ParseObject();
|
||||
|
||||
Token Peek();
|
||||
Token Next();
|
||||
|
||||
unsigned long index_;
|
||||
std::vector<Token> tokens_;
|
||||
|
||||
std::map<std::string, int> top_level_;
|
||||
};
|
||||
} // namespace fex
|
||||
|
||||
#endif // INCLUDE_CORE_PARSER_H
|
19
include/lib/fex/parser_util.h
Normal file
19
include/lib/fex/parser_util.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef PARSER_UTIL_H
|
||||
#define PARSER_UTIL_H
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class ParserUtil
|
||||
{
|
||||
public:
|
||||
ParserUtil(QString root);
|
||||
QStringList ReadDefines(QString filename, QString prefix);
|
||||
QStringList ReadDefinesValueSort(QString filename, QString prefix);
|
||||
|
||||
private:
|
||||
QString root_;
|
||||
};
|
||||
|
||||
|
||||
#endif // PARSER_UTIL_H
|
|
@ -187,6 +187,7 @@ public:
|
|||
bool readObjEventGfxConstants();
|
||||
bool readSongNames();
|
||||
bool readEventGraphics();
|
||||
QMap<QString, QMap<QString, QString>> readObjEventGfxInfo();
|
||||
|
||||
void setEventPixmap(Event * event, bool forceLoad = false);
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ SOURCES += src/core/block.cpp \
|
|||
src/core/regionmap.cpp \
|
||||
src/core/wildmoninfo.cpp \
|
||||
src/core/editcommands.cpp \
|
||||
src/lib/fex/lexer.cpp \
|
||||
src/lib/fex/parser.cpp \
|
||||
src/lib/fex/parser_util.cpp \
|
||||
src/lib/orderedjson.cpp \
|
||||
src/core/regionmapeditcommands.cpp \
|
||||
src/mainwindow_scriptapi.cpp \
|
||||
|
@ -108,6 +111,12 @@ HEADERS += include/core/block.h \
|
|||
include/core/wildmoninfo.h \
|
||||
include/core/editcommands.h \
|
||||
include/core/regionmapeditcommands.h \
|
||||
include/lib/fex/array.h \
|
||||
include/lib/fex/array_value.h \
|
||||
include/lib/fex/define_statement.h \
|
||||
include/lib/fex/lexer.h \
|
||||
include/lib/fex/parser.h \
|
||||
include/lib/fex/parser_util.h \
|
||||
include/lib/orderedmap.h \
|
||||
include/lib/orderedjson.h \
|
||||
include/ui/aboutporymap.h \
|
||||
|
|
21
src/lib/fex/LICENSE
Normal file
21
src/lib/fex/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Ashley Coleman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
2
src/lib/fex/README
Normal file
2
src/lib/fex/README
Normal file
|
@ -0,0 +1,2 @@
|
|||
This is a slightly-modified library for parsing C code originally written by Ashley Coleman
|
||||
(https://github.com/V-FEXrt).
|
505
src/lib/fex/lexer.cpp
Normal file
505
src/lib/fex/lexer.cpp
Normal file
|
@ -0,0 +1,505 @@
|
|||
#include "lib/fex/lexer.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace fex
|
||||
{
|
||||
|
||||
bool Lexer::IsNumber()
|
||||
{
|
||||
char c = Peek();
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
bool Lexer::IsWhitespace()
|
||||
{
|
||||
char c = Peek();
|
||||
return (c == ' ' || c == '\t' || c == '\r' || c == '\n');
|
||||
}
|
||||
|
||||
bool Lexer::IsHexAlpha()
|
||||
{
|
||||
char c = Peek();
|
||||
return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
||||
}
|
||||
|
||||
bool Lexer::IsAlpha()
|
||||
{
|
||||
char c = Peek();
|
||||
return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
|
||||
}
|
||||
|
||||
bool Lexer::IsAlphaNumber()
|
||||
{
|
||||
return IsAlpha() || IsNumber();
|
||||
};
|
||||
|
||||
char Lexer::Peek()
|
||||
{
|
||||
return data_[index_];
|
||||
}
|
||||
|
||||
char Lexer::Next()
|
||||
{
|
||||
char c = Peek();
|
||||
index_++;
|
||||
return c;
|
||||
}
|
||||
|
||||
Token Lexer::ConsumeKeyword(Token identifier)
|
||||
{
|
||||
const std::string &value = identifier.string_value();
|
||||
|
||||
if (value == "extern")
|
||||
{
|
||||
return Token(Token::Type::kExtern, identifier.filename(), identifier.line_number());
|
||||
}
|
||||
if (value == "const")
|
||||
{
|
||||
return Token(Token::Type::kConst, identifier.filename(), identifier.line_number());
|
||||
}
|
||||
if (value == "struct")
|
||||
{
|
||||
return Token(Token::Type::kStruct, identifier.filename(), identifier.line_number());
|
||||
}
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
Token Lexer::ConsumeIdentifier()
|
||||
{
|
||||
std::string identifer = "";
|
||||
|
||||
while (IsAlphaNumber() || Peek() == '_')
|
||||
{
|
||||
identifer += Next();
|
||||
}
|
||||
|
||||
return ConsumeKeyword(Token(Token::Type::kIdentifier, filename_, line_number_, identifer));
|
||||
}
|
||||
|
||||
Token Lexer::ConsumeNumber()
|
||||
{
|
||||
std::string identifer = "";
|
||||
|
||||
if (Peek() == '0')
|
||||
{
|
||||
identifer += Next();
|
||||
if (Peek() == 'x')
|
||||
{
|
||||
identifer += Next();
|
||||
}
|
||||
|
||||
while (IsNumber() || IsHexAlpha())
|
||||
{
|
||||
identifer += Next();
|
||||
}
|
||||
|
||||
return Token(Token::Type::kNumber, filename_, line_number_, std::stoi(identifer, nullptr, 16));
|
||||
}
|
||||
|
||||
while (IsNumber())
|
||||
{
|
||||
identifer += Next();
|
||||
}
|
||||
|
||||
return Token(Token::Type::kNumber, filename_, line_number_, std::stoi(identifer));
|
||||
}
|
||||
|
||||
// TODO: Doesn't currently support escape characters
|
||||
Token Lexer::ConsumeString()
|
||||
{
|
||||
std::string value = "";
|
||||
if (Next() != '\"')
|
||||
{
|
||||
// Error
|
||||
}
|
||||
|
||||
// TODO: error if we never see a quote
|
||||
while (Peek() != '\"')
|
||||
{
|
||||
value += Next();
|
||||
}
|
||||
Next(); // Consume final quote
|
||||
return Token(Token::Type::kString, filename_, line_number_, value);
|
||||
}
|
||||
|
||||
Token Lexer::ConsumeMacro()
|
||||
{
|
||||
Token id = ConsumeIdentifier();
|
||||
|
||||
if (id.string_value() == "ifdef")
|
||||
{
|
||||
return Token(Token::Type::kIfDef, filename_, line_number_);
|
||||
}
|
||||
if (id.string_value() == "ifndef")
|
||||
{
|
||||
return Token(Token::Type::kIfNDef, filename_, line_number_);
|
||||
}
|
||||
if (id.string_value() == "define")
|
||||
{
|
||||
return Token(Token::Type::kDefine, filename_, line_number_);
|
||||
}
|
||||
if (id.string_value() == "endif")
|
||||
{
|
||||
return Token(Token::Type::kEndIf, filename_, line_number_);
|
||||
}
|
||||
|
||||
if (id.string_value() == "include")
|
||||
{
|
||||
return Token(Token::Type::kInclude, filename_, line_number_);
|
||||
}
|
||||
|
||||
return Token(Token::Type::kDefine, filename_, line_number_);
|
||||
}
|
||||
|
||||
std::vector<Token> Lexer::LexString(const std::string &data)
|
||||
{
|
||||
filename_ = "string literal";
|
||||
line_number_ = 1;
|
||||
index_ = 0;
|
||||
data_ = data;
|
||||
|
||||
return Lex();
|
||||
}
|
||||
|
||||
std::vector<Token> Lexer::LexFile(const std::string &path)
|
||||
{
|
||||
filename_ = path;
|
||||
line_number_ = 1;
|
||||
|
||||
std::ifstream file;
|
||||
file.open(path);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << file.rdbuf();
|
||||
|
||||
index_ = 0;
|
||||
data_ = stream.str();
|
||||
|
||||
file.close();
|
||||
|
||||
return Lex();
|
||||
}
|
||||
|
||||
void Lexer::LexFileDumpTokens(const std::string &path, const std::string &out)
|
||||
{
|
||||
std::ofstream file;
|
||||
file.open(out);
|
||||
|
||||
for (Token token : LexFile(path))
|
||||
{
|
||||
file << token.ToString() << std::endl;
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
std::vector<Token> Lexer::Lex()
|
||||
{
|
||||
std::vector<Token> tokens;
|
||||
|
||||
while (index_ < data_.length())
|
||||
{
|
||||
while (IsWhitespace())
|
||||
{
|
||||
if (Peek() == '\n')
|
||||
{
|
||||
line_number_++;
|
||||
}
|
||||
Next();
|
||||
}
|
||||
|
||||
if (IsAlpha())
|
||||
{
|
||||
tokens.push_back(ConsumeIdentifier());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsNumber())
|
||||
{
|
||||
tokens.push_back(ConsumeNumber());
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (Peek())
|
||||
{
|
||||
case '*':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kTimes, filename_, line_number_));
|
||||
break;
|
||||
case '-':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kMinus, filename_, line_number_));
|
||||
break;
|
||||
case '+':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kPlus, filename_, line_number_));
|
||||
break;
|
||||
case '(':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kOpenParen, filename_, line_number_));
|
||||
break;
|
||||
case ')':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kCloseParen, filename_, line_number_));
|
||||
break;
|
||||
case '&':
|
||||
Next();
|
||||
if (Peek() == '&')
|
||||
{
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kLogicalAnd, filename_, line_number_));
|
||||
break;
|
||||
}
|
||||
tokens.push_back(Token(Token::Type::kBitAnd, filename_, line_number_));
|
||||
break;
|
||||
case '|':
|
||||
Next();
|
||||
if (Peek() == '|')
|
||||
{
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kLogicalOr, filename_, line_number_));
|
||||
break;
|
||||
}
|
||||
tokens.push_back(Token(Token::Type::kBitOr, filename_, line_number_));
|
||||
break;
|
||||
case '^':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kBitXor, filename_, line_number_));
|
||||
break;
|
||||
case ',':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kComma, filename_, line_number_));
|
||||
break;
|
||||
case '=':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kEqual, filename_, line_number_));
|
||||
break;
|
||||
case ';':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kSemicolon, filename_, line_number_));
|
||||
break;
|
||||
case '[':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kOpenSquare, filename_, line_number_));
|
||||
break;
|
||||
case ']':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kCloseSquare, filename_, line_number_));
|
||||
break;
|
||||
case '{':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kOpenCurly, filename_, line_number_));
|
||||
break;
|
||||
case '}':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kCloseCurly, filename_, line_number_));
|
||||
break;
|
||||
case '.':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kPeriod, filename_, line_number_));
|
||||
break;
|
||||
case '_':
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kUnderscore, filename_, line_number_));
|
||||
break;
|
||||
case '#':
|
||||
Next();
|
||||
tokens.push_back(ConsumeMacro());
|
||||
break;
|
||||
case '\"':
|
||||
tokens.push_back(ConsumeString());
|
||||
break;
|
||||
case '<':
|
||||
Next();
|
||||
if (Peek() == '<')
|
||||
{
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kLeftShift, filename_, line_number_));
|
||||
break;
|
||||
}
|
||||
if (Peek() == '=')
|
||||
{
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kLessThanEqual, filename_, line_number_));
|
||||
break;
|
||||
}
|
||||
tokens.push_back(Token(Token::Type::kLessThan, filename_, line_number_));
|
||||
break;
|
||||
case '>':
|
||||
Next();
|
||||
if (Peek() == '>')
|
||||
{
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kRightShift, filename_, line_number_));
|
||||
break;
|
||||
}
|
||||
if (Peek() == '=')
|
||||
{
|
||||
Next();
|
||||
tokens.push_back(Token(Token::Type::kGreaterThanEqual, filename_, line_number_));
|
||||
break;
|
||||
}
|
||||
tokens.push_back(Token(Token::Type::kGreaterThan, filename_, line_number_));
|
||||
break;
|
||||
|
||||
case '/':
|
||||
Next();
|
||||
switch (Peek())
|
||||
{
|
||||
case '/':
|
||||
while (Next() != '\n')
|
||||
;
|
||||
continue;
|
||||
case '*':
|
||||
while (Next() != '*')
|
||||
;
|
||||
Next(); // last /
|
||||
continue;
|
||||
default:
|
||||
tokens.push_back(Token(Token::Type::kDivide, filename_, line_number_));
|
||||
continue;
|
||||
}
|
||||
|
||||
case '\0':
|
||||
Next();
|
||||
break;
|
||||
|
||||
default:
|
||||
char c = Next();
|
||||
std::cout << "[WARNING] Unable to lex unknown char: '" << c << "' (0x" << std::hex << (int)c << ")" << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
std::string Token::ToString() const
|
||||
{
|
||||
std::string out = filename() + ":" + std::to_string(line_number()) + " - ";
|
||||
switch (type())
|
||||
{
|
||||
case Token::Type::kIfDef:
|
||||
out += "Macro: IfDef";
|
||||
break;
|
||||
case Token::Type::kIfNDef:
|
||||
out += "Macro: IfNDef";
|
||||
break;
|
||||
case Token::Type::kDefine:
|
||||
out += "Macro: Define";
|
||||
break;
|
||||
case Token::Type::kEndIf:
|
||||
out += "Macro: EndIf";
|
||||
break;
|
||||
case Token::Type::kInclude:
|
||||
out += "Macro: Include";
|
||||
break;
|
||||
case Token::Type::kNumber:
|
||||
out += "Number: " + std::to_string(int_value());
|
||||
break;
|
||||
case Token::Type::kString:
|
||||
out += "String: " + string_value();
|
||||
break;
|
||||
case Token::Type::kIdentifier:
|
||||
out += "Identifier: " + string_value();
|
||||
break;
|
||||
case Token::Type::kOpenParen:
|
||||
out += "Symbol: (";
|
||||
break;
|
||||
case Token::Type::kCloseParen:
|
||||
out += "Symbol: )";
|
||||
break;
|
||||
case Token::Type::kLessThan:
|
||||
out += "Symbol: <";
|
||||
break;
|
||||
case Token::Type::kGreaterThan:
|
||||
out += "Symbol: >";
|
||||
break;
|
||||
case Token::Type::kLeftShift:
|
||||
out += "Symbol: <<";
|
||||
break;
|
||||
case Token::Type::kRightShift:
|
||||
out += "Symbol: >>";
|
||||
break;
|
||||
case Token::Type::kPlus:
|
||||
out += "Symbol: +";
|
||||
break;
|
||||
case Token::Type::kMinus:
|
||||
out += "Symbol: -";
|
||||
break;
|
||||
case Token::Type::kTimes:
|
||||
out += "Symbol: *";
|
||||
break;
|
||||
case Token::Type::kDivide:
|
||||
out += "Symbol: /";
|
||||
break;
|
||||
case Token::Type::kBitXor:
|
||||
out += "Symbol: ^";
|
||||
break;
|
||||
case Token::Type::kBitAnd:
|
||||
out += "Symbol: &";
|
||||
break;
|
||||
case Token::Type::kBitOr:
|
||||
out += "Symbol: |";
|
||||
break;
|
||||
case Token::Type::kQuote:
|
||||
out += "Symbol: \"";
|
||||
break;
|
||||
case Token::Type::kComma:
|
||||
out += "Symbol: ,";
|
||||
break;
|
||||
case Token::Type::kLessThanEqual:
|
||||
out += "Symbol: <=";
|
||||
break;
|
||||
case Token::Type::kGreaterThanEqual:
|
||||
out += "Symbol: >=";
|
||||
break;
|
||||
case Token::Type::kEqual:
|
||||
out += "Symbol: =";
|
||||
break;
|
||||
case Token::Type::kLogicalAnd:
|
||||
out += "Symbol: &&";
|
||||
break;
|
||||
case Token::Type::kLogicalOr:
|
||||
out += "Symbol: ||";
|
||||
break;
|
||||
case Token::Type::kSemicolon:
|
||||
out += "Symbol: ;";
|
||||
break;
|
||||
case Token::Type::kExtern:
|
||||
out += "Keyword: extern";
|
||||
break;
|
||||
case Token::Type::kConst:
|
||||
out += "Keyword: const";
|
||||
break;
|
||||
case Token::Type::kStruct:
|
||||
out += "Keyword: struct";
|
||||
break;
|
||||
case Token::Type::kOpenSquare:
|
||||
out += "Symbol: [";
|
||||
break;
|
||||
case Token::Type::kCloseSquare:
|
||||
out += "Symbol: ]";
|
||||
break;
|
||||
case Token::Type::kOpenCurly:
|
||||
out += "Symbol: {";
|
||||
break;
|
||||
case Token::Type::kCloseCurly:
|
||||
out += "Symbol: }";
|
||||
break;
|
||||
case Token::Type::kPeriod:
|
||||
out += "Symbol: .";
|
||||
break;
|
||||
case Token::Type::kUnderscore:
|
||||
out += "Symbol: _";
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace fex
|
538
src/lib/fex/parser.cpp
Normal file
538
src/lib/fex/parser.cpp
Normal file
|
@ -0,0 +1,538 @@
|
|||
#include "lib/fex/parser.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
namespace fex
|
||||
{
|
||||
int Parser::GetPrecedence(const Token &token)
|
||||
{
|
||||
switch (token.type())
|
||||
{
|
||||
case Token::Type::kTimes:
|
||||
return 3;
|
||||
case Token::Type::kDivide:
|
||||
return 3;
|
||||
case Token::Type::kPlus:
|
||||
return 4;
|
||||
case Token::Type::kMinus:
|
||||
return 4;
|
||||
case Token::Type::kLeftShift:
|
||||
return 5;
|
||||
case Token::Type::kRightShift:
|
||||
return 5;
|
||||
case Token::Type::kBitAnd:
|
||||
return 8;
|
||||
case Token::Type::kBitXor:
|
||||
return 9;
|
||||
case Token::Type::kBitOr:
|
||||
return 10;
|
||||
|
||||
default:
|
||||
{
|
||||
std::cout << "Asked for precedence of unmapped token: " << token.ToString() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Token> Parser::ToPostfix()
|
||||
{
|
||||
std::vector<Token::Type> types = {
|
||||
Token::Type::kNumber,
|
||||
Token::Type::kIdentifier,
|
||||
Token::Type::kOpenParen,
|
||||
Token::Type::kCloseParen,
|
||||
Token::Type::kLeftShift,
|
||||
Token::Type::kRightShift,
|
||||
Token::Type::kPlus,
|
||||
Token::Type::kMinus,
|
||||
Token::Type::kTimes,
|
||||
Token::Type::kDivide,
|
||||
Token::Type::kBitXor,
|
||||
Token::Type::kBitAnd,
|
||||
Token::Type::kBitOr,
|
||||
};
|
||||
|
||||
std::vector<Token> output;
|
||||
std::vector<Token> stack;
|
||||
|
||||
while (std::find(types.begin(), types.end(), Peek().type()) != types.end())
|
||||
{
|
||||
Token token = Next();
|
||||
if (token.type() == Token::Type::kNumber || token.type() == Token::Type::kIdentifier)
|
||||
{
|
||||
output.push_back(token);
|
||||
}
|
||||
else if (token.type() == Token::Type::kOpenParen)
|
||||
{
|
||||
stack.push_back(token);
|
||||
}
|
||||
else if (token.type() == Token::Type::kCloseParen)
|
||||
{
|
||||
while (!stack.empty() && stack.back().type() != Token::Type::kOpenParen)
|
||||
{
|
||||
Token back = stack.back();
|
||||
stack.pop_back();
|
||||
output.push_back(back);
|
||||
}
|
||||
|
||||
// Next();
|
||||
|
||||
if (!stack.empty())
|
||||
{
|
||||
// pop the left parenthesis token
|
||||
stack.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Mismatched parentheses detected in expression!" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// token is an operator
|
||||
while (!stack.empty() && stack.back().type() != Token::Type::kOpenParen && GetPrecedence(stack.back()) <= GetPrecedence(token))
|
||||
{
|
||||
Token back = stack.back();
|
||||
stack.pop_back();
|
||||
output.push_back(back);
|
||||
}
|
||||
stack.push_back(token);
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
if (stack.back().type() == Token::Type::kOpenParen || stack.back().type() == Token::Type::kCloseParen)
|
||||
{
|
||||
std::cout << "Mismatched parentheses detected in expression!" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Token back = stack.back();
|
||||
stack.pop_back();
|
||||
output.push_back(back);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
Token Parser::Peek() { return tokens_[index_]; }
|
||||
|
||||
Token Parser::Next()
|
||||
{
|
||||
Token t = Peek();
|
||||
index_++;
|
||||
return t;
|
||||
}
|
||||
|
||||
int Parser::ResolveIdentifier(const Token &token)
|
||||
{
|
||||
std::string iden_val = token.string_value();
|
||||
|
||||
if (top_level_.find(iden_val) == top_level_.end())
|
||||
{
|
||||
std::cout << "[WARNING] Unknown identifier " << iden_val << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return top_level_[iden_val];
|
||||
}
|
||||
|
||||
bool Parser::IsOperator(const Token &token)
|
||||
{
|
||||
std::vector<Token::Type> types = {
|
||||
Token::Type::kLeftShift,
|
||||
Token::Type::kRightShift,
|
||||
Token::Type::kPlus,
|
||||
Token::Type::kMinus,
|
||||
Token::Type::kTimes,
|
||||
Token::Type::kDivide,
|
||||
Token::Type::kBitXor,
|
||||
Token::Type::kBitAnd,
|
||||
Token::Type::kBitOr,
|
||||
};
|
||||
return std::find(types.begin(), types.end(), token.type()) != types.end();
|
||||
}
|
||||
|
||||
int Parser::EvaluateExpression(std::vector<Token> tokens)
|
||||
{
|
||||
std::vector<Token> stack;
|
||||
for (Token token : tokens)
|
||||
{
|
||||
if (IsOperator(token) && stack.size() > 1)
|
||||
{
|
||||
int op2 = stack.back().int_value();
|
||||
stack.pop_back();
|
||||
int op1 = stack.back().int_value();
|
||||
stack.pop_back();
|
||||
int result = 0;
|
||||
if (token.type() == Token::Type::kTimes)
|
||||
{
|
||||
result = op1 * op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kDivide)
|
||||
{
|
||||
result = op1 / op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kPlus)
|
||||
{
|
||||
result = op1 + op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kMinus)
|
||||
{
|
||||
result = op1 - op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kLeftShift)
|
||||
{
|
||||
result = op1 << op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kRightShift)
|
||||
{
|
||||
result = op1 >> op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kBitAnd)
|
||||
{
|
||||
result = op1 & op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kBitXor)
|
||||
{
|
||||
result = op1 ^ op2;
|
||||
}
|
||||
if (token.type() == Token::Type::kBitOr)
|
||||
{
|
||||
result = op1 | op2;
|
||||
}
|
||||
|
||||
stack.push_back(Token(Token::Type::kNumber, token.filename(), token.line_number(), result));
|
||||
}
|
||||
|
||||
if (token.type() == Token::Type::kNumber)
|
||||
{
|
||||
stack.push_back(token);
|
||||
}
|
||||
|
||||
if (token.type() == Token::Type::kIdentifier)
|
||||
{
|
||||
stack.push_back(Token(Token::Type::kNumber, token.filename(), token.line_number(), ResolveIdentifier(token)));
|
||||
}
|
||||
}
|
||||
return stack.size() ? stack.back().int_value() : 0;
|
||||
}
|
||||
|
||||
bool Parser::IsParamMacro()
|
||||
{
|
||||
int save_index = index_;
|
||||
|
||||
if (Peek().type() != Token::Type::kOpenParen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Next(); // Consume open so next if doesn't see it
|
||||
|
||||
while (Peek().type() != Token::Type::kCloseParen)
|
||||
{
|
||||
// Nested parens aren't allowed in param list
|
||||
if (Peek().type() == Token::Type::kOpenParen)
|
||||
{
|
||||
index_ = save_index;
|
||||
return false;
|
||||
}
|
||||
|
||||
Next();
|
||||
}
|
||||
// Consume closing
|
||||
Next();
|
||||
|
||||
std::vector<Token::Type> types = {
|
||||
Token::Type::kNumber,
|
||||
Token::Type::kIdentifier,
|
||||
Token::Type::kOpenParen,
|
||||
Token::Type::kCloseParen,
|
||||
Token::Type::kLeftShift,
|
||||
Token::Type::kRightShift,
|
||||
Token::Type::kPlus,
|
||||
Token::Type::kMinus,
|
||||
Token::Type::kTimes,
|
||||
Token::Type::kDivide,
|
||||
Token::Type::kBitXor,
|
||||
Token::Type::kBitAnd,
|
||||
Token::Type::kBitOr,
|
||||
};
|
||||
|
||||
// read value before resetting.
|
||||
bool out = std::find(types.begin(), types.end(), Peek().type()) != types.end();
|
||||
index_ = save_index;
|
||||
return out;
|
||||
}
|
||||
|
||||
DefineStatement Parser::ParseDefine()
|
||||
{
|
||||
if (Next().type() != Token::Type::kDefine)
|
||||
{
|
||||
// error
|
||||
}
|
||||
|
||||
if (Peek().type() != Token::Type::kIdentifier)
|
||||
{
|
||||
// error
|
||||
}
|
||||
|
||||
std::string identifer = Next().string_value();
|
||||
int value = 0;
|
||||
|
||||
if (IsParamMacro())
|
||||
{
|
||||
std::cout << "[WARNING] Macro:" << identifer << " has parameters which is not currently supported. Returning a dummy value instead." << std::endl;
|
||||
value = 0xDEAD;
|
||||
// Parameters (x, y, x) Expression
|
||||
Next();
|
||||
|
||||
while (Peek().type() != Token::Type::kCloseParen)
|
||||
{
|
||||
auto formal = Next().string_value();
|
||||
if (Peek().type() == Token::Type::kComma)
|
||||
{
|
||||
Next();
|
||||
}
|
||||
}
|
||||
|
||||
Next();
|
||||
|
||||
// In all current use cases, the macro is #define MACRO(a, b, c) (( something ))
|
||||
// we have consumed through the parameter list at this point. Consume all remaing
|
||||
// contents inside of parens
|
||||
if (Peek().type() != Token::Type::kOpenParen)
|
||||
{
|
||||
std::cout << "[FATAL] Must seen open parenthesis to continue processing." << std::endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
Next();
|
||||
int paren_count = 1;
|
||||
while (paren_count > 0)
|
||||
{
|
||||
if (Peek().type() == Token::Type::kOpenParen)
|
||||
{
|
||||
paren_count++;
|
||||
}
|
||||
if (Peek().type() == Token::Type::kCloseParen)
|
||||
{
|
||||
paren_count--;
|
||||
}
|
||||
|
||||
Next();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value = EvaluateExpression(ToPostfix());
|
||||
}
|
||||
|
||||
top_level_[identifer] = value;
|
||||
return DefineStatement(identifer, value);
|
||||
}
|
||||
|
||||
std::map<std::string, int> Parser::ReadDefines(const std::string &filename, std::vector<std::string> matching)
|
||||
{
|
||||
std::map<std::string, int> out;
|
||||
|
||||
Lexer lexer;
|
||||
auto tokens = lexer.LexFile(filename);
|
||||
auto defines = Parse(tokens);
|
||||
|
||||
for (const auto &define : defines)
|
||||
{
|
||||
for (const std::string &match : matching)
|
||||
{
|
||||
if (std::regex_match(define.name(), std::regex(match)))
|
||||
{
|
||||
out[define.name()] = define.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
ArrayValue Parser::ParseObject()
|
||||
{
|
||||
if (Peek().type() == Token::Type::kOpenSquare)
|
||||
{
|
||||
Next(); // [
|
||||
std::string identifier = Next().string_value();
|
||||
Next(); // ]
|
||||
Next(); // =
|
||||
std::unique_ptr<ArrayValue> value = std::unique_ptr<ArrayValue>(new ArrayValue(ParseObject()));
|
||||
|
||||
std::pair<std::string, std::unique_ptr<ArrayValue>> pair(identifier, std::move(value));
|
||||
return ArrayValue::ValuePair(std::move(pair));
|
||||
}
|
||||
|
||||
if (Peek().type() == Token::Type::kOpenCurly)
|
||||
{
|
||||
std::vector<ArrayValue> values;
|
||||
Next(); // {
|
||||
values.push_back(ParseObject());
|
||||
while(Peek().type() == Token::Type::kComma) {
|
||||
Next();
|
||||
values.push_back(ParseObject());
|
||||
}
|
||||
Next(); // }
|
||||
|
||||
if (values.size() == 1) {
|
||||
return std::move(values[0]);
|
||||
}
|
||||
|
||||
return ArrayValue::ValueList(std::move(values));
|
||||
}
|
||||
|
||||
if (Peek().type() == Token::Type::kNumber)
|
||||
{
|
||||
int value = Next().int_value();
|
||||
return ArrayValue::Number(value);
|
||||
}
|
||||
|
||||
if (Peek().type() == Token::Type::kBitAnd)
|
||||
{
|
||||
// Just skip past any reference indicators before identifiers.
|
||||
// This is not the right way to handle this, but it's good enough
|
||||
// for our parsing needs.
|
||||
Next(); // &
|
||||
}
|
||||
|
||||
if (Peek().type() == Token::Type::kIdentifier)
|
||||
{
|
||||
std::vector<ArrayValue> idens = {};
|
||||
idens.push_back(ArrayValue::Identifier(Next().string_value()));
|
||||
|
||||
// NELEMS(...)
|
||||
if (Peek().type() == Token::Type::kOpenParen)
|
||||
{
|
||||
while (Peek().type() != Token::Type::kCloseParen)
|
||||
{
|
||||
std::string out = Next().ToString();
|
||||
}
|
||||
Next(); // )
|
||||
}
|
||||
|
||||
// ABC | DEF | GHI
|
||||
while (Peek().type() == Token::Type::kBitOr) {
|
||||
Next();
|
||||
idens.push_back(ArrayValue::Identifier(Next().string_value()));
|
||||
}
|
||||
|
||||
if (idens.size() == 1)
|
||||
{
|
||||
return std::move(idens[0]);
|
||||
}
|
||||
|
||||
return ArrayValue::ValueList(std::move(idens));
|
||||
}
|
||||
|
||||
if (Peek().type() == Token::Type::kUnderscore)
|
||||
{
|
||||
Next(); // _
|
||||
Next(); // (
|
||||
std::string value = Next().string_value();
|
||||
Next(); // )
|
||||
return ArrayValue::String(value);
|
||||
}
|
||||
|
||||
if (Peek().type() == Token::Type::kPeriod)
|
||||
{
|
||||
Next(); // .
|
||||
std::string identifier = Next().string_value();
|
||||
Next(); // =
|
||||
|
||||
std::unique_ptr<ArrayValue> value = std::unique_ptr<ArrayValue>(new ArrayValue(ParseObject()));
|
||||
|
||||
std::pair<std::string, std::unique_ptr<ArrayValue>> pair(identifier, std::move(value));
|
||||
return ArrayValue::ValuePair(std::move(pair));
|
||||
}
|
||||
|
||||
return ArrayValue::Empty();
|
||||
}
|
||||
|
||||
std::vector<Array> Parser::ParseTopLevelArrays(std::vector<Token> tokens)
|
||||
{
|
||||
index_ = 0;
|
||||
tokens_ = std::move(tokens);
|
||||
|
||||
std::vector<Array> items;
|
||||
|
||||
while (index_ < tokens_.size())
|
||||
{
|
||||
while (Next().type() != Token::Type::kConst)
|
||||
;
|
||||
Next(); // struct
|
||||
|
||||
std::string type = Next().string_value();
|
||||
std::string name = Next().string_value();
|
||||
|
||||
Array value(type, name);
|
||||
|
||||
Next(); // [
|
||||
Next(); // ]
|
||||
Next(); // =
|
||||
value.Add(ParseObject());
|
||||
Next(); // ;
|
||||
|
||||
items.push_back(std::move(value));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
std::map<std::string, ArrayValue> Parser::ParseTopLevelObjects(std::vector<Token> tokens)
|
||||
{
|
||||
index_ = 0;
|
||||
tokens_ = std::move(tokens);
|
||||
|
||||
std::map<std::string, ArrayValue> items;
|
||||
|
||||
while (index_ < tokens_.size())
|
||||
{
|
||||
while (Next().type() != Token::Type::kConst)
|
||||
;
|
||||
Next(); // struct
|
||||
|
||||
Next(); // type
|
||||
std::string name = Next().string_value();
|
||||
|
||||
Next(); // =
|
||||
items[name] = ParseObject();
|
||||
Next(); // ;
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
std::vector<DefineStatement> Parser::Parse(std::vector<Token> tokens)
|
||||
{
|
||||
index_ = 0;
|
||||
tokens_ = std::move(tokens);
|
||||
std::vector<DefineStatement> statements;
|
||||
|
||||
while (index_ < tokens_.size())
|
||||
{
|
||||
switch (Peek().type())
|
||||
{
|
||||
case Token::Type::kDefine:
|
||||
statements.push_back(ParseDefine());
|
||||
break;
|
||||
|
||||
default:
|
||||
Next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
} // namespace fex
|
50
src/lib/fex/parser_util.cpp
Normal file
50
src/lib/fex/parser_util.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "lib/fex/parser_util.h"
|
||||
|
||||
#include <QMap>
|
||||
|
||||
#include "lib/fex/parser.h"
|
||||
|
||||
ParserUtil::ParserUtil(QString root): root_(root) {}
|
||||
|
||||
QStringList ParserUtil::ReadDefines(QString filename, QString prefix)
|
||||
{
|
||||
if (filename.isEmpty()) {
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QString filepath = root_ + "/" + filename;
|
||||
|
||||
fex::Parser parser;
|
||||
|
||||
std::vector<std::string> match_list = { prefix.toStdString() + ".*" };
|
||||
std::map<std::string, int> defines = parser.ReadDefines(filepath.toStdString(), match_list);
|
||||
|
||||
QStringList out;
|
||||
for(auto const& define : defines) {
|
||||
out.append(QString::fromStdString(define.first));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
QStringList ParserUtil::ReadDefinesValueSort(QString filename, QString prefix)
|
||||
{
|
||||
|
||||
if (filename.isEmpty()) {
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QString filepath = root_ + "/" + filename;
|
||||
|
||||
fex::Parser parser;
|
||||
|
||||
std::vector<std::string> match_list = { prefix.toStdString() + ".*" };
|
||||
std::map<std::string, int> defines = parser.ReadDefines(filepath.toStdString(), match_list);
|
||||
|
||||
QMultiMap<int, QString> defines_keyed_by_value;
|
||||
for (const auto& pair : defines) {
|
||||
defines_keyed_by_value.insert(pair.second, QString::fromStdString(pair.first));
|
||||
}
|
||||
|
||||
return defines_keyed_by_value.values();
|
||||
}
|
|
@ -10,6 +10,8 @@
|
|||
#include "map.h"
|
||||
|
||||
#include "orderedjson.h"
|
||||
#include "lib/fex/lexer.h"
|
||||
#include "lib/fex/parser.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QJsonArray>
|
||||
|
@ -2471,16 +2473,20 @@ bool Project::readEventGraphics() {
|
|||
qDeleteAll(eventGraphicsMap);
|
||||
eventGraphicsMap.clear();
|
||||
QStringList gfxNames = gfxDefines.keys();
|
||||
QMap<QString, QMap<QString, QString>> gfxInfos = readObjEventGfxInfo();
|
||||
for (QString gfxName : gfxNames) {
|
||||
EventGraphics * eventGraphics = new EventGraphics;
|
||||
|
||||
QString info_label = pointerHash[gfxName].replace("&", "");
|
||||
QStringList gfx_info = parser.readCArray("src/data/object_events/object_event_graphics_info.h", info_label);
|
||||
if (!gfxInfos.contains(info_label))
|
||||
continue;
|
||||
|
||||
eventGraphics->inanimate = (gfx_info.value(8) == "TRUE");
|
||||
QString pic_label = gfx_info.value(14);
|
||||
QString dimensions_label = gfx_info.value(11);
|
||||
QString subsprites_label = gfx_info.value(12);
|
||||
QMap<QString, QString>gfxInfoAttributes = gfxInfos[info_label];
|
||||
|
||||
eventGraphics->inanimate = gfxInfoAttributes.value("inanimate") == "TRUE";
|
||||
QString pic_label = gfxInfoAttributes.value("images");
|
||||
QString dimensions_label = gfxInfoAttributes.value("oam");
|
||||
QString subsprites_label = gfxInfoAttributes.value("subspriteTables");
|
||||
|
||||
QString gfx_label = parser.readCArray("src/data/object_events/object_event_pic_tables.h", pic_label).value(0);
|
||||
gfx_label = gfx_label.section(QRegularExpression("[\\(\\)]"), 1, 1);
|
||||
|
@ -2515,6 +2521,28 @@ bool Project::readEventGraphics() {
|
|||
return true;
|
||||
}
|
||||
|
||||
QMap<QString, QMap<QString, QString>> Project::readObjEventGfxInfo() {
|
||||
// TODO: refactor this to be more general if we end up directly parsing C
|
||||
// for more use cases in the future.
|
||||
auto cParser = fex::Parser();
|
||||
auto tokens = fex::Lexer().LexFile((root + "/src/data/object_events/object_event_graphics_info.h").toStdString());
|
||||
auto gfxInfoObjects = cParser.ParseTopLevelObjects(tokens);
|
||||
QMap<QString, QMap<QString, QString>> gfxInfos;
|
||||
for (auto it = gfxInfoObjects.begin(); it != gfxInfoObjects.end(); it++) {
|
||||
QMap<QString, QString> values;
|
||||
for (const fex::ArrayValue &v : it->second.values()) {
|
||||
if (v.type() != fex::ArrayValue::Type::kValuePair)
|
||||
continue;
|
||||
QString key = QString::fromStdString(v.pair().first);
|
||||
QString value = QString::fromStdString(v.pair().second->string_value());
|
||||
values.insert(key, value);
|
||||
}
|
||||
gfxInfos.insert(QString::fromStdString(it->first), values);
|
||||
}
|
||||
|
||||
return gfxInfos;
|
||||
}
|
||||
|
||||
bool Project::readSpeciesIconPaths() {
|
||||
speciesToIconPath.clear();
|
||||
QString srcfilename = "src/pokemon_icon.c";
|
||||
|
|
Loading…
Reference in a new issue