git.iko.soy/typeable/inflections-hs
on master help sign in
Parser tests and fixes
4d3bb48mrkkrp
CHANGELOG.md+7
@@ -9,6 +9,13 @@
9
9
10* Renamed `defaultMap` to `defaultTransliterations`.
10* Renamed `defaultMap` to `defaultTransliterations`.
11
11
12+* Words now can contain digits (recognized by all parsers).
13+
14+* `parseSnakeCase` now is not confused when a word happens to have prefix
15+ coinciding with an acronym. This is harder to fix for `parseCamelCase`
16+ because acronym may contain capital letters, so old behavior is preserved
17+ for `parseCamelCase` for now.
18+
12* `parseCamelCase` and `parseSnakeCase` take any instance of `Foldable` as a
19* `parseCamelCase` and `parseSnakeCase` take any instance of `Foldable` as a
13 collection of acronyms, not just lists.
20 collection of acronyms, not just lists.
14
21
Text/Inflections/Parse/CamelCase.hs+1
@@ -28,6 +28,7 @@ import qualified Data.Text as T
28import Prelude hiding (Word)
28import Prelude hiding (Word)
29#else
29#else
30import Data.Foldable
30import Data.Foldable
31+import Prelude hiding (elem)
31#endif
32#endif
32
33
33-- | Parse a CamelCase string.
34-- | Parse a CamelCase string.
Text/Inflections/Parse/SnakeCase.hs+10−20
@@ -27,6 +27,7 @@ import qualified Data.Text as T
27import Prelude hiding (Word)
27import Prelude hiding (Word)
28#else
28#else
29import Data.Foldable
29import Data.Foldable
30+import Prelude hiding (elem)
30#endif
31#endif
31
32
32-- | Parse a snake_case string.
33-- | Parse a snake_case string.
@@ -48,25 +49,14 @@ parseSnakeCase acronyms = parse (parser acronyms) ""
48parser :: (Foldable f, Functor f)
49parser :: (Foldable f, Functor f)
49 => f (Word 'Acronym)
50 => f (Word 'Acronym)
50 -> Parser [SomeWord]
51 -> Parser [SomeWord]
51−parser acronyms = ((a <|> n) `sepBy` char '_') <* eof
52+parser acronyms = (pWord acronyms `sepBy` char '_') <* eof
52− where
53− n = SomeWord <$> word
54− a = SomeWord <$> acronym acronyms
55
53
56−acronym :: (Foldable f, Functor f)
54+pWord :: (Foldable f, Functor f)
57 => f (Word 'Acronym)
55 => f (Word 'Acronym)
58− -> Parser (Word 'Acronym)
56+ -> Parser SomeWord
59−acronym acronyms = do
57+pWord acronyms = do
60− x <- T.pack <$> choice (string . T.unpack . unWord <$> acronyms)
58+ let acs = unWord <$> acronyms
61− case mkAcronym x of
59+ r <- T.pack <$> some alphaNumChar
62− Nothing -> empty -- cannot happen if the system is sound
60+ if r `elem` acs
63− Just acr -> return acr
61+ then maybe empty (return . SomeWord) (mkAcronym r)
64−{-# INLINE acronym #-}
62+ else maybe empty (return . SomeWord) (mkWord r)
65−
66−word :: Parser (Word 'Normal)
67−word = do
68− x <- T.pack <$> (some lowerChar <|> some digitChar)
69− case mkWord x of
70− Nothing -> empty -- cannot happen if the system is sound
71− Just wrd -> return wrd
72−{-# INLINE word #-}
Text/Inflections/Types.hs+3
@@ -106,6 +106,9 @@ data SomeWord where
106instance Show SomeWord where
106instance Show SomeWord where
107 show (SomeWord w) = show w
107 show (SomeWord w) = show w
108
108
109+instance Eq SomeWord where
110+ x == y = unSomeWord id x == unSomeWord id y
111+
109-- | Extract 'Text' from 'SomeWord' and apply given function only if the
112-- | Extract 'Text' from 'SomeWord' and apply given function only if the
110-- word inside wasn't an acronym.
113-- word inside wasn't an acronym.
111--
114--
test/Text/Inflections/Parse/CamelCaseSpec.hs+40−1
@@ -1,8 +1,47 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
1module Text.Inflections.Parse.CamelCaseSpec
3module Text.Inflections.Parse.CamelCaseSpec
2 ( spec )
4 ( spec )
3where
5where
4
6
5import Test.Hspec
7import Test.Hspec
8+import Test.Hspec.Megaparsec
9+import Text.Inflections
6
10
7spec :: Spec
11spec :: Spec
8−spec = return ()
12+spec =
13+ describe "parseCamelCase" $ do
14+ context "when given no acronyms" $ do
15+ context "when first word is capitalized" $
16+ it "parses CamelCase correctly" $ do
17+ r <- mapM (fmap SomeWord . mkWord) ["One","Two","Three"]
18+ parseCamelCase [] "OneTwoThree" `shouldParse` r
19+ context "when first word is not capitalized" $
20+ it "parses camelCase correctly" $ do
21+ r <- mapM (fmap SomeWord . mkWord) ["one","Two","Three"]
22+ parseCamelCase [] "oneTwoThree" `shouldParse` r
23+ context "when there are digits in the words" $
24+ it "parses CamelCase correctly" $ do
25+ r <- mapM (fmap SomeWord . mkWord) ["one1two","Three3"]
26+ parseCamelCase [] "one1twoThree3" `shouldParse` r
27+ context "when given some acronyms" $ do
28+ context "when first word is capitalized" $
29+ it "parses CamelCase correctly" $ do
30+ a <- mkAcronym "BOO"
31+ r <- mapM (fmap SomeWord . mkWord) ["One","BOO","One"]
32+ parseCamelCase [a] "OneBOOOne" `shouldParse` r
33+ context "when first word is not capitalized" $
34+ it "parses camelCase correctly" $ do
35+ a <- mkAcronym "BOO"
36+ r <- mapM (fmap SomeWord . mkWord) ["one","Two","BOO"]
37+ parseCamelCase [a] "oneTwoBOO" `shouldParse` r
38+ context "when there are digits in the words" $
39+ it "parses CamelCase correctly" $ do
40+ a <- mkAcronym "BOO"
41+ r <- mapM (fmap SomeWord . mkWord) ["one1two","Three3"]
42+ parseCamelCase [a] "one1twoThree3" `shouldParse` r
43+ context "when a word has a suffix coinciding with a acronym" $
44+ it "is still parsed correctly as a normala word" $ do
45+ a <- mkAcronym "boo"
46+ r <- mapM (fmap SomeWord . mkWord) ["fooboo","Bar"]
47+ parseCamelCase [a] "foobooBar" `shouldParse` r
test/Text/Inflections/Parse/SnakeCaseSpec.hs+28−1
@@ -1,8 +1,35 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
1module Text.Inflections.Parse.SnakeCaseSpec
3module Text.Inflections.Parse.SnakeCaseSpec
2 ( spec )
4 ( spec )
3where
5where
4
6
5import Test.Hspec
7import Test.Hspec
8+import Test.Hspec.Megaparsec
9+import Text.Inflections
6
10
7spec :: Spec
11spec :: Spec
8−spec = return ()
12+spec =
13+ describe "parseSnakeCase" $ do
14+ context "when given no acronyms" $ do
15+ it "parses snake_case correctly" $ do
16+ r <- mapM (fmap SomeWord . mkWord) ["OneTwo","Three"]
17+ parseSnakeCase [] "OneTwo_Three" `shouldParse` r
18+ it "handles digits in the words correctly" $ do
19+ r <- mapM (fmap SomeWord . mkWord) ["one4a","two00"]
20+ parseSnakeCase [] "one4a_two00" `shouldParse` r
21+ context "when given some acronyms" $ do
22+ it "parses snake_case correctly" $ do
23+ a <- mkAcronym "BOO"
24+ r <- mapM (fmap SomeWord . mkWord) ["BOO","one","one"]
25+ parseSnakeCase [a] "BOO_one_one" `shouldParse` r
26+ context "when acronym happens to be a prefix of other word" $
27+ it "parses the word correctly" $ do
28+ a <- mkAcronym "BOO"
29+ r <- mapM (fmap SomeWord . mkWord) ["one","BOOtwo"]
30+ parseSnakeCase [a] "one_BOOtwo" `shouldParse` r
31+ context "when acronym happens to be a suffix of other word" $
32+ it "parses the word correctly" $ do
33+ a <- mkAcronym "BOO"
34+ r <- mapM (fmap SomeWord . mkWord) ["oneBOO","two"]
35+ parseSnakeCase [a] "oneBOO_two" `shouldParse` r