You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Dart中解析这类逻辑表达式字符串?求入门示例指引

使用PetitParser解析并校验逻辑表达式

问题背景

我有如下格式的逻辑表达式字符串:

(((S|69|L || S|69|R || S|72|L || S|72|R) && ((S|62|L && (S|78|L || S|55|L) && (S|77|L || S|1|L)) || (S|62|R && (S|78|R || S|55|R) && (S|77|R || S|1|R)))) && (M|34|L || M|34|R) && (((M|40|L && M|39|L && M|36|L) || (M|40|R && M|39|R && M|36|R)) || ((M|38|L && M|36|L && M|37|L) || (M|38|R && M|36|R && M|37|R))))

需要校验类似S|69|L的条目是否满足该字符串中的逻辑条件(由括号组织的&&(与)、||(或)操作序列)。尝试用PetitParser库但难以入门,目前已经写出能去除外层括号的代码,希望获得相关技巧和入门示例:

String testString = '((1||2) || 3)';

final inner = undefined();
final paren = (char('(').trim() & inner.star().flatten() & char(')').trim())
    .map((values) => values[1]);
inner.set(paren | pattern('^)'));
final parser = inner.end();
final result = parser.parse(testString);
print(result.value);

入门技巧与完整实现示例

1. 从原子条目解析入手

先定义最基础的S|69|L这类原子项的解析规则,明确匹配格式:

// 匹配字母(S/M)|数字串|字母(L/R)的原子条目
final atom = (letter() & char('|') & digit().plus() & char('|') & letter())
    .flatten()
    .trim(); // 自动忽略前后空格

2. 构建递归表达式解析器

PetitParser处理嵌套括号的核心是用undefined()先占位,再逐步构建层级结构(注意&&优先级高于||):

final expression = undefined();

// 解析括号包裹的表达式:( 内部表达式 ),最终返回内部内容
final parenthesized = (char('(').trim() & expression & char(')').trim())
    .map((values) => values[1]);

// 基础项:要么是原子条目,要么是括号包裹的表达式
final term = parenthesized | atom;

// 解析OR操作:多个term用||连接,转成带类型的结构
final orExpression = term.separatedBy(string('||').trim(), includeSeparators: true)
    .map((parts) => {
          'type': 'or',
          'children': parts.where((part) => part != '||').toList()
        });

// 解析AND操作:多个or表达式用&&连接,优先级更高
final andExpression = orExpression.separatedBy(string('&&').trim(), includeSeparators: true)
    .map((parts) => {
          'type': 'and',
          'children': parts.where((part) => part != '&&').toList()
        });

// 把最终的表达式指向andExpression,完成递归定义
expression.set(andExpression);

3. 将字符串解析为AST(抽象语法树)

现在可以用上述解析器把逻辑字符串转成结构化的AST,方便后续校验:

void main() {
  String logicExpr = '(((S|69|L || S|69|R) && (M|34|L || M|34|R)))';
  
  // 插入上面的解析器定义代码...
  
  final parseResult = expression.parse(logicExpr);
  if (parseResult.isSuccess) {
    print('解析成功,AST结构:');
    print(parseResult.value);
  } else {
    print('解析失败:${parseResult.message}');
  }
}

4. 实现条件校验逻辑

基于AST递归遍历,判断给定的条目集合是否满足逻辑条件:

// 递归校验AST与条目集合的匹配度
bool checkCondition(dynamic ast, Set<String> satisfiedItems) {
  if (ast is String) {
    // 原子项直接判断是否在满足集合中
    return satisfiedItems.contains(ast);
  }
  if (ast is Map<String, dynamic>) {
    switch (ast['type']) {
      case 'or':
        // OR逻辑:只要有一个子项满足则整体满足
        return (ast['children'] as List).any((child) => checkCondition(child, satisfiedItems));
      case 'and':
        // AND逻辑:所有子项都满足才整体满足
        return (ast['children'] as List).every((child) => checkCondition(child, satisfiedItems));
      default:
        return false;
    }
  }
  return false;
}

// 使用示例
void main() {
  // ... 前面的解析代码 ...
  
  final ast = parseResult.value;
  final satisfiedItems = {'S|69|L', 'M|34|R'};
  print('是否满足条件:${checkCondition(ast, satisfiedItems)}'); // 输出true
}

实用优化建议

  • 给原子项加更严格的规则:比如限制第一个字母只能是S或M,最后一个字母只能是L或R,避免匹配非法条目
  • 封装AST为实体类(如OrExpr、AndExpr、AtomExpr),比Map结构更易维护
  • 增加错误处理:对解析失败的情况输出详细位置信息,方便排查表达式格式问题

内容的提问来源于stack exchange,提问作者Coltuxumab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 21:50:39