当前位置:  开发笔记 > 编程语言 > 正文

EBNF到Scala解析器组合器

如何解决《EBNF到Scala解析器组合器》经验,为你挑选了1个好方法。

我有以下要解析的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> Daniel Spiew..:

我会这样做:

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,精确满足我们对高阶树结构的需求.

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有