2013-04-16 23 views
6

Ben Parsec için (ve genel olarak çözümleyicinin) yeni, ben yazdım bu ayrıştırıcı ile bazı sorunlar yaşıyorum ve:Zorluk bir Parsec ayrıştırıcı boşluk atlamak için elde doğru

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

fikri etmektir Bu biçimdeki listeleri ayrıştırmak (ı s-ifadesine kadar çalışıyorum):

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:

(firstElement secondElement thirdElement and so on) 

bunu test etmek için bu kodu yazdım onun ben çıkış almak edilir: Listenin son elemanı ve kapanış ) arasında beyaz boşluk olduğunda Gördüğünüz gibi

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 

, başarısız olur. Beyaz alanın neden tarafından tüketilmediğini anlamıyorum, <* char ')''dan hemen önce koymuştum. Ne aptalca bir hata yaptım?

cevap

12

sorun, son boşluklar many argüman olarak spaces tarafından tüketildiği

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

ve daha sonra ayrıştırıcı başarısız böylece some letter beklenerek, bir kapatma ayracı bulmuştur. Bunu çözmek için

, sadece jeton sonra istenilen şekilde çalışır

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

boşluk tüketmek:

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

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

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

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

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

Testing "()": 
[] 
0

O biraz zor. Parsers varsayılan olarak açgözlüdür. Senin durumunda ne anlama geliyor? (hello world) ayrıştırmaya çalıştığınızda, ( ayrıştırmadan başlarsanız, bazı boşlukları ve tanımlayıcıları eşleştirmeye çalışıyorsunuz demektir. Yani bunu yapıyoruz. Boşluk yok, ama tanımlayıcı var. İşimiz bitti. Dünya ile tekrar deneyeceğiz. Şimdi kalan _) var. Ayrıştırıcıyı (spaces *> some letter)'u deneyin. Bu açgözlülük yapar: bu yüzden alanla eşleşiyorsunuz ve şimdi biraz mektup bekliyorsunuz, ancak bunun yerine ) elde edersiniz. Bu anda ayrıştırıcı başarısız olur, ancak zaten alan tüketir, böylece mahkum olursunuz. Sen try bağdaştırıcının kullanarak geriye do bu ayrıştırıcı yapabilirsiniz: try (many (spaces *> some letter))

3

sorun çözümleyici many (spaces *> some letter) varsayılan olarak Parsec beri, başka bir öğe ayrıştırma taahhüt boşluk görür sadece bir kez önde bir karakter görünüyor ve backtrack olmamasıdır .

balyoz çözüm backtracking etkinleştirmek için try kullanmaktır, fakat Daniel's answer görüldüğü gibi böyle sorunlar iyi, her simge yerine basitçe sonra isteğe bağlı boşluk ayrıştırma önlenir.