2014-06-08 25 views
5

chcę analizować wejściowych ciągów tak: "this is \"test \" message \"sample\" text"Parser dla cudzysłowie wykorzystaniem Parsek

Teraz Pisałem parser do parsowania indywidualny tekst bez żadnych cytatów:

parseString :: Parser String 
parseString = do 
    char '"' 
    x <- (many $ noneOf "\"") 
    char '"' 
    return x 

ten analizuje proste łańcuchy jak ten : "test message"

Potem napisał parser dla cudzysłowami:

quotedString :: Parser String 
quotedString = do 
    initial <- string "\\\"" 
    x <- many $ noneOf "\\\"" 
    end <- string "\\\"" 
    return $ initial ++ x ++ end 

Ten parser dla ciągów takich jak ten: \"test message\"

Czy istnieje sposób, że mogę połączyć oba parsery, aby uzyskać pożądany cel? Jaki jest dokładnie idomatyczny sposób rozwiązania tego problemu?

+0

Dlaczego chcesz usunąć początkowe i końcowe znaki cudzysłowu, ale pozostawić nienaruszone odwrotne ukośniki? Sądzę, że chciałbyś przetworzyć dane wejściowe '" \ "ab \\\" c \ "" 'jako albo" "\" ab \\\ "c \" "(parsowanie ściśle dla sprawdzania poprawności) lub jako' "ab \" c "', ale wydaje się, że chcesz '' ab \\\ "c" ', co nie wydaje się tak oczywiste. – dfeuer

+0

@dfeuer Bez szczególnego powodu graliśmy z Parsesem. – Sibi

Odpowiedz

17

To jest to, co chciałbym zrobić:

escape :: Parser String 
escape = do 
    d <- char '\\' 
    c <- oneOf "\\\"0nrvtbf" -- all the characters which can be escaped 
    return [d, c] 

nonEscape :: Parser Char 
nonEscape = noneOf "\\\"\0\n\r\v\t\b\f" 

character :: Parser String 
character = fmap return nonEscape <|> escape 

parseString :: Parser String 
parseString = do 
    char '"' 
    strings <- many character 
    char '"' 
    return $ concat strings 

Teraz wszystko co musisz zrobić to nazwać:

parse parseString "test" "\"this is \\\"test \\\" message \\\"sample\\\" text\"" 

parsera kombinatorów są nieco trudne do zrozumienia na początku, ale gdy pojawi się zrozumienie tego jest łatwiejsze niż pisanie gramatyk BNF.

+1

Czy nie powinno być "nonEscape" po prostu 'noneOf" \\\ "", aby znaki specjalne pojawiały się dosłownie, a prawdopodobnie przyspieszyły przetwarzanie? – dfeuer

+0

@dfeuer Myślę, że dodał kilka dodatkowych znaków, żeby to pokazać, na wypadek, gdyby chciałem je dodać. :) – Sibi

+0

@Sibi, moim celem było to, że lepiej byłoby umieścić dodatkowe znaki ucieczki w definicji 'escape' * bez * wykluczania ich z' nonEscape'. Jedyne rzeczy, które oczywiście * muszą * być wykluczone przez 'nonEscape' to' '\ "' i' '\\' '. – dfeuer

2
quotedString = do 
    char '"' 
    x <- many (noneOf "\"" <|> (char '\\' >> char '\"')) 
    char '"' 
    return x 

Wierzę, że to powinno zadziałać.

+0

Obejmuje to \ w wyniku. '" \ "" zostanie sparsowane do '\" 'not'" ' –

+0

@Banthar, co wydaje się być intencją PO.Kod nie wydaje się być jednak tak elastyczny jak w odpowiedzi Aadita M. Shah'a i wygląda na to, że trudno byłoby go rozszerzyć, aby obsługiwał uciekające ukośniki odwrotne. – dfeuer

0

Preferuję ponieważ jest to łatwiejsze do odczytania:

quotedString :: Parser String 
quotedString = do 
    a <- string "\"" 
    b <- concat <$> many quotedChar 
    c <- string "\"" 
    -- return (a ++ b ++ c) -- if you want to preserve the quotes 
    return b 
    where quotedChar = try (string "\\\\") 
        <|> try (string "\\\"") 
        <|> ((noneOf "\"\n") >>= \x -> return [x]) 

rozwiązanie Aadit może być szybsze, ponieważ nie używa try ale to chyba trudniejsze do odczytania.

Należy zauważyć, że różni się on od rozwiązania Aadita. Moje rozwiązanie ignoruje rzeczy, które się ułożyły w łańcuchu, a tak naprawdę dotyczy tylko \" i \\.

Załóżmy na przykład, że masz znak tabulacji w ciągu znaków. Moje rozwiązanie pomyślnie analizuje numer "\"\t\"" na Right "\t". Rozwiązania Aadita mówią: unexpected "\t"expecting "\\" or "\"".

Należy również zauważyć, że rozwiązanie Aadita akceptuje tylko "prawidłowe" znaki ucieczki. Na przykład odrzuca "\"\\a\"". \a nie jest prawidłową sekwencją ucieczki (dobrze według man ascii, reprezentuje dzwon systemowy i jest poprawna). Moje rozwiązanie po prostu zwraca Right "\\a".

Mamy więc dwa różne przypadki użycia.

  • Moje rozwiązanie: Przetwarza cytowane sznurki z możliwie uciekł cytaty i uciekł ucieka

  • rozwiązanie Aadit za: Przetwarza podane ciągi z ważnych ciągów ewakuacyjnych gdzie ważne ucieczek oznacza "\\\"\0\n\r\v\t\b\f"

0

Chciałem przeanalizuj cytowane ciągi i usuń wszelkie ukośniki odwrotne używane do przechodzenia w kroku analizy. W moim prostym języku jedynymi możliwymi postaciami były podwójne cytaty i odwrotne ukośniki. Oto moje rozwiązanie:

quotedString = do 
    string <- between (char '"') (char '"') (many quotedStringChar) 
    return string 
    where 
    quotedStringChar = escapedChar <|> normalChar 
    escapedChar = (char '\\') *> (oneOf ['\\', '"']) 
    normalChar = noneOf "\"" 
0

W przypadku ktoś szuka bardziej z roztworu skrzynki, this answer in code-review zapewnia tylko to. Oto pełny przykład z właściwym importem:

import   Text.Parsec 
import   Text.Parsec.Language 
import   Text.Parsec.Token 

lexer :: GenTokenParser String u Identity 
lexer = makeTokenParser haskellDef 

strParser :: Parser String 
strParser = stringLiteral lexer 

parseString :: String -> Either ParseError String 
parseString = parse strParser ""