05a685c952
- calculator_designer_ext(Day2 增强版:表达式求值 + 括号,递归下降解析器)接入 cmake - calculator_mainwindow(Day3 上午):增强版计算器装进 QMainWindow, 菜单/状态栏/历史停靠窗,纯代码 + 显式 connect,与 mainwindow_showcase 形成坑卡对照 - calculator_mainwindow_qss(Day3 下午):上午版 + QSS 双主题美化(按键分色/伪状态/深浅切换) - 验证 mainwindow_showcase(QMainWindow 六大组件综合演示,.ui + .qrc) - OUTLINE_4DAY.md / ASSIGNMENTS.md Day3 衔接增强版起点 + 参考实现指引 - 补提交 Day2 calculator/ 与 calculator_designer/ 参考实现(CMakeLists 已引用) - 全量构建 28/28,4 目标离屏自测退出码 0 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
146 lines
4.3 KiB
C++
146 lines
4.3 KiB
C++
#include "expressionevaluator.h"
|
|
|
|
namespace {
|
|
|
|
// 递归下降解析器:expression := term (('+'|'-') term)*
|
|
// term := factor (('*'|'/') factor)*
|
|
// factor := ('+'|'-') factor | '(' expression ')' | number
|
|
// 括号会先被完整求值,天然实现"优先级提升";乘除法在 term 层处理,
|
|
// 天然高于 expression 层的加减法。
|
|
// 【Day3 calculator_mainwindow】与 ../calculator_designer_ext/ 版一字不差。
|
|
class Parser {
|
|
public:
|
|
explicit Parser(const QString& text) : m_text(text) {}
|
|
|
|
ExpressionEvaluator::Result parse() {
|
|
ExpressionEvaluator::Result result;
|
|
skipSpaces();
|
|
if (atEnd()) {
|
|
result.errorMessage = QStringLiteral("表达式为空");
|
|
return result;
|
|
}
|
|
|
|
bool ok = true;
|
|
const double value = parseExpression(ok);
|
|
skipSpaces();
|
|
if (!ok) {
|
|
result.errorMessage = m_error;
|
|
return result;
|
|
}
|
|
if (!atEnd()) {
|
|
result.errorMessage = QStringLiteral("表达式包含无法识别的字符");
|
|
return result;
|
|
}
|
|
|
|
result.ok = true;
|
|
result.value = value;
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
double parseExpression(bool& ok) {
|
|
double value = parseTerm(ok);
|
|
while (ok) {
|
|
skipSpaces();
|
|
if (peek() == QLatin1Char('+')) {
|
|
advance();
|
|
value += parseTerm(ok);
|
|
} else if (peek() == QLatin1Char('-')) {
|
|
advance();
|
|
value -= parseTerm(ok);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
double parseTerm(bool& ok) {
|
|
double value = parseFactor(ok);
|
|
while (ok) {
|
|
skipSpaces();
|
|
if (peek() == QLatin1Char('*')) {
|
|
advance();
|
|
value *= parseFactor(ok);
|
|
} else if (peek() == QLatin1Char('/')) {
|
|
advance();
|
|
const double rhs = parseFactor(ok);
|
|
if (!ok) return 0.0;
|
|
if (rhs == 0.0) {
|
|
fail(QStringLiteral("除数不能为零"), ok);
|
|
return 0.0;
|
|
}
|
|
value /= rhs;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
double parseFactor(bool& ok) {
|
|
skipSpaces();
|
|
if (peek() == QLatin1Char('+')) {
|
|
advance();
|
|
return parseFactor(ok);
|
|
}
|
|
if (peek() == QLatin1Char('-')) {
|
|
advance();
|
|
return -parseFactor(ok);
|
|
}
|
|
if (peek() == QLatin1Char('(')) {
|
|
advance();
|
|
const double value = parseExpression(ok);
|
|
if (!ok) return 0.0;
|
|
skipSpaces();
|
|
if (peek() != QLatin1Char(')')) {
|
|
fail(QStringLiteral("括号不匹配"), ok);
|
|
return 0.0;
|
|
}
|
|
advance();
|
|
return value;
|
|
}
|
|
return parseNumber(ok);
|
|
}
|
|
|
|
double parseNumber(bool& ok) {
|
|
skipSpaces();
|
|
const int start = m_pos;
|
|
bool hasDigits = false;
|
|
while (!atEnd() && peek().isDigit()) { advance(); hasDigits = true; }
|
|
if (!atEnd() && peek() == QLatin1Char('.')) {
|
|
advance();
|
|
while (!atEnd() && peek().isDigit()) { advance(); hasDigits = true; }
|
|
}
|
|
if (!hasDigits) {
|
|
fail(QStringLiteral("表达式格式错误,缺少数字"), ok);
|
|
return 0.0;
|
|
}
|
|
const QString token = m_text.mid(start, m_pos - start);
|
|
bool numOk = false;
|
|
const double value = token.toDouble(&numOk);
|
|
if (!numOk) {
|
|
fail(QStringLiteral("数字格式错误"), ok);
|
|
return 0.0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void skipSpaces() { while (!atEnd() && peek().isSpace()) advance(); }
|
|
bool atEnd() const { return m_pos >= m_text.size(); }
|
|
QChar peek() const { return atEnd() ? QChar() : m_text.at(m_pos); }
|
|
void advance() { ++m_pos; }
|
|
void fail(const QString& message, bool& ok) { ok = false; m_error = message; }
|
|
|
|
const QString& m_text;
|
|
int m_pos = 0;
|
|
QString m_error;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
ExpressionEvaluator::Result ExpressionEvaluator::evaluate(const QString& expression) {
|
|
Parser parser(expression);
|
|
return parser.parse();
|
|
}
|