parsing scala parser-combinators

parsing - EBNF to Scala parser combinator



parser-combinators (1)

Tengo el siguiente EBNF que quiero analizar:

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

Y esto es lo que obtuve:

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 manera, preferiblemente sin tener que recurrir al casting (asInstanceOf).


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

expressions abreviadas a expr para abreviar, así como agregar el alias tipo E por el mismo motivo.

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

Tenga en cuenta que LengthExpression simplemente se devuelve como un valor (usando ^^^ ) de su producción respectiva. Esto funciona porque los objetos complementarios para las clases de casos (suponiendo que LengthExpression sea ​​de hecho 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 una sola Expression y devuelve una nueva instancia de LengthExpression , que satisface nuestras necesidades para la construcción de árbol de orden superior.