Flutter表达式计算插件fast_expressions的使用

Flutter表达式计算插件fast_expressions的使用

Fast Expressions 是一个用于解析和评估表达式的库。它在解析表达式时具有高性能,通过使用非常快速的解析器来实现。解析后的表达式会被包装成函数调用。在评估表达式时,通过不使用任何类并直接执行表达式函数来提高性能。

关于这个软件

Fast Expressions 是一个用于解析和评估表达式的库。高效率的解析性能通过使用非常快速的解析器实现。解析后的表达式被封装为函数调用。在评估表达式时,通过不使用任何类并直接执行表达式函数来实现高效率。

示例

以下是一些使用 fast_expressions 库的例子:

import 'dart:math';

import 'package:fast_expressions/fast_expressions.dart';

void main(List<String> args) {
  // 示例 1: 使用三元运算符
  {
    const e = '1.isEven ? "Yes, 1 is even" : "No, 1 is odd"';
    final r = parseExpression(
      e,
      resolve: _resolve,
    );
    print(r()); // 输出: No, 1 is odd
  }

  // 示例 2: 简单算术表达式
  {
    const e = '1 + 2 * 3';
    final r = parseExpression(e);
    print(r()); // 输出: 7
  }

  // 示例 3: 使用变量
  {
    const e = '1 + 2 * x';
    final r = parseExpression(
      e,
      context: {
        'x': 3,
      },
    );
    print(r()); // 输出: 7
  }

  // 示例 4: 数组访问
  {
    const e = '1 + 2 * x[y]';
    final r = parseExpression(
      e,
      context: {
        'x': [1, 2, 3],
        'y': 2,
      },
    );
    print(r()); // 输出: 7
  }

  // 示例 5: 自定义函数
  {
    const e = '1 + 2 * add(1, 2)';
    final r = parseExpression(
      e,
      context: {
        'add': (num x, num y) => x + y,
      },
    );
    print(r()); // 输出: 7
  }

  // 示例 6: 带有命名参数的自定义函数
  {
    const e = '1 + 2 * sub(x: 7, y: 4)';
    final r = parseExpression(
      e,
      context: {
        'sub': ({required num x, required num y}) => x - y,
      },
    );
    print(r()); // 输出: 5
  }

  // 示例 7: 调用对象的方法
  {
    const e = '1 + 2 * foo.add(1, 2)';
    final r = parseExpression(
      e,
      context: {
        'foo': Foo(),
      },
      resolve: _resolve,
    );
    print(r()); // 输出: 7
  }

  // 示例 8: 复杂表达式
  {
    const e = '1 + 2 * foo.list()[foo.add(1, 1)]';
    final r = parseExpression(
      e,
      context: {
        'foo': Foo(),
      },
      resolve: _resolve,
    );
    print(r()); // 输出: 4
  }

  // 示例 9: 字符串拼接
  {
    const e = '''
"Hello, " + friends[random()].name
''';
    final friends = [
      Person('Jack'),
      Person('Jerry'),
      Person('John'),
    ];
    final r = parseExpression(
      e,
      context: {
        'friends': friends,
        'random': () => Random().nextInt(friends.length - 1),
      },
      resolve: _resolve,
    );
    print(r()); // 输出随机名字,例如: Hello, Jack
  }
}

// 解析对象成员的函数
dynamic _resolve(dynamic object, String member) {
  Never error() {
    throw StateError("Invalid member '$member', object is $object");
  }

  if (object is Foo) {
    switch (member) {
      case 'add':
        return object.add;
      case 'list':
        return object.list;
    }
  }

  if (object is Person) {
    switch (member) {
      case 'name':
        return object.name;
    }
  }

  if (object is int) {
    switch (member) {
      case 'isEven':
        return object.isEven;
    }
  }

  error();
}

// 示例类
class Foo {
  num add(num x, num y) => x + y;

  List<num> list() => [1, 2, 3];
}

// 示例类
class Person {
  final String name;

  Person(this.name);
}

关于解析器的实现

解析器是通过 PEG 语法生成的。用于生成解析器的软件可以通过以下链接获取: PEG

以下是语法源代码:

%{

typedef Expression = dynamic Function();

String _escape(int charCode) {
    switch (charCode) {
      case 0x22:
        return '"';
      case 0x2f:
        return '/';
      case 0x5c:
        return '\\';
      case 0x62:
        return '\b';
      case 0x66:
        return '\f';
      case 0x6e:
        return '\n';
      case 0x72:
        return '\r';
      case 0x74:
        return '\t';
      default:
        throw StateError('Unable to escape charCode: $charCode');
    }
  }

}%

%%
  final Map<String, dynamic> context;

  final dynamic Function(dynamic object, String member)? resolve;

  ExpressionParser({
    this.context = const {},
    this.resolve,
  });

  Expression _binary(Expression? left, ({String op, Expression expr}) next) {
    final op = next.op;
    final right = next.expr;
    final l = left!;
    switch (op) {
      case '+':
        return () => l() + right();
      case '-':
        return () => l() - right();
      case '/':
        return () => l() / right();
      case '*':
        return () => l() * right();
      case '%':
        return () => l() % right();
      case '~/':
        return () => l() ~/ right();
      case '&lt;&lt;':
        return () => l() &lt;&lt; right();
      case '&gt;&gt;':
        return () => l() &gt;&gt; right();
      case '&gt;&gt;&gt;':
        return () => l() &gt;&gt;&gt; right();
      case '&amp;':
        return () => l() &amp; right();
      case '^':
        return () => l() ^ right();
      case '|':
        return () => l() | right();
      case '&gt;':
        return () => l() &gt; right();
      case '&gt;=':
        return () => l() &gt;= right();
      case '&lt;':
        return () => l() &lt; right();
      case '&lt;=':
        return () => l() &lt;= right();
      case '||':
        return () =&gt; l() as bool || right() as bool;
      case '&amp;&amp;':
        return () =&gt; l() as bool &amp;&amp; right() as bool;
      case '??':
        return () =&gt; l() ?? right();
      case '==':
        return () =&gt; l() == right();
      case '!=':
        return () =&gt; l() != right();
      default:
        throw StateError('Unknown operator: $op');
    }
  }

  Expression _prefix(String? operator, Expression operand) {
    if (operator == null) {
      return operand;
    }

    switch (operator) {
      case '-':
        return () =&gt; -operand();
      case '!':
        return () =&gt; !(operand() as bool);
      case '~':
        return () =&gt; ~operand();
      default:
        throw StateError('Unknown operator: $operator');
    }
  }

  Expression _postfix(
      Expression object, List&lt;({String kind, dynamic arguments})&gt; selectors) {
    for (final selector in selectors) {
      final kind = selector.kind;
      final arguments = selector.arguments;
      switch (kind) {
        case '.':
          final member = arguments as String;
          final object2 = object;
          object = () {
            final object3 = object2();
            if (resolve case final resolve?) {
              return resolve(object3, member);
            } else {
              throw StateError(
                  "Unable to resolve member '$member' for $object3");
            }
          };
          break;
        case '[':
          final index = arguments as dynamic Function();
          final object2 = object;
          object = () {
            final object3 = object2();
            final index2 = index();
            return object3[index2];
          };
          break;
        case '(':
          final object2 = object;
          final arguments2 =
              arguments as List&lt;({String name, Expression expr})&gt;;
          object = () {
            final object3 = object2() as Function;
            final positionalArguments = &lt;dynamic&gt;[];
            final namedArguments = &lt;Symbol, dynamic&gt;{};
            for (final element in arguments2) {
              final name = element.name;
              final expr = element.expr;
              if (name.isEmpty) {
                positionalArguments.add(expr());
              } else {
                if (namedArguments.containsKey(name)) {
                  throw StateError('Duplicate named argument: $name');
                }

                final key = Symbol(name);
                namedArguments[key] = expr();
              }
            }

            return Function.apply(object3, positionalArguments, namedArguments);
          };
          break;
        default:
          throw StateError('Unknown selector: $kind');
      }
    }

    return object;
  }
%%

Start = Spaces v:Expression Eof ;

Expression = Conditional ;

Expression
Conditional =
    e1:IfNull Question e2:Expression Colon e3:Expression { $$ = () =&gt; e1() as bool ? e2() : e3(); }
  / IfNull ;

Expression
IfNull = h:LogicalOr t:(op:IfNullOp ↑ expr:LogicalOr)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

IfNullOp = v:'??' Spaces ;

Expression
LogicalOr = h:LogicalAnd t:(op:LogicalOrOp ↑ expr:LogicalAnd)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

LogicalOrOp = v:'||' Spaces ;

Expression
LogicalAnd = h:Equality t:(op:LogicalAndOp ↑ expr:Equality)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

LogicalAndOp = v:'&amp;&amp;' Spaces ;

Expression
Equality = h:Relational t:(op:EqualityOp ↑ expr:Relational)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

EqualityOp = v:('==' / '!=') Spaces ;

Expression
Relational = h:BitwiseOr t:(op:RelationalOp ↑ expr:BitwiseOr)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

RelationalOp = v:('&gt;=' / '&gt;' / '&lt;=' / '&lt;') Spaces ;

Expression
BitwiseOr = h:BitwiseXor t:(op:BitwiseOrOp ↑ expr:BitwiseXor)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

BitwiseOrOp = !'||' v:'|' Spaces ;

Expression
BitwiseXor = h:BitwiseAnd t:(op:BitwiseXorOp ↑ expr:BitwiseAnd)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

BitwiseXorOp = v:'^' Spaces ;

Expression
BitwiseAnd = h:Shift t:(op:BitwiseAndOp ↑ expr:Shift)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

BitwiseAndOp = !'&amp;&amp;' v:'&amp;' Spaces ;

Expression
Shift = h:Additive t:(op:ShiftOp ↑ expr:Additive)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

ShiftOp = v:('&lt;&lt;' / '&gt;&gt;&gt;' / '&gt;&gt;') Spaces ;

Expression
Additive = h:Multiplicative t:(op:AdditiveOp ↑ expr:Multiplicative)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

AdditiveOp = v:('-' / '+') Spaces ;

Expression
Multiplicative = h:UnaryPrefix t:(op:MultiplicativeOp ↑ expr:UnaryPrefix)* { $$ = t.isEmpty ? h : t.fold(h, _binary); } ;

MultiplicativeOp = v:('/' / '*' / '%' / '~/') Spaces ;

UnaryPrefix = [@expected](/user/expected)('expression', UnaryPrefix_) ;

[@inline](/user/inline)
Expression
UnaryPrefix_ = op:UnaryPrefixOp? expr:UnaryPostfix { $$ = _prefix(op, expr); } ;

UnaryPrefixOp = v:('-' / '!' / '~') Spaces ;

Expression
UnaryPostfix = object:Primary selectors:Selector* { $$ = _postfix(object, selectors); } ;

({String kind, dynamic arguments})
Selector =
    kind:Dot arguments:Identifier_
  / kind:OpenBracket arguments:Expression CloseBracket
  / kind:OpenParenthesis arguments:Arguments CloseParenthesis ;

Arguments = [@list](/user/list)(NamedArgument / PositionalArgument, Comma ↑ v:(NamedArgument / PositionalArgument)) ;

NamedArgument = name:Identifier_ Colon expr:Expression ;

PositionalArgument = name:'' expr:Expression ;

Primary =
    Number
  / Boolean
  / String
  / Null
  / Identifier
  / OpenParenthesis v:Expression CloseParenthesis ;

Expression
Null = 'null' Spaces { $$ = () =&gt; null; } ;

Expression
Boolean =
    'true' Spaces { $$ = () =&gt; true; }
  / 'false' Spaces { $$ = () =&gt; false; } ;

Expression
Number = v:$(
  [-]?
  ([0] / [1-9][0-9]*)
  ([.] [0-9]+)?
  ([eE] ↑ [-+]? [0-9]+)?
  ) Spaces {
    final n = num.parse(v);
    $$ = () =&gt; n;
  } ;

[@inline](/user/inline)
String
EscapeChar = c:["/bfnrt\\] { $$ = _escape(c); } ;

[@inline](/user/inline)
String
EscapeHex = 'u' v:HexNumber { $$ = String.fromCharCode(v); } ;

HexNumber = [@indicate](/user/indicate)('Expected 4 digit hex number', HexNumber_) ;

[@inline](/user/inline)
int
HexNumber_ = v:$([0-9A-Za-z]{4}) { $$ = int.parse(v, radix: 16); } ;

String
StringChars =
    $[\u{20}-\u{21}\u{23}-\u{5b}\u{5d}-\u{10ffff}]+
  / '\\' v:(EscapeChar / EscapeHex) ;

String
StringRaw = '"' v:StringChars* DoubleQuote { $$ = v.join(); } ;

Expression
String = v:StringRaw { $$ = () =&gt; v; } ;

[@inline](/user/inline)
String
Identifier_ = v:[@expected](/user/expected)('identifier', $([a-zA-Z_$] [a-zA-Z_$0-9]*)) Spaces ;

Expression
Identifier = v:Identifier_ {
    $$ = () {
      if (!context.containsKey(v)) {
        throw StateError("Variable '$v' not found");
      }
      return context[v];
    };
  } ;

Eof = !. ;

CloseBracket = v:']' Spaces ;

CloseParenthesis = v:')' Spaces ;

Colon = v:':' Spaces ;

Comma = v:',' Spaces ;

Dot = v:'.' Spaces ;

DoubleQuote = v:'"' Spaces ;

OpenBracket = v:'[' Spaces ;

OpenParenthesis = v:'(' Spaces ;

Question = v:'?' Spaces ;

Spaces = [ \n\r\t]* ;

更多关于Flutter表达式计算插件fast_expressions的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter表达式计算插件fast_expressions的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用Flutter表达式计算插件fast_expressions的代码示例。这个插件允许你解析和计算数学表达式。

首先,你需要在你的pubspec.yaml文件中添加fast_expressions依赖:

dependencies:
  flutter:
    sdk: flutter
  fast_expressions: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来获取依赖。

接下来,你可以在你的Flutter应用中使用fast_expressions来计算表达式。以下是一个简单的示例,展示如何在Flutter应用中实现这一功能:

import 'package:flutter/material.dart';
import 'package:fast_expressions/fast_expressions.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Expression Calculator',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: CalculatorScreen(),
    );
  }
}

class CalculatorScreen extends StatefulWidget {
  @override
  _CalculatorScreenState createState() => _CalculatorScreenState();
}

class _CalculatorScreenState extends State<CalculatorScreen> {
  final TextEditingController _controller = TextEditingController();
  String _result = '';

  void _calculate() {
    try {
      final expression = _controller.text;
      final parser = ExpressionParser();
      final compiled = parser.parse(expression);
      final context = ExpressionContext();
      final result = compiled.evaluate(context);
      setState(() {
        _result = result.toString();
      });
    } catch (e) {
      setState(() {
        _result = 'Error: $e';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Expression Calculator'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextField(
              controller: _controller,
              decoration: InputDecoration(
                labelText: 'Enter Expression',
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.multiline,
              maxLines: null,
            ),
            SizedBox(height: 16.0),
            ElevatedButton(
              onPressed: _calculate,
              child: Text('Calculate'),
            ),
            SizedBox(height: 16.0),
            Text(
              _result,
              style: TextStyle(fontSize: 24.0, color: Colors.blue),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,包含一个文本字段用于输入数学表达式,一个按钮用于触发计算,以及一个文本组件用于显示计算结果。

  1. 依赖添加:在pubspec.yaml中添加fast_expressions依赖。
  2. UI布局:使用ScaffoldTextFieldElevatedButtonText组件构建UI。
  3. 表达式计算:在_calculate方法中,我们使用ExpressionParser解析表达式,然后使用ExpressionContext评估表达式并获取结果。

请注意,ExpressionParserExpressionContextfast_expressions插件提供的类,用于解析和评估数学表达式。

确保你已经安装了最新版本的fast_expressions插件,并根据需要调整代码以适应你的应用逻辑。

回到顶部