如何在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
相关产品推荐
相关产品推荐

