2010-03-15 10 views
8

Mam listę, które muszę przeanalizować, gdzie wszystkie oprócz ostatniego element musi być analizowany przez jeden analizator składni, a ostatni element musi być analizowany przez inny parser.Haskell parsec parsowanie ciąg elementów

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

Początkowo próbowałem

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

Problemem jest to, że można spożywać parse1 wejście parse2. Tak więc parse1 zawsze pochłania całą listę i pozostawia parse2 bez niczego.

Czy istnieje sposób, aby zastosować parse1 do wszystkiego oprócz ostatniego elementu w łańcuchu, a następnie zastosować parse2?

Odpowiedz

2

Jak o:

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

Euf powoduje błędy, ponieważ powoduje to, że ten parser nie jest kompozycyjny. To znaczy.nie można było powiedzieć:

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

Wykonanie tego jest bardzo trudne dla parsera. Jak to jest wiedzieć, aby przejść do char "), nie próbując przy każdej okazji i zobaczyć, czy się nie uda? Może to być czas wykładniczy.

Jeśli potrzebujesz go do kompozycyjnego, czy twój problem ma jakąś dodatkową strukturę, którą możesz wykorzystać? Czy możesz na przykład przeanalizować listę wszystkich elementów, a następnie przetworzyć ostatnią po fakcie?

2

Jeśli można czynnik parser1 tak że jest zdefiniowana tak:

parser1 = (try parser2) <|> parser1extra 

Wtedy problemem staje się lista parser1extra lub parser2 że musi kończyć się później. można kodować że:

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

pan może, ale nie musi połączeń try zależności od tego czy te parser ma żadnego nakładania prefiksu.

Jeśli nie chcesz, wartość zwracana będzie na liście, ale zamiast Twój AParse punkt odniesienia, a następnie można ponownie zapisać go w ten sposób:

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) 

Lub pełny przykład:

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) 

W rzeczywistości połączenia do wypróbowania nie są w tym przypadku potrzebne, ponieważ parseNum i parseWord nie zawierają wspólnego przedrostka. Zauważ, że parsePartListEndingInWord faktycznie nie odwoływać parsePart, ale zamiast tego, obie opcje, które składają się na definicję parsePart dydaktycznego


(odpowiedź oryginalny, rozwiązując nieco inną sytuację :)

Jak o coś takiego:

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

Biorąc interpunkcji z twoich parserami i aż do parseTest pozwala wykorzystać kombinatorów between i endBy do pracy dla ty. Na koniec znajduje się tam try, więc jeśli parser1 i parser2 pasują do wspólnego prefiksu, endBy wykona poprawną pełną kopię zapasową na początek wspólnego przedrostka.

zależności od parserami, możliwe jest, że można zostawić dopasowanie interpunkcyjny wewnątrz swoich podrzędnych parserami, a wszystko, co trzeba może być A try wokół parser1:

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

popełniłem błąd w pytaniu i stwierdził, że miałem listę elementów. Powinienem powiedzieć, że mam cały szereg przedmiotów. Przepraszam za to. Wprowadziłem poprawkę do pytania. Drugi przykład kodu nie zadziała, ponieważ parser1 zużyje cały ciąg. – Chris

+0

Dzięki za kod. Czy parser1extra nie zużyje całego ciągu znaków? – Chris

+0

Pomysł polegał na tym, że parser1extra analizowałby tylko te rzeczy, które powinny znajdować się w parser1, ale nie pasują do parser2. Dlatego parser1extra pasuje tylko wtedy, gdy parser2 tego nie robi. – MtnViewMark

0

I niby połączeniu dwóch podejść:

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 

myślę, że to będzie działać do moich celów. Dzięki!

0

Ten rade:

parser1 `manyTill` (try parser2) 
Powiązane problemy