Move parseutil to core/, and fix include paths during build
This commit is contained in:
parent
473cca4eee
commit
56499411b1
5 changed files with 271 additions and 268 deletions
434
parseutil.cpp → core/parseutil.cpp
Executable file → Normal file
434
parseutil.cpp → core/parseutil.cpp
Executable file → Normal file
|
@ -1,217 +1,217 @@
|
|||
#include "parseutil.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QStack>
|
||||
|
||||
ParseUtil::ParseUtil()
|
||||
{
|
||||
}
|
||||
|
||||
void ParseUtil::strip_comment(QString *line) {
|
||||
bool in_string = false;
|
||||
for (int i = 0; i < line->length(); i++) {
|
||||
if (line->at(i) == '"') {
|
||||
in_string = !in_string;
|
||||
} else if (line->at(i) == '@') {
|
||||
if (!in_string) {
|
||||
line->truncate(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QStringList>* ParseUtil::parseAsm(QString text) {
|
||||
QList<QStringList> *parsed = new QList<QStringList>;
|
||||
QStringList lines = text.split('\n');
|
||||
for (QString line : lines) {
|
||||
QString label;
|
||||
//QString macro;
|
||||
//QStringList *params;
|
||||
strip_comment(&line);
|
||||
if (line.trimmed().isEmpty()) {
|
||||
} else if (line.contains(':')) {
|
||||
label = line.left(line.indexOf(':'));
|
||||
QStringList *list = new QStringList;
|
||||
list->append(".label"); // This is not a real keyword. It's used only to make the output more regular.
|
||||
list->append(label);
|
||||
parsed->append(*list);
|
||||
// There should not be anything else on the line.
|
||||
// gas will raise a syntax error if there is.
|
||||
} else {
|
||||
line = line.trimmed();
|
||||
//parsed->append(line.split(QRegExp("\\s*,\\s*")));
|
||||
QString macro;
|
||||
QStringList params;
|
||||
int index = line.indexOf(QRegExp("\\s+"));
|
||||
macro = line.left(index);
|
||||
params = line.right(line.length() - index).trimmed().split(QRegExp("\\s*,\\s*"));
|
||||
params.prepend(macro);
|
||||
parsed->append(params);
|
||||
}
|
||||
//if (macro != NULL) {
|
||||
// if (macros->contains(macro)) {
|
||||
// void* function = macros->value(macro);
|
||||
// if (function != NULL) {
|
||||
// std::function function(params);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
int ParseUtil::evaluateDefine(QString define, QMap<QString, int>* knownDefines) {
|
||||
QList<Token> tokens = tokenizeExpression(define, knownDefines);
|
||||
QList<Token> postfixExpression = generatePostfix(tokens);
|
||||
return evaluatePostfix(postfixExpression);
|
||||
}
|
||||
|
||||
// arg here is the text in the file src/data/heal_locations.h
|
||||
// returns a list of HealLocations (mapname, x, y)
|
||||
QList<HealLocation>* ParseUtil::parseHealLocs(QString text) {
|
||||
QList<HealLocation> *parsed = new QList<HealLocation>;
|
||||
QStringList lines = text.split('\n');
|
||||
|
||||
int i = 1;
|
||||
for (auto line : lines){
|
||||
if (line.contains("MAP_GROUP")){
|
||||
QList<QString> li = line.replace(" ","").chopped(2).remove('{').split(',');
|
||||
HealLocation hloc = HealLocation(li[1].remove("MAP_NUM(").remove(")"), i, li[2].toUShort(), li[3].toUShort());
|
||||
parsed->append(hloc);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
QList<Token> ParseUtil::tokenizeExpression(QString expression, QMap<QString, int>* knownIdentifiers) {
|
||||
QList<Token> tokens;
|
||||
|
||||
QStringList tokenTypes = (QStringList() << "hex" << "decimal" << "identifier" << "operator" << "leftparen" << "rightparen");
|
||||
QRegularExpression re("^(?<hex>0x[0-9a-fA-F]+)|(?<decimal>[0-9]+)|(?<identifier>[a-zA-Z_0-9]+)|(?<operator>[+\\-*\\/<>|^%]+)|(?<leftparen>\\()|(?<rightparen>\\))");
|
||||
|
||||
expression = expression.trimmed();
|
||||
while (!expression.isEmpty()) {
|
||||
QRegularExpressionMatch match = re.match(expression);
|
||||
if (!match.hasMatch()) {
|
||||
qDebug() << "Failed to tokenize expression: " << expression;
|
||||
break;
|
||||
}
|
||||
for (QString tokenType : tokenTypes) {
|
||||
QString token = match.captured(tokenType);
|
||||
if (!token.isEmpty()) {
|
||||
if (tokenType == "identifier") {
|
||||
if (knownIdentifiers->contains(token)) {
|
||||
QString actualToken = QString("%1").arg(knownIdentifiers->value(token));
|
||||
expression = expression.replace(0, token.length(), actualToken);
|
||||
token = actualToken;
|
||||
tokenType = "decimal";
|
||||
} else {
|
||||
qDebug() << "Unknown identifier found in expression: " << token;
|
||||
}
|
||||
}
|
||||
|
||||
tokens.append(Token(token, tokenType));
|
||||
expression = expression.remove(0, token.length()).trimmed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
QMap<QString, int> Token::precedenceMap = QMap<QString, int>(
|
||||
{
|
||||
{"*", 3},
|
||||
{"/", 3},
|
||||
{"+", 4},
|
||||
{"-", 4},
|
||||
{"<<", 5},
|
||||
{">>", 5},
|
||||
{"&", 8},
|
||||
{"^", 9},
|
||||
{"|", 10}
|
||||
});
|
||||
|
||||
// Shunting-yard algorithm for generating postfix notation.
|
||||
// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
||||
QList<Token> ParseUtil::generatePostfix(QList<Token> tokens) {
|
||||
QList<Token> output;
|
||||
QStack<Token> operatorStack;
|
||||
for (Token token : tokens) {
|
||||
if (token.type == TokenType::Number) {
|
||||
output.append(token);
|
||||
} else if (token.value == "(") {
|
||||
operatorStack.push(token);
|
||||
} else if (token.value == ")") {
|
||||
while (!operatorStack.empty() && operatorStack.top().value != "(") {
|
||||
output.append(operatorStack.pop());
|
||||
}
|
||||
if (!operatorStack.empty()) {
|
||||
// pop the left parenthesis token
|
||||
operatorStack.pop();
|
||||
} else {
|
||||
qDebug() << "Mismatched parentheses detected in expression!";
|
||||
}
|
||||
} else {
|
||||
// token is an operator
|
||||
while (!operatorStack.isEmpty()
|
||||
&& operatorStack.top().operatorPrecedence <= token.operatorPrecedence
|
||||
&& operatorStack.top().value != "(") {
|
||||
output.append(operatorStack.pop());
|
||||
}
|
||||
operatorStack.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
while (!operatorStack.isEmpty()) {
|
||||
if (operatorStack.top().value == "(" || operatorStack.top().value == ")") {
|
||||
qDebug() << "Mismatched parentheses detected in expression!";
|
||||
} else {
|
||||
output.append(operatorStack.pop());
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Evaluate postfix expression.
|
||||
// https://en.wikipedia.org/wiki/Reverse_Polish_notation#Postfix_evaluation_algorithm
|
||||
int ParseUtil::evaluatePostfix(QList<Token> postfix) {
|
||||
QStack<Token> stack;
|
||||
for (Token token : postfix) {
|
||||
if (token.type == TokenType::Operator) {
|
||||
int op2 = stack.pop().value.toInt(nullptr, 0);
|
||||
int op1 = stack.pop().value.toInt(nullptr, 0);
|
||||
int result = 0;
|
||||
if (token.value == "*") {
|
||||
result = op1 * op2;
|
||||
} else if (token.value == "/") {
|
||||
result = op1 / op2;
|
||||
} else if (token.value == "+") {
|
||||
result = op1 + op2;
|
||||
} else if (token.value == "-") {
|
||||
result = op1 - op2;
|
||||
} else if (token.value == "<<") {
|
||||
result = op1 << op2;
|
||||
} else if (token.value == ">>") {
|
||||
result = op1 >> op2;
|
||||
} else if (token.value == "&") {
|
||||
result = op1 & op2;
|
||||
} else if (token.value == "^") {
|
||||
result = op1 ^ op2;
|
||||
} else if (token.value == "|") {
|
||||
result = op1 | op2;
|
||||
} else {
|
||||
qDebug() << "Unsupported postfix operator: " << token.value;
|
||||
}
|
||||
stack.push(Token(QString("%1").arg(result), "decimal"));
|
||||
} else {
|
||||
stack.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
return stack.pop().value.toInt(nullptr, 0);
|
||||
}
|
||||
#include "parseutil.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QStack>
|
||||
|
||||
ParseUtil::ParseUtil()
|
||||
{
|
||||
}
|
||||
|
||||
void ParseUtil::strip_comment(QString *line) {
|
||||
bool in_string = false;
|
||||
for (int i = 0; i < line->length(); i++) {
|
||||
if (line->at(i) == '"') {
|
||||
in_string = !in_string;
|
||||
} else if (line->at(i) == '@') {
|
||||
if (!in_string) {
|
||||
line->truncate(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QStringList>* ParseUtil::parseAsm(QString text) {
|
||||
QList<QStringList> *parsed = new QList<QStringList>;
|
||||
QStringList lines = text.split('\n');
|
||||
for (QString line : lines) {
|
||||
QString label;
|
||||
//QString macro;
|
||||
//QStringList *params;
|
||||
strip_comment(&line);
|
||||
if (line.trimmed().isEmpty()) {
|
||||
} else if (line.contains(':')) {
|
||||
label = line.left(line.indexOf(':'));
|
||||
QStringList *list = new QStringList;
|
||||
list->append(".label"); // This is not a real keyword. It's used only to make the output more regular.
|
||||
list->append(label);
|
||||
parsed->append(*list);
|
||||
// There should not be anything else on the line.
|
||||
// gas will raise a syntax error if there is.
|
||||
} else {
|
||||
line = line.trimmed();
|
||||
//parsed->append(line.split(QRegExp("\\s*,\\s*")));
|
||||
QString macro;
|
||||
QStringList params;
|
||||
int index = line.indexOf(QRegExp("\\s+"));
|
||||
macro = line.left(index);
|
||||
params = line.right(line.length() - index).trimmed().split(QRegExp("\\s*,\\s*"));
|
||||
params.prepend(macro);
|
||||
parsed->append(params);
|
||||
}
|
||||
//if (macro != NULL) {
|
||||
// if (macros->contains(macro)) {
|
||||
// void* function = macros->value(macro);
|
||||
// if (function != NULL) {
|
||||
// std::function function(params);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
int ParseUtil::evaluateDefine(QString define, QMap<QString, int>* knownDefines) {
|
||||
QList<Token> tokens = tokenizeExpression(define, knownDefines);
|
||||
QList<Token> postfixExpression = generatePostfix(tokens);
|
||||
return evaluatePostfix(postfixExpression);
|
||||
}
|
||||
|
||||
// arg here is the text in the file src/data/heal_locations.h
|
||||
// returns a list of HealLocations (mapname, x, y)
|
||||
QList<HealLocation>* ParseUtil::parseHealLocs(QString text) {
|
||||
QList<HealLocation> *parsed = new QList<HealLocation>;
|
||||
QStringList lines = text.split('\n');
|
||||
|
||||
int i = 1;
|
||||
for (auto line : lines){
|
||||
if (line.contains("MAP_GROUP")){
|
||||
QList<QString> li = line.replace(" ","").chopped(2).remove('{').split(',');
|
||||
HealLocation hloc = HealLocation(li[1].remove("MAP_NUM(").remove(")"), i, li[2].toUShort(), li[3].toUShort());
|
||||
parsed->append(hloc);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
QList<Token> ParseUtil::tokenizeExpression(QString expression, QMap<QString, int>* knownIdentifiers) {
|
||||
QList<Token> tokens;
|
||||
|
||||
QStringList tokenTypes = (QStringList() << "hex" << "decimal" << "identifier" << "operator" << "leftparen" << "rightparen");
|
||||
QRegularExpression re("^(?<hex>0x[0-9a-fA-F]+)|(?<decimal>[0-9]+)|(?<identifier>[a-zA-Z_0-9]+)|(?<operator>[+\\-*\\/<>|^%]+)|(?<leftparen>\\()|(?<rightparen>\\))");
|
||||
|
||||
expression = expression.trimmed();
|
||||
while (!expression.isEmpty()) {
|
||||
QRegularExpressionMatch match = re.match(expression);
|
||||
if (!match.hasMatch()) {
|
||||
qDebug() << "Failed to tokenize expression: " << expression;
|
||||
break;
|
||||
}
|
||||
for (QString tokenType : tokenTypes) {
|
||||
QString token = match.captured(tokenType);
|
||||
if (!token.isEmpty()) {
|
||||
if (tokenType == "identifier") {
|
||||
if (knownIdentifiers->contains(token)) {
|
||||
QString actualToken = QString("%1").arg(knownIdentifiers->value(token));
|
||||
expression = expression.replace(0, token.length(), actualToken);
|
||||
token = actualToken;
|
||||
tokenType = "decimal";
|
||||
} else {
|
||||
qDebug() << "Unknown identifier found in expression: " << token;
|
||||
}
|
||||
}
|
||||
|
||||
tokens.append(Token(token, tokenType));
|
||||
expression = expression.remove(0, token.length()).trimmed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
QMap<QString, int> Token::precedenceMap = QMap<QString, int>(
|
||||
{
|
||||
{"*", 3},
|
||||
{"/", 3},
|
||||
{"+", 4},
|
||||
{"-", 4},
|
||||
{"<<", 5},
|
||||
{">>", 5},
|
||||
{"&", 8},
|
||||
{"^", 9},
|
||||
{"|", 10}
|
||||
});
|
||||
|
||||
// Shunting-yard algorithm for generating postfix notation.
|
||||
// https://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
||||
QList<Token> ParseUtil::generatePostfix(QList<Token> tokens) {
|
||||
QList<Token> output;
|
||||
QStack<Token> operatorStack;
|
||||
for (Token token : tokens) {
|
||||
if (token.type == TokenType::Number) {
|
||||
output.append(token);
|
||||
} else if (token.value == "(") {
|
||||
operatorStack.push(token);
|
||||
} else if (token.value == ")") {
|
||||
while (!operatorStack.empty() && operatorStack.top().value != "(") {
|
||||
output.append(operatorStack.pop());
|
||||
}
|
||||
if (!operatorStack.empty()) {
|
||||
// pop the left parenthesis token
|
||||
operatorStack.pop();
|
||||
} else {
|
||||
qDebug() << "Mismatched parentheses detected in expression!";
|
||||
}
|
||||
} else {
|
||||
// token is an operator
|
||||
while (!operatorStack.isEmpty()
|
||||
&& operatorStack.top().operatorPrecedence <= token.operatorPrecedence
|
||||
&& operatorStack.top().value != "(") {
|
||||
output.append(operatorStack.pop());
|
||||
}
|
||||
operatorStack.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
while (!operatorStack.isEmpty()) {
|
||||
if (operatorStack.top().value == "(" || operatorStack.top().value == ")") {
|
||||
qDebug() << "Mismatched parentheses detected in expression!";
|
||||
} else {
|
||||
output.append(operatorStack.pop());
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Evaluate postfix expression.
|
||||
// https://en.wikipedia.org/wiki/Reverse_Polish_notation#Postfix_evaluation_algorithm
|
||||
int ParseUtil::evaluatePostfix(QList<Token> postfix) {
|
||||
QStack<Token> stack;
|
||||
for (Token token : postfix) {
|
||||
if (token.type == TokenType::Operator) {
|
||||
int op2 = stack.pop().value.toInt(nullptr, 0);
|
||||
int op1 = stack.pop().value.toInt(nullptr, 0);
|
||||
int result = 0;
|
||||
if (token.value == "*") {
|
||||
result = op1 * op2;
|
||||
} else if (token.value == "/") {
|
||||
result = op1 / op2;
|
||||
} else if (token.value == "+") {
|
||||
result = op1 + op2;
|
||||
} else if (token.value == "-") {
|
||||
result = op1 - op2;
|
||||
} else if (token.value == "<<") {
|
||||
result = op1 << op2;
|
||||
} else if (token.value == ">>") {
|
||||
result = op1 >> op2;
|
||||
} else if (token.value == "&") {
|
||||
result = op1 & op2;
|
||||
} else if (token.value == "^") {
|
||||
result = op1 ^ op2;
|
||||
} else if (token.value == "|") {
|
||||
result = op1 | op2;
|
||||
} else {
|
||||
qDebug() << "Unsupported postfix operator: " << token.value;
|
||||
}
|
||||
stack.push(Token(QString("%1").arg(result), "decimal"));
|
||||
} else {
|
||||
stack.push(token);
|
||||
}
|
||||
}
|
||||
|
||||
return stack.pop().value.toInt(nullptr, 0);
|
||||
}
|
94
parseutil.h → core/parseutil.h
Executable file → Normal file
94
parseutil.h → core/parseutil.h
Executable file → Normal file
|
@ -1,47 +1,47 @@
|
|||
#ifndef PARSEUTIL_H
|
||||
#define PARSEUTIL_H
|
||||
|
||||
#include "core/heallocation.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
enum TokenType {
|
||||
Number,
|
||||
Operator,
|
||||
};
|
||||
|
||||
class Token {
|
||||
public:
|
||||
Token(QString value = "", QString type = "") {
|
||||
this->value = value;
|
||||
this->type = TokenType::Operator;
|
||||
if (type == "decimal" || type == "hex") {
|
||||
this->type = TokenType::Number;
|
||||
this->operatorPrecedence = -1;
|
||||
} else if (type == "operator") {
|
||||
this->operatorPrecedence = precedenceMap[value];
|
||||
}
|
||||
}
|
||||
static QMap<QString, int> precedenceMap;
|
||||
QString value;
|
||||
TokenType type;
|
||||
int operatorPrecedence; // only relevant for operator tokens
|
||||
};
|
||||
|
||||
class ParseUtil
|
||||
{
|
||||
public:
|
||||
ParseUtil();
|
||||
void strip_comment(QString*);
|
||||
QList<QStringList>* parseAsm(QString);
|
||||
int evaluateDefine(QString, QMap<QString, int>*);
|
||||
QList<HealLocation>* parseHealLocs(QString);
|
||||
private:
|
||||
QList<Token> tokenizeExpression(QString expression, QMap<QString, int>* knownIdentifiers);
|
||||
QList<Token> generatePostfix(QList<Token> tokens);
|
||||
int evaluatePostfix(QList<Token> postfix);
|
||||
};
|
||||
|
||||
#endif // PARSEUTIL_H
|
||||
#ifndef PARSEUTIL_H
|
||||
#define PARSEUTIL_H
|
||||
|
||||
#include "core/heallocation.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
enum TokenType {
|
||||
Number,
|
||||
Operator,
|
||||
};
|
||||
|
||||
class Token {
|
||||
public:
|
||||
Token(QString value = "", QString type = "") {
|
||||
this->value = value;
|
||||
this->type = TokenType::Operator;
|
||||
if (type == "decimal" || type == "hex") {
|
||||
this->type = TokenType::Number;
|
||||
this->operatorPrecedence = -1;
|
||||
} else if (type == "operator") {
|
||||
this->operatorPrecedence = precedenceMap[value];
|
||||
}
|
||||
}
|
||||
static QMap<QString, int> precedenceMap;
|
||||
QString value;
|
||||
TokenType type;
|
||||
int operatorPrecedence; // only relevant for operator tokens
|
||||
};
|
||||
|
||||
class ParseUtil
|
||||
{
|
||||
public:
|
||||
ParseUtil();
|
||||
void strip_comment(QString*);
|
||||
QList<QStringList>* parseAsm(QString);
|
||||
int evaluateDefine(QString, QMap<QString, int>*);
|
||||
QList<HealLocation>* parseHealLocs(QString);
|
||||
private:
|
||||
QList<Token> tokenizeExpression(QString expression, QMap<QString, int>* knownIdentifiers);
|
||||
QList<Token> generatePostfix(QList<Token> tokens);
|
||||
int evaluatePostfix(QList<Token> postfix);
|
||||
};
|
||||
|
||||
#endif // PARSEUTIL_H
|
|
@ -2,7 +2,7 @@
|
|||
#include "ui_mainwindow.h"
|
||||
#include "project.h"
|
||||
#include "editor.h"
|
||||
#include "eventpropertiesframe.h"
|
||||
#include "ui/eventpropertiesframe.h"
|
||||
#include "ui_objectpropertiesframe.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
|
|
@ -20,6 +20,7 @@ SOURCES += core/block.cpp \
|
|||
core/historyitem.cpp \
|
||||
core/maplayout.cpp \
|
||||
core/metatile.cpp \
|
||||
core/parseutil.cpp \
|
||||
core/tile.cpp \
|
||||
core/tileset.cpp \
|
||||
ui/eventpropertiesframe.cpp \
|
||||
|
@ -35,7 +36,6 @@ SOURCES += core/block.cpp \
|
|||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
map.cpp \
|
||||
parseutil.cpp \
|
||||
project.cpp
|
||||
|
||||
HEADERS += core/block.h \
|
||||
|
@ -46,6 +46,7 @@ HEADERS += core/block.h \
|
|||
core/mapconnection.h \
|
||||
core/maplayout.h \
|
||||
core/metatile.h \
|
||||
core/parseutil.h \
|
||||
core/tile.h \
|
||||
core/tileset.h \
|
||||
ui/eventpropertiesframe.h \
|
||||
|
@ -60,7 +61,6 @@ HEADERS += core/block.h \
|
|||
graphicsview.h \
|
||||
mainwindow.h \
|
||||
map.h \
|
||||
parseutil.h \
|
||||
project.h
|
||||
|
||||
FORMS += mainwindow.ui \
|
||||
|
@ -68,3 +68,6 @@ FORMS += mainwindow.ui \
|
|||
|
||||
RESOURCES += \
|
||||
resources/images.qrc
|
||||
|
||||
INCLUDEPATH += core
|
||||
INCLUDEPATH += ui
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "parseutil.h"
|
||||
#include "project.h"
|
||||
#include "core/history.h"
|
||||
#include "core/historyitem.h"
|
||||
#include "core/parseutil.h"
|
||||
#include "core/tile.h"
|
||||
#include "core/tileset.h"
|
||||
#include "event.h"
|
||||
|
|
Loading…
Reference in a new issue