如何让Scala Parser Combinator支持逻辑运算及大小写不敏感并提取键?
刚好我之前用过Scala Parser Combinator做类似的表达式解析,给你一套完整的实现方案,完美匹配你的需求——提取比较表达式左边的键,支持大小写不敏感的逻辑运算(AND/OR),还能处理括号嵌套:
首先,我们基于Scala内置的RegexParsers来构建解析器,核心思路是拆解表达式结构:先识别单个比较表达式(提取键),再组合成带逻辑运算和括号的完整表达式,最后收集所有键。
直接上可运行的代码,带详细注释:
import scala.util.parsing.combinator.RegexParsers class KeyParser extends RegexParsers { // 大小写不敏感的逻辑运算符:匹配AND/Or/aNd等任意大小写组合 private val and: Parser[String] = "(?i)and".r private val or: Parser[String] = "(?i)or".r // 匹配$包裹的变量(仅做语法校验,不需要提取内容) private val variable: Parser[String] = "$" ~> "[a-zA-Z_][a-zA-Z0-9_]*".r <~ "$" // 支持的比较运算符:可根据需求扩展 private val compOp: Parser[String] = "!=" | "=" | "<>" | ">=" | "<=" | ">" | "<" // 解析单个比较表达式,返回左边的键 private val comparison: Parser[String] = ident ~ compOp ~ variable ^^ { case key ~ _ ~ _ => key // 只保留左边的键,忽略运算符和变量 } // 原子表达式:带括号的完整表达式,或者单个比较表达式 // 把单个比较表达式包装成List,方便后续合并所有键 private def atomicExpr: Parser[List[String]] = "(" ~> expr <~ ")" | comparison ^^ (List(_)) // 完整逻辑表达式:多个原子表达式通过AND/OR连接 // 合并所有原子表达式中的键,返回最终的键列表 private def expr: Parser[List[String]] = atomicExpr ~ rep( (and | or) ~ atomicExpr ) ^^ { case firstKeys ~ rest => firstKeys ++ rest.flatMap(_._2) } // 对外暴露的解析入口,返回Either处理成功/失败 def parseKeys(input: String): Either[String, List[String]] = { parseAll(expr, input) match { case Success(result, _) => Right(result) case failure: NoSuccess => Left(s"解析失败:${failure.msg},位置:${failure.next.pos}") } } } // 测试用例,直接运行就能看到结果 object KeyParserDemo extends App { val parser = new KeyParser() val expr1 = "local_province != $province_name$ or city=$city_name$ or people_number<>$some_digit$" println(parser.parseKeys(expr1)) // 输出:Right(List(local_province, city, people_number)) val expr2 = "(local_province=$province_name$)" println(parser.parseKeys(expr2)) // 输出:Right(List(local_province)) val expr3 = "(local_province=$province_name$ or city=$city_name$) and (lib_name=$lib$ or version>=1.0)" println(parser.parseKeys(expr3)) // 输出:Right(List(local_province, city, lib_name)) }
关键细节说明:
- 大小写不敏感的逻辑运算:通过正则的
(?i)标志实现,不管是AND、Or还是aNd都能被正确匹配。 - 括号嵌套支持:
atomicExpr递归调用expr,完美处理多层括号的情况(比如((a=$x$ and b=$y$) or c=$z$)也能正常解析)。 - 键收集逻辑:每个原子表达式返回自己的键列表,逻辑表达式把所有子表达式的键合并成一个大列表,确保不会遗漏任何键。
- 扩展性:如果需要添加更多比较运算符,直接在
compOp里加对应的符号即可;如果需要键大小写不敏感,只需要在comparison里把key转成小写/大写(比如key.toLowerCase)。
测试一下你的例子,完全符合预期!如果还有其他特殊需求(比如支持函数调用、更复杂的变量格式),可以再调整parser的规则。
内容的提问来源于stack exchange,提问作者xyz
相关产品推荐
相关产品推荐

