如何在独立测试类中测试fastparse解析器并匹配完整字符串?
FastParse解析器测试中隐式参数缺失问题解决
问题场景
编写了FastParse解析器:
object MyParser: def int[$: P]: P[Int] = digit.rep(1).!.map(s => s.toInt) def digit[$: P]: P[Unit] = CharIn("0-9")
需要在独立测试类中单独测试每个解析器,且要求测试时必须匹配完整字符串(部分匹配视为失败),因此编写测试类:
class MyParserTest extends AnyFunSuite with Matchers: def parseAll[$: P, T](p: => P[T]): P[T] = Start ~ p ~ End test("int parses correctly") { fastparse.parse("1234", parseAll(MyParser.int)) shouldBe Success(1234, 4) }
运行时触发错误:
No given instance of type fastparse.ParsingRun[$] was found for an implicit parameter of method int in object MyParser where: $ is a type variable
直接调用fastparse.parse("1234", MyParser.int)无问题,尝试过导入MyParser.given、将parseAll移入MyParser均无效。
问题原因
错误根源在于parseAll的参数p: => P[T]是按名称调用的,Scala不会自动将parseAll上下文里的隐式P[$]传递给MyParser.int的调用。MyParser.int需要隐式的ParsingRun[$]实例,但在按名称参数的求值过程中,这个隐式无法被正确捕获。
解决方案
方案一:显式传递隐式参数
修改parseAll,让它接收一个依赖隐式P[$]的函数,内部显式传递隐式参数:
class MyParserTest extends AnyFunSuite with Matchers: def parseAll[$: P, T](p: Given[P[$]] => P[T]): P[T] = Start ~ p(given) ~ End test("int parses correctly") { fastparse.parse("1234", parseAll(MyParser.int)) shouldBe Success(1234, 4) }
方案二:调整参数类型
直接将parseAll的参数改为非按名称类型,确保隐式参数能正确传递:
class MyParserTest extends AnyFunSuite with Matchers: def parseAll[$: P, T](p: P[T]): P[T] = Start ~ p ~ End test("int parses correctly") { fastparse.parse("1234", parseAll(MyParser.int)) shouldBe Success(1234, 4) }
验证效果
修改后,测试会强制匹配完整字符串:
- 输入
"1234"会返回Success(1234, 4) - 输入
"123abc"会解析失败,因为无法匹配End,符合测试要求。
内容的提问来源于stack exchange,提问作者Todd O'Bryan
相关产品推荐
相关产品推荐

