packages feed

ipa (empty) → 0.1.0.0

raw patch · 7 files changed

+1089/−0 lines, 7 filesdep +basedep +hspecdep +ipa

Dependencies added: base, hspec, ipa, text, unicode-transforms

Files

+ CHANGELOG.org view
@@ -0,0 +1,2 @@+* 0.1.0.0++ Initial release
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2021, Rory Tyler Hayford++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Rory Tyler Hayford nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.org view
@@ -0,0 +1,60 @@+* ipa+** About+~ipa~ is a Haskell library for dealing with International Phonetic Alphabet (IPA) transcriptions. It provides a set of datatypes for working with speech segments and suprasegmentals which can be represented using IPA transcription.+** Usage+IPA values can be created by constructing ~Segment~ or ~Syllable~ values and applying ~toIPA~.+For instance:+#+begin_src haskell+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE OverloadedStrings #-}+    +import           Language.IPA+import qualified Data.Text.IO as T++main :: IO ()                +main = maybe message (T.putStrLn . unIPA) (toIPA b)+  where+    b       = PulmonicConsonant Voiced Bilabial Plosive+    message = T.putStrLn "Invalid or unrepresentable segment!"+     +#+end_src+~Segment~ and ~Syllable~ values can be adorned with additional articulatory information as well, and can further be nested to created more complex structures, e.g.:+#+begin_src haskell+someComplexVowel :: Segment+someComplexVowel = WithSegmentalFeature +    (SuperScriptNumeric 255)+    (WithSegmentalFeature +     (Length OverLong)+     (WithSegmentalFeature +      Compressed+      (Vowel Close Back Unrounded)))+-- ɯᵝːː²⁵⁵ +   +#+end_src+~IPA~ is an instance of ~Semigroup~, so values can be concatenated with ~<>~:+#+begin_src haskell+someSyllables :: Maybe IPA+someSyllables = (<>) <$> toIPA syllable1 <*> toIPA syllable2+  where+    syllable1 =+        WithSuprasegmentalFeature Linking+                                  (Syllable [ WithSegmentalFeature --+                                              (Length Long)+                                              (Vowel Close Back Rounded)+                                            ])++    syllable2 = Syllable [ PulmonicConsonant Voiced Palatal Approximant+                         , Vowel OpenMid Front Unrounded+                         , Vowel Mid Central Unrounded+                         ]+-- uː‿jɛə                        ++#+end_src+~toIPA~ is a method of the ~ReprIPA~ typeclass; I decided a typeclass was best in order to allow users to create their own instances if the library-supplied ones were insufficient. You could create an instance, for example, for a hypothetical ~Mora~ datatype, etc...+** License+This library is distributed under the BSD 3-Clause revised license.+** TODO TODO+- [ ] Export constant values for common segments+- [ ] Convenience functions for wrapping IPA values in different trasncription delimiters (phonemic, phonetic, etc...)+- [ ] Support for XSampa transcription and IPA-XSampa conversion+- [ ] Parsing and validation of user-supplied IPA literals
+ ipa.cabal view
@@ -0,0 +1,56 @@+cabal-version:      2.4+name:               ipa+version:            0.1.0.0+synopsis:           Internal Phonetic Alphabet (IPA)+description:+  Types and tools for working with the International Phonetic Alphabet (IPA)++license:            BSD-3-Clause+license-file:       LICENSE+author:             Rory Tyler Hayford+maintainer:         rory.hayford@protonmail.com+copyright:          (c) Rory Tyler Hayford, 2021+homepage:           https://gitlab.com/ngua/ipa-hs/-/blob/master/README.org+category:           language+extra-source-files:+  README.org+  CHANGELOG.org++source-repository head+  type:     git+  location: https://gitlab.com/ngua/ipa-hs++common common-options+  ghc-options:+    -Wall -Wcompat -Widentities -Wincomplete-record-updates+    -Wincomplete-uni-patterns -Wpartial-fields -Wredundant-constraints++common common-extensions+  default-extensions: OverloadedStrings++library+  import:           common-options, common-extensions+  build-depends:+    , base                ^>=4.13.0.0+    , text                ^>=1.2.4+    , unicode-transforms  ^>=0.3.7++  exposed-modules:+    Language.IPA+    Language.IPA.Types++  hs-source-dirs:   src+  default-language: Haskell2010++test-suite ipa-testsuite+  import:           common-options, common-extensions+  type:             exitcode-stdio-1.0+  hs-source-dirs:   test+  build-depends:+    , base+    , hspec+    , ipa+    , text++  main-is:          Main.hs+  default-language: Haskell2010
+ src/Language/IPA.hs view
@@ -0,0 +1,588 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module      : Language.IPA+-- Copyright   : (c) 2021 Rory Tyler Hayford+--+-- License     : BSD-3-Clause+-- Maintainer  : rory.hayford@protonmail.com+-- Stability   : experimental+-- Portability : GHC+--+-- Working with IPA transcriptions+module Language.IPA+    ( module M+      -- * Converting to IPA representations+    , ReprIPA(..)+    ) where++import           Data.Char          ( digitToInt )+import           Data.Text          ( Text )+import qualified Data.Text          as T++import           Language.IPA.Types as M++-- | Entities representable through IPA transcription+class ReprIPA a where+    -- | Produces an 'IPA' transcription given a valid 'Segment'; a result+    -- of @Nothing@ indicates either an unattested-yet-possible segment, or one+    -- considered impossible+    toIPA :: a -> Maybe IPA++    -- | Partial function for creating an 'IPA'. Useful if you are certain that+    -- the sound in question is representable+    toIPA' :: a -> IPA+    toIPA' (toIPA -> Just x) = x+    toIPA' _                 = error "Illegal/unrepresentable value"++instance Traversable t => ReprIPA (Syllable t) where+    toIPA = \case+        -- Syllable without any suprasegmental information+        Syllable ss+            | null ss -> Nothing+            | otherwise -> foldr1 (<>) <$> traverse toIPA ss+        WithSuprasegmentalFeature feature s ->+            withSuprasegmentalFeature s feature++instance ReprIPA Segment where+    toIPA = \case+        Zero -> mkJustIPA "∅"+        Consonant c -> consonant c+        v@Vowel {} -> vowel v+        WithSegmentalFeature feature s -> withSegmentalFeature s feature++mkJustIPA :: Text -> Maybe IPA+mkJustIPA = Just . mkIPA++mkIPAOp :: ReprIPA a => (b -> Maybe IPA) -> a -> b -> Maybe IPA+mkIPAOp f x y = (<>) <$> toIPA x <*> f y++withSuprasegmentalFeature+    :: Traversable t => Syllable t -> SuprasegmentalFeature -> Maybe IPA+withSuprasegmentalFeature s = \case+    -- Level tones, non-diacritic+    LevelLexicalTone tone -> mkIPAOp ipaTone s tone+      where+        ipaTone = \case+            ExtraHighTone -> mkJustIPA "˥"+            HighTone      -> mkJustIPA "˦"+            MidTone       -> mkJustIPA "˧"+            LowTone       -> mkJustIPA "˨"+            ExtraLowTone  -> mkJustIPA "˩"+            -- Down-step and up-step are represented with+            -- diacritics, not tone characters+            _             -> Nothing++    -- Level tones, diacritic. The text value inside the 'IPA' is a+    -- combining character+    LevelLexicalToneDiacritic tone -> mkIPAOp ipaTone s tone+      where+        ipaTone = \case+            ExtraHighTone -> mkJustIPA "\x030b" -- ◌̋+            HighTone      -> mkJustIPA "\x0341" -- ◌́+            MidTone       -> mkJustIPA "\x0304" -- ◌̄+            LowTone       -> mkJustIPA "\x0340" -- ◌̀+            ExtraLowTone  -> mkJustIPA "\x030f" -- ◌̏+            DownStep      -> mkJustIPA "ꜜ"+            UpStep        -> mkJustIPA "ꜛ"++    -- Tone contours, non-diacritic+    LexicalToneContour tone -> mkIPAOp ipaToneContour s tone+      where+        ipaToneContour = \case+            Rising        -> mkJustIPA "˩˥"+            Falling       -> mkJustIPA "˥˩"+            HighRising    -> mkJustIPA "˧˥"+            LowRising     -> mkJustIPA "˩˧"+            HighFalling   -> mkJustIPA "˥˧"+            LowFalling    -> mkJustIPA "˧˩"+            RisingFalling -> mkJustIPA "˧˦˨"+            FallingRising -> mkJustIPA "˧˨˦"+            GlobalRise    -> mkJustIPA "↗"+            GlobalFall    -> mkJustIPA "↙"++    -- Tone contours, diacritic+    LexicalToneContourDiacritic tone -> mkIPAOp ipaToneContour s tone+      where+        ipaToneContour = \case+            Rising        -> mkJustIPA "\x0302" -- ◌̂+            Falling       -> mkJustIPA "\x030c" -- ◌̌+            HighRising    -> mkJustIPA "\x1dc9" -- ◌᷉+            LowRising     -> mkJustIPA "\x1dc5" -- ◌᷅+            HighFalling   -> mkJustIPA "\x1dc7" -- ◌᷇+            LowFalling    -> mkJustIPA "\x1dc6" -- ◌᷆+            RisingFalling -> mkJustIPA "\x1dc8" -- ◌᷈+            FallingRising -> mkJustIPA "\x1dc9" -- ◌᷉+            -- 'GlobalRise' and 'GlobalFall' don't have+            -- diacritic representations+            _             -> Nothing++    -- Syllable stress+    Stress stress -> mkIPAOp ipaStress s stress+      where+        ipaStress Primary   = mkJustIPA "ˈ"+        ipaStress Secondary = mkJustIPA "ˌ"++    -- Explicit syllable break+    Break -> (<>) <$> toIPA s <*> mkJustIPA "."++    -- Syllable non-break+    Linking -> (<>) <$> toIPA s <*> mkJustIPA "‿"++withSegmentalFeature :: Segment -> SegmentalFeature -> Maybe IPA+withSegmentalFeature s = \case+    Voicing v -> mkIPAOp ipaVoicing s v+      where+        ipaVoicing = \case+            Voiceless -> mkJustIPA "\x030a" -- ◌̊+            Voiced    -> mkJustIPA "\x030c" -- ◌̌+    Length l -> mkIPAOp ipaLength s l+      where+        ipaLength = \case+            OverLong   -> mkJustIPA "ːː"+            HalfLong   -> mkJustIPA "ˑ"+            Long       -> mkJustIPA "ː"+            Short      -> mkJustIPA mempty+            ExtraShort -> mkJustIPA "\x0306" -- ◌ ̆+    SecondaryArticulation sa -> mkIPAOp secondaryArticulation s sa+    SuperScriptNumeric ns -> (<>) <$> toIPA s <*> mkJustIPA digits+      where+        digits = T.concat $ T.pack <$> (code . digitToInt <$> show ns)++        code   = \case+            0 -> "\x2070"+            1 -> "\x00b9"+            2 -> "\x00b2"+            3 -> "\x00b3"+            4 -> "\x2074"+            5 -> "\x2075"+            6 -> "\x2076"+            7 -> "\x2077"+            8 -> "\x2078"+            9 -> "\x2079"+            _ -> mempty++    feature -> (<>) <$> toIPA s <*> case feature of+        Aspirated           -> mkJustIPA "\x02b0" -- ◌ʰ+        MoreRounded         -> mkJustIPA "\x0339" -- ◌̹+        LessRounded         -> mkJustIPA "\x031c" -- ◌̜+        Advanced            -> mkJustIPA "\x031f" -- ◌̟+        Retracted           -> mkJustIPA "\x0320" -- ◌̠+        Centralized         -> mkJustIPA "\x0308" -- ◌̈+        MidCentralized      -> mkJustIPA "\x033d" -- ◌̽+        Compressed          -> mkJustIPA "\x1d5d" -- ◌ᵝ+        Syllabic            -> mkJustIPA "\x0329" -- ◌̩+        NonSyllabic         -> mkJustIPA "\x032f" -- ◌̯+        Rhotacized          -> mkJustIPA "\x02de" -- ◌˞+        BreathyVoice        -> mkJustIPA "\x0324" -- ◌̤+        CreakyVoice         -> mkJustIPA "\x0330" -- ◌̰+        LinguoLabialized    -> mkJustIPA "\x033c" -- ◌̼+        Labialized          -> mkJustIPA "\x02b7" -- ◌ʷ+        Palatalized         -> mkJustIPA "\x02b2" -- ◌ʲ+        Velarized           -> mkJustIPA "\x02e0" -- ◌ˠ+        Pharyngealized      -> mkJustIPA "\x02e4" -- ◌ˤ+        Raised              -> mkJustIPA "\x031d" -- ◌̝+        Lowered             -> mkJustIPA "\x031e" -- ◌̞+        AdvancedTongueRoot  -> mkJustIPA "\x0318" -- ◌̘+        RetractedTongueRoot -> mkJustIPA "\x0319" -- ◌̙+        Dentalized          -> mkJustIPA "\x032a" -- ◌̪+        Apical              -> mkJustIPA "\x033a" -- ◌̺+        Laminal             -> mkJustIPA "\x033b" -- ◌̻+        Nasalized           -> mkJustIPA "\x0303" -- ◌̃+        NasalRelease        -> mkJustIPA "\x207f" -- ◌ⁿ+        LateralRelease      -> mkJustIPA "\x02e1" -- ◌ˡ+        NoAudibleRelease    -> mkJustIPA "\x031a" -- ◌̚+        _                   -> mkJustIPA "" --++consonant :: Consonant -> Maybe IPA+consonant = \case+    -- Pulmonic consonants+    -- Bilabials+    Pulmonic Voiceless Bilabial Nasal -> mkJustIPA "m̥"+    Pulmonic Voiced Bilabial Nasal -> mkJustIPA "m"+    Pulmonic Voiced Bilabial Plosive -> mkJustIPA "b"+    Pulmonic Voiceless Bilabial Plosive -> mkJustIPA "p"+    Pulmonic Voiceless Bilabial (Affricate NonSibilant) -> mkJustIPA "p͡ɸ"+    Pulmonic Voiced Bilabial (Affricate NonSibilant) -> mkJustIPA "b͡β"+    Pulmonic Voiceless Bilabial (Fricative NonSibilant) -> mkJustIPA "ɸ"+    Pulmonic Voiced Bilabial (Fricative NonSibilant) -> mkJustIPA "β"+    Pulmonic Voiced Bilabial Flap -> mkJustIPA "ⱱ̟"+    Pulmonic Voiceless Bilabial Trill -> mkJustIPA "ʙ̥"+    Pulmonic Voiced Bilabial Trill -> mkJustIPA "ʙ"++    -- Labio-dentals+    Pulmonic Voiced LabioDental Nasal -> mkJustIPA "ɱ"+    Pulmonic Voiceless LabioDental Plosive -> mkJustIPA "p̪"+    Pulmonic Voiced LabioDental Plosive -> mkJustIPA "b̪"+    Pulmonic Voiceless LabioDental (Affricate NonSibilant) ->+        mkJustIPA "p̪͡f"+    Pulmonic Voiced LabioDental (Affricate NonSibilant) -> mkJustIPA "b̪͡v"+    Pulmonic Voiceless LabioDental (Fricative NonSibilant) -> mkJustIPA "f"+    Pulmonic Voiced LabioDental (Fricative NonSibilant) -> mkJustIPA "v"+    Pulmonic Voiced LabioDental Approximant -> mkJustIPA "ʋ"+    Pulmonic Voiced LabioDental Flap -> mkJustIPA "ⱱ"++    -- Linguo-labials+    Pulmonic Voiced LinguoLabial Nasal -> mkJustIPA "n̼"+    Pulmonic Voiceless LinguoLabial Plosive -> mkJustIPA "t̼"+    Pulmonic Voiced LinguoLabial Plosive -> mkJustIPA "d̼"+    Pulmonic Voiceless LinguoLabial (Fricative NonSibilant) ->+        mkJustIPA "θ̼ "+    Pulmonic Voiced LinguoLabial (Fricative NonSibilant) -> mkJustIPA "ð̼"+    Pulmonic Voiced LinguoLabial Flap -> mkJustIPA "ɾ̼"++    -- Dentals+    Pulmonic Voiceless Dental (Affricate NonSibilant) -> mkJustIPA "t̼͡θ"+    Pulmonic Voiced Dental (Affricate NonSibilant) -> mkJustIPA "d̼͡ð"+    Pulmonic Voiceless Dental (Fricative NonSibilant) -> mkJustIPA "θ"+    Pulmonic Voiced Dental (Fricative NonSibilant) -> mkJustIPA "ð"++    -- Alveolars+    Pulmonic Voiceless Alveolar Nasal -> mkJustIPA "n̥"+    Pulmonic Voiced Alveolar Nasal -> mkJustIPA "n"+    Pulmonic Voiceless Alveolar Plosive -> mkJustIPA "t"+    Pulmonic Voiced Alveolar Plosive -> mkJustIPA "d"+    Pulmonic Voiceless Alveolar (Affricate Sibilant) -> mkJustIPA "t͡s"+    Pulmonic Voiced Alveolar (Affricate Sibilant) -> mkJustIPA "d͡z"+    Pulmonic Voiceless Alveolar (Affricate NonSibilant) ->+        mkJustIPA "t͡ɹ̝̊"+    Pulmonic Voiced Alveolar (Affricate NonSibilant) -> mkJustIPA "d͡ɹ̝"+    Pulmonic Voiceless Alveolar (Fricative Sibilant) -> mkJustIPA "s"+    Pulmonic Voiced Alveolar (Fricative Sibilant) -> mkJustIPA "z"+    Pulmonic Voiced Alveolar Approximant -> mkJustIPA "ɹ"+    Pulmonic Voiceless Alveolar Flap -> mkJustIPA "ɾ̥"+    Pulmonic Voiced Alveolar Flap -> mkJustIPA "ɾ"+    Pulmonic Voiceless Alveolar Trill -> mkJustIPA "r"+    Pulmonic Voiced Alveolar Trill -> mkJustIPA "r̥"+    Pulmonic Voiceless Alveolar LateralAffricate -> mkJustIPA "tɬ"+    Pulmonic Voiced Alveolar LateralAffricate -> mkJustIPA "dɮ"+    Pulmonic Voiceless Alveolar LateralFricative -> mkJustIPA "ɬ"+    Pulmonic Voiced Alveolar LateralFricative -> mkJustIPA "ɮ"+    Pulmonic Voiced Alveolar LateralApproximant -> mkJustIPA "l"+    Pulmonic Voiceless Alveolar LateralFlap -> mkJustIPA "ɺ̥"+    Pulmonic Voiced Alveolar LateralFlap -> mkJustIPA "ɺ"++    -- Post-alveolars+    Pulmonic Voiceless PostAlveolar (Affricate Sibilant) -> mkJustIPA "t͡ʃ"+    Pulmonic Voiced PostAlveolar (Affricate Sibilant) -> mkJustIPA "d͡ʒ"+    Pulmonic Voiceless PostAlveolar (Affricate NonSibilant) ->+        mkJustIPA "tɹ̠̊˔"+    Pulmonic Voiced PostAlveolar (Affricate NonSibilant) ->+        mkJustIPA "d͡ɹ̠˔"+    Pulmonic Voiceless PostAlveolar (Fricative Sibilant) -> mkJustIPA "ʃ"+    Pulmonic Voiced PostAlveolar (Fricative Sibilant) -> mkJustIPA "ʒ"+    Pulmonic Voiceless PostAlveolar (Fricative NonSibilant) ->+        mkJustIPA "ɹ̠̊˔"+    Pulmonic Voiced PostAlveolar (Fricative NonSibilant) -> mkJustIPA "ɹ̠˔"++    -- Retroflexes+    Pulmonic Voiceless Retroflex Nasal -> mkJustIPA "ɳ̊"+    Pulmonic Voiced Retroflex Nasal -> mkJustIPA "ɳ"+    Pulmonic Voiceless Retroflex Plosive -> mkJustIPA "ʈ"+    Pulmonic Voiced Retroflex Plosive -> mkJustIPA "ɖ"+    Pulmonic Voiceless Retroflex (Affricate Sibilant) -> mkJustIPA "ʈ͡ʂ"+    Pulmonic Voiced Retroflex (Affricate Sibilant) -> mkJustIPA "ɖ͡ʐ"+    Pulmonic Voiceless Retroflex (Fricative Sibilant) -> mkJustIPA "ʂ"+    Pulmonic Voiced Retroflex (Fricative Sibilant) -> mkJustIPA "ʐ"+    Pulmonic Voiced Retroflex (Fricative NonSibilant) -> mkJustIPA "ɻ˔"+    Pulmonic Voiced Retroflex Approximant -> mkJustIPA "ɻ"+    Pulmonic Voiceless Retroflex Flap -> mkJustIPA "ɽ̊"+    Pulmonic Voiced Retroflex Flap -> mkJustIPA "ɽ"+    Pulmonic Voiceless Retroflex Trill -> mkJustIPA "ɽ̊r̥"+    Pulmonic Voiced Retroflex Trill -> mkJustIPA "ɽr"+    Pulmonic Voiceless Retroflex LateralAffricate -> mkJustIPA "ʈɭ̊˔"+    Pulmonic Voiced Retroflex LateralAffricate -> mkJustIPA "ɖɭ˔"+    Pulmonic Voiceless Retroflex LateralFricative -> mkJustIPA "ɭ̊˔"+    Pulmonic Voiced Retroflex LateralFricative -> mkJustIPA "ɭ˔"+    Pulmonic Voiced Retroflex LateralApproximant -> mkJustIPA "ɭ"+    Pulmonic Voiceless Retroflex LateralFlap -> mkJustIPA "ɭ̥̆"+    Pulmonic Voiced Retroflex LateralFlap -> mkJustIPA "ɭ̆"++    -- Palatals+    Pulmonic Voiceless Palatal Nasal -> mkJustIPA "ɲ̊"+    Pulmonic Voiced Palatal Nasal -> mkJustIPA "ɲ"+    Pulmonic Voiceless Palatal Plosive -> mkJustIPA "c"+    Pulmonic Voiced Palatal Plosive -> mkJustIPA "ɟ"+    Pulmonic Voiceless Palatal (Affricate Sibilant) -> mkJustIPA "t͡ɕ"+    Pulmonic Voiced Palatal (Affricate Sibilant) -> mkJustIPA "d͡ʑ"+    Pulmonic Voiceless Palatal (Affricate NonSibilant) -> mkJustIPA "c͡ç"+    Pulmonic Voiced Palatal (Affricate NonSibilant) -> mkJustIPA "ɟ͡ʝ"+    Pulmonic Voiceless Palatal (Fricative Sibilant) -> mkJustIPA "ɕ"+    Pulmonic Voiced Palatal (Fricative Sibilant) -> mkJustIPA "ʑ"+    Pulmonic Voiceless Palatal (Fricative NonSibilant) -> mkJustIPA "ç"+    Pulmonic Voiced Palatal (Fricative NonSibilant) -> mkJustIPA "ʝ"+    Pulmonic Voiced Palatal Approximant -> mkJustIPA "j"+    Pulmonic Voiceless Palatal LateralAffricate -> mkJustIPA "cʎ̝̊"+    Pulmonic Voiced Palatal LateralAffricate -> mkJustIPA "ɟʎ̝"+    Pulmonic Voiceless Palatal LateralFricative -> mkJustIPA "ʎ̝̊"+    Pulmonic Voiced Palatal LateralFricative -> mkJustIPA "ʎ̝"+    Pulmonic Voiced Palatal LateralApproximant -> mkJustIPA "ʎ"+    Pulmonic Voiced Palatal LateralFlap -> mkJustIPA "ʎ̆"++    -- Velars+    Pulmonic Voiceless Velar Nasal -> mkJustIPA "ŋ̊"+    Pulmonic Voiced Velar Nasal -> mkJustIPA "ŋ"+    Pulmonic Voiceless Velar Plosive -> mkJustIPA "k"+    Pulmonic Voiced Velar Plosive -> mkJustIPA "g"+    Pulmonic Voiceless Velar (Affricate NonSibilant) -> mkJustIPA "k͡x"+    Pulmonic Voiced Velar (Affricate NonSibilant) -> mkJustIPA "g͡ɣ"+    Pulmonic Voiceless Velar (Fricative NonSibilant) -> mkJustIPA "x"+    Pulmonic Voiced Velar (Fricative NonSibilant) -> mkJustIPA "ɣ"+    Pulmonic Voiced Velar Approximant -> mkJustIPA "ɰ"+    Pulmonic Voiceless Velar LateralAffricate -> mkJustIPA "kʟ̝̊"+    Pulmonic Voiced Velar LateralAffricate -> mkJustIPA "ɡʟ̝"+    Pulmonic Voiceless Velar LateralFricative -> mkJustIPA "ʟ̝̊"+    Pulmonic Voiced Velar LateralFricative -> mkJustIPA "ʟ̝"+    Pulmonic Voiced Velar LateralApproximant -> mkJustIPA "ʟ"+    Pulmonic Voiced Velar LateralFlap -> mkJustIPA "ʟ̆"++    -- Uvulars+    Pulmonic Voiced Uvular Nasal -> mkJustIPA "ɴ"+    Pulmonic Voiceless Uvular Plosive -> mkJustIPA "q"+    Pulmonic Voiced Uvular Plosive -> mkJustIPA "ɢ"+    Pulmonic Voiceless Uvular (Affricate NonSibilant) -> mkJustIPA "q͡χ"+    Pulmonic Voiced Uvular (Affricate NonSibilant) -> mkJustIPA "ɢ͡ʁ"+    Pulmonic Voiceless Uvular (Fricative NonSibilant) -> mkJustIPA "χ"+    Pulmonic Voiced Uvular (Fricative NonSibilant) -> mkJustIPA "ʁ"+    Pulmonic Voiced Uvular Flap -> mkJustIPA "ɢ̆"+    Pulmonic Voiceless Uvular Trill -> mkJustIPA "ʀ"+    Pulmonic Voiced Uvular Trill -> mkJustIPA "ʀ̥"+    Pulmonic Voiced Uvular LateralApproximant -> mkJustIPA "ʟ̠"++    -- Pharyngeals+    Pulmonic Voiceless Pharyngeal Plosive -> mkJustIPA "ʡ"+    Pulmonic Voiced Pharyngeal (Affricate NonSibilant) -> mkJustIPA "ʡ͡ʢ"+    Pulmonic Voiceless Pharyngeal (Fricative NonSibilant) -> mkJustIPA "ħ"+    Pulmonic Voiced Pharyngeal (Fricative NonSibilant) -> mkJustIPA "ʕ"+    Pulmonic Voiced Pharyngeal Flap -> mkJustIPA "̆ʡ̆"+    Pulmonic Voiceless Pharyngeal Trill -> mkJustIPA "ʜ"+    Pulmonic Voiced Pharyngeal Trill -> mkJustIPA "ʢ"++    -- Glottals+    Pulmonic Voiceless Glottal Plosive -> mkJustIPA "ʔ"+    Pulmonic Voiceless Glottal (Affricate NonSibilant) -> mkJustIPA "ʔ͡h"+    Pulmonic Voiceless Glottal (Fricative NonSibilant) -> mkJustIPA "h"+    Pulmonic Voiced Glottal (Fricative NonSibilant) -> mkJustIPA "ɦ"+    Pulmonic Voiced Glottal Approximant -> mkJustIPA "̆̆ʔ̞"++    -- Ejectives+    -- Bilabials+    Ejective Bilabial Plosive -> mkJustIPA "pʼ"+    Ejective Bilabial (Fricative NonSibilant) -> mkJustIPA "ɸʼ"++    -- Labio-dentals+    Ejective LabioDental (Affricate NonSibilant) -> mkJustIPA "p̪͡fʼ"+    Ejective LabioDental (Fricative NonSibilant) -> mkJustIPA "fʼ"++    -- Dentals+    Ejective Dental Plosive -> mkJustIPA "t̪ʼ"+    Ejective Dental (Affricate NonSibilant) -> mkJustIPA "t̪͡θʼ"+    Ejective Dental (Fricative NonSibilant) -> mkJustIPA "θʼ"++    -- Alveolars+    Ejective Alveolar Plosive -> mkJustIPA "tʼ"+    Ejective Alveolar (Affricate Sibilant) -> mkJustIPA "t͡sʼ"+    Ejective Alveolar (Fricative Sibilant) -> mkJustIPA "sʼ"+    Ejective Alveolar LateralAffricate -> mkJustIPA "t͡ɬʼ"+    Ejective Alveolar LateralFricative -> mkJustIPA "ɬʼ"++    -- Post-alveolars+    Ejective PostAlveolar (Affricate Sibilant) -> mkJustIPA "t͡ʃʼ"+    Ejective PostAlveolar (Fricative Sibilant) -> mkJustIPA "ʃʼ"++    -- Retroflexes+    Ejective Retroflex Plosive -> mkJustIPA "ʈʼ"+    Ejective Retroflex (Affricate Sibilant) -> mkJustIPA "ʈ͡ʂʼ"+    Ejective Retroflex (Fricative Sibilant) -> mkJustIPA "ʂʼ"++    -- Palatals+    Ejective Palatal Plosive -> mkJustIPA "cʼ"+    Ejective Palatal (Affricate Sibilant) -> mkJustIPA "t͡ɕʼ"+    Ejective Palatal (Fricative Sibilant) -> mkJustIPA "ɕʼ"+    Ejective Palatal LateralAffricate -> mkJustIPA "cʎ̝̊ʼ"++    -- Velars+    Ejective Velar Plosive -> mkJustIPA "kʼ"+    Ejective Velar (Affricate NonSibilant) -> mkJustIPA "k͡xʼ"+    Ejective Velar (Fricative NonSibilant) -> mkJustIPA "xʼ"+    Ejective Velar LateralAffricate -> mkJustIPA "kʟ̝̊ʼ"++    -- Uvulars+    Ejective Uvular Plosive -> mkJustIPA "qʼ"+    Ejective Uvular (Affricate NonSibilant) -> mkJustIPA "q͡χʼ"+    Ejective Uvular (Fricative NonSibilant) -> mkJustIPA "χʼ"++    -- Pharyngeals+    Ejective Pharyngeal Plosive -> mkJustIPA "ʡʼ "++    -- Implosives+    Implosive Voiceless Bilabial -> mkJustIPA "ƥ"+    Implosive Voiced Bilabial -> mkJustIPA "ɓ"+    Implosive Voiceless Dental -> mkJustIPA "ƭ̪"+    Implosive Voiced Dental -> mkJustIPA "ɗ̪"+    Implosive Voiceless Alveolar -> mkJustIPA "ɗ"+    Implosive Voiced Alveolar -> mkJustIPA "ƭ"+    Implosive Voiceless Retroflex -> mkJustIPA "ᶑ"+    Implosive Voiced Retroflex -> mkJustIPA "ƭ̢"+    Implosive Voiceless Palatal -> mkJustIPA "ƈ"+    Implosive Voiced Palatal -> mkJustIPA "ʄ"+    Implosive Voiceless Velar -> mkJustIPA "ƙ"+    Implosive Voiced Velar -> mkJustIPA "ɠ"+    Implosive Voiceless Uvular -> mkJustIPA "ʠ"+    Implosive Voiced Uvular -> mkJustIPA "ʛ"++    -- Clicks+    Click Bilabial -> mkJustIPA "ʘ"+    Click Dental -> mkJustIPA "ǀ"+    Click Alveolar -> mkJustIPA "ǃ"+    Click PostAlveolar -> mkJustIPA "ǁ" -- lateral click+    Click Palatal -> mkJustIPA "ǂ"++    -- Double articulation+    DoublyArticulated Voiced Bilabial Alveolar Nasal -> mkJustIPA "n͡m"+    DoublyArticulated Voiceless Bilabial Alveolar Plosive -> mkJustIPA "t͡p"+    DoublyArticulated Voiced Bilabial Alveolar Plosive -> mkJustIPA "d͡b"+    DoublyArticulated Voiced Bilabial Velar Nasal -> mkJustIPA "ŋ͡m"+    DoublyArticulated Voiceless Bilabial Velar Plosive -> mkJustIPA "k͡p"+    DoublyArticulated Voiced Bilabial Velar Plosive -> mkJustIPA "g͡b"+    DoublyArticulated Voiceless Uvular Pharyngeal Plosive -> mkJustIPA "q͡ʡ"+    DoublyArticulated Voiceless Bilabial Palatal (Fricative NonSibilant) ->+        mkJustIPA "ɥ̊"+    DoublyArticulated Voiced Bilabial Palatal Approximant -> mkJustIPA "ɥ"+    DoublyArticulated Voiceless Bilabial Velar (Fricative NonSibilant) ->+        mkJustIPA "ʍ"+    DoublyArticulated Voiced Bilabial Velar Approximant -> mkJustIPA "w"+    DoublyArticulated Voiced Alveolar Velar LateralApproximant ->+        mkJustIPA "ɫ"+    -- The sj-sound in Swedish phonology; actual realization is contested+    -- and appears to vary between dialects+    DoublyArticulated Voiceless PostAlveolar Velar (Fricative Sibilant) ->+        mkJustIPA "ɧ"+    DoublyArticulated Voiceless LabioDental Velar (Fricative Sibilant) ->+        mkJustIPA "ɧ"++    _ -> Nothing++vowel :: Segment -> Maybe IPA+vowel = \case+    Vowel Close Front Unrounded -> mkJustIPA "i"+    Vowel Close Front Rounded -> mkJustIPA "y"+    Vowel Close Central Unrounded -> mkJustIPA "ɨ"+    Vowel Close Central Rounded -> mkJustIPA "ʉ"+    Vowel Close Back Unrounded -> mkJustIPA "ɯ"+    Vowel Close Back Rounded -> mkJustIPA "u"+    Vowel NearClose Front Unrounded -> mkJustIPA "ɪ"+    Vowel NearClose Front Rounded -> mkJustIPA "ʏ"+    Vowel NearClose Back Rounded -> mkJustIPA "ʊ"+    Vowel CloseMid Front Unrounded -> mkJustIPA "e"+    Vowel CloseMid Front Rounded -> mkJustIPA "ø"+    Vowel CloseMid Central Unrounded -> mkJustIPA "ɘ"+    Vowel CloseMid Central Rounded -> mkJustIPA "ɵ"+    Vowel CloseMid Back Unrounded -> mkJustIPA "ɤ"+    Vowel CloseMid Back Rounded -> mkJustIPA "o"+    Vowel Mid Front Unrounded -> mkJustIPA "e̞"+    Vowel Mid Front Rounded -> mkJustIPA "ø̞"+    Vowel Mid Central Unrounded -> mkJustIPA "ə"+    Vowel Mid Central Rounded -> mkJustIPA "ə"+    Vowel Mid Back Unrounded -> mkJustIPA "ɤ̞"+    Vowel Mid Back Rounded -> mkJustIPA "o̞"+    Vowel OpenMid Front Unrounded -> mkJustIPA "ɛ"+    Vowel OpenMid Front Rounded -> mkJustIPA "œ"+    Vowel OpenMid Central Unrounded -> mkJustIPA "ɜ"+    Vowel OpenMid Central Rounded -> mkJustIPA "ɞ"+    Vowel OpenMid Back Unrounded -> mkJustIPA "ʌ"+    Vowel OpenMid Back Rounded -> mkJustIPA "ɔ"+    Vowel NearOpen Front Unrounded -> mkJustIPA "æ"+    Vowel NearOpen Central Unrounded -> mkJustIPA "ɐ"+    Vowel Open Front Unrounded -> mkJustIPA "a"+    Vowel Open Front Rounded -> mkJustIPA "ɶ"+    Vowel Open Central Unrounded -> mkJustIPA "ä"+    Vowel Open Back Unrounded -> mkJustIPA "ɑ"+    Vowel Open Back Rounded -> mkJustIPA "ɒ"+    _ -> Nothing++secondaryArticulation :: Segment -> Maybe IPA+secondaryArticulation = \case+    Consonant c -> case c of+        Pulmonic Voiced Bilabial Nasal -> mkJustIPA "\x1d50"+        Pulmonic Voiced LabioDental Nasal -> mkJustIPA "\x1dac"+        Pulmonic Voiced Alveolar Nasal -> mkJustIPA "\x207f"+        Pulmonic Voiced Retroflex Nasal -> mkJustIPA "\x1daf"+        Pulmonic Voiced Palatal Nasal -> mkJustIPA "\x1dae"+        Pulmonic Voiced Velar Nasal -> mkJustIPA "\x1d51"+        Pulmonic Voiced Uvular Nasal -> mkJustIPA "\x1db0"++        Pulmonic Voiced Bilabial Plosive -> mkJustIPA "\x1d56"+        Pulmonic Voiceless Bilabial Plosive -> mkJustIPA "\x1d47"+        Pulmonic Voiceless Alveolar Plosive -> mkJustIPA "\x1d57"+        Pulmonic Voiced Alveolar Plosive -> mkJustIPA "\x1d48"+        Pulmonic Voiceless Palatal Plosive -> mkJustIPA "\x1d9c"+        Pulmonic Voiced Palatal Plosive -> mkJustIPA "\x1da1"+        Pulmonic Voiceless Velar Plosive -> mkJustIPA "\x1d4f"+        Pulmonic Voiced Velar Plosive -> mkJustIPA "\x1da2"+        Pulmonic Voiceless Glottal Plosive -> mkJustIPA "\x02c0"++        Pulmonic Voiced Bilabial (Fricative NonSibilant) -> mkJustIPA "\x1db2"+        Pulmonic Voiceless Bilabial (Fricative NonSibilant) ->+            mkJustIPA "\x1d5d"+        Pulmonic Voiced LabioDental (Fricative NonSibilant) ->+            mkJustIPA "\x1da0"+        Pulmonic Voiceless LabioDental (Fricative NonSibilant) ->+            mkJustIPA "\x1d5b"+        Pulmonic Voiceless Dental (Fricative NonSibilant) -> mkJustIPA "\x1dbf"+        Pulmonic Voiced Dental (Fricative NonSibilant) -> mkJustIPA "\x1d9e"+        Pulmonic Voiceless Alveolar (Fricative Sibilant) -> mkJustIPA "\x02e2"+        Pulmonic Voiced Alveolar (Fricative Sibilant) -> mkJustIPA "\x1dbb"+        Pulmonic Voiceless PostAlveolar (Fricative Sibilant) ->+            mkJustIPA "\x1db4"+        Pulmonic Voiced PostAlveolar (Fricative Sibilant) -> mkJustIPA "\x1dbe"+        Pulmonic Voiceless Palatal (Fricative Sibilant) -> mkJustIPA "\x1d9d"+        Pulmonic Voiced Palatal (Fricative Sibilant) -> mkJustIPA "\x1dbd"+        Pulmonic Voiceless Palatal (Fricative NonSibilant) ->+            mkJustIPA "\x1d9c\x0327"+        Pulmonic Voiced Palatal (Fricative NonSibilant) -> mkJustIPA "\x1da8"+        Pulmonic Voiceless Velar (Fricative NonSibilant) -> mkJustIPA "\x02e3"+        Pulmonic Voiced Velar (Fricative NonSibilant) -> mkJustIPA "\x02e0"+        Pulmonic Voiceless Uvular (Fricative NonSibilant) -> mkJustIPA "\x1d61"+        Pulmonic Voiced Uvular (Fricative NonSibilant) -> mkJustIPA "\x02b6"+        Pulmonic Voiceless Glottal (Fricative NonSibilant) ->+            mkJustIPA "\x02b0"+        Pulmonic Voiced Glottal (Fricative NonSibilant) -> mkJustIPA "\x02b1"++        Pulmonic Voiced LabioDental Approximant -> mkJustIPA "\x1db9"+        Pulmonic Voiced Alveolar Approximant -> mkJustIPA "\x02b4"+        Pulmonic Voiced Retroflex Approximant -> mkJustIPA "\x02b5"+        Pulmonic Voiced Palatal Approximant -> mkJustIPA "\x02b2"+        Pulmonic Voiceless Velar Approximant -> mkJustIPA "\xab69"+        Pulmonic Voiced Velar Approximant -> mkJustIPA "\x1dad"++        Pulmonic Voiced Alveolar Trill -> mkJustIPA "\x02b3"++        _ -> Nothing++    Vowel Close Front Unrounded -> mkJustIPA "\x2071"+    Vowel Close Front Rounded -> mkJustIPA "\x02b8"+    Vowel Close Central Unrounded -> mkJustIPA "\x1da4"+    Vowel Close Central Rounded -> mkJustIPA "\x1db6"+    Vowel Close Back Unrounded -> mkJustIPA "\x1d5a"+    Vowel Close Back Rounded -> mkJustIPA "\x1d58"+    Vowel NearClose Front Unrounded -> mkJustIPA "\x1da6"+    Vowel NearClose Central Unrounded -> mkJustIPA "\x1da7"+    Vowel NearClose Back Rounded -> mkJustIPA "\x1db7"+    Vowel Mid Central Unrounded -> mkJustIPA "\x1d4a"+    Vowel Mid Central Rounded -> mkJustIPA "\x1d4a"+    Vowel OpenMid Front Unrounded -> mkJustIPA "\x1d4b"+    Vowel OpenMid Front Rounded -> mkJustIPA "\xa7f9"+    Vowel OpenMid Central Unrounded -> mkJustIPA "\x1d9f"+    Vowel OpenMid Back Unrounded -> mkJustIPA "\x1dba"+    Vowel OpenMid Back Rounded -> mkJustIPA "\x1d53"+    Vowel NearOpen Front Unrounded -> mkJustIPA "\x1d46"+    Vowel NearOpen Central Unrounded -> mkJustIPA "\x1d44"+    Vowel NearOpen Back Unrounded -> mkJustIPA "\x1d45"+    Vowel NearOpen Back Rounded -> mkJustIPA "\x1d9b"+    Vowel Open Front Unrounded -> mkJustIPA "\x1d43"+    Vowel Open Back Rounded -> mkJustIPA "\x1d44"++    _ -> Nothing+
+ src/Language/IPA/Types.hs view
@@ -0,0 +1,263 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Module      : Language.IPA.Types+-- Copyright   : (c) 2021 Rory Tyler Hayford+--+-- License     : BSD-3-Clause+-- Maintainer  : rory.hayford@protonmail.com+-- Stability   : experimental+-- Portability : GHC+--+--  Types representing segments representable in the IPA+module Language.IPA.Types+    ( -- * Notation systems+      IPA(..)+    , mkIPA+    , XSampa(XSampa)+      -- * Segments+    , Segment(..)+      -- ** Consonants (pulmonic and non-pulmonic)+    , Consonant(..)+      -- **** Convenience patterns+      -- $pat+    , pattern PulmonicConsonant+    , pattern EjectiveConsonant+    , pattern ClickConsonant+    , pattern ImplosiveConsonant+      -- *** Consonant features+    , Manner(..)+    , Place(..)+    , Phonation(..)+    , Sibilance(..)+      -- ** Vowel features+    , Height(..)+    , Backness(..)+    , Roundedness(..)+      -- * Suprasegmentals+    , Syllable(..)+      -- ** Suprasegmental features+    , SuprasegmentalFeature(..)+    , ToneContour(..)+    , LevelTone(..)+    , Stress(..)+      -- ** Segmental articulatory features+    , Length(..)+    , SegmentalFeature(..)+    ) where++import           Data.Text           ( Text )+import           Data.Text.Normalize ( NormalizationMode(NFC), normalize )++import           GHC.Generics        ( Generic )++-- | Textual representation of a speech segment or grouping of segments, with+-- zero or more articulatory features; encoded in IPA transcription. Note this+-- has a 'Semigroup' instance, so various 'IPA's can conveniently be concatenated+-- with @<>@+newtype IPA = IPA { unIPA :: Text }+    deriving ( Show, Eq, Generic, Semigroup )++-- | Normalizes text value to NFC before wrapping it in 'IPA'. This is to ensure+-- the comparability of 'IPA' values+mkIPA :: Text -> IPA+mkIPA = IPA . normalize NFC++newtype XSampa = XSampa Text+    deriving ( Show, Eq, Generic )++-- | A single segment, or combination of a segment and articulatory feature+data Segment+    = Consonant Consonant -- ^ Pulmonic and non-pulmonic consonants+    | Vowel Height Backness Roundedness+    | Zero -- ^ A null/zero phone+    | WithSegmentalFeature SegmentalFeature Segment -- ^ Various other articulatory features+    deriving ( Show, Eq, Generic )++-- | Multiple segments, or combination of multiple segments with+-- suprasegmental feature+data Syllable t+    = Syllable (t Segment) -- ^ A grouping of segments+    | WithSuprasegmentalFeature SuprasegmentalFeature (Syllable t)+      -- ^ Articulatory features that affect/encompass the entire syllable+    deriving ( Generic )++deriving instance Show (t Segment) => Show (Syllable t)++deriving instance Eq (t Segment) => Eq (Syllable t)++-- | Pulmonic and non-pulmonic consonants+data Consonant+    = Pulmonic Phonation Place Manner+    | Ejective Place Manner+    | Implosive Phonation Place+    | Click Place+    | DoublyArticulated Phonation Place Place Manner+    deriving ( Show, Eq, Generic )++-- $pat+-- These are convenience patterns for creating different types of 'Consonant'+-- 'Segment's, as the nesting of data constructors might otherwise be somewhat+-- troublesome+pattern PulmonicConsonant :: Phonation -> Place -> Manner -> Segment+pattern PulmonicConsonant ph pl m = Consonant (Pulmonic ph pl m)++pattern EjectiveConsonant :: Place -> Manner -> Segment+pattern EjectiveConsonant pl m = Consonant (Ejective pl m)++pattern ClickConsonant :: Place -> Segment+pattern ClickConsonant pl = Consonant (Click pl)++pattern ImplosiveConsonant :: Phonation -> Place -> Segment+pattern ImplosiveConsonant ph pl = Consonant (Implosive ph pl)++-- | Consonantal manner of articulation+data Manner+    = Nasal+    | Plosive+    | Fricative Sibilance+    | Affricate Sibilance+    | Approximant+    | Flap+    | Trill+    | LateralAffricate+    | LateralFricative+    | LateralApproximant+    | LateralFlap+    deriving ( Show, Eq, Generic )++-- | Consonantal place of articulation+data Place+    = Bilabial+    | LabioDental+    | LinguoLabial+    | Dental+    | Alveolar+    | PostAlveolar+    | Retroflex+    | Palatal+    | Velar+    | Uvular+    | Pharyngeal+    | Glottal+    deriving ( Show, Eq, Generic, Ord )++-- | Phonation (voicing)+data Phonation = Voiced | Voiceless+    deriving ( Show, Eq, Generic )++-- | Sibilance for fricative consonants+data Sibilance = Sibilant | NonSibilant+    deriving ( Show, Eq, Generic )++-- | Vowel height+data Height = Close | NearClose | CloseMid | Mid | OpenMid | NearOpen | Open+    deriving ( Show, Eq, Generic )++-- | Vowel backness+data Backness = Front | NearFront | Central | NearBack | Back+    deriving ( Show, Eq, Generic, Ord )++-- | Vowel roundedness+data Roundedness = Rounded | Unrounded+    deriving ( Show, Eq, Generic )++-- | Lexical tone with Chao-style tone letters+data LevelTone+    = ExtraHighTone+    | HighTone+    | MidTone+    | LowTone+    | ExtraLowTone+    | DownStep+    | UpStep+    deriving ( Show, Eq, Generic, Ord )++-- | Lexical tone represented as a contour+data ToneContour+    = Rising+    | Falling+    | HighRising+    | LowRising+    | HighFalling+    | LowFalling+    | RisingFalling+    | FallingRising+    | GlobalRise+    | GlobalFall+    deriving ( Show, Eq, Generic, Ord )++-- | Vowel length; also serves as a notation for consonant gemination in the IPA+data Length+    = OverLong+    | HalfLong+    | Long+    | Short+      -- ^ The default/unmarked length; using this constructor doesn't affect the+      -- default IPA representation of the segment+    | ExtraShort+    deriving ( Show, Eq, Generic, Ord )++-- | Various articulatory features with a diacritic represenation in the IPA.+-- These can be combined with 'Segment's using the 'WithSegmentalFeature'+-- constructor of that type to produce a 'Segment' annotated with a given+-- articulatory feature, e.g.:+--+-- >>> toIPA $ WithSegmentalFeature (Length Long) (Vowel Close Front Unrounded)+-- Just (IPA "i\720") -- iː+data SegmentalFeature+    = Voicing Phonation+    | Length Length+    | SecondaryArticulation Segment+    | SuperScriptNumeric Int+    | Aspirated+    | MoreRounded+    | LessRounded+    | Advanced+    | Retracted+    | Centralized+    | MidCentralized+    | Compressed+    | Syllabic+    | NonSyllabic+    | Rhotacized+    | BreathyVoice+    | CreakyVoice+    | LinguoLabialized+    | Labialized+    | Palatalized+    | Velarized+    | Pharyngealized+    | Raised+    | Lowered+    | AdvancedTongueRoot+    | RetractedTongueRoot+    | Dentalized+    | Apical+    | Laminal+    | Nasalized+    | NasalRelease+    | LateralRelease+    | NoAudibleRelease+    deriving ( Show, Eq, Generic )++data Stress = Primary | Secondary+    deriving ( Show, Eq, Generic, Ord )++data SuprasegmentalFeature+    = LevelLexicalTone LevelTone -- ^ Level lexical tone+    | LevelLexicalToneDiacritic LevelTone+      -- ^ Level lexical tone, diactritic notation+    | LexicalToneContour ToneContour-- ^ Lexical tone as a contour+    | LexicalToneContourDiacritic ToneContour+      -- ^ Lexical tone as a contour, diacritic notation+    | Stress Stress+    | Break+    | Linking+    deriving ( Show, Eq, Generic )+
+ test/Main.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE PatternSynonyms #-}++-- |+module Main where++import           Data.Foldable      ( traverse_ )+import           Data.Maybe         ( isNothing )++import           Language.IPA       ( ReprIPA(toIPA', toIPA) )+import           Language.IPA.Types+                 ( Backness(..)+                 , Height(..)+                 , IPA(IPA)+                 , Length(..)+                 , Manner(..)+                 , Phonation(..)+                 , Place(..)+                 , Roundedness(..)+                 , Segment(..)+                 , SegmentalFeature(..)+                 , Sibilance(..)+                 , Stress(Primary)+                 , SuprasegmentalFeature(..)+                 , Syllable(..)+                 , ToneContour(..)+                 , pattern ImplosiveConsonant+                 , pattern PulmonicConsonant+                 )++import           Test.Hspec+                 ( describe+                 , hspec+                 , it+                 , shouldBe+                 , shouldSatisfy+                 )++{- HLINT ignore "Redundant do" -}+main :: IO ()+main = hspec $ do+    describe "Segments" $ do+        it "Rejects nonsense segments" $ do+            let phones = toIPA+                    <$> [ PulmonicConsonant Voiceless+                                            Uvular+                                            (Fricative Sibilant)+                        , PulmonicConsonant Voiced Velar Flap+                        , PulmonicConsonant Voiced Pharyngeal Plosive+                        , PulmonicConsonant Voiceless Glottal Trill+                        , Vowel NearClose Central Unrounded+                        , Vowel NearOpen Back Rounded+                        ]+            traverse_ (`shouldSatisfy` isNothing) phones++    describe "Segmental features" $ do+        it "Correctly makes 'Short' a noop" $ do+            let vcfu   = Vowel Close Front Unrounded+                i      = toIPA' vcfu+                iShort = toIPA' $ WithSegmentalFeature (Length Short) vcfu++            i `shouldBe` iShort++        it "Chains articulatory features" $ do+            let vowel       = Vowel Close Back Unrounded+                IPA chained = toIPA'+                    $ WithSegmentalFeature (Length OverLong)+                                           (WithSegmentalFeature Compressed+                                                                 vowel)+            chained `shouldBe` "ɯᵝːː"++    describe "Suprasegmentals" $ do+        it "Preserves individual segments and order" $ do+            let segments     = [ ImplosiveConsonant Voiced Bilabial+                               , Vowel CloseMid Back Unrounded+                               ]+                IPA syllable = toIPA' $ Syllable segments+            syllable `shouldBe` "ɓɤ"++    describe "Suprasegmental features" $ do+        it "Chains articulatory features" $ do+            let segments    = [ PulmonicConsonant Voiced Alveolar Plosive+                              , Vowel CloseMid Back Rounded+                              ]+                syllable    = Syllable segments+                tone        = LexicalToneContour Rising+                IPA chained = toIPA'+                    $ WithSuprasegmentalFeature --+                    (Stress Primary)+                    (WithSuprasegmentalFeature tone syllable)+            chained `shouldBe` "do˩˥ˈ"