2016-08-16 2 views
2

Donc, je vous écris un petit analyseur qui va extraire tout le contenu de la balise <td> avec classe spécifique, comme celui-ci <td class="liste">some content</td> --> Right "some content"Comment arrêter Haskell analyseur parsec à EOF

Je vais analyse syntaxique grand fichier html mais je n » Je me soucie vraiment de tout le bruit, donc l'idée était de consommer tous les caractères jusqu'à atteindre <td class="liste"> que je consommerais tous les caractères (contenu) jusqu'à </td> et retourner la chaîne de contenu.

Cela fonctionne bien si le dernier élément dans un fichier est mon tag td.liste, mais si j'ai un texte après ou eof que mon analyseur consomme et jette unexpected end of input si vous exécutez parseMyTest test3.

- EDIT
Voir la fin de test3 pour comprendre ce qu'est le cas de bordure.

Voici mon code à ce jour:

import Text.Parsec 
import Text.Parsec.String 

import Data.ByteString.Lazy (ByteString) 
import Data.ByteString.Char8 (pack) 

colOP :: Parser String 
colOP = string "<td class=\"liste\">" 

colCL :: Parser String 
colCL = string "</td>" 

col :: Parser String 
col = do 
    manyTill anyChar (try colOP) 
    content <- manyTill anyChar $ try colCL 
    return content 

cols :: Parser [String] 
cols = many col 

test1 :: String 
test1 = "<td class=\"liste\">Hello world!</td>" 

test2 :: String 
test2 = read $ show $ pack test1 

test3 :: String 
test3 = "\n\r<html>asdfasd\n\r<td class=\"liste\">Hello world 1!</td>\n<td class=\"liste\">Hello world 2!</td>\n\rasldjfasldjf<td class=\"liste\">Hello world 3!</td><td class=\"liste\">Hello world 4!</td>adsafasd" 

parseMyTest :: String -> Either ParseError [String] 
parseMyTest test = parse cols "test" test 

btos :: ByteString -> String 
btos = read . show 
+1

BTW - pour l'analyse syntaxique HTML je trouve que [tagsoup] (https://hackage.haskell.org/package/ tagsoup) fonctionne bien. – ErikR

+0

Je sais, mais j'explore le parsec en ce moment: D – Reygoch

Répondre

3

J'ai créé un combinateur skipTill p end qui applique p jusqu'à end matchs puis retourne ce que end retours.

En revanche, manyTill p end applique p jusqu'à end matchs puis retourne ce que les p parseurs assortis.

import Text.Parsec 
import Text.Parsec.String 

skipTill :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m end 
skipTill p end = scan 
    where 
     scan = end <|> do { p; scan } 

td :: Parser String 
td = do 
    string "(" 
    manyTill anyChar (try (string ")")) 

tds = do r <- many (try (skipTill anyChar (try td))) 
     many anyChar -- discard stuff at end 
     return r 

test1 = parse tds "" "111(abc)222(def)333" -- Right ["abc", "def"] 

test2 = parse tds "" "111"     -- Right [] 

test3 = parse tds "" "111(abc"    -- Right [] 

test4 = parse tds "" "111(abc)222(de"  -- Right ["abc"] 

Mise à jour

Cela semble également travailler:

tds' = scan 
    where scan = (eof >> return []) 
       <|> do { r <- try td; rs <- scan; return (r:rs) } 
       <|> do { anyChar; scan }