From 56499411b1eec935c277e6076d29794b99c97dde Mon Sep 17 00:00:00 2001 From: Marcus Huderle Date: Mon, 24 Sep 2018 19:04:42 -0500 Subject: [PATCH] Move parseutil to core/, and fix include paths during build --- parseutil.cpp => core/parseutil.cpp | 434 ++++++++++++++-------------- parseutil.h => core/parseutil.h | 94 +++--- mainwindow.cpp | 2 +- porymap.pro | 7 +- project.cpp | 2 +- 5 files changed, 271 insertions(+), 268 deletions(-) rename parseutil.cpp => core/parseutil.cpp (97%) mode change 100755 => 100644 rename parseutil.h => core/parseutil.h (96%) mode change 100755 => 100644 diff --git a/parseutil.cpp b/core/parseutil.cpp old mode 100755 new mode 100644 similarity index 97% rename from parseutil.cpp rename to core/parseutil.cpp index 4ff232f6..a64ce33c --- a/parseutil.cpp +++ b/core/parseutil.cpp @@ -1,217 +1,217 @@ -#include "parseutil.h" - -#include -#include -#include - -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* ParseUtil::parseAsm(QString text) { - QList *parsed = new QList; - 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* knownDefines) { - QList tokens = tokenizeExpression(define, knownDefines); - QList 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* ParseUtil::parseHealLocs(QString text) { - QList *parsed = new QList; - QStringList lines = text.split('\n'); - - int i = 1; - for (auto line : lines){ - if (line.contains("MAP_GROUP")){ - QList 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 ParseUtil::tokenizeExpression(QString expression, QMap* knownIdentifiers) { - QList tokens; - - QStringList tokenTypes = (QStringList() << "hex" << "decimal" << "identifier" << "operator" << "leftparen" << "rightparen"); - QRegularExpression re("^(?0x[0-9a-fA-F]+)|(?[0-9]+)|(?[a-zA-Z_0-9]+)|(?[+\\-*\\/<>|^%]+)|(?\\()|(?\\))"); - - 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 Token::precedenceMap = QMap( -{ - {"*", 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 ParseUtil::generatePostfix(QList tokens) { - QList output; - QStack 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 postfix) { - QStack 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 +#include +#include + +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* ParseUtil::parseAsm(QString text) { + QList *parsed = new QList; + 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* knownDefines) { + QList tokens = tokenizeExpression(define, knownDefines); + QList 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* ParseUtil::parseHealLocs(QString text) { + QList *parsed = new QList; + QStringList lines = text.split('\n'); + + int i = 1; + for (auto line : lines){ + if (line.contains("MAP_GROUP")){ + QList 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 ParseUtil::tokenizeExpression(QString expression, QMap* knownIdentifiers) { + QList tokens; + + QStringList tokenTypes = (QStringList() << "hex" << "decimal" << "identifier" << "operator" << "leftparen" << "rightparen"); + QRegularExpression re("^(?0x[0-9a-fA-F]+)|(?[0-9]+)|(?[a-zA-Z_0-9]+)|(?[+\\-*\\/<>|^%]+)|(?\\()|(?\\))"); + + 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 Token::precedenceMap = QMap( +{ + {"*", 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 ParseUtil::generatePostfix(QList tokens) { + QList output; + QStack 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 postfix) { + QStack 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); +} diff --git a/parseutil.h b/core/parseutil.h old mode 100755 new mode 100644 similarity index 96% rename from parseutil.h rename to core/parseutil.h index b8dbdea4..6bebd4a2 --- a/parseutil.h +++ b/core/parseutil.h @@ -1,47 +1,47 @@ -#ifndef PARSEUTIL_H -#define PARSEUTIL_H - -#include "core/heallocation.h" - -#include -#include -#include - -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 precedenceMap; - QString value; - TokenType type; - int operatorPrecedence; // only relevant for operator tokens -}; - -class ParseUtil -{ -public: - ParseUtil(); - void strip_comment(QString*); - QList* parseAsm(QString); - int evaluateDefine(QString, QMap*); - QList* parseHealLocs(QString); -private: - QList tokenizeExpression(QString expression, QMap* knownIdentifiers); - QList generatePostfix(QList tokens); - int evaluatePostfix(QList postfix); -}; - -#endif // PARSEUTIL_H +#ifndef PARSEUTIL_H +#define PARSEUTIL_H + +#include "core/heallocation.h" + +#include +#include +#include + +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 precedenceMap; + QString value; + TokenType type; + int operatorPrecedence; // only relevant for operator tokens +}; + +class ParseUtil +{ +public: + ParseUtil(); + void strip_comment(QString*); + QList* parseAsm(QString); + int evaluateDefine(QString, QMap*); + QList* parseHealLocs(QString); +private: + QList tokenizeExpression(QString expression, QMap* knownIdentifiers); + QList generatePostfix(QList tokens); + int evaluatePostfix(QList postfix); +}; + +#endif // PARSEUTIL_H diff --git a/mainwindow.cpp b/mainwindow.cpp index af9d9992..8938cb4f 100755 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -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 diff --git a/porymap.pro b/porymap.pro index 1e7d54e1..9a3ef231 100755 --- a/porymap.pro +++ b/porymap.pro @@ -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 diff --git a/project.cpp b/project.cpp index fa77030e..18db5b18 100755 --- a/project.cpp +++ b/project.cpp @@ -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"