EBNF到Scala解析器组合器

11

我有以下的EBNF表达式需要解析:

PostfixExp      -> PrimaryExp ( "[" Exp "]" 
                                | . id "(" ExpList ")" 
                                | . length )*

这就是我得到的:

def postfixExp: Parser[Expression] = (
    primaryExp ~ rep(
        "[" ~ expression ~ "]"
        | "." ~ ident ~"(" ~ repsep(expression, "," ) ~ ")" 
        | "." ~ "length") ^^ {
        case primary ~ list =>  list.foldLeft(primary)((prim,post) =>
                post match {
                    case "[" ~ length ~ "]" => ElementExpression(prim, length.asInstanceOf[Expression])
                    case "." ~ function ~"(" ~ arguments ~ ")" =>  CallMethodExpression(prim, function.asInstanceOf[String], arguments.asInstanceOf[List[Expression]])
                    case _ => LengthExpression(prim)
                }
            )
    })

但我想知道是否有更好的方法,最好不用转换类型(asInstanceOf)。

1个回答

12

我会这样做:

type E = Expression

def postfixExp = primaryExp ~ rep(
    "[" ~> expr <~ "]" ^^ { e => ElementExpression(_:E, e) }
  | "." ~ "length" ^^^ LengthExpression
  | "." ~> ident ~ ("(" ~> repsep(expr, ",") <~ ")") ^^ flatten2 { (f, args) =>
      CallMethodExpression(_:E, f, args)
    }
) ^^ flatten2 { (e, ls) => collapse(ls)(e) }

def expr: Parser[E] = ...

def collapse(ls: List[E=>E])(e: E) = {
  ls.foldLeft(e) { (e, f) => f(e) }
}

为了简洁起见,我将expressions缩写为expr,并添加了类型别名E

在内部生产中返回一个函数值可以避免丑陋的情况分析。这个函数接受一个Expression(将是primary),然后根据第一个表达式返回一个新的Expression。这统一了点分派和带括号表达式的两种情况。最后,使用collapse方法将函数值的线性列表合并为一个正确的AST,从指定的主表达式开始。

请注意,LengthExpression仅作为其相应产生式的值(使用^^^)返回。这是因为伴随对象对于案例类(假设LengthExpression确实是案例类)扩展相应的函数值委托给它们的构造函数。因此,由LengthExpression表示的函数接受一个Expression并返回一个新的LengthExpression实例,完全满足我们对高阶树构造的需求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接