Avec parsec, comment puis-je parse zéro ou plus foo1 terminée par foo2 et tous séparés par point?
Question
Ce que je suis en train de faire semble assez simple, mais comme je suis un newb parsec Haskell, la solution est me éludant.
J'ai deux parseurs, disons foo1
et foo2
où foo1
peut analyser un terme intermedate et foo2
parse un terme fin. Les termes sont séparés par un symbole, "."
.
Les phrases que je dois analyser sont
-
foo2
-
foo1.foo2
-
foo1.foo1.foo2
et ainsi de suite.
Ma pensée originale était de faire
do k <- sepBy foo1 (char'.')
j <- foo2
mais ce ne serait pas attraper le cas de foo2
seule.
La solution
Vous voulez endBy
, pas sepBy
.
foo = do k <- foo1 `endBy` char '.'
j <- foo2
...
Cela forcera le séparateur à être présente après chaque occurrence de foo1
.
Bien sûr, endBy
est trivialement remplaçable par many
, qui peut être plus clair.
foo = do k <- many $ foo1 <* char '.'
j <- foo2
...
ou, sans Control.Applicative
:
foo = do k <- many $ do x <- foo1; char '.'; return x
j <- foo2
...
Autres conseils
D'abord, vous voulez endBy
au lieu de sepBy
:
do k <- endBy foo1 (char'.')
j <- foo2
En second lieu, il serait
attraper le cas juste foo2
De documentation :
Parsis de
endBy p sep
zéro ou plusieurs occurrences dep
, séparés parsep
. Renvoie la liste des valeurs renvoyées parp
.
Essayez quelque chose comme
many (foo1 >>= (\v -> char '.' >> return v)) >>= \v1 ->
foo2 >>= \v2 ->
-- ...
-- combine v1 & v2 somehow
(Juste un croquis, bien sûr.)
En général, le combinateur many
est l'équivalent de parsec Kleene étoiles ; et si vous allez ajouter quelque chose de simple comme un point de fuite à un analyseur existant, en utilisant >>
/ >>=
peut effectivement être plus propre et plus simple que d'utiliser la notation do
.
Bien sûr, il attraperait le cas de foo2. L'utilisation de votre foo1, la parole de Leiden:
let a = sepBy word (char '.')
parseTest a "foo.bar.baz"
parseTest a "foo"
parseTest a ".baz"