Hinweis: Diese Antwort wurde geschrieben in literate Haskell. Speichern Sie es als Example.lhs
und laden Sie es in GHCi oder ähnlichem.
Die Sache ist, sepBy
implementiert als:
sepBy p s = liftA2 (:) p ((s *> sepBy1 p s) <|> pure []) <|> pure []
Dies bedeutet, dass der zweite Parser s
wird nach der erste Parser gelungen bezeichnet werden. Dies bedeutet auch, dass, wenn Sie Leerzeichen in die Klasse der Zeichen Katalog auf, dass Sie mit
["This test and that test","this test particularly"]
enden würde, da and
jetzt parseable von p
ist. Das ist nicht einfach zu beheben: Sie müssten nach vorne schauen, sobald Sie ein Leerzeichen drücken, und prüfen, ob nach einer beliebigen Anzahl von Leerzeichen ein "und" folgt, und wenn dies der Fall ist, hören Sie auf zu analysieren. Nur dann ein Parser geschrieben mit sepBy
wird funktionieren.
lässt also einen Parser schreiben, die Worte stattdessen nimmt (der Rest dieser Antwort ist, lesen und schreiben Haskell):
> {-# LANGUAGE OverloadedStrings #-}
> import Control.Applicative
> import Data.Attoparsec.Text
> import qualified Data.Text as T
> import Control.Monad (mzero)
> word = takeWhile1 . inClass $ "-'a-zA-Z"
>
> wordsP = fmap (T.intercalate " ") $ k `sepBy` many space
> where k = do
> a <- word
> if (a == "and") then mzero
> else return a
wordsP
nun mehrere Wörter, bis es entweder trifft etwas nimmt, das ist nicht ein Wort oder ein Wort das entspricht "und". Die zurück mzero
ein parsing failure zeigen, bei dem ein anderen Parser übernehmen kann:
> andP = many space *> "and" *> many1 space *> pure()
>
> limiter = choice [
> "," *> andP,
> "," *> many1 space *> pure(),
> andP
> ]
limiter
sind meist die gleichen Parser Sie bereits geschrieben haben, es ist das gleiche wie die Regex /,\s+and|,\s+|\s*and\s+/
.
Jetzt können wir tatsächlich sepBy
verwenden, da unser erster Parser mit dem zweiten überlappt nicht mehr:
> test = "This test and that test, this test particular, and even that test"
>
> main = print $ parseOnly (wordsP `sepBy` limiter) test
Das Ergebnis ist ["This test","that test","this test particular","even that test"]
, wie wir wollten. Beachten Sie, dass dieser Parser keine Whitespaces speichert.
Wenn Sie also einen Parser mit sepBy
erstellen möchten, stellen Sie sicher, dass beide Parser nicht überlappen.
Warum nicht 'nameSep. takeWhile1 $ inClass "\ t-'a-zA-Z" '? - In deiner Ausgabe werden Räume offenbar nicht unterschiedlich behandelt. Warum sollten sie nicht in die Zeichenklasse aufgenommen werden? Wenn du 'space' anstelle von expliziten Zeichen wie' "\ t" 'etc magst, kannst du' nameSep 'verwenden. TakeWhile1 $ inClass "-'a-zA-Z" <|> Leerzeichen' – AndrewC