2010-03-05 15 views
7

Sto sperimentando con i combinatori di parser e spesso mi imbatto in quelle che sembrano ricchezze infinite. Ecco il primo che ho incontrato:Il combinatore di parser non si chiude - come registrare cosa sta succedendo?

import util.parsing.combinator.Parsers 
import util.parsing.input.CharSequenceReader 

class CombinatorParserTest extends Parsers { 

    type Elem = Char 

    def notComma = elem("not comma", _ != ',') 

    def notEndLine = elem("not end line", x => x != '\r' && x != '\n') 

    def text = rep(notComma | notEndLine) 

} 

object CombinatorParserTest { 

    def main(args:Array[String]): Unit = { 
    val p = new CombinatorParserTest() 
    val r = p.text(new CharSequenceReader(",")) 
    // does not get here 
    println(r) 
    } 

} 

Come posso stampare cosa sta succedendo? E perché non finisce?

risposta

4

La registrazione dei tentativi di analisi notComma emostra che si tratta della fine del file (visualizzata come CTRL-Z nel registro (...) ("mesg") output che viene ripetutamente analizzata. Ecco come ho modificato il parser per questo scopo:

def text = rep(log(notComma)("notComma") | log(notEndLine)("notEndLine")) 

Non sono del tutto sicuro di quello che sta succedendo (Ho provato molte variazioni sulla grammatica), ma penso che sia qualcosa di simile: L'EOF non è realmente un carattere introdotto artificialmente nel flusso di input, ma piuttosto una sorta di condizione perpetua alla fine dell'input. Quindi questo pseudo-carattere EOF mai consumato viene ripetutamente analizzato come "non una virgola o non una fine riga".

+0

Penso che EOF è artificialmente introdotta, ma hai ragione nel dire che è più volte analizzato al sembra ripetutamente fornito al momento della richiesta un carattere aggiuntivo quando l'input è già alla fine della sequenza. – huynhjl

2

Ok, penso di aver capito questo. `CharSequenceReader restituisce '\ 032' come marker per la fine dell'input. Quindi, se modifico il mio ingresso in questo modo, funziona:

import util.parsing.combinator.Parsers 
import util.parsing.input.CharSequenceReader 

class CombinatorParserTest extends Parsers { 

    type Elem = Char 

    import CharSequenceReader.EofCh 

    def notComma = elem("not comma", x => x != ',' && x!=EofCh) 

    def notEndLine = elem("not end line", x => x != '\r' && x != '\n' && x!=EofCh) 

    //def text = rep(notComma | notEndLine) 
    def text = rep(log(notComma)("notComma") | log(notEndLine)("notEndLine")) 

} 

object CombinatorParserTest { 

    def main(args:Array[String]): Unit = { 
    val p = new CombinatorParserTest() 
    val r = p.text(new CharSequenceReader(",")) 
    println(r) 
    } 

} 

See codice sorgente per CharSequenceReaderhere. Se lo scaladoc lo ha menzionato, mi avrebbe risparmiato un sacco di tempo.

+1

Scopri dove dovrebbe essere menzionato e apri un ticket doc. Se è possibile fornire una patch con lo scaladoc modificato, tanto meglio. –

+0

Inviato https://lampsvn.epfl.ch/trac/scala/ticket/3147. Ci sono più file usando 'EofCh', quindi non sono sicuro di dove sia il posto migliore. – huynhjl

0

Trovo che la funzione di registrazione sia estremamente difficile da digitare. Mi piace perché devo fare log(parser)("string")? Perché non avere qualcosa di semplice come parser.log("string")? In ogni modo, di superare quella, ho fatto questo, invece:

trait Logging { self: Parsers => 

    // Used to turn logging on or off 
    val debug: Boolean 

    // Much easier than having to wrap a parser with a log function and type a message 
    // i.e. log(someParser)("Message") vs someParser.log("Message") 
    implicit class Logged[+A](parser: Parser[A]) { 
     def log(msg: String): Parser[A] = 
      if (debug) self.log(parser)(msg) else parser 
    } 
} 

Ora nel tuo parser, si può mescolare in questo tratto in questo modo:

import scala.util.parsing.combinator.Parsers 
import scala.util.parsing.input.CharSequenceReader 


object CombinatorParserTest extends App with Parsers with Logging { 

    type Elem = Char 

    override val debug: Boolean = true 

    def notComma: Parser[Char] = elem("not comma", _ != ',') 
    def notEndLine: Parser[Char] = elem("not end line", x => x != '\r' && x != '\n') 
    def text: Parser[List[Char]] = rep(notComma.log("notComma") | notEndLine.log("notEndLine")) 

    val r = text(new CharSequenceReader(",")) 

    println(r) 
} 

È inoltre possibile eseguire l'override del campo debug per spegnere la registrazione se lo si desidera.

L'esecuzione di questo dimostra anche il secondo parser correttamente analizzato la virgola:

trying notComma at [email protected] 
notComma --> [1.1] failure: not comma expected 

, 
^ 
trying notEndLine at [email protected] 
notEndLine --> [1.2] parsed: , 
trying notComma at [email protected] 
notComma --> [1.2] failure: end of input 

, 
^ 
trying notEndLine at [email protected] 
notEndLine --> [1.2] failure: end of input 

, 
^ 
The result is List(,) 

Process finished with exit code 0 
Problemi correlati