Files
SimpleMathCalc/lib/parser.dart

238 lines
6.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:simple_math_calc/calculator.dart';
class Parser {
final String input;
int pos = 0;
Parser(this.input);
bool get isEnd => pos >= input.length;
String get current => isEnd ? '' : input[pos];
void eat() => pos++;
void skipSpaces() {
while (!isEnd && input[pos] == ' ') {
eat();
}
}
Expr parse() {
var expr = parseAdd();
skipSpaces();
if (!isEnd && current == '%') {
eat();
expr = PercentExpr(expr);
}
return expr;
}
Expr parseAdd() {
var expr = parseMul();
skipSpaces();
while (!isEnd && (current == '+' || current == '-')) {
var op = current;
eat();
var right = parseMul();
expr = op == '+' ? AddExpr(expr, right) : SubExpr(expr, right);
skipSpaces();
}
return expr;
}
Expr parseMul() {
var expr = parsePow();
skipSpaces();
while (!isEnd &&
(current == '*' ||
current == '/' ||
current == '%' ||
RegExp(r'[a-zA-Z\d]').hasMatch(current) ||
current == '(')) {
if (current == '*' || current == '/') {
var op = current;
eat();
var right = parsePow();
expr = op == '*' ? MulExpr(expr, right) : DivExpr(expr, right);
} else if (current == '%') {
eat();
expr = PercentExpr(expr);
} else {
// implicit multiplication
var right = parsePow();
expr = MulExpr(expr, right);
}
skipSpaces();
}
return expr;
}
Expr parsePow() {
var expr = parseAtom();
skipSpaces();
if (!isEnd && current == '^') {
eat();
var right = parsePow(); // right associative
return PowExpr(expr, right);
}
return expr;
}
Expr parseAtom() {
skipSpaces();
bool negative = false;
if (current == '-') {
negative = true;
eat();
skipSpaces();
}
Expr expr;
if (current == '(') {
eat();
expr = parse();
if (current != ')') throw Exception("缺少 )");
eat();
} else if (input.startsWith("sqrt", pos)) {
pos += 4;
if (current != '(') throw Exception("sqrt 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("sqrt 缺少 )");
eat();
expr = SqrtExpr(inner);
} else if (input.startsWith("cos", pos)) {
pos += 3;
if (current != '(') throw Exception("cos 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("cos 缺少 )");
eat();
expr = CosExpr(inner);
} else if (input.startsWith("sin", pos)) {
pos += 3;
if (current != '(') throw Exception("sin 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("sin 缺少 )");
eat();
expr = SinExpr(inner);
} else if (input.startsWith("tan", pos)) {
pos += 3;
if (current != '(') throw Exception("tan 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("tan 缺少 )");
eat();
expr = TanExpr(inner);
} else if (input.startsWith("log", pos)) {
pos += 3;
if (current != '(') throw Exception("log 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("log 缺少 )");
eat();
expr = LogExpr(inner);
} else if (input.startsWith("exp", pos)) {
pos += 3;
if (current != '(') throw Exception("exp 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("exp 缺少 )");
eat();
expr = ExpExpr(inner);
} else if (input.startsWith("asin", pos)) {
pos += 4;
if (current != '(') throw Exception("asin 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("asin 缺少 )");
eat();
expr = AsinExpr(inner);
} else if (input.startsWith("acos", pos)) {
pos += 4;
if (current != '(') throw Exception("acos 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("acos 缺少 )");
eat();
expr = AcosExpr(inner);
} else if (input.startsWith("atan", pos)) {
pos += 4;
if (current != '(') throw Exception("atan 缺少 (");
eat();
var inner = parse();
if (current != ')') throw Exception("atan 缺少 )");
eat();
expr = AtanExpr(inner);
} else if (current == '|') {
eat();
var inner = parse();
if (current != '|') throw Exception("abs 缺少 |");
eat();
expr = AbsExpr(inner);
} else if (RegExp(r'[a-zA-Z]').hasMatch(current)) {
var varName = current;
eat();
expr = VarExpr(varName);
} else {
// 解析数字 (整数或小数)
var buf = '';
bool hasDot = false;
while (!isEnd &&
(RegExp(r'\d').hasMatch(current) || (!hasDot && current == '.'))) {
if (current == '.') hasDot = true;
buf += current;
eat();
}
if (buf.isEmpty) throw Exception("无法解析: $current");
if (hasDot) {
expr = DoubleExpr(double.parse(buf));
} else {
expr = IntExpr(int.parse(buf));
}
}
if (negative) {
expr = SubExpr(IntExpr(0), expr);
}
return expr;
}
}
/// 计算角度表达式(如 30+45 = 75
int? evaluateAngleExpression(String expr) {
final parts = expr.split('+');
int sum = 0;
for (final part in parts) {
final num = int.tryParse(part.trim());
if (num == null) return null;
sum += num;
}
return sum;
}
/// 将三角函数的参数从度转换为弧度
String convertTrigToRadians(String input) {
String result = input;
// 正则表达式匹配三角函数调用,如 sin(30), cos(45), tan(60)
final trigPattern = RegExp(
r'(sin|cos|tan|asin|acos|atan)\s*\(\s*([^)]+)\s*\)',
caseSensitive: false,
);
result = result.replaceAllMapped(trigPattern, (match) {
final func = match.group(1)!;
final arg = match.group(2)!;
// 如果参数已经是弧度相关的表达式(包含 pi 或 π),则不转换
if (arg.contains('pi') || arg.contains('π') || arg.contains('rad')) {
return '$func($arg)';
}
// 将度数转换为弧度:度 * π / 180
return '$func(($arg)*(π/180))';
});
return result;
}