Sto creando un DSL e utilizzando la libreria combinatore di parser di Scala per analizzare il DSL. La DSL segue una sintassi semplice, simile a Ruby. Un file sorgente può contenere una serie di blocchi che assomigliano a questo:Come posso creare un combinatore di parser in cui le terminazioni di linea sono significative?
create_model do
at 0,0,0
end
fine riga sono significativi nel DSL, in quanto sono effettivamente utilizzati come terminatori dichiarazione.
ho scritto un parser Scala che assomiglia a questo:
class ML3D extends JavaTokenParsers {
override val whiteSpace = """[ \t]+""".r
def model: Parser[Any] = commandList
def commandList: Parser[Any] = rep(commandBlock)
def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"
def eol: Parser[Any] = """(\r?\n)+""".r
def command: Parser[Any] = commandName~opt(commandLabel)
def commandName: Parser[Any] = ident
def commandLabel: Parser[Any] = stringLiteral
def statementList: Parser[Any] = rep(statement)
def statement: Parser[Any] = functionName~argumentList~eol
def functionName: Parser[Any] = ident
def argumentList: Parser[Any] = repsep(argument, ",")
def argument: Parser[Any] = stringLiteral | constant
def constant: Parser[Any] = wholeNumber | floatingPointNumber
}
Da fine riga importa, io overrode whiteSpace
in modo che ti trattano solo spazi e tabulazioni come spazi bianchi (invece di trattare nuove linee come spazi bianchi, e quindi ignorandoli).
Questo funziona, ad eccezione dell'istruzione "fine" per commandBlock
. Poiché il mio file sorgente contiene una nuova riga finale, il parser si lamenta che si aspettava solo un end
ma ha ottenuto una nuova riga dopo la parola chiave end
.
Così ho cambiato la definizione commandBlock
s' a questo:
def commandBlock: Parser[Any] = command~"do"~eol~statementList~"end"~opt(eol)
(Cioè, ho aggiunto una nuova linea opzionale dopo "fine").
Ma ora, durante l'analisi del file di origine, ottengo il seguente errore:
[4.1] failure: `end' expected but `' found
ho che questo è perché, dopo che succhia la nuova linea di uscita, il parser incontra una stringa vuota che pensa non sia valido, ma non sono sicuro del motivo per cui lo sta facendo.
Qualche consiglio su come risolvere questo problema? Potrei estendere il parser errato dalla libreria del parser combinator di Scala, quindi qualsiasi suggerimento su come creare una definizione di lingua con significativi nuovi caratteri di riga è anch'esso benvenuto.
Mi piace la tua interpretazione del messaggio di errore. Mi chiedo se c'è un modo per far sì che il parser stampi ciò che sta cercando di abbinare mentre procede. Ciò renderebbe la risoluzione dei problemi più facile. – huynhjl
Puoi racchiudere qualsiasi riferimento a una produzione che appare nel lato destro di un'altra produzione in 'log (...)' e otterrai l'output di traccia ogni volta che l'analisi tenta di far corrispondere quel non-terminale. Ad esempio, per registrare un particolare tentativo di abbinamento di 'model' sostituire quel riferimento non terminale in una regola con' log (model) '. –
Ah, sì, ora vedo il problema - 'end' veniva letto in' functionName', poiché * era * un nome di funzione valido. Ho implementato le tue modifiche e ora funziona bene, grazie mille. – mipadi