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 readObjEventGfxConstants();
|
||||||
bool readSongNames();
|
bool readSongNames();
|
||||||
bool readEventGraphics();
|
bool readEventGraphics();
|
||||||
|
QMap<QString, QMap<QString, QString>> readObjEventGfxInfo();
|
||||||
|
|
||||||
void setEventPixmap(Event * event, bool forceLoad = false);
|
void setEventPixmap(Event * event, bool forceLoad = false);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,9 @@ SOURCES += src/core/block.cpp \
|
||||||
src/core/regionmap.cpp \
|
src/core/regionmap.cpp \
|
||||||
src/core/wildmoninfo.cpp \
|
src/core/wildmoninfo.cpp \
|
||||||
src/core/editcommands.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/lib/orderedjson.cpp \
|
||||||
src/core/regionmapeditcommands.cpp \
|
src/core/regionmapeditcommands.cpp \
|
||||||
src/mainwindow_scriptapi.cpp \
|
src/mainwindow_scriptapi.cpp \
|
||||||
|
@ -108,6 +111,12 @@ HEADERS += include/core/block.h \
|
||||||
include/core/wildmoninfo.h \
|
include/core/wildmoninfo.h \
|
||||||
include/core/editcommands.h \
|
include/core/editcommands.h \
|
||||||
include/core/regionmapeditcommands.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/orderedmap.h \
|
||||||
include/lib/orderedjson.h \
|
include/lib/orderedjson.h \
|
||||||
include/ui/aboutporymap.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 "map.h"
|
||||||
|
|
||||||
#include "orderedjson.h"
|
#include "orderedjson.h"
|
||||||
|
#include "lib/fex/lexer.h"
|
||||||
|
#include "lib/fex/parser.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
@ -2471,16 +2473,20 @@ bool Project::readEventGraphics() {
|
||||||
qDeleteAll(eventGraphicsMap);
|
qDeleteAll(eventGraphicsMap);
|
||||||
eventGraphicsMap.clear();
|
eventGraphicsMap.clear();
|
||||||
QStringList gfxNames = gfxDefines.keys();
|
QStringList gfxNames = gfxDefines.keys();
|
||||||
|
QMap<QString, QMap<QString, QString>> gfxInfos = readObjEventGfxInfo();
|
||||||
for (QString gfxName : gfxNames) {
|
for (QString gfxName : gfxNames) {
|
||||||
EventGraphics * eventGraphics = new EventGraphics;
|
EventGraphics * eventGraphics = new EventGraphics;
|
||||||
|
|
||||||
QString info_label = pointerHash[gfxName].replace("&", "");
|
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");
|
QMap<QString, QString>gfxInfoAttributes = gfxInfos[info_label];
|
||||||
QString pic_label = gfx_info.value(14);
|
|
||||||
QString dimensions_label = gfx_info.value(11);
|
eventGraphics->inanimate = gfxInfoAttributes.value("inanimate") == "TRUE";
|
||||||
QString subsprites_label = gfx_info.value(12);
|
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);
|
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);
|
gfx_label = gfx_label.section(QRegularExpression("[\\(\\)]"), 1, 1);
|
||||||
|
@ -2515,6 +2521,28 @@ bool Project::readEventGraphics() {
|
||||||
return true;
|
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() {
|
bool Project::readSpeciesIconPaths() {
|
||||||
speciesToIconPath.clear();
|
speciesToIconPath.clear();
|
||||||
QString srcfilename = "src/pokemon_icon.c";
|
QString srcfilename = "src/pokemon_icon.c";
|
||||||
|
|
Loading…
Reference in a new issue