FastParse。如何强制执行一次规则

时间:2016-08-14 22:45:21

标签: scala fastparse

我正在尝试使用FastParse API实现以下语法。

  1. Expr只能包含Foo,Bar,Baz子表达式
  2. Expr必须包含至少1个子表达式Foo / Bar / Bar。它不能为空
  3. Foo / Bar / Baz可以按照Expr。
  4. 中的任何顺序出现
  5. Foo / Bar / Baz不能重复,所以你只能使用它们
  6. 因此有效表达式为Expr(Baz(10),Foo(10),Bar(10)),无效表达式为Expr()Expr(Bar(10),Bar(10))

    到目前为止,我编写了这段代码,可以强制执行并解析1,2,3规则。但是第4号规则被证明是棘手的。

    import fastparse.noApi._
    import fastparse.WhitespaceApi
    
    object FastParsePOC {
    
       val White = WhitespaceApi.Wrapper{
          import fastparse.all._
          NoTrace(" ".rep)
       }
    
       def print(input: Parsed[(String, String, Seq[(String, String)])]) : Unit = {
          input match {
             case Parsed.Success(value, index) =>
                println(s"${value._1} ${value._2}")
                value._3.foreach{case (name, index) => println(s"$name $index")}
             case f @ Parsed.Failure(error, line, col) => println(s"Error: $error $line $col ${f.extra.traced.trace}")
          }
       }
    
       def main(args: Array[String]) : Unit = {
          import White._
          val base = P("(" ~ (!")" ~ AnyChar).rep(1).! ~ ")")
          val foo = P("Foo".! ~ base)
          val bar = P("Bar".! ~ base)
          val baz = P("Baz".! ~ base)
          val foobarbaz = (foo | bar | baz)
          val parser = P("Expr" ~ "(" ~ foobarbaz ~ ",".? ~ (foobarbaz).rep(sep=",") ~ ")")
          val input3 = "Expr(Baz(20),Bar(10),Foo(30))"
          val parsed = parser.parse(input3)
          print(parsed)
       }
    }
    

1 个答案:

答案 0 :(得分:1)

您可以查看"确切一次" filter调用约束:

test("foo bar baz") {
  val arg: P0 = P("(") ~ (!P(")") ~ AnyChar).rep(1) ~ ")"

  val subExpr: P[String] = (P("Foo") | P("Bar") | P("Baz")).! ~ arg

  val subExprList: P[Seq[String]] = subExpr.rep(min = 1, sep = P(",")).filter { list =>
    list.groupBy(identity[String]).values.forall(_.length == 1)
  }

  val expr: P[Seq[String]] = P("Expr") ~ "(" ~ subExprList ~ ")"

  expr.parse("Expr(Foo(10))").get.value

  expr.parse("Expr(Foo(10),Bar(20),Baz(30))").get.value

  intercept[Throwable] {
    expr.parse("Expr()").get.value
  }

  intercept[Throwable] {
    expr.parse("Expr(Foo(10),Foo(20))").get.value
  }
}