diff --git a/KactusAPI/expressions/SystemVerilogExpressionParser.cpp b/KactusAPI/expressions/SystemVerilogExpressionParser.cpp index 27a48873c..43f554a61 100644 --- a/KactusAPI/expressions/SystemVerilogExpressionParser.cpp +++ b/KactusAPI/expressions/SystemVerilogExpressionParser.cpp @@ -32,7 +32,7 @@ namespace const QRegularExpression BINARY_OPERATOR(QStringLiteral("[/%^+-]|<<|>>|<=?|>=?|!==?|===?|[&|*]{1,2}|[$]pow")); const QRegularExpression UNARY_OPERATOR(QStringLiteral( - "[$]clog2|[$]exp|[$]sqrt|[$]ipxact_mode_condition|[$]ipxact_port_value|[$]ipxact_field_value|~")); + "[$]clog2|[$]exp|[$]sqrt|[$]ipxact_mode_condition|[$]ipxact_port_value|[$]ipxact_field_value|~|`")); const QRegularExpression TERNARY_OPERATOR(QStringLiteral("[?:]")); @@ -64,7 +64,9 @@ namespace //----------------------------------------------------------------------------- QString SystemVerilogExpressionParser::parseExpression(QStringView expression, bool* validExpression) const { - return solveRPN(convertToRPN(expression), validExpression); + // Copy of expression needs to be created for replacing unary minuses with special character. + QString expressionCopy = expression.toString(); + return solveRPN(convertToRPN(expressionCopy), validExpression); } //----------------------------------------------------------------------------- @@ -89,8 +91,8 @@ bool SystemVerilogExpressionParser::isPlainValue(QStringView expression) const int SystemVerilogExpressionParser::baseForExpression(QStringView expression) const { int greatestBase = 0; - - for (auto const& token : convertToRPN(expression)) + QString asStr = expression.toString(); + for (auto const& token : convertToRPN(asStr)) { if (isLiteral(token)) { @@ -108,7 +110,7 @@ int SystemVerilogExpressionParser::baseForExpression(QStringView expression) con //----------------------------------------------------------------------------- // Function: SystemVerilogExpressionParser::convertToRPN() //----------------------------------------------------------------------------- -QVector SystemVerilogExpressionParser::convertToRPN(QStringView expression) +QVector SystemVerilogExpressionParser::convertToRPN(QString& expression) { // Convert expression to Reverse Polish Notation (RPN) using the Shunting Yard algorithm. QVector output; @@ -117,7 +119,28 @@ QVector SystemVerilogExpressionParser::convertToRPN(QStringView exp int openParenthesis = 0; bool nextMayBeLiteral = true; + qsizetype previousIndex = -1; const auto SIZE = expression.size(); + + // Convert unary minuses to backticks. Needs to be done before main loop, otherwise string views will be invalidated. + // SolveRPN knows to treat backticks as unary minus. + for (qsizetype index = 0; index < SIZE; ++index) + { + const QChar current = expression.at(index); + if (current.isSpace()) + { + continue; + } + + // Minus == unary minus if preceded by opening parenthesis or if first token + if (current == QLatin1Char('-') && (previousIndex == -1 || expression.at(previousIndex) == OPEN_PARENTHESIS_STRING)) + { + expression[index] = QLatin1Char('`'); + } + + previousIndex = index; + } + for (qsizetype index = 0; index < SIZE; /*index incremented inside loop*/) { const QChar current = expression.at(index); @@ -215,7 +238,8 @@ QVector SystemVerilogExpressionParser::convertToRPN(QStringView exp else { static const QRegularExpression separator(ANY_OPERATOR.pattern() % QStringLiteral("|[(){},]")); - const auto unknown = expression.mid(index, separator.match(expression, index).capturedStart() - index); + auto unknown = QStringView(expression); + unknown = unknown.mid(index, separator.match(expression, index).capturedStart() - index); output.append(unknown.trimmed()); @@ -589,6 +613,15 @@ QString SystemVerilogExpressionParser::solveUnary(QStringView operation, QString { return QString::number(~term.toLongLong()); } + else if (operation.compare(QLatin1String("`")) == 0) + { + if (!term.contains(QLatin1Char('.'))) + { + return QString::number(-term.toLongLong()); + } + + return QString::number(-term.toDouble(), 'f', precisionOf(term)); + } return QStringLiteral("x"); } @@ -710,6 +743,7 @@ unsigned int SystemVerilogExpressionParser::operatorPrecedence(QStringView oper) {QStringLiteral("*") , 12}, {QStringLiteral("/") , 12}, {QStringLiteral("**") , 13}, + {QStringLiteral("`") , 13}, // unary minus {QStringLiteral("~") , 14}, {QStringLiteral("$clog2") , 14}, {QStringLiteral("$pow") , 14}, diff --git a/KactusAPI/include/ParameterizableInterface.h b/KactusAPI/include/ParameterizableInterface.h index 48eed8ab3..4899a1b87 100644 --- a/KactusAPI/include/ParameterizableInterface.h +++ b/KactusAPI/include/ParameterizableInterface.h @@ -73,7 +73,7 @@ class KACTUS2_API ParameterizableInterface * * @return The value of the expression in decimal form. */ - QString parseExpressionToDecimal(QString const& expression) const; + QString parseExpressionToDecimal(QString const& expression, bool* expressionIsValid = nullptr) const; /*! * Parse the selected expression to the selected base number. @@ -83,7 +83,7 @@ class KACTUS2_API ParameterizableInterface * * @return The value of the expression in the base number form. */ - QString parseExpressionToBaseNumber(QString const& expression, unsigned int baseNumber) const; + QString parseExpressionToBaseNumber(QString const& expression, unsigned int baseNumber, bool* expressionIsValid = nullptr) const; private: diff --git a/KactusAPI/include/SystemVerilogExpressionParser.h b/KactusAPI/include/SystemVerilogExpressionParser.h index d818c2a48..d8efb8813 100644 --- a/KactusAPI/include/SystemVerilogExpressionParser.h +++ b/KactusAPI/include/SystemVerilogExpressionParser.h @@ -149,7 +149,7 @@ class KACTUS2_API SystemVerilogExpressionParser : public ExpressionParser * * @return The conversion result. */ - static QVector convertToRPN(QStringView expression); + static QVector convertToRPN(QString& expression); /*! * Solves the given RPN expression. diff --git a/KactusAPI/interfaces/common/AbstractParameterInterface.cpp b/KactusAPI/interfaces/common/AbstractParameterInterface.cpp index 314044b35..bdfc63fb9 100644 --- a/KactusAPI/interfaces/common/AbstractParameterInterface.cpp +++ b/KactusAPI/interfaces/common/AbstractParameterInterface.cpp @@ -339,7 +339,10 @@ std::string AbstractParameterInterface::getValue(std::string const& parameterNam } else { - return parseExpressionToBaseNumber(parameter->getValue(), baseNumber).toStdString(); + bool expressionIsValid = false; + auto value = parseExpressionToBaseNumber(parameter->getValue(), baseNumber, &expressionIsValid).toStdString(); + + return expressionIsValid ? value : std::string("x"); } } diff --git a/KactusAPI/interfaces/common/ParameterizableInterface.cpp b/KactusAPI/interfaces/common/ParameterizableInterface.cpp index 49bdd9ff6..2473d8840 100644 --- a/KactusAPI/interfaces/common/ParameterizableInterface.cpp +++ b/KactusAPI/interfaces/common/ParameterizableInterface.cpp @@ -39,15 +39,15 @@ QString ParameterizableInterface::formattedValueFor(QString const& expression) c //----------------------------------------------------------------------------- // Function: ParameterizableInterface::parseExpressionToDecimal() //----------------------------------------------------------------------------- -QString ParameterizableInterface::parseExpressionToDecimal(QString const& expression) const +QString ParameterizableInterface::parseExpressionToDecimal(QString const& expression, bool* expressionIsValid /*= nullptr*/) const { - return expressionParser_->parseExpression(expression); + return expressionParser_->parseExpression(expression, expressionIsValid); } //----------------------------------------------------------------------------- // Function: ParameterizableInterface::parseExpressionToBaseNumber() //----------------------------------------------------------------------------- -QString ParameterizableInterface::parseExpressionToBaseNumber(QString const& expression, unsigned int baseNumber) const +QString ParameterizableInterface::parseExpressionToBaseNumber(QString const& expression, unsigned int baseNumber, bool* expressionIsValid /*= nullptr*/) const { - return valueFormatter_->format(parseExpressionToDecimal(expression), baseNumber); + return valueFormatter_->format(parseExpressionToDecimal(expression, expressionIsValid), baseNumber); } diff --git a/editors/ComponentEditor/common/ExpressionEditor.cpp b/editors/ComponentEditor/common/ExpressionEditor.cpp index 8ec0c95ee..730520cfe 100644 --- a/editors/ComponentEditor/common/ExpressionEditor.cpp +++ b/editors/ComponentEditor/common/ExpressionEditor.cpp @@ -309,3 +309,11 @@ int ExpressionEditor::getSelectionLastWord() const { return toPlainText().left(textCursor().selectionEnd()).count(WORD_DELIMITER); } + +//----------------------------------------------------------------------------- +// Function: ExpressionEditor::getSelectionStartIndex() +//----------------------------------------------------------------------------- +int ExpressionEditor::getSelectionStartIndex() const +{ + return textCursor().selectionStart(); +} diff --git a/editors/ComponentEditor/common/ExpressionEditor.h b/editors/ComponentEditor/common/ExpressionEditor.h index e241cfac3..fc7ae0d61 100644 --- a/editors/ComponentEditor/common/ExpressionEditor.h +++ b/editors/ComponentEditor/common/ExpressionEditor.h @@ -234,6 +234,13 @@ private slots: * @return Length of the last word in the selection. */ int getSelectionLastWord() const final; + + /*! + * Get the start index of the selection. + * + * @return The start index of the current selection. + */ + int getSelectionStartIndex() const final; }; #endif // EXPRESSIONEDITOR_H \ No newline at end of file diff --git a/editors/ComponentEditor/common/ExpressionLineEditor.cpp b/editors/ComponentEditor/common/ExpressionLineEditor.cpp index 076c3d1ba..fc92f1668 100644 --- a/editors/ComponentEditor/common/ExpressionLineEditor.cpp +++ b/editors/ComponentEditor/common/ExpressionLineEditor.cpp @@ -240,3 +240,11 @@ int ExpressionLineEditor::getSelectionLastWord() const { return text().left(selectionEnd()).count(WORD_DELIMITER); } + +//----------------------------------------------------------------------------- +// Function: ExpressionLineEditor::getSelectionStartIndex() +//----------------------------------------------------------------------------- +int ExpressionLineEditor::getSelectionStartIndex() const +{ + return selectionStart(); +} diff --git a/editors/ComponentEditor/common/ExpressionLineEditor.h b/editors/ComponentEditor/common/ExpressionLineEditor.h index 639073b38..4a6d145c3 100644 --- a/editors/ComponentEditor/common/ExpressionLineEditor.h +++ b/editors/ComponentEditor/common/ExpressionLineEditor.h @@ -224,6 +224,13 @@ private slots: * @return Length of the last word in the selection. */ int getSelectionLastWord() const final; + + /*! + * Get the start index of the selection. + * + * @return The start index of the current selection. + */ + int getSelectionStartIndex() const final; }; #endif // EXPRESSIONLINEEDITOR_H \ No newline at end of file diff --git a/editors/ComponentEditor/common/MasterExpressionEditor.cpp b/editors/ComponentEditor/common/MasterExpressionEditor.cpp index cb4854e96..e929bbe13 100644 --- a/editors/ComponentEditor/common/MasterExpressionEditor.cpp +++ b/editors/ComponentEditor/common/MasterExpressionEditor.cpp @@ -490,7 +490,7 @@ void MasterExpressionEditor::replaceSelectionInExpression(QKeyEvent* keyEvent) int firstTermPos = indexOfNthWord(firstWord, expression_); expression_.remove(expression_.indexOf(selectedText, firstTermPos), selectedText.length()); - expression_.insert(firstTermPos, keyEvent->text()); // Don't just remove selection, also insert whatever was typed. + expression_.insert(getSelectionStartIndex(), keyEvent->text()); // Don't just remove selection, also insert whatever was typed. } //----------------------------------------------------------------------------- diff --git a/editors/ComponentEditor/common/MasterExpressionEditor.h b/editors/ComponentEditor/common/MasterExpressionEditor.h index 9612e9ba1..893f89f5a 100644 --- a/editors/ComponentEditor/common/MasterExpressionEditor.h +++ b/editors/ComponentEditor/common/MasterExpressionEditor.h @@ -274,6 +274,13 @@ class MasterExpressionEditor */ virtual int getSelectionLastWord() const = 0; + /*! + * Get the start index of the selection. + * + * @return The start index of the current selection. + */ + virtual int getSelectionStartIndex() const = 0; + /*! * Checks if the editing would change text in the middle of a referencing term. * diff --git a/tests/Core/tst_SystemVerilogExpressionParser.cpp b/tests/Core/tst_SystemVerilogExpressionParser.cpp index 5b51b1b2c..45b3a200b 100644 --- a/tests/Core/tst_SystemVerilogExpressionParser.cpp +++ b/tests/Core/tst_SystemVerilogExpressionParser.cpp @@ -647,6 +647,8 @@ void tst_SystemVerilogExpressionParser::testParseMultipleOperations_data() QTest::newRow("clog2() precedes power operation") << "2**$clog2(4) + 1" << "5"; QTest::newRow("Value of clog2() preceds other operations") << "(2 + 2)*3*$clog2(4*2*2) + 2" << "50"; + + QTest::newRow("Unary minus in expression") << "-$clog2(32)-($sqrt(25)-(-$pow(2,2)))" << "-14"; } //----------------------------------------------------------------------------- diff --git a/tests/Core/tst_SystemVerilogExpressionParser.pro b/tests/Core/tst_SystemVerilogExpressionParser.pro index ac30095e9..6c1e1f7e3 100644 --- a/tests/Core/tst_SystemVerilogExpressionParser.pro +++ b/tests/Core/tst_SystemVerilogExpressionParser.pro @@ -16,17 +16,19 @@ TARGET = tst_SystemVerilogExpressionParser QT += core xml gui testlib CONFIG += c++11 testcase console -LIBS += -L$$PWD/../../executable/ -lKactusAPI win32:CONFIG(release, debug|release) { + LIBS += -L$$PWD/../../executable/ -lKactusAPI DESTDIR = ./release } else:win32:CONFIG(debug, debug|release) { + LIBS += -L$$PWD/../../executable/ -lKactusAPId DESTDIR = ./debug } else:unix { + LIBS += -L$$PWD/../../executable/ -lKactusAPI DESTDIR = ./release - } +} INCLUDEPATH += $$DESTDIR INCLUDEPATH += ../../ diff --git a/version.h b/version.h index 27d1a2edf..a85336111 100644 --- a/version.h +++ b/version.h @@ -10,20 +10,20 @@ #ifndef VERSIONNO__H #define VERSIONNO__H -#define VERSION_FULL 3.13.525.0 +#define VERSION_FULL 3.13.534.0 #define VERSION_BASEYEAR 0 -#define VERSION_DATE "2024-07-17" -#define VERSION_TIME "14:08:14" +#define VERSION_DATE "2024-07-29" +#define VERSION_TIME "13:53:28" #define VERSION_MAJOR 3 #define VERSION_MINOR 13 -#define VERSION_BUILDNO 525 +#define VERSION_BUILDNO 534 #define VERSION_EXTEND 0 -#define VERSION_FILE 3,13,525,0 -#define VERSION_PRODUCT 3,13,525,0 -#define VERSION_FILESTR "3,13,525,0" -#define VERSION_PRODUCTSTR "3,13,525,0" +#define VERSION_FILE 3,13,534,0 +#define VERSION_PRODUCT 3,13,534,0 +#define VERSION_FILESTR "3,13,534,0" +#define VERSION_PRODUCTSTR "3,13,534,0" #endif