我有以下要解析的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)。
答案 0 :(得分: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
方法用于将函数值的线性List
合并到适当的AST中,从指定的主表达式开始。
请注意,LengthExpression
只是作为一个值(使用^^^
)从其各自的作品中返回。这是因为case类的伴随对象(假设LengthExpression
确实是一个case类)扩展了委托给它们的构造函数的相应函数值。因此,LengthExpression
表示的函数只需一个Expression
并返回LengthExpression
的新实例,正好满足了我们对高阶树构造的需求。