| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- // 粘贴用的解析器:把 Symja 文本转回节点树。
- //
- // 重点是**往返一致**:复制出来的东西粘回去应当还是同一个表达式;
- // 以及**不认识就报错**,绝不猜着解析(猜错等于给用户一个算错的答案)。
- import 'package:flutter_test/flutter_test.dart';
- import 'package:cal/expr_node.dart';
- import 'package:cal/expr_parse.dart';
- /// 解析再序列化,应当和输入一模一样。
- String rt(String s) => symjaOf(parseSymjaExpr(s));
- void main() {
- group('基本往返', () {
- test('数字与四则', () {
- expect(rt('2'), '2');
- expect(rt('2+3'), '2+3');
- expect(rt('2x+1'), '2x+1');
- expect(rt('2*x+1'), '2*x+1');
- expect(rt('2.5'), '2.5');
- });
- test('函数调用', () {
- expect(rt('Sin[x]'), 'Sin[x]');
- expect(rt('Log[2x+1]'), 'Log[2x+1]');
- expect(rt('Integrate[x^2, x]'), 'Integrate[x^2, x]');
- expect(rt('Sin[]'), 'Sin[]');
- });
- test('幂与分式做成结构(要竖排/上标显示)', () {
- final doc = parseSymjaExpr('x^2');
- expect(doc.children.single, isA<PowNode>());
- final frac = parseSymjaExpr('1/3');
- expect(frac.children.single, isA<FracNode>());
- });
- test('分式分母是复合表达式时序列化补括号', () {
- expect(rt('1/(x+1)'), '1/(x+1)');
- });
- test('括号与花括号', () {
- expect(rt('(x+1)'), '(x+1)');
- expect(rt('2(x+1)'), '2(x+1)'); // 并置乘法原样保留,Symja 认这个写法
- expect(rt('{n, 1, Infinity}'), '{n, 1, Infinity}');
- expect(rt('Sum[f, {n, 1, Infinity}]'), 'Sum[f, {n, 1, Infinity}]');
- });
- test('空槽写成 Null,解析回来还是空槽', () {
- final t = parseSymjaExpr('Integrate[Null, x]');
- final fn = t.children.single as FuncNode;
- expect(fn.args.first.isEmpty, isTrue);
- expect(symjaOf(t), 'Integrate[Null, x]');
- });
- });
- group('运算符与词法', () {
- test('多字符运算符', () {
- expect(rt('x->0'), 'x->0');
- expect(rt('x^2-1==0'), 'x^2-1==0');
- expect(rt('f[x_]:=x^2'), 'f[x_]:=x^2');
- });
- test('一元负号', () {
- expect(rt('-x'), '-x');
- expect(rt('-1/2'), '-1/2');
- });
- test('科学计数法不能被拆成并置乘法', () {
- expect(rt('1e-3'), '1e-3');
- expect(rt('2.5E10'), '2.5E10');
- });
- test(r'标识符里的下划线和 $', () {
- expect(rt('x_'), 'x_');
- expect(rt(r'$ans'), r'$ans');
- });
- test('空白随便加', () {
- expect(rt(' 2 x + 1 '), '2x+1');
- });
- });
- group('不认识就报错,不猜', () {
- void bad(String s) {
- expect(() => parseSymjaExpr(s), throwsA(isA<ExprParseException>()),
- reason: '「$s」应当解析失败');
- }
- test('不支持的运算符', () {
- bad('a@b');
- bad('a&b');
- bad('x;;y');
- bad('a//b');
- });
- test('括号不配平', () {
- bad('(x+1');
- bad('Sin[x');
- bad('Abs[x]]');
- });
- test('多余的尾巴', () {
- bad('x y z ]');
- bad('1+2)');
- });
- test('空串', () {
- bad('');
- });
- test('报错信息带位置', () {
- try {
- parseSymjaExpr('x@y');
- fail('应当抛异常');
- } on ExprParseException catch (e) {
- expect(e.position, 1);
- expect(e.toString(), contains('第 2 个字符'));
- }
- });
- });
- }
|