2016-08-02 9 views
0

Ich versuche, einige Parse-Funktion zu implementieren, die nur ein bestimmtes Zeichen am Ende dieser Zeichenfolge akzeptieren, die .*!$ wäre, wenn das geben-Zeichen ! mit regulären Ausdruck ist.Gibt es eine Möglichkeit, Ende in Parsec auszudrücken?

Ich habe versucht, die folgende Funktion zu verwenden, aber es funktioniert nicht, da es Zeichen vor dem Abgleichen konsumieren wird.

endWith :: Char -> Parser() 
endWith x = many anyChar >> char x >> return() 

Eine Sache zu beachten ist: die erwartete Ausgabe für "ab!cd!" ist ("ab!cd!", "") und "ab!cd" sollte gar nicht von diesem Parser verzehrt werden, da es nicht mit ! ist beendet. Der All- oder Nordwert ist sehr wichtig bei der Verwendung von <|>

Ist es möglich mit Parsec? Ich nehme an, dass eine fortgeschrittene Kombination benötigt wird.

+1

Ihr gegebener regulärer Ausdruck entspricht nicht ganz Ihrer Spezifikation. Denken Sie daran, dass '*' Zeichen gierig konsumiert. Sie wollen '. * ?!', das so wenig Zeichen wie möglich verbraucht, bis es ein '!' Oder (meine Vorliebe) '[^!] *!' Vorfindet, das ein beliebiges Zeichen außer '!' Gefolgt von '!' Enthält –

+0

Wie lautet die richtige Schreibweise? 'Akzeptieren Sie nur ein Zeichen am Ende dieser Zeichenfolge '? – Kamel

+1

Wenn Sie möchten, dass die Zeichenfolge durch das '!' _ended_ wird, müssen Sie '$' verwenden, was mit EOL übereinstimmt. '[^!] *! $' –

Antwort

0

Ist das wonach Sie suchen?

import Text.Parsec 
import Text.Parsec.String 

endWith :: Char -> Parser String 
endWith x = do cs <- many anyChar -- consume rest of input 
       case cs of 
       [] -> fail "expecting !" 
       _ -> if last cs == '!' then return cs 
             else fail "did not end in !" 

test1 = parseTest (endWith '!') "This is a test!" 
test2 = parseTest (endWith '!') "ab!cd!" 
test3 = parseTest (endWith '!') "ab!cd" 
+0

Was ist der Unterschied zwischen 'fail" erwartet "' und 'return()'? – Kamel

+1

'return()' gelingt mit dem Wert '()'; 'fail ...' scheitert die Analyse mit der angegebenen Fehlermeldung Wenn es Alternativen gibt (dh erstellt mit '<|>'), dann wird Parsec diese Möglichkeiten zurückverfolgen und untersuchen.Falls es keine Alternativen gibt, schlägt das Parsen mit der angegebenen Nachricht fehl – ErikR

+0

Die unten beschriebene manyTill-Lösung ist allgemeiner (der letzte Ausdruck kann länger als sein ein einzelnes Zeichen), einfacher und schneller.Eine Liste zu erstellen und dann bis zum Ende eines Tests zu durchlaufen, ist langsam. –

3

manyTill tut dies.

endWith :: Char -> Parser String 
endWith x = anyChar `manyTill` char x 
+0

Würde es 'ab! Cd!' Als '(" ab! "," Cd! ")" Oder "(" ab! Cd! "," ")"? " – Kamel

+0

Ersteres sollte ich mir überlegen. Warum testest du es nicht? –

+1

Da meine Hakellumgebung zu dieser Zeit nicht verfügbar war. es ist der frühere, der nicht mein erwarteter ist. :( – Kamel

2

Hier ist eine, die sowohl erfolgreich "ab!" und "ab!cd!" parst, lehnt aber "ab" und "ab!cd":

import Text.Parsec 
import Text.Parsec.String 

endWith :: Char -> Parser String 
endWith c = manyTill anyChar (try $ char c <* eof) 

(beachten Sie, dass das Ergebnis zurückgegeben nicht die Hinter c enthalten):

"ab!"  Succeeds with "ab" 
"ab!cd!" Succeeds with "ab!cd" 
"ab"  Fails 
"ab!cd" Fails