Flutter解析与构建语法树插件petitparser的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter解析与构建语法树插件petitparser的使用

PetitParser 是一个强大的Dart库,用于定义和组合解析器。它允许开发者以对象的形式建模语法和解析器,并且这些对象可以在运行时动态配置。PetitParser结合了无扫描解析、解析器组合、解析表达式文法(PEG)和Packrat解析器的思想,使得编写可组合和可重用的解析器变得容易。

安装

首先,在pubspec.yaml文件中添加依赖:

dependencies:
  petitparser: ^4.0.0

然后在你的Dart代码中导入PetitParser:

import 'package:petitparser/petitparser.dart';

如果你只需要核心功能或特定部分的功能,可以更精确地导入:

import 'package:petitparser/core.dart';
import 'package:petitparser/parser.dart';

写一个简单的语法

让我们从一个简单的例子开始:创建一个解析标识符(由字母开头,后面跟随零个或多个字母或数字)的解析器。

final id = letter() & (letter() | digit()).star();

这段代码构建了一个解析器对象树:

  • SequenceParser: 接受其子解析器的序列。
    • SingleCharacterParser: 接受单个字母。
    • PossessiveRepeatingParser: 接受零个或多个其子解析器的结果。
      • ChoiceParser: 接受第一个成功的子解析器,否则失败。
        • SingleCharacterParser: 接受单个字母。
        • SingleCharacterParser: 接受单个数字。

你可以使用不同的方式来写相同的解析器:

final id1 = letter().seq(letter().or(digit()).star());
final id2 = [letter(), [letter(), digit()].toChoiceParser().star()].toSequenceParser();
final id3 = seq2(letter(), [letter(), digit()].toChoiceParser().star());

解析输入

要实际解析输入字符串,可以使用Parser.parse方法:

final result1 = id.parse('yeah');
final result2 = id.parse('f12');

print(result1.value); // ['y', ['e', 'a', 'h']]
print(result2.value); // ['f', ['1', '2']]

如果解析失败,会返回一个Failure对象:

final result3 = id.parse('123');
print(result3.message); // 'letter expected'
print(result3.position); // 0

你还可以使用模式匹配来处理解析结果:

switch (id.parse(input)) {
  case Success(value: final value):
    print('Success: $value');
  case Failure(message: final message, position: final position):
    print('Failure at $position: $message');
}

如果你想只检查输入是否有效,可以使用Parser.accept方法:

print(id.accept('foo')); // true
print(id.accept('123')); // false

编写更复杂的语法

接下来我们尝试编写一个更复杂的语法,用于解析并计算简单的算术表达式。首先定义整数的解析规则:

final number = digit().plus().flatten().trim().map(int.parse);

然后定义加法和乘法的优先级顺序。由于存在递归引用,我们需要先定义未解析的解析器,稍后设置它们的引用:

final term = undefined();
final prod = undefined();
final prim = undefined();

final add = (prod & char('+').trim() & term)
    .map((values) => values[0] + values[2]);
term.set(add | prod);

final mul = (prim & char('*').trim() & prod)
    .map((values) => values[0] * values[2]);
prod.set(mul | prim);

final parens = (char('(').trim() & term & char(')').trim())
    .map((values) => values[1]);
final number = digit().plus().flatten().trim().map(int.parse);
prim.set(parens | number);

final parser = term.end();

现在我们可以测试这个解析器和求值器:

parser.parse('1 + 2 * 3'); // 7
parser.parse('(1 + 2) * 3'); // 9

使用表达式生成器

编写这样的表达式解析器可能会很复杂,PetitParser 提供了一个ExpressionBuilder来简化这个过程。以下是创建一个支持浮点数和各种运算符的解析器的例子:

final builder = ExpressionBuilder<num>();

builder.primitive(digit()
    .plus()
    .seq(char('.').seq(digit().plus()).optional())
    .flatten()
    .trim()
    .map(num.parse));

builder.group().wrapper(
    char('(').trim(), char(')').trim(), (left, value, right) => value);

builder.group().prefix(char('-').trim(), (operator, value) => -value);

builder.group().right(
    char('^').trim(), (left, operator, right) => math.pow(left, right));

builder.group()
  ..left(char('*').trim(), (left, operator, right) => left * right)
  ..left(char('/').trim(), (left, operator, right) => left / right);

builder.group()
  ..left(char('+').trim(), (left, operator, right) => left + right)
  ..left(char('-').trim(), (left, operator, right) => left - right);

final parser = builder.build().end();

parser.parse('-8'); // -8
parser.parse('1+2*3'); // 7
parser.parse('1*2+3'); // 5
parser.parse('8/4/2'); // 1
parser.parse('2^2^3'); // 256

测试和调试

为了确保你的语法正确工作,建议逐步开发并测试每个部分。例如,可以单独测试数字解析器:

test('number parsing', () {
  final definition = EvaluatorDefinition();
  final parser = definition.buildFrom(definition.number);
  expect(parser.parse('42').value, 42);
});

PetitParser还提供了一个Linter工具,可以帮助你检测常见问题:

test('detect common problems', () {
  final definition = EvaluatorDefinition();
  final parser = definition.build();
  expect(linter(parser), isEmpty);
});

如果你遇到解析器行为不符合预期的情况,可以使用trace函数来跟踪解析过程:

final parser = letter() & word().star();
trace(parser).parse('f1');

这将输出详细的解析步骤,帮助你理解解析器的行为。

通过上述步骤,你应该能够使用PetitParser轻松地解析和构建复杂的语法树。更多详细信息和示例请参考官方文档和示例仓库。


更多关于Flutter解析与构建语法树插件petitparser的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter解析与构建语法树插件petitparser的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用petitparser库来解析和构建语法树的示例代码。petitparser是一个强大的解析库,可以用于定义和解析各种语法。

1. 添加依赖

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

dependencies:
  flutter:
    sdk: flutter
  petitparser: ^4.0.0  # 请确保使用最新版本

2. 创建一个Flutter项目

假设你已经有一个Flutter项目,如果没有,可以使用以下命令创建一个新的Flutter项目:

flutter create my_parser_app
cd my_parser_app

3. 定义语法解析器

在你的lib目录下创建一个新的Dart文件,例如parser.dart,并在其中定义你的语法解析器。

import 'package:petitparser/petitparser.dart';

class MyParser {
  final Parser<dynamic> parser;

  MyParser() {
    // 定义一些基本的解析器
    final digit = Parser.digit();
    final integer = digit.plus().flatten().map(int.parse);
    final plus = Parser.string('+').token();
    final minus = Parser.string('-').token();
    final multiply = Parser.string('*').token();
    final divide = Parser.string('/').token();
    final whitespace = Parser.whitespace().skipMany();
    final leftParen = Parser.string('(').token();
    final rightParen = Parser.string(')').token();

    // 定义表达式解析器
    final factor = integer.or(leftParen.seq(parser.subParser().lazy(), rightParen));
    final term = factor.seq(
        (plus.or(minus)).optional().map((op) => op ?? Parser.success(null)),
        factor
      ).map(([left, op, right]) => op == null ? left : op.value == '+' ? left + right : left - right);

    final expression = term.seq(
        (multiply.or(divide)).optional().map((op) => op ?? Parser.success(null)),
        term
      ).map(([left, op, right]) => op == null ? left : op.value == '*' ? left * right : left / right);

    // 最终解析器
    parser = whitespace.seq(expression).end();
  }

  Result parse(String input) {
    return parser.parse(input);
  }
}

4. 使用解析器

在你的主Dart文件(例如main.dart)中使用这个解析器。

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Petitparser Example'),
        ),
        body: Center(
          child: MyParserWidget(),
        ),
      ),
    );
  }
}

class MyParserWidget extends StatefulWidget {
  @override
  _MyParserWidgetState createState() => _MyParserWidgetState();
}

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

  void _parse() {
    final input = _controller.text;
    final parser = MyParser();
    final result = parser.parse(input);

    if (result.isSuccess) {
      _result = 'Parsed result: ${result.value}';
    } else {
      _result = 'Error at ${result.errorPosition}: ${result.message}';
    }

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        TextField(
          controller: _controller,
          decoration: InputDecoration(hintText: 'Enter an expression'),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _parse,
          child: Text('Parse'),
        ),
        SizedBox(height: 20),
        Text(_result),
      ],
    );
  }
}

5. 运行应用

确保你的Flutter环境已经正确配置,然后运行应用:

flutter run

现在你应该可以在Flutter应用中输入表达式,并点击“Parse”按钮来解析并显示结果。这个示例展示了一个简单的算术表达式解析器,你可以根据需要扩展它以支持更复杂的语法。

回到顶部