2013-04-16 9 views
6

Jestem nowy parsec (i parserami w ogóle), i mam pewne problemy z tego parsera I napisał:trudności coraz Parsek parser pominąć spacje prawidłowo

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 

Chodzi o to, aby analizowania list w tym formacie (pracuję się do s-wyrażeń):

(firstElement secondElement thirdElement and so on) 

napisałem ten kod, aby go przetestować:

import Control.Applicative 
import Text.ParserCombinators.Parsec hiding (many) 

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 

test s = do 
    putStrLn $ "Testing " ++ show s ++ ":" 
    parseTest list s 
    putStrLn "" 

main = do 
    test "()" 
    test "(hello)" 
    test "(hello world)" 
    test "(hello world)" 
    test "(hello world)" 
    test "()" 

T jego jest wyjście uzyskać:

Testing "()": 
[] 

Testing "(hello)": 
["hello"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
parse error at (line 1, column 14): 
unexpected ")" 
expecting space or letter 

Testing "()": 
parse error at (line 1, column 3): 
unexpected ")" 
expecting space or letter 

Jak widać, to się nie powiedzie, gdy nie jest biała przestrzeń pomiędzy ostatnim elemencie listy i zamknięciu ). Nie rozumiem, dlaczego biała przestrzeń nie jest zużywana przez spaces, którą umieszczam tuż przed <* char ')'. Jaki głupi błąd popełniłem?

Odpowiedz

12

Problemem jest to, że przestrzenie końcowe są wykorzystywane przez spaces w argumencie many,

list = char '(' *> many (spaces *> some letter) <* spaces <* char ')' 
        -- ^^^^^^ that one 

i parser oczekuje some letter ale znajduje nawias zamykającego i w ten sposób kończy się niepowodzeniem.

Aby go rozwiązać, zużywają obowiązuje tylko po żetonów,

list = char '(' *> spaces *> many (some letter <* spaces) <* char ')' 

, który działa zgodnie z oczekiwaniami:

$ runghc lisplists.hs 
Testing "()": 
[] 

Testing "(hello)": 
["hello"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "(hello world)": 
["hello","world"] 

Testing "()": 
[] 
0

To trochę skomplikowane. Parsery są domyślnie chciwe. Co to oznacza w twoim przypadku? Podczas próby przeanalizowania (hello world) zaczynasz od analizowania (, a następnie próbujesz dopasować spacje i identyfikator. Więc to robimy. Nie ma spacji, ale jest identyfikator. Skończyliśmy. Spróbujemy ponownie ze światem. Teraz pozostało: _). Możesz spróbować parser (spaces *> some letter). To sprawia, że ​​jest chciwy: więc dopasowujesz przestrzeń, a teraz oczekujesz jakiejś litery, ale zamiast tego dostajesz ). W tej chwili parser nie działa, ale już zużył przestrzeń, więc jesteś skazany na zagładę. Można zrobić to parser nie backtracking za pomocą try COMBINATOR: try (many (spaces *> some letter))

3

Problem polega na tym, że gdy parser many (spaces *> some letter) widzi miejsca to zobowiązuje się do analizowania inny element, ponieważ parsec domyślnie wygląda tylko jeden znak do przodu i nie wracać .

rozwiązanie

młotem jest użycie try włączyć Backtracking, ale problemy takie jak ten są najlepiej unikać po prostu parsowania opcjonalnego spacje po każdy żeton zamiast, jak widać na Daniel's answer.