Uwaga: tę odpowiedź zapisano w literate Haskell. Zapisz go jako Example.lhs
i załaduj go do GHCi lub podobnego.
Chodzi o to, sepBy
jest zaimplementowany jako:
sepBy p s = liftA2 (:) p ((s *> sepBy1 p s) <|> pure []) <|> pure []
Oznacza to, że drugi parser s
zostanie wywołana po pierwszy parser udało. Oznacza to również, że jeśli były, aby dodać spacje do klasy znaków, że chcesz skończyć z
["This test and that test","this test particularly"]
od and
jest teraz parsowalnym przez p
. Nie jest to łatwe do naprawienia: będziesz musiał patrzeć w przyszłość, gdy tylko uderzysz w spację, i sprawdzić, czy po dowolnej liczbie spacji pojawia się znak "i", a jeśli tak, przestań parsować. Tylko , a następnie parser napisany z sepBy
będzie działać.
Więc pozwala napisać parser który zaczyna rozmowę zamiast (reszta tej odpowiedzi jest literat 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
teraz trwa kilka słów aż do niego albo uderza coś, że nie jest to słowo lub słowo to równa "i". Zwracany mzero
wskaże parsing failure, w których inny parser może przejąć:
> andP = many space *> "and" *> many1 space *> pure()
>
> limiter = choice [
> "," *> andP,
> "," *> many1 space *> pure(),
> andP
> ]
limiter
jest w większości takie same parser już napisany, jest taka sama jak regex /,\s+and|,\s+|\s*and\s+/
.
Teraz możemy faktycznie korzysta sepBy
, ponieważ nasz pierwszy parser nie pokrywają się z drugim już:
> test = "This test and that test, this test particular, and even that test"
>
> main = print $ parseOnly (wordsP `sepBy` limiter) test
Rezultatem jest ["This test","that test","this test particular","even that test"]
, tak jak chcieliśmy. Zauważ, że ten konkretny analizator składni nie zachowuje białych znaków.
Zawsze, gdy chcesz utworzyć analizator składni z sepBy
, upewnij się, że oba parsery się nie nakładają.
Dlaczego nie 'nameSep. takeWhile1 $ inClass "\ t-'a-zA-Z" '? - twoje wyniki wyraźnie nie traktują przestrzeni inaczej, dlaczego nie uwzględnić ich w klasie znaków? Jeśli podoba ci się "spacja" zamiast jawnych znaków takich jak "\ t" itp., Możesz użyć 'nameSep. takeWhile1 $ inClass "-'a-zA-Z" <|> space' – AndrewC