retour significatif des messages d'erreur d'un analyseur écrit avec Scala Parser Combinators
-
08-10-2019 - |
Question
J'essaie d'écrire un analyseur en utilisant scala Parser Combinators. Si je correspond récursive,
def body: Parser[Body] =
("begin" ~> statementList ) ^^ {
case s => { new Body(s); }
}
def statementList : Parser[List[Statement]] =
("end" ^^ { _ => List() } )|
(statement ~ statementList ^^ { case statement ~ statementList => statement :: statementList })
je reçois de bons errormessages chaque fois qu'il ya une faute dans un communiqué. Cependant, ce code est laid à long. Je voudrais donc écrire ceci:
def body: Parser[Body] =
("begin" ~> statementList <~ "end" ) ^^ {
case s => { new Body(s); }
}
def statementList : Parser[List[Statement]] =
rep(statement)
Ce code fonctionne, mais seulement imprime des messages significatifs s'il y a une erreur dans la première déclaration. Si elle est dans un communiqué plus tard, le message devient douloureusement inutilisable, car l'analyseur veut voir toute déclaration erronée remplacée par le jeton « fin »:
Exception in thread "main" java.lang.RuntimeException: [4.2] error: "end" expected but "let" found
let b : string = x(3,b,"WHAT???",!ERRORHERE!,7 )
^
Ma question: est-il un moyen d'obtenir représentant et repsep travailler en combinaison avec des messages d'erreur significatifs, cet endroit le caret au bon endroit au lieu de le commencer du fragment de répétition?
La solution 2
Ah, a trouvé la solution! Il se avère que vous devez utiliser l'expression de fonction sur votre analyseur principal pour renvoyer un nouvel analyseur que est moins enclin à suivre en arrière. (Je me demande ce exactement signifie, peut-être que si elle trouve une coupure de ligne, il ne sera pas suivi de retour?) suit la dernière position sur Wich une défaillance est survenue.
changé:
def parseCode(code: String): Program = {
program(new lexical.Scanner(code)) match {
case Success(program, _) => program
case x: Failure => throw new RuntimeException(x.toString())
case x: Error => throw new RuntimeException(x.toString())
}
}
def program : Parser[Program] ...
dans:
def parseCode(code: String): Program = {
phrase(program)(new lexical.Scanner(code)) match {
case Success(program, _) => program
case x: Failure => throw new RuntimeException(x.toString())
case x: Error => throw new RuntimeException(x.toString())
}
}
def program : Parser[Program] ...
Autres conseils
Vous pouvez le faire en combinant une « maison » méthode rep
avec les non-retours en arrière dans les déclarations. Par exemple:
scala> object X extends RegexParsers {
| def myrep[T](p: => Parser[T]): Parser[List[T]] = p ~! myrep(p) ^^ { case x ~ xs => x :: xs } | success(List())
| def t1 = "this" ~ "is" ~ "war"
| def t2 = "this" ~! "is" ~ "war"
| def t3 = "begin" ~ rep(t1) ~ "end"
| def t4 = "begin" ~ myrep(t2) ~ "end"
| }
defined module X
scala> X.parse(X.t4, "begin this is war this is hell end")
res13: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] =
[1.27] error: `war' expected but ` ' found
begin this is war this is hell end
^
scala> X.parse(X.t3, "begin this is war this is hell end")
res14: X.ParseResult[X.~[X.~[String,List[X.~[X.~[String,String],String]]],String]] =
[1.19] failure: `end' expected but ` ' found
begin this is war this is hell end
^