2012-10-17 16 views
14

Piszę swój pierwszy program z Parsec. Chcę analizować zrzuty schematów MySQL i chciałbym wymyślić ładny sposób analizowania ciągów reprezentujących określone słowa kluczowe w sposób niewrażliwy na wielkość znaków. Oto kod ilustrujący podejście, którego używam do analizy "CREATE" lub "create". Czy jest lepszy sposób to zrobić? Odpowiedź, która nie ucieka się do buildExpressionParser, byłaby najlepsza. Biorę tutaj małe kroki.Jaki jest najczystszy sposób na przetwarzanie niewrażliwe na wielkość liter w Text.Combinators.Parsec?

p_create_t :: GenParser Char st Statement 
    p_create_t = do 
     x <- (string "CREATE" <|> string "create") 
     xs <- manyTill anyChar (char ';') 
     return $ CreateTable (x ++ xs) [] -- refine later 
+5

Zakładam, że 'map to to Lower' na wejściu, zanim nawet uruchomienie parsera nie jest opcją? Ponadto oczekuję, że "wielkość liter nie będzie odpowiednia", aby dopasować również "Utwórz", "Definiuj", "Twórz" lub dowolną inną odmianę, którą Twój przykład odrzuci. Które chcesz? –

+0

To działa. Dzięki. Nie pomyślałem o tym! – dan

+1

@dan Po prostu pamiętaj, że jeśli twoje dane wejściowe zawierają ciągi znaków, będą również pisane małymi literami. Na przykład, jeśli dowolna kolumna zawiera domyślne wartości ciągu. –

Odpowiedz

17

Można zbudować parser niewrażliwy na dane z parserów znaków.

-- Match the lowercase or uppercase form of 'c' 
caseInsensitiveChar c = char (toLower c) <|> char (toUpper c) 

-- Match the string 's', accepting either lowercase or uppercase form of each character 
caseInsensitiveString s = try (mapM caseInsensitiveChar s) <?> "\"" ++ s ++ "\"" 
8

Powtarzając to, co powiedział w komentarzu, jak to było najwyraźniej pomocne:

Prostym rozwiązaniem Sledgehammer tutaj jest po prostu map toLower na całej wejścia przed uruchomieniem parsera, a następnie wykonywać wszystkie twe dopasowania słów kluczowych małymi literami.

Stanowi to oczywistą trudność, jeśli analizujesz coś, co w niektórych miejscach musi być niewrażliwe na rozróżnianie wielkości liter, a w innych ważne jest rozróżnianie wielkości liter, lub jeśli zależy Ci na zachowaniu skrzynek z przyczyn kosmetycznych. Na przykład, mimo że znaczniki HTML nie rozróżniają wielkości liter, przekonwertowanie całej strony na małe litery podczas jej analizy prawdopodobnie byłoby niepożądane. Nawet przy kompilacji języka niewrażliwego na wielkość znaków konwertowanie identyfikatorów może być denerwujące, ponieważ wszelkie komunikaty o błędach nie będą zgodne z tym, co napisał programista.

0

Zamiast mapowaniu całą wejście z toLower, należy rozważyć użycie caseString z Text.ParserCombinators.Parsec.Rfc2234 (z pakietu hsemail)

Text.ParsecCombinators.Parsec.Rfc2234

p_create_t :: GenParser Char st Statement 
p_create_t = do 
    x <- (caseString "create") 
    xs <- manyTill anyChar (char ';') 
    return $ CreateTable (x ++ xs) [] -- refine later 

więc teraz x będzie cokolwiek przypadek wariant jest obecny na wejściu bez zmiany wejścia.

PS: Wiem, że jest to starożytny pytanie, pomyślałem, że chciałbym dodać to jako pytanie to wpadł, gdy szukałem podobnego problemu

+0

Nie pochodzi z Text.ParsecCombinators.Parsec. Jest to z Text.ParsecCombinators.Parsec.Rfc2234. Twój link jest poprawny, ale Twój tytuł jest błędny. Należy również zauważyć, że jest to część pakietu hsail, który ktoś mógł jeszcze nie zainstalować. – Sean

2

Nie, Parsek nie może zrobić tego w czysty sposób. string jest implementowany na podstawowym kombinatorze prymitywnym tokens, który jest zakodowany na stałe w celu użycia testu równości (==). To jest nieco prostsze do analizowania znaków niewrażliwych na wielkość liter, ale prawdopodobnie chcesz więcej.

Istnieje jednak nowoczesny widelec z parsec, zwany Megaparsec który ma wbudowanych rozwiązań dla wszystkiego może chcesz:

λ> parseTest (char' 'a') "b" 
parse error at line 1, column 1: 
unexpected 'b' 
expecting 'A' or 'a' 
λ> parseTest (string' "foo") "Foo" 
"Foo" 
λ> parseTest (string' "foo") "FOO" 
"FOO" 
λ> parseTest (string' "foo") "fo!" 
parse error at line 1, column 1: 
unexpected "fo!" 
expecting "foo" 

Uwaga ostatni komunikat o błędzie, to lepiej niż to, co można dostać parsowania znaków jeden po drugim (szczególnie przydatne w konkretnym przypadku). string' jest zaimplementowana tak jak w przypadku Parsera string, ale w porównaniu do porównania znaków używa niewrażliwego na wielkość liter . W niektórych przypadkach mogą być pomocne także oneOf' i noneOf', które mogą być pomocne.


Ujawnienie: Jestem jednym z autorów megaparseka.

+0

Rzeczywiście zaskakujące, że 'tokeny' nie pozwalają, aby funkcja porównania została mu przekazana w celu wykonania porównania w oryginalnym Parsecie. – MicroVirus

Powiązane problemy