2009-02-08 106 views
11

我有以下EBNF,我想分析:EBNF Scala的解析器組合

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)。

回答

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) } 
} 

縮短expressionsexpr爲簡便起見,以及添加的類型別名E出於同樣的原因。

我在這裏用來避免醜陋的案例分析的訣竅是從內部生產中返回函數值。此功能需要Expression(將爲primary),然後根據第一個返回新的Expression。這將點分派和括號表達式的兩種情況統一起來。最後,使用collapse方法將函數值的線性List合併到適當的AST中,從指定的主表達式開始。

請注意,LengthExpression僅作爲其相應產量的值(使用^^^)返回。這是可行的,因爲case類的伴隨對象(假設LengthExpression確實是一個case類)擴展了委託給它們構造函數的相應函數值。因此,由LengthExpression表示的函數取單個Expression並返回一個新實例LengthExpression,這正好滿足了我們對高階樹構造的需求。

相關問題