2010-12-11 56 views
4

J'essaye d'écrire un analyseur dans Scala en utilisant des combinateurs d'analyseurs. Si je correspondance récursive,Retour de messages d'erreur significatifs à partir d'un analyseur écrit avec les combinateurs d'analyseurs Scala

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

puis-je obtenir de bons errormessages chaque fois qu'il ya une faute dans un communiqué. Cependant, c'est un code long moche. 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 des messages significatifs si imprimé est 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 « end »:

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 fonctionnant en combinaison avec des messages d'erreur significatifs, qui placent le curseur sur le bon endroit au lieu de sur le début du fragment répétitif?

Répondre

1

Ah, j'ai trouvé la solution! Il s'avère que vous devez utiliser la phrase de fonction sur votre analyseur principal pour retourner un nouvel analyseur que est moins enclin à suivre. (Je me demande ce que cela signifie exactement, peut-être que s'il trouve un saut de ligne, il ne suivra pas?) suit la dernière position sur laquelle une défaillance s'est produite.

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] ... 
+1

'phrase' renvoie un' Parser' que ne réussira que s'il n'y a pas d'entrée après avoir accepté quelque chose. Je ne sais pas pourquoi cela changerait la marche arrière de cette façon, cependant. –

+0

Il s'avère que cela ne change pas du tout le backtracking, mais la phrase a un peu d'état pour vérifier l'échec qui a eu le résultat le plus long. Il est décrit dans un paragraphe intitulé "Error Reporting" dans le chapitre sur les analyseurs dans le livre d'Odersky. – Jan

1

Vous pouvez le faire en combinant une "maison" méthode rep avec des déclarations à l'intérieur non-retours en arrière. 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 
       ^
+0

Bonjour Daniel. A partir de la scala 2.11, votre réponse est-elle toujours la même? Ou les PC ont-ils été améliorés pour fournir des messages d'erreur natifs et de meilleure qualité? –

+0

@KevinMeredith Je ne sais pas, mais j'en doute. –