2009-02-08 15 views
11

tengo el siguiente EBNF que quiero analizar:EBNF a Scala combinador analizador

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

Y esto es lo que tengo:

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

pero me gustaría saber si hay una mejor, preferiblemente sin tener que recurrir a la transmisión (asInstanceOf).

Respuesta

12

lo haría así:

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

acorta expressions-expr por razones de brevedad, así como añade el tipo de alias E por la misma razón.

El truco que estoy usando aquí para evitar el feo análisis de casos es devolver un valor de función desde dentro de la producción interna. Esta función toma un Expression (que será el primary) y luego devuelve un nuevo Expression basado en el primero. Esto unifica los dos casos de expresiones de puntos y expresiones entre corchetes. Finalmente, el método collapse se usa para fusionar el List lineal de valores de función en un AST apropiado, comenzando con la expresión primaria especificada.

Tenga en cuenta que LengthExpression acaba de devolver como valor (utilizando ^^^) de su producción respectiva. Esto funciona porque los objetos complementarios para las clases de casos (suponiendo que LengthExpression es realmente una clase de caso) amplían el valor de la función correspondiente delegando a su constructor. Por lo tanto, la función representada por LengthExpression toma un solo Expression y devuelve una nueva instancia de LengthExpression, que satisface con precisión nuestras necesidades para la construcción de árbol de orden superior.

Cuestiones relacionadas