Frage

Ich habe eine Liste, die ich brauche zu analysieren, wo die alle, aber das letzte Element Bedürfnisse von einem Parser analysiert werden, und das letzte Element Bedarf durch einen anderen Parser analysiert werden.

a = "p1 p1b ... p2"
or
a = "p2"

Ursprünglich habe ich versucht,

parser = do parse1 <- many parser1
            parse2 <- parser2
            return AParse parse1 parse2

Das Problem ist, dass Parse1 einen parse2 Eingang verbrauchen kann. So Parse1 verbraucht immer die gesamte Liste und lassen parse2 mit nichts.

Gibt es eine Möglichkeit zu sagen, Parse1 alles außer dem letzten Element in einen String anwenden, und dann parse2 anwenden?

War es hilfreich?

Lösung

Wie wäre:

parseTrain car caboose = choice
    [ fmap (:[]) $ try (caboose `endBy` eof), 
    , liftM2 (:) car (parseTrain car caboose) 
    [

Die eof nervt mich, denn das macht diesen Parser nicht kompositorische. D. h Sie konnte nicht sagen:

char '(' >> parseTrain p1 p2 >> char ')'

Dieses compsitionally zu tun, ist sehr schwer für einen Parser. Wie ist es denn zu wissen, auf dem Weg zu char ‚)‘, ohne bei jeder Gelegenheit zu versuchen und zu sehen, wenn es scheitert? Andernfalls kann es exponentieller Zeit.

Wenn Sie es brauchen kompositorischen zu sein, ist Ihr Problem einige zusätzliche Struktur haben Sie nutzen können? Können Sie zum Beispiel eine Liste aller Elemente analysieren und dann die letzten nach der Tat verarbeiten?

Andere Tipps

Wenn Sie Faktor parser1 so dass wie so definiert sind:

parser1 = (try parser2) <|> parser1extra

Dann wird das Problem eine Liste von parser1extra oder parser2, die in der später enden. Sie können Code, wie:

parserList =
    liftM2 (:) (try parser1extra) parserList
    <|>
    liftM2 (:) (try parser2) (option [] parserList)

Sie können oder müssen nicht die try Anrufe je nachdem, ob diese Parser haben keine Präfix überlappen.

Wenn Sie nicht den Rückgabewert sollen eine Liste sein, sondern Ihr AParse Datum, dann könnten Sie neu zu schreiben es auf diese Weise:

parserList =
    do
        a <- try parser1extra
        prefix a parserList
    <|>
    do
        a <- try parser2
        option (AParse [] a) (prefix a parserList)

    where prefix a p = do
            (AParse as t) <- p
            return $ (AParse (a:as) t)

Oder ein vollständiges Beispiel:

import Control.Monad
import Text.ParserCombinators.Parsec

parseNum = do { v <- many1 digit; spaces; return v }
parseWord = do { v <- many1 letter; spaces; return v }
parsePart = parseNum <|> parseWord

parsePartListEndingInWord =
    liftM2 (:) (try parseNum) parsePartListEndingInWord
    <|>
    liftM2 (:) (try parseWord) (option [] parsePartListEndingInWord)

Eigentlich werden die Anrufe zu versuchen, in diesem Fall nicht erforderlich, da parseNum und parseWord Spiel keine gemeinsamen Präfix. Beachten Sie, dass parsePartListEndingInWord nicht wirklich Referenz parsePart, sondern die beiden Optionen, die parsePart Definition

Make-up

(Original Antwort, eine etwas andere Situation zu lösen:)

Wie wäre es so etwas wie:

parserTest = between (char '[') (char ']') $ do
    p1s <- try parser1 `endBy` char ',' 
    p2 <- parser2
    return $ AParse p1s p2

Unter der Interpunktion aus Ihrem Parsern und bis in parseTest können Sie die combinators between und endBy verwenden die Arbeit für Sie zu tun. Schließlich ist die try ist es so, dass, wenn parser1 und parser2 einen gemeinsamen Präfix übereinstimmen, endBy die korrekte vollständige Sicherung durchführen wird von dem gemeinsamen Präfix beginnen.

Je nach Parsern ist es möglich, dass Sie die Zeichensetzung Anpassung in Ihrem Unter Parsern verlassen können, und alle müssen Sie vielleicht die eine try um parser1:

parseTest = do parse1 <- many (try parser1)
               parse2 <- parser2
               return AParse parse1 parse2

I kombinierter Art der zwei Ansätze:

parserList = try (do a <- parser2
                     eof
                     return $ AParse [] a)
             <|>
             do a <- parser1
                prefix a parserList
             where
                prefix a p = do
                    (AParse as t) <- p
                    return $ AParse a:as t

ich glaube, dass dies für meine Zwecke arbeiten. Dank!

Dies wird den Trick tun:

parser1 `manyTill` (try parser2)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top