Domanda

Io cerco di scrivere un parser in scala utilizzando Parser combinatori. Se abbino in modo ricorsivo,

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

allora ottengo buoni errormessages ogni volta che c'è un guasto in un comunicato. Tuttavia, questo è il codice lungo brutto. Così mi piacerebbe scrivere questo:

def body: Parser[Body] =
("begin" ~> statementList <~ "end"  )  ^^ {
   case s => {   new Body(s); }
}

def statementList : Parser[List[Statement]] = 
    rep(statement)

Questo codice funziona, ma stampa solo messaggi significativi se si verifica un errore nella prima istruzione. Se si trova in una dichiarazione successiva, il messaggio diventa dolorosamente inutilizzabile, perché il parser vuole vedere l'intera dichiarazione erronea sostituita dalla "fine" token:

Exception in thread "main" java.lang.RuntimeException: [4.2] error: "end" expected but "let" found

 let b : string = x(3,b,"WHAT???",!ERRORHERE!,7 ) 

 ^ 

La mia domanda: c'è un modo per ottenere rep e repsep lavorare in combinazione con i messaggi di errore significativi, che posto il cursore sul posto giusto invece che sulla cominciano del frammento ripetere?

È stato utile?

Soluzione 2

Ah, ha trovato la soluzione! Si scopre che è necessario utilizzare la funzione di frase sul parser principale per restituire un nuovo parser che è meno incline a risalire. (Mi chiedo che cosa esattamente significa, forse, che se trova un'interruzione di linea non sarà risalire?) tiene traccia l'ultima posizione sulla quale si è verificato un errore.

cambiato:

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

in:

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

Altri suggerimenti

È possibile farlo mediante la combinazione di un "fatto in casa" metodo rep con i non-backtracking all'interno dichiarazioni. Ad esempio:

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
                  ^
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top