module Language.Drasil.NounPhrase (
  -- * Types
  NounPhrase(..), NP,
  -- * Phrase Accessors
  atStartNP, atStartNP', titleizeNP, titleizeNP',
  -- * Constructors
  -- ** Common Noun Constructors
  cn, cn', cn'', cn''', cnICES, cnIES, cnIP, cnIS, cnIrr, cnUM,
  -- ** Proper Noun Constructors
  pn, pn', pn'', pn''', pnIrr,
  -- ** Noun Phrase Constructors
  nounPhrase, nounPhrase', nounPhrase'', nounPhraseSP, nounPhraseSent,
  -- * Combinators
  compoundPhrase,
  compoundPhrase', compoundPhrase'', compoundPhrase''', compoundPhraseP1,
  -- * Re-exported Types
  CapitalizationRule(..), PluralRule(..)
  ) where

import Data.Char (isLatin1, isLetter, toLower, toUpper)

import Language.Drasil.NounPhrase.Core -- uses whole module
import Language.Drasil.Sentence (Sentence((:+:), S, Ch, P), (+:+), TermCapitalization(..))

--Linguistically, nounphrase might not be the best name (yet!), but once
-- it is fleshed out and/or we do more with it, it will likely be a good fit

class NounPhrase n where
  -- | Retrieves singular form of term. Ex. "the quick brown fox".
  phraseNP :: n -> Sentence 
  -- | Retrieves plural form of term. Ex. "the quick brown foxes".
  pluralNP :: n -> PluralForm
    --Could replace plural string with a function.
  -- | Retrieves the singular form and applies a captalization 
  -- rule (usually capitalizes the first word) to produce a 'Sentence'.
  -- Ex. "The quick brown fox".
  sentenceCase :: n -> (NP -> Sentence) -> Capitalization 
    --Should this be replaced with a data type instead?
    --Data types should use functions to determine capitalization based
    -- on rules.
  -- | Retrieves the singular form and applies a captalization 
  -- rule (usually capitalizes all words) to produce a 'Sentence'.
  -- Ex. "The Quick Brown Fox".
  titleCase :: n -> (NP -> Sentence) -> Capitalization 

-- | Type synonym for 'Sentence'.
type Capitalization = Sentence
-- | Type synonym for 'String'.
type PluralString   = String

-- | Defines NP as a NounPhrase. 
-- Default capitalization rules for proper and common nouns
-- are 'CapFirst' for sentence case and 'CapWords' for title case.
-- Also accepts a 'Phrase' where the capitalization case may be specified.
instance NounPhrase NP where
  phraseNP :: NP -> PluralForm
phraseNP (ProperNoun n :: String
n _)           = String -> PluralForm
S String
n
  phraseNP (CommonNoun n :: String
n _ _)         = String -> PluralForm
S String
n
  phraseNP (Phrase n :: PluralForm
n _ _ _)           = PluralForm
n
  pluralNP :: NP -> PluralForm
pluralNP n :: NP
n@(ProperNoun _ p :: PluralRule
p)         = PluralForm -> PluralRule -> PluralForm
sPlur (NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
n) PluralRule
p
  pluralNP n :: NP
n@(CommonNoun _ p :: PluralRule
p _)       = PluralForm -> PluralRule -> PluralForm
sPlur (NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
n) PluralRule
p
  pluralNP (Phrase _ p :: PluralForm
p _ _)           = PluralForm
p
  sentenceCase :: NP -> (NP -> PluralForm) -> PluralForm
sentenceCase n :: NP
n@ProperNoun {}      _ = NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
n
  sentenceCase n :: NP
n@(CommonNoun _ _ r :: CapitalizationRule
r) f :: NP -> PluralForm
f = PluralForm -> CapitalizationRule -> PluralForm
cap (NP -> PluralForm
f NP
n) CapitalizationRule
r
  sentenceCase n :: NP
n@(Phrase _ _ r :: CapitalizationRule
r _)   f :: NP -> PluralForm
f = PluralForm -> CapitalizationRule -> PluralForm
cap (NP -> PluralForm
f NP
n) CapitalizationRule
r
  titleCase :: NP -> (NP -> PluralForm) -> PluralForm
titleCase n :: NP
n@ProperNoun {}         _ = NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
n
  titleCase n :: NP
n@CommonNoun {}         f :: NP -> PluralForm
f = PluralForm -> CapitalizationRule -> PluralForm
cap (NP -> PluralForm
f NP
n) CapitalizationRule
CapWords
  titleCase n :: NP
n@(Phrase _ _ _ r :: CapitalizationRule
r)      f :: NP -> PluralForm
f = PluralForm -> CapitalizationRule -> PluralForm
cap (NP -> PluralForm
f NP
n) CapitalizationRule
r
  
-- ===Constructors=== --
-- | Constructs a Proper Noun, it is always capitalized as written.
pn, pn', pn'', pn''' :: String -> NP
-- | Self plural.
pn :: String -> NP
pn    n :: String
n = String -> PluralRule -> NP
ProperNoun String
n PluralRule
SelfPlur
-- | Plural form simply adds "s" (ex. Henderson -> Hendersons).
pn' :: String -> NP
pn'   n :: String
n = String -> PluralRule -> NP
ProperNoun String
n PluralRule
AddS
-- | Plural form adds "e".
pn'' :: String -> NP
pn''  n :: String
n = String -> PluralRule -> NP
ProperNoun String
n PluralRule
AddE
-- | Plural form adds "es" (ex. Bush -> Bushes).
pn''' :: String -> NP
pn''' n :: String
n = String -> PluralRule -> NP
ProperNoun String
n PluralRule
AddES

-- | Constructs a 'ProperNoun' with a custom plural rule (using 'IrregPlur' from 'PluralRule').
-- First argument is the String representing the noun, second is the rule.
pnIrr :: String -> PluralRule -> NP
pnIrr :: String -> PluralRule -> NP
pnIrr = String -> PluralRule -> NP
ProperNoun

-- | Constructs a common noun which capitalizes the first letter of the first word
-- at the beginning of a sentence.
cn, cn', cn'', cn''' :: String -> NP
-- | Self plural.
cn :: String -> NP
cn    n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n PluralRule
SelfPlur CapitalizationRule
CapFirst
-- | Plural form simply adds "s" (ex. dog -> dogs).
cn' :: String -> NP
cn'   n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n PluralRule
AddS CapitalizationRule
CapFirst
-- | Plural form adds "e" (ex. formula -> formulae).
cn'' :: String -> NP
cn''  n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n PluralRule
AddE CapitalizationRule
CapFirst
-- | Plural form adds "es" (ex. bush -> bushes).
cn''' :: String -> NP
cn''' n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n PluralRule
AddES CapitalizationRule
CapFirst

-- | Constructs a common noun that pluralizes by dropping the last letter and adding an "ies"
-- ending (ex. body -> bodies).
cnIES :: String -> NP
cnIES :: String -> NP
cnIES n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n ((String -> String) -> PluralRule
IrregPlur (\x :: String
x -> String -> String
forall a. [a] -> [a]
init String
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ "ies")) CapitalizationRule
CapFirst

--FIXME: Shouldn't this just be drop one and add "ces"?
-- | Construct a common noun that pluralizes by dropping the last two letters and adding an 
-- "ices" ending (ex. matrix -> matrices).
cnICES :: String -> NP
cnICES :: String -> NP
cnICES n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n ((String -> String) -> PluralRule
IrregPlur (\x :: String
x -> String -> String
forall a. [a] -> [a]
init (String -> String
forall a. [a] -> [a]
init String
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ "ices")) CapitalizationRule
CapFirst

-- | Constructs a common noun that pluralizes by dropping the last two letters and adding
-- "es" (ex. analysis -> analyses).
cnIS :: String -> NP
cnIS :: String -> NP
cnIS n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n ((String -> String) -> PluralRule
IrregPlur (\x :: String
x -> String -> String
forall a. [a] -> [a]
init (String -> String
forall a. [a] -> [a]
init String
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ "es")) CapitalizationRule
CapFirst

-- | Constructs a common noun that pluralizes by dropping the last two letters and adding "a"
-- (ex. datum -> data).
cnUM :: String -> NP
cnUM :: String -> NP
cnUM n :: String
n = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n ((String -> String) -> PluralRule
IrregPlur (\x :: String
x -> String -> String
forall a. [a] -> [a]
init (String -> String
forall a. [a] -> [a]
init String
x) String -> String -> String
forall a. [a] -> [a] -> [a]
++ "a")) CapitalizationRule
CapFirst

-- | Constructs a common noun that allows you to specify the pluralization rule 
-- (as in 'pnIrr').
cnIP :: String -> PluralRule -> NP
cnIP :: String -> PluralRule -> NP
cnIP n :: String
n p :: PluralRule
p = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun String
n PluralRule
p CapitalizationRule
CapFirst

-- | Common noun that allows you to specify both the pluralization rule and the
-- capitalization rule for sentence case (if the noun is used at the beginning
-- of a sentence).
cnIrr :: String -> PluralRule -> CapitalizationRule -> NP
cnIrr :: String -> PluralRule -> CapitalizationRule -> NP
cnIrr = String -> PluralRule -> CapitalizationRule -> NP
CommonNoun 

-- | Creates a 'NP' with a given singular and plural form (as 'String's) that capitalizes the first
-- letter of the first word for sentence case.
nounPhrase :: String -> PluralString -> NP
nounPhrase :: String -> String -> NP
nounPhrase s :: String
s p :: String
p = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase (String -> PluralForm
S String
s) (String -> PluralForm
S String
p) CapitalizationRule
CapFirst CapitalizationRule
CapWords

-- | Similar to 'nounPhrase', but takes a specified capitalization rule for the sentence case.
nounPhrase' :: String -> PluralString -> CapitalizationRule -> NP
nounPhrase' :: String -> String -> CapitalizationRule -> NP
nounPhrase' s :: String
s p :: String
p c :: CapitalizationRule
c = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase (String -> PluralForm
S String
s) (String -> PluralForm
S String
p) CapitalizationRule
c CapitalizationRule
CapWords

-- | Custom noun phrase constructor that takes a singular form ('Sentence'), plural form ('Sentence'), 
-- sentence case capitalization rule, and title case capitalization rule.
nounPhrase'' :: Sentence -> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
nounPhrase'' :: PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
nounPhrase'' = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase

-- | For things that should not be pluralized (or are self-plural). Works like 'nounPhrase', but with
-- only the first argument.
nounPhraseSP :: String -> NP
nounPhraseSP :: String -> NP
nounPhraseSP s :: String
s = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase (String -> PluralForm
S String
s) (String -> PluralForm
S String
s) CapitalizationRule
CapFirst CapitalizationRule
CapWords

-- | Similar to nounPhrase, except it only accepts one 'Sentence'. Used for Requirements,
-- Assumptions, LikelyChanges, etc. to allow for referencing.
-- Plural case is just 'AddS'.
nounPhraseSent :: Sentence -> NP
nounPhraseSent :: PluralForm -> NP
nounPhraseSent s :: PluralForm
s = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase PluralForm
s (PluralForm -> PluralRule -> PluralForm
sPlur PluralForm
s PluralRule
AddS) CapitalizationRule
CapFirst CapitalizationRule
CapWords

-- | Combine two noun phrases. The singular form becomes 'phrase' from t1 followed
-- by 'phrase' of t2. The plural becomes 'phrase' of t1 followed by 'plural' of t2.
-- Uses standard 'CapFirst' sentence case and 'CapWords' title case.
-- For example: @compoundPhrase system constraint@ will have singular form
-- "system constraint" and plural "system constraints".
compoundPhrase :: (NounPhrase a, NounPhrase b) => a -> b -> NP
compoundPhrase :: a -> b -> NP
compoundPhrase t1 :: a
t1 t2 :: b
t2 = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase 
  (a -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP a
t1 PluralForm -> PluralForm -> PluralForm
+:+ b -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP b
t2) (a -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP a
t1 PluralForm -> PluralForm -> PluralForm
+:+ b -> PluralForm
forall n. NounPhrase n => n -> PluralForm
pluralNP b
t2) CapitalizationRule
CapFirst CapitalizationRule
CapWords
  
-- | Similar to 'compoundPhrase', but the sentence case is the same
-- as the title case ('CapWords').
compoundPhrase' :: NP -> NP -> NP
compoundPhrase' :: NP -> NP -> NP
compoundPhrase' t1 :: NP
t1 t2 :: NP
t2 = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase
  (NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
t1 PluralForm -> PluralForm -> PluralForm
+:+ NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
t2) (NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
t1 PluralForm -> PluralForm -> PluralForm
+:+ NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
pluralNP NP
t2) CapitalizationRule
CapWords CapitalizationRule
CapWords

-- | Similar to 'compoundPhrase'', but accepts two functions that will be used to
-- construct the plural form. For example,
-- @compoundPhrase'' plural phrase system constraint@ would have the plural
-- form "systems constraint". 
compoundPhrase'' :: (NP -> Sentence) -> (NP -> Sentence) -> NP -> NP -> NP
compoundPhrase'' :: (NP -> PluralForm) -> (NP -> PluralForm) -> NP -> NP -> NP
compoundPhrase'' f1 :: NP -> PluralForm
f1 f2 :: NP -> PluralForm
f2 t1 :: NP
t1 t2 :: NP
t2 = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase
  (NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
t1 PluralForm -> PluralForm -> PluralForm
+:+ NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
t2) (NP -> PluralForm
f1 NP
t1 PluralForm -> PluralForm -> PluralForm
+:+ NP -> PluralForm
f2 NP
t2) CapitalizationRule
CapWords CapitalizationRule
CapWords

--More primes might not be wanted but fixes two issues
-- pluralization problem with software requirements specification (Documentation.hs)
-- SWHS program not being about to use a compound to create the NamedChunk
-- | Similar to 'compoundPhrase', but used when you need a special function applied 
-- to the first term of both singular and pluralcases (eg. short or plural).
compoundPhrase''' :: (NP -> Sentence) -> NP -> NP -> NP
compoundPhrase''' :: (NP -> PluralForm) -> NP -> NP -> NP
compoundPhrase''' f1 :: NP -> PluralForm
f1 t1 :: NP
t1 t2 :: NP
t2 = PluralForm
-> PluralForm -> CapitalizationRule -> CapitalizationRule -> NP
Phrase 
  (NP -> PluralForm
f1 NP
t1 PluralForm -> PluralForm -> PluralForm
+:+ NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP NP
t2) (NP -> PluralForm
f1 NP
t1 PluralForm -> PluralForm -> PluralForm
+:+ NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
pluralNP NP
t2) CapitalizationRule
CapFirst CapitalizationRule
CapWords

--For Data.Drasil.Documentation
-- | Similar to 'compoundPhrase', but pluralizes the first 'NP' for both singular and plural cases.
compoundPhraseP1 :: NP -> NP -> NP
compoundPhraseP1 :: NP -> NP -> NP
compoundPhraseP1 = (NP -> PluralForm) -> NP -> NP -> NP
compoundPhrase''' NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
pluralNP

-- === Helpers === 
-- | Helper function for getting the sentence case of a noun phrase.
atStartNP, atStartNP' :: NounPhrase n => n -> Capitalization
-- | Singular sentence case.
atStartNP :: n -> PluralForm
atStartNP  n :: n
n = n -> (NP -> PluralForm) -> PluralForm
forall n. NounPhrase n => n -> (NP -> PluralForm) -> PluralForm
sentenceCase n
n NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP
-- | Plural sentence case.
atStartNP' :: n -> PluralForm
atStartNP' n :: n
n = n -> (NP -> PluralForm) -> PluralForm
forall n. NounPhrase n => n -> (NP -> PluralForm) -> PluralForm
sentenceCase n
n NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
pluralNP

-- | Helper function for getting the title case of a noun phrase.
titleizeNP, titleizeNP' :: NounPhrase n => n -> Capitalization
-- | Singular title case.
titleizeNP :: n -> PluralForm
titleizeNP  n :: n
n = n -> (NP -> PluralForm) -> PluralForm
forall n. NounPhrase n => n -> (NP -> PluralForm) -> PluralForm
titleCase n
n NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
phraseNP
-- | Plural title case.
titleizeNP' :: n -> PluralForm
titleizeNP' n :: n
n = n -> (NP -> PluralForm) -> PluralForm
forall n. NounPhrase n => n -> (NP -> PluralForm) -> PluralForm
titleCase n
n NP -> PluralForm
forall n. NounPhrase n => n -> PluralForm
pluralNP

-- DO NOT EXPORT --                
-- | Pluralization helper function.
sPlur :: Sentence -> PluralRule -> Sentence
sPlur :: PluralForm -> PluralRule -> PluralForm
sPlur (S s :: String
s) AddS = String -> PluralForm
S (String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ "s")
sPlur (S s :: String
s) AddE = String -> PluralForm
S (String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ "e")
sPlur s :: PluralForm
s@(S _) AddES = PluralForm -> PluralRule -> PluralForm
sPlur (PluralForm -> PluralRule -> PluralForm
sPlur PluralForm
s PluralRule
AddE) PluralRule
AddS
sPlur s :: PluralForm
s@(S _) SelfPlur = PluralForm
s
sPlur (S sts :: String
sts) (IrregPlur f :: String -> String
f) = String -> PluralForm
S (String -> PluralForm) -> String -> PluralForm
forall a b. (a -> b) -> a -> b
$ String -> String
f String
sts --Custom pluralization
sPlur (a :: PluralForm
a :+: b :: PluralForm
b) pt :: PluralRule
pt = PluralForm
a PluralForm -> PluralForm -> PluralForm
:+: PluralForm -> PluralRule -> PluralForm
sPlur PluralForm
b PluralRule
pt
sPlur a :: PluralForm
a _ = String -> PluralForm
S "MISSING PLURAL FOR:" PluralForm -> PluralForm -> PluralForm
+:+ PluralForm
a

-- | Capitalization helper function given a sentence.
cap :: Sentence -> CapitalizationRule -> Sentence
cap :: PluralForm -> CapitalizationRule -> PluralForm
cap _ (Replace s :: PluralForm
s) = PluralForm
s
cap (S (s :: Char
s:ss :: String
ss)) CapFirst = String -> PluralForm
S (Char -> Char
toUpper Char
s Char -> String -> String
forall a. a -> [a] -> [a]
: String
ss)
cap (S s :: String
s)      CapWords = String -> (String -> String) -> (String -> String) -> PluralForm
capString String
s String -> String
capFirstWord String -> String
capWords
cap (P symb :: Symbol
symb :+: x :: PluralForm
x) CapFirst = Symbol -> PluralForm
P Symbol
symb PluralForm -> PluralForm -> PluralForm
:+: PluralForm
x -- TODO: See why the Table of Symbols uses the CapWords case instead of CapFirst for items of the form:
cap (P symb :: Symbol
symb :+: x :: PluralForm
x) CapWords = Symbol -> PluralForm
P Symbol
symb PluralForm -> PluralForm -> PluralForm
:+: PluralForm
x -- "x-component". Instead, it displays as "x-Component". Using a temp fix for now by ignoring everything after a P symbol.
cap (Ch style :: SentenceStyle
style _ s :: UID
s) CapFirst = SentenceStyle -> TermCapitalization -> UID -> PluralForm
Ch SentenceStyle
style TermCapitalization
CapF UID
s
cap (Ch style :: SentenceStyle
style _ s :: UID
s) CapWords = SentenceStyle -> TermCapitalization -> UID -> PluralForm
Ch SentenceStyle
style TermCapitalization
CapW UID
s
cap (S s1 :: String
s1 :+: S s2 :: String
s2 :+: x :: PluralForm
x) r :: CapitalizationRule
r = PluralForm -> CapitalizationRule -> PluralForm
cap (String -> PluralForm
S (String
s1 String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s2) PluralForm -> PluralForm -> PluralForm
:+: PluralForm
x) CapitalizationRule
r
cap (s1 :: PluralForm
s1 :+: s2 :: PluralForm
s2) CapWords = PluralForm -> CapitalizationRule -> PluralForm
cap PluralForm
s1 CapitalizationRule
CapWords PluralForm -> PluralForm -> PluralForm
+:+ PluralForm -> PluralForm
capTail PluralForm
s2 --FIXME: why does this use +:+ instead of :+:? Could unwords be a problem?
cap (s1 :: PluralForm
s1 :+: s2 :: PluralForm
s2) CapFirst = PluralForm -> CapitalizationRule -> PluralForm
cap PluralForm
s1 CapitalizationRule
CapFirst PluralForm -> PluralForm -> PluralForm
:+: PluralForm
s2
cap a :: PluralForm
a _ = PluralForm
a

-- | Helper for 'cap' and for capitalizing the end of a 'Sentence' (assumes 'CapWords').
capTail :: Sentence -> Sentence
capTail :: PluralForm -> PluralForm
capTail (S s :: String
s) = String -> (String -> String) -> (String -> String) -> PluralForm
capString String
s String -> String
capWords String -> String
capWords
capTail (Ch style :: SentenceStyle
style _ s :: UID
s) = SentenceStyle -> TermCapitalization -> UID -> PluralForm
Ch SentenceStyle
style TermCapitalization
CapW UID
s
capTail (a :: PluralForm
a :+: b :: PluralForm
b) = PluralForm -> PluralForm
capTail PluralForm
a PluralForm -> PluralForm -> PluralForm
:+: PluralForm -> PluralForm
capTail PluralForm
b
capTail x :: PluralForm
x = PluralForm
x

-- | Helper for capitalizing a string.
capString :: String -> (String -> String) -> (String -> String) -> Sentence 
capString :: String -> (String -> String) -> (String -> String) -> PluralForm
capString s :: String
s f :: String -> String
f g :: String -> String
g = String -> PluralForm
S (String -> PluralForm)
-> ([String] -> String) -> [String] -> PluralForm
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> String -> String
findHyph String -> String
g (String -> String) -> ([String] -> String) -> [String] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> String
unwords ([String] -> PluralForm) -> [String] -> PluralForm
forall a b. (a -> b) -> a -> b
$ [String] -> [String]
process (String -> [String]
words String
s)
  where
    process :: [String] -> [String]
process (x :: String
x:xs :: [String]
xs) = String -> String
f String
x String -> [String] -> [String]
forall a. a -> [a] -> [a]
: (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
g [String]
xs
    process []     = []

-- | Finds hyphens in a 'String' and applies capitalization to words after a hyphen.
findHyph :: (String -> String) -> String -> String
findHyph :: (String -> String) -> String -> String
findHyph _ "" = ""
findHyph _ [x :: Char
x] = [Char
x]
findHyph f :: String -> String
f (x :: Char
x:xs :: String
xs)
  | Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '-'  = '-' Char -> String -> String
forall a. a -> [a] -> [a]
: (String -> String) -> String -> String
findHyph String -> String
f (String -> String
f String
xs)
  | Bool
otherwise = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: (String -> String) -> String -> String
findHyph String -> String
f String
xs

-- | Capitalize first word of a 'String'. Does not ignore prepositions, articles, or conjunctions (intended for beginning of a phrase/sentence).
capFirstWord :: String -> String
capFirstWord :: String -> String
capFirstWord "" = ""
capFirstWord w :: String
w@(c :: Char
c:cs :: String
cs)
  | Bool -> Bool
not (Char -> Bool
isLetter Char
c) = String
w
  | Bool -> Bool
not (Char -> Bool
isLatin1 Char
c) = String
w
  | Bool
otherwise        = Char -> Char
toUpper Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String
cs

-- | Capitalize all words of a 'String' (unless they are prepositions, articles, or conjunctions).
capWords :: String -> String
capWords :: String -> String
capWords "" = ""
capWords w :: String
w@(c :: Char
c:cs :: String
cs)
  | Bool -> Bool
not (Char -> Bool
isLetter Char
c)   = String
w
  | Bool -> Bool
not (Char -> Bool
isLatin1 Char
c)   = String
w
  | String
w String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
doNotCaps = Char -> Char
toLower Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String
cs
  | Bool
otherwise          = Char -> Char
toUpper Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String
cs

-- | Words that should not be capitalized in a title (prepositions, articles, or conjunctions).
doNotCaps :: [String]
doNotCaps :: [String]
doNotCaps = ["a", "an", "the", "at", "by", "for", "in", "of",
  "on", "to", "up", "and", "as", "but", "or", "nor"] --Ref http://grammar.yourdictionary.com