packages feed

owoify-hs (empty) → 1.0.0.0

raw patch · 13 files changed

+1065/−0 lines, 13 filesdep +HUnitdep +basedep +owoify-hssetup-changed

Dependencies added: HUnit, base, owoify-hs, random, regex, regex-with-pcre, text

Files

+ ChangeLog.md view
@@ -0,0 +1,4 @@+# Changelog for owoify-hs++## 1.0.0.0+- Initial release.
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2021 Chehui Chou++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,114 @@+# owoify-hs++Turning your worst nightmare into a Haskell package.++[![Haskell CI](https://github.com/deadshot465/owoify-hs/actions/workflows/haskell.yml/badge.svg)](https://github.com/deadshot465/owoify-hs/actions/workflows/haskell.yml)++This is a Haskell port of [mohan-cao's owoify-js](https://github.com/mohan-cao/owoify-js), which will help you turn any string into nonsensical babyspeak similar to LeafySweet's infamous Chrome extension.++Just like my other Owoify ports, three levels of owoness are available. Each level has been implemented using a discriminated union, so it should work like enums people are used to seeing in other imperative languages.++1. **owo (default)**: The most vanilla one.+2. **uwu**: The moderate one.+3. **uvu**: Litewawwy unweadabwal.+Please refer to the original [owoify-js repository](https://github.com/mohan-cao/owoify-js) for more information.++Functional programming is fun, so of course I'll port it to FP languages. 😜++Also I love Haskell. ❤️++## Reason for development++Because I love Haskell and currently Hackage doesn't have any owoify package yet. Also PureScript is influenced by Haskell and they have nearly the same syntax, so it doesn't make sense to have a PureScript port while not having a Haskell port.++The difference is that owoify-hs makes use of Haskell's lazy evaluation. `Data.Text.Lazy` is used instead of `Data.Text`. This *possibly* solved stack overflow problem with purescript-owoify when owoifying very long texts (i.e. chapter 1-20 of Tolstoy's *War and Peace*). The reason for using `Data.Text.Lazy.Text` instead of the good old `String` is because `Data.Text.Lazy.Text` and the eager version `Data.Text.Text` are meant for handling Unicode texts.++Having said that, you would need to do `Data.Text.Lazy.pack <text>` before passing the text to the `owoify` function.++## Install instructions++Add `owoify-hs` to your `package.yaml` in your Stack project:++```yaml+dependencies:+- owoify-hs == 1.0.0+```++If you are using Cabal, add the package the usual way.++```bash+cabal install owoify-hs+```++## Usage++owoify-hs is implemented as a single function. You will need to provide a source string and a owoify level. Due to random number generator and regular expressions, the result will be inside `IO` monad, so you will also need to handle that.++```haskell+module MyAwesomeModule where++import Prelude++import Data.Owoify.Owoify (OwoifyLevel(..), owoify)+import Data.Text.Lazy (pack, unpack)++main :: IO ()+main = do+  owoResult <- owoify (pack "This is the string to owo! Kinda cute, isn't it?") Owo+  uvuResult <- owoify (pack "This is the string to owo! Kinda cute, isn't it?") Uvu+  -- `print` won't print out Unicode emojis correctly.+  putStrLn $ unpack owoResult+  putStrLn $ unpack uvuResult++-- Possible outputs:+-- This is teh stwing two owo! Kinda cute, isn't it?+-- fwis is teh stwing two owowouvu Kinda cuteowo isn't it?+```++## Disclaimer++As usual, I'm writing this package for both practicing and bots' needs. Performance is **NOT** guaranteed.++That being said, `Data.Text.Lazy` is used, so it should be able to handle both short texts and long texts without any problem.++## See also++- [owoify-js](https://github.com/mohan-cao/owoify-js) - The original owoify-js repository.+- [Owoify.Net](https://www.nuget.org/packages/Owoify.Net/1.0.1) - The C# port of Owoify written by me.+- [Owoify++](https://github.com/deadshot465/OwoifyCpp) - The C++ header-only port of Owoify written by me.+- [owoify_rs](https://crates.io/crates/owoify_rs) - The Rust port of Owoify written by me.+- [owoify-py](https://pypi.org/project/owoify-py/) - The Python port of Owoify written by me.+- [owoify_dart](https://pub.dev/packages/owoify_dart) - The Dart port of Owoify written by me.+- [owoify_rb](https://rubygems.org/gems/owoify_rb) - The Ruby port of Owoify written by me.+- [owoify-go](https://github.com/deadshot465/owoify-go) - The Go port of Owoify written by me.+- [owoifySwift](https://github.com/deadshot465/OwoifySwift) - The Swift port of Owoify written by me.+- [owoifyKt](https://github.com/deadshot465/owoifyKt) - The Kotlin port of Owoify written by me.+- [owoify_ex](https://github.com/deadshot465/owoify_ex) - The Elixir port of Owoify written by me.+- [owoify_cr](https://github.com/deadshot465/owoify_cr) - The Crystal port of Owoify written by me.+- [owoifynim](https://github.com/deadshot465/owoifynim) - The Nim port of Owoify written by me.+- [owoify-clj](https://clojars.org/net.clojars.deadshot465/owoify-clj) - The Clojure port of Owoify written by me.+- [purescript-owoify](https://github.com/deadshot465/purescript-owoify) - The PureScript port of Owoify written by me.++## License++MIT License++Copyright (c) 2021 Chehui Chou++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/Main.hs view
@@ -0,0 +1,13 @@+module Main where++import Data.Owoify.Owoify (owoify, OwoifyLevel(..))+import Data.Text.Lazy (pack, unpack)++main :: IO ()+main = do+  owoResult <- owoify (pack "This is the string to owo! Kinda cute, isn't it?") Owo+  uvuResult <- owoify (pack "This is the string to owo! Kinda cute, isn't it?") Uvu+  helloWorld <- owoify (pack "Hello World") Owo+  putStrLn $ unpack owoResult+  putStrLn $ unpack uvuResult+  putStrLn $ unpack helloWorld
+ owoify-hs.cabal view
@@ -0,0 +1,74 @@+cabal-version:      1.12+name:               owoify-hs+version:            1.0.0.0+license:            MIT+license-file:       LICENSE+copyright:          2021 Chehui Chou+maintainer:         tetsuki.syu1315@gmail.com+author:             Chehui Chou+homepage:           https://github.com/deadshot465/owoify-hs#readme+bug-reports:        https://github.com/deadshot465/owoify-hs/issues+synopsis:           Turn any English text into nonsensical babyspeaks.+description:+    Please see the README on GitHub at <https://github.com/deadshot465/owoify-hs#readme>++category:           Text+build-type:         Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+    type:     git+    location: https://github.com/deadshot465/owoify-hs++library+    exposed-modules:+        Data.Owoify.Internal.Data.Mappings+        Data.Owoify.Internal.Data.Presets+        Data.Owoify.Internal.Entity.Word+        Data.Owoify.Internal.Parser.OwoifyParser+        Data.Owoify.Internal.Util.Interleave+        Data.Owoify.Owoify++    hs-source-dirs:   src+    other-modules:    Paths_owoify_hs+    default-language: Haskell2010+    build-depends:+        HUnit >=1.6.2.0 && <1.7,+        base >=4.7 && <5,+        random >=1.2.0 && <1.3,+        regex ==1.1.0.0,+        regex-with-pcre ==1.1.0.0,+        text >=1.2.4 && <1.3++executable owoify-hs-exe+    main-is:          Main.hs+    hs-source-dirs:   app+    other-modules:    Paths_owoify_hs+    default-language: Haskell2010+    ghc-options:      -threaded -rtsopts -with-rtsopts=-N+    build-depends:+        HUnit >=1.6.2.0 && <1.7,+        base >=4.7 && <5,+        owoify-hs -any,+        random >=1.2.0 && <1.3,+        regex ==1.1.0.0,+        regex-with-pcre ==1.1.0.0,+        text >=1.2.4 && <1.3++test-suite owoify-hs-test+    type:             exitcode-stdio-1.0+    main-is:          Spec.hs+    hs-source-dirs:   test+    other-modules:    Paths_owoify_hs+    default-language: Haskell2010+    ghc-options:      -threaded -rtsopts -with-rtsopts=-N+    build-depends:+        HUnit >=1.6.2.0 && <1.7,+        base >=4.7 && <5,+        owoify-hs -any,+        random >=1.2.0 && <1.3,+        regex ==1.1.0.0,+        regex-with-pcre ==1.1.0.0,+        text >=1.2.4 && <1.3
+ src/Data/Owoify/Internal/Data/Mappings.hs view
@@ -0,0 +1,441 @@+module Data.Owoify.Internal.Data.Mappings+  ( mapBracketsToStartrails+  , mapConsonantRToConsonantW+  , mapDeadToDed+  , mapEwToUwu+  , mapFiToFwi+  , mapFucToFwuc+  , mapHahaToHeheXd+  , mapHeyToHay+  , mapLeToWal+  , mapLlToWw+  , mapLOrROToWo+  , mapLyToWy+  , mapMeToMwe+  , mapMomToMwom+  , mapNrToNw+  , mapNVowelTToNd+  , mapNVowelToNy+  , mapOldToOwld+  , mapOlToOwl+  , mapOToOwo+  , mapOveToUv+  , mapOverToOwor+  , mapPeriodCommaExclamationSemicolonToKaomojis+  , mapPleToPwe+  , mapPoiToPwoi+  , mapReadToWead+  , mapROrLToW+  , mapRyToWwy+  , mapSpecificConsonantsLeToLetterAndWal+  , mapSpecificConsonantsOToLetterAndWo+  , mapThatToDat+  , mapThToF+  , mapTheToTeh+  , mapTimeToTim+  , mapVerToWer+  , mapVeToWe+  , mapVOrWLeToWal+  , mapVowelOrRExceptOLToWl+  , mapWorseToWose+  , mapYouToU+  )+  where++import Prelude++import Data.Owoify.Internal.Entity.Word (innerReplace, innerReplaceWithFuncMultiple, innerReplaceWithFuncSingle, InnerWord)+import Data.Text.Lazy (Text, pack, toUpper)+import Text.RE.PCRE.Text.Lazy (compileRegex, RE)+import System.Random.Stateful (applyAtomicGen, uniformR)+import System.Random (getStdRandom, Random (randomR))++compileDefaultOptionRegex :: String -> IO RE+compileDefaultOptionRegex = compileRegex++oToOwo :: IO RE+oToOwo = compileDefaultOptionRegex "o"++ewToUwu :: IO RE+ewToUwu = compileDefaultOptionRegex "ew"++heyToHay :: IO RE+heyToHay = compileDefaultOptionRegex "([Hh])ey"++deadToDedUpper :: IO RE+deadToDedUpper = compileDefaultOptionRegex "Dead"++deadToDedLower :: IO RE+deadToDedLower = compileDefaultOptionRegex "dead"++nVowelTToNd :: IO RE+nVowelTToNd = compileDefaultOptionRegex "n[aeiou]*t"++readToWeadUpper :: IO RE+readToWeadUpper = compileDefaultOptionRegex "Read"++readToWeadLower :: IO RE+readToWeadLower = compileDefaultOptionRegex "read"++bracketsToStartrailsFore :: IO RE+bracketsToStartrailsFore = compileDefaultOptionRegex "[({<]"++bracketsToStartrailsRear :: IO RE+bracketsToStartrailsRear = compileDefaultOptionRegex "[)}>]"++periodCommaExclamationSemicolonToKaomojisFirst :: IO RE+periodCommaExclamationSemicolonToKaomojisFirst = compileDefaultOptionRegex "[.,](?![0-9])"++periodCommaExclamationSemicolonToKaomojisSecond :: IO RE+periodCommaExclamationSemicolonToKaomojisSecond = compileDefaultOptionRegex "[!;]+"++thatToDatUpper :: IO RE+thatToDatUpper = compileDefaultOptionRegex "That"++thatToDatLower :: IO RE+thatToDatLower = compileDefaultOptionRegex "that"++thToFUpper :: IO RE+thToFUpper = compileDefaultOptionRegex "TH(?!E)"++thToFLower :: IO RE+thToFLower = compileDefaultOptionRegex "[Tt]h(?![Ee])"++leToWal :: IO RE+leToWal = compileDefaultOptionRegex "le$"++veToWeUpper :: IO RE+veToWeUpper = compileDefaultOptionRegex "Ve"++veToWeLower :: IO RE+veToWeLower = compileDefaultOptionRegex "ve"++ryToWwy :: IO RE+ryToWwy = compileDefaultOptionRegex "ry"++rOrLToWUpper :: IO RE+rOrLToWUpper = compileDefaultOptionRegex "(?:R|L)"++rOrLToWLower :: IO RE+rOrLToWLower = compileDefaultOptionRegex "(?:r|l)"++llToWw :: IO RE+llToWw = compileDefaultOptionRegex "ll"++vowelOrRExceptOLToWlUpper :: IO RE+vowelOrRExceptOLToWlUpper = compileDefaultOptionRegex "[AEIUR]([lL])$"++vowelOrRExceptOLToWlLower :: IO RE+vowelOrRExceptOLToWlLower = compileDefaultOptionRegex "[aeiur]l$"++oldToOwldUpper :: IO RE+oldToOwldUpper = compileDefaultOptionRegex "OLD"++oldToOwldLower :: IO RE+oldToOwldLower = compileDefaultOptionRegex "([Oo])ld"++olToOwlUpper :: IO RE+olToOwlUpper = compileDefaultOptionRegex "OL"++olToOwlLower :: IO RE+olToOwlLower = compileDefaultOptionRegex "([Oo])l"++lOrROToWoUpper :: IO RE+lOrROToWoUpper = compileDefaultOptionRegex "[LR]([oO])"++lOrROToWoLower :: IO RE+lOrROToWoLower = compileDefaultOptionRegex "[lr]o"++specificConsonantsOToLetterAndWoUpper :: IO RE+specificConsonantsOToLetterAndWoUpper = compileDefaultOptionRegex "([BCDFGHJKMNPQSTXYZ])([oO])"++specificConsonantsOToLetterAndWoLower :: IO RE+specificConsonantsOToLetterAndWoLower = compileDefaultOptionRegex "([bcdfghjkmnpqstxyz])o"++vOrWLeToWal :: IO RE+vOrWLeToWal = compileDefaultOptionRegex "[vw]le"++fiToFwiUpper :: IO RE+fiToFwiUpper = compileDefaultOptionRegex "FI"++fiToFwiLower :: IO RE+fiToFwiLower = compileDefaultOptionRegex "([Ff])i"++verToWer :: IO RE+verToWer = compileDefaultOptionRegex "([Vv])er"++poiToPwoi :: IO RE+poiToPwoi = compileDefaultOptionRegex "([Pp])oi"++specificConsonantsLeToLetterAndWal :: IO RE+specificConsonantsLeToLetterAndWal = compileDefaultOptionRegex "([DdFfGgHhJjPpQqRrSsTtXxYyZz])le$"++consonantRToConsonantW :: IO RE+consonantRToConsonantW = compileDefaultOptionRegex "([BbCcDdFfGgKkPpQqSsTtWwXxZz])r"++lyToWyUpper :: IO RE+lyToWyUpper = compileDefaultOptionRegex "Ly"++lyToWyLower :: IO RE+lyToWyLower = compileDefaultOptionRegex "ly"++pleToPwe :: IO RE+pleToPwe = compileDefaultOptionRegex "([Pp])le"++nrToNwUpper :: IO RE+nrToNwUpper = compileDefaultOptionRegex "NR"++nrToNwLower :: IO RE+nrToNwLower = compileDefaultOptionRegex "nr"++funcToFwuc :: IO RE+funcToFwuc = compileDefaultOptionRegex "([Ff])uc"++momToMwom :: IO RE+momToMwom = compileDefaultOptionRegex "([Mm])om"++meToMwe :: IO RE+meToMwe = compileDefaultOptionRegex "([Mm])e"++nVowelToNyFirst :: IO RE+nVowelToNyFirst = compileDefaultOptionRegex "n([aeiou])"++nVowelToNySecond :: IO RE+nVowelToNySecond = compileDefaultOptionRegex "N([aeiou])"++nVowelToNyThird :: IO RE+nVowelToNyThird = compileDefaultOptionRegex "N([AEIOU])"++oveToUvUpper :: IO RE+oveToUvUpper = compileDefaultOptionRegex "OVE"++oveToUvLower :: IO RE+oveToUvLower = compileDefaultOptionRegex "ove"++hahaToHeheXd :: IO RE+hahaToHeheXd = compileDefaultOptionRegex "\\b(ha|hah|heh|hehe)+\\b"++theToTeh :: IO RE+theToTeh = compileDefaultOptionRegex "\\b([Tt])he\\b"++youToUUpper :: IO RE+youToUUpper = compileDefaultOptionRegex "\\bYou\\b"++youToULower :: IO RE+youToULower = compileDefaultOptionRegex "\\byou\\b"++timeToTim :: IO RE+timeToTim = compileDefaultOptionRegex "\\b([Tt])ime\\b"++overToOwor :: IO RE+overToOwor = compileDefaultOptionRegex "([Oo])ver"++worseToWose :: IO RE+worseToWose = compileDefaultOptionRegex "([Ww])orse"++faces :: [Text]+faces = pack <$>+  [ "(・`ω´・)"+  , ";;w;;"+  , "owo"+  , "UwU"+  , ">w<"+  , "^w^"+  , "(* ^ ω ^)"+  , "(⌒ω⌒)"+  , "ヽ(*・ω・)ノ"+  , "(o´∀`o)"+  ,"(o・ω・o)"+  , "\(^▽^)/"+  , "(*^ω^)"+  , "(◕‿◕✿)"+  , "(◕ᴥ◕)"+  , "ʕ•ᴥ•ʔ"+  , "ʕ→ᴥ←ʔ"+  , "(*^.^*)"+  , "(。♥‿♥。)"+  , "OwO"+  , "uwu"+  , "uvu"+  , "UvU"+  , "(* ̄з ̄)"+  , "(つ✧ω✧)つ"+  , "(/ =ω=)/"+  , "(╯°□°)╯︵ ┻━┻"+  ,"┬─┬ ノ( ゜-゜ノ)"+  , "¯\\_(ツ)_/¯"+  ]++mapOToOwo :: InnerWord -> IO InnerWord+mapOToOwo word = do+  n <- getStdRandom (randomR (0, 1)) :: IO Int+  let emoji = if n > 0 then pack "owo" else pack "o"+  re <- oToOwo+  pure $ innerReplace word re emoji False++mapEwToUwu :: InnerWord -> IO InnerWord+mapEwToUwu word = ewToUwu >>= \re -> pure $ innerReplace word re (pack "uwu") False++mapHeyToHay :: InnerWord -> IO InnerWord+mapHeyToHay word = heyToHay >>= \re -> pure $ innerReplace word re (pack "$1ay") False++mapDeadToDed :: InnerWord -> IO InnerWord+mapDeadToDed word = do+  w <- deadToDedUpper >>= \re -> pure $ innerReplace word re (pack "Ded") False+  deadToDedLower >>= \re -> pure $ innerReplace w re (pack "ded") False++mapNVowelTToNd :: InnerWord -> IO InnerWord+mapNVowelTToNd word = nVowelTToNd >>= \re -> pure $ innerReplace word re (pack "nd") False++mapReadToWead :: InnerWord -> IO InnerWord+mapReadToWead word = do+  w <- readToWeadUpper >>= \re -> pure $ innerReplace word re (pack "Wead") False+  readToWeadLower >>= \re -> pure $ innerReplace w re (pack "wead") False++mapBracketsToStartrails :: InnerWord -> IO InnerWord+mapBracketsToStartrails word = do+  w <- bracketsToStartrailsFore >>= \re -> pure $ innerReplace word re (pack "。・:*:・゚★,。・:*:・゚☆") False+  bracketsToStartrailsRear >>= \re -> pure $ innerReplace w re (pack "☆゚・:*:・。,★゚・:*:・。") False++mapPeriodCommaExclamationSemicolonToKaomojis :: InnerWord -> IO InnerWord+mapPeriodCommaExclamationSemicolonToKaomojis word = do+  n <- getStdRandom (randomR (0, length faces - 1)) :: IO Int+  let face = faces !! n+  let w = periodCommaExclamationSemicolonToKaomojisFirst >>= \re -> pure $ innerReplaceWithFuncSingle word re (const face) False+  n' <- getStdRandom (randomR (0, length faces - 1)) :: IO Int+  let face' = faces !! n'+  let re' = periodCommaExclamationSemicolonToKaomojisSecond+  (\r w' ->+    innerReplaceWithFuncSingle w' r (const face') False)+    <$> re'+    <*> w++mapThatToDat :: InnerWord -> IO InnerWord+mapThatToDat word = do+  w <- thatToDatLower >>= \re -> pure $ innerReplace word re (pack "dat") False+  thatToDatUpper >>= \re -> pure $ innerReplace w re (pack "Dat") False++mapThToF :: InnerWord -> IO InnerWord+mapThToF word = do+  w <- thToFLower >>= \re -> pure $ innerReplace word re (pack "f") False+  thToFUpper >>= \re -> pure $ innerReplace w re (pack "F") False++mapLeToWal :: InnerWord -> IO InnerWord+mapLeToWal word = leToWal >>= \re -> pure $ innerReplace word re (pack "wal") False++mapVeToWe :: InnerWord -> IO InnerWord+mapVeToWe word = do+  w <- veToWeLower >>= \re -> pure $ innerReplace word re (pack "we") False+  veToWeUpper >>= \re -> pure $ innerReplace w re (pack "We") False++mapRyToWwy :: InnerWord -> IO InnerWord+mapRyToWwy word = ryToWwy >>= \re -> pure $ innerReplace word re (pack "wwy") False++mapROrLToW :: InnerWord -> IO InnerWord+mapROrLToW word = do+  w <- rOrLToWLower >>= \re -> pure $ innerReplace word re (pack "w") False+  rOrLToWUpper >>= \re -> pure $ innerReplace w re (pack "W") False++mapLlToWw :: InnerWord -> IO InnerWord+mapLlToWw word = llToWw >>= \re -> pure $ innerReplace word re (pack "ww") False++mapVowelOrRExceptOLToWl :: InnerWord -> IO InnerWord+mapVowelOrRExceptOLToWl word = do+  w <- vowelOrRExceptOLToWlLower >>= \re -> pure $ innerReplace word re (pack "wl") False+  vowelOrRExceptOLToWlUpper >>= \re -> pure $ innerReplace w re (pack "W$1") False++mapOldToOwld :: InnerWord -> IO InnerWord+mapOldToOwld word = do+  w <- oldToOwldLower >>= \re -> pure $ innerReplace word re (pack "$1wld") False+  oldToOwldUpper >>= \re -> pure $ innerReplace w re (pack "OWLD") False++mapOlToOwl :: InnerWord -> IO InnerWord+mapOlToOwl word = do+  w <- olToOwlLower >>= \re -> pure $ innerReplace word re (pack "$1wl") False+  olToOwlUpper >>= \re -> pure $ innerReplace w re (pack "OWL") False++mapLOrROToWo :: InnerWord -> IO InnerWord+mapLOrROToWo word = do+  w <- lOrROToWoLower >>= \re -> pure $ innerReplace word re (pack "wo") False+  lOrROToWoUpper >>= \re -> pure $ innerReplace w re (pack "W$1") False++mapSpecificConsonantsOToLetterAndWo :: InnerWord -> IO InnerWord+mapSpecificConsonantsOToLetterAndWo word = do+  w <- specificConsonantsOToLetterAndWoLower >>= \re -> pure $ innerReplace word re (pack "$1wo") False+  specificConsonantsOToLetterAndWoUpper >>= \re ->+    pure $ innerReplaceWithFuncMultiple w re (\c1 c2 -> c1 <> (if toUpper c2 == c2 then pack "W" else pack "w") <> c2) False++mapVOrWLeToWal :: InnerWord -> IO InnerWord+mapVOrWLeToWal word = vOrWLeToWal >>= \re -> pure $ innerReplace word re (pack "wal") False++mapFiToFwi :: InnerWord -> IO InnerWord+mapFiToFwi word = do+  w <- fiToFwiLower >>= \re -> pure $ innerReplace word re (pack "$1wi") False+  fiToFwiUpper >>= \re -> pure $ innerReplace w re (pack "FWI") False++mapVerToWer :: InnerWord -> IO InnerWord+mapVerToWer word = verToWer >>= \re -> pure $ innerReplace word re (pack "wer") False++mapPoiToPwoi :: InnerWord -> IO InnerWord+mapPoiToPwoi word = poiToPwoi >>= \re -> pure $ innerReplace word re (pack "$1woi") False++mapSpecificConsonantsLeToLetterAndWal :: InnerWord -> IO InnerWord+mapSpecificConsonantsLeToLetterAndWal word =+  specificConsonantsLeToLetterAndWal >>= \re -> pure $ innerReplace word re (pack "$1wal") False++mapConsonantRToConsonantW :: InnerWord -> IO InnerWord+mapConsonantRToConsonantW word =+  consonantRToConsonantW >>= \re -> pure $ innerReplace word re (pack "$1w") False++mapLyToWy :: InnerWord -> IO InnerWord+mapLyToWy word = do+  w <- lyToWyLower >>= \re -> pure $ innerReplace word re (pack "wy") False+  lyToWyUpper >>= \re -> pure $ innerReplace w re (pack "Wy") False++mapPleToPwe :: InnerWord -> IO InnerWord+mapPleToPwe word = pleToPwe >>= \re -> pure $ innerReplace word re (pack "$1we") False++mapNrToNw :: InnerWord -> IO InnerWord+mapNrToNw word = do+  w <- nrToNwLower >>= \re -> pure $ innerReplace word re (pack "nw") False+  nrToNwUpper >>= \re -> pure $ innerReplace w re (pack "NW") False++mapFucToFwuc :: InnerWord -> IO InnerWord+mapFucToFwuc word = funcToFwuc >>= \re -> pure $ innerReplace word re (pack "$1wuc") False++mapMomToMwom :: InnerWord -> IO InnerWord+mapMomToMwom word = momToMwom >>= \re -> pure $ innerReplace word re (pack "$1wom") False++mapMeToMwe :: InnerWord -> IO InnerWord+mapMeToMwe word = meToMwe >>= \re -> pure $ innerReplace word re (pack "$1we") False++mapNVowelToNy :: InnerWord -> IO InnerWord+mapNVowelToNy word = do+  w <- nVowelToNyFirst >>= \re -> pure $ innerReplace word re (pack "ny$1") False+  w' <- nVowelToNySecond >>= \re -> pure $ innerReplace w re (pack "Ny$1") False+  nVowelToNyThird >>= \re -> pure $ innerReplace w' re (pack "NY$1") False++mapOveToUv :: InnerWord -> IO InnerWord+mapOveToUv word = do+  w <- oveToUvLower >>= \re -> pure $ innerReplace word re (pack "uv") False+  oveToUvUpper >>= \re -> pure $ innerReplace w re (pack "UV") False++mapHahaToHeheXd :: InnerWord -> IO InnerWord+mapHahaToHeheXd word = hahaToHeheXd >>= \re -> pure $ innerReplace word re (pack "hehe xD") False++mapTheToTeh :: InnerWord -> IO InnerWord+mapTheToTeh word = theToTeh >>= \re -> pure $ innerReplace word re (pack "$1eh") False++mapYouToU :: InnerWord -> IO InnerWord+mapYouToU word = do+  w <- youToUUpper >>= \re -> pure $ innerReplace word re (pack "U") False+  youToULower >>= \re -> pure $ innerReplace w re (pack "u") False++mapTimeToTim :: InnerWord -> IO InnerWord+mapTimeToTim word = timeToTim >>= \re -> pure $ innerReplace word re (pack "$1im") False++mapOverToOwor :: InnerWord -> IO InnerWord+mapOverToOwor word = overToOwor >>= \re -> pure $ innerReplace word re (pack "$1wor") False++mapWorseToWose :: InnerWord -> IO InnerWord+mapWorseToWose word = worseToWose >>= \re -> pure $ innerReplace word re (pack "$1ose") False
+ src/Data/Owoify/Internal/Data/Presets.hs view
@@ -0,0 +1,60 @@+module Data.Owoify.Internal.Data.Presets where++import qualified Data.Owoify.Internal.Data.Mappings as Mappings+import Data.Owoify.Internal.Entity.Word ( InnerWord )++specificWordMappingList :: [InnerWord -> IO InnerWord]+specificWordMappingList =+  [ Mappings.mapFucToFwuc+  , Mappings.mapMomToMwom+  , Mappings.mapTimeToTim+  , Mappings.mapMeToMwe+  , Mappings.mapNVowelToNy+  , Mappings.mapOverToOwor+  , Mappings.mapOveToUv+  , Mappings.mapHahaToHeheXd+  , Mappings.mapTheToTeh+  , Mappings.mapYouToU+  , Mappings.mapReadToWead+  , Mappings.mapWorseToWose+  ]++uvuMappingList :: [InnerWord -> IO InnerWord]+uvuMappingList =+  [ Mappings.mapOToOwo+  , Mappings.mapEwToUwu+  , Mappings.mapHeyToHay+  , Mappings.mapDeadToDed+  , Mappings.mapNVowelTToNd+  ]++uwuMappingList :: [InnerWord -> IO InnerWord]+uwuMappingList =+  [ Mappings.mapBracketsToStartrails+  , Mappings.mapPeriodCommaExclamationSemicolonToKaomojis+  , Mappings.mapThatToDat+  , Mappings.mapThToF+  , Mappings.mapLeToWal+  , Mappings.mapVeToWe+  , Mappings.mapRyToWwy+  , Mappings.mapROrLToW+  ]++owoMappingList :: [InnerWord -> IO InnerWord]+owoMappingList =+  [ Mappings.mapLlToWw+  , Mappings.mapVowelOrRExceptOLToWl+  , Mappings.mapOldToOwld+  , Mappings.mapOlToOwl+  , Mappings.mapLOrROToWo+  , Mappings.mapSpecificConsonantsOToLetterAndWo+  , Mappings.mapVOrWLeToWal+  , Mappings.mapFiToFwi+  , Mappings.mapVerToWer+  , Mappings.mapPoiToPwoi+  , Mappings.mapSpecificConsonantsLeToLetterAndWal+  , Mappings.mapConsonantRToConsonantW+  , Mappings.mapLyToWy+  , Mappings.mapPleToPwe+  , Mappings.mapNrToNw+  ]
+ src/Data/Owoify/Internal/Entity/Word.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE NamedFieldPuns #-}+module Data.Owoify.Internal.Entity.Word+  ( InnerWord(..)+  , innerReplace+  , innerReplaceWithFuncSingle+  , innerReplaceWithFuncMultiple+  , toText+  )+  where++import Prelude++import Data.Maybe (listToMaybe)+import Data.Text.Lazy (strip, Text)+import qualified Data.Text.Lazy (replace)+import Text.RE.PCRE.Text.Lazy ((*=~), anyMatches, matches, RE)+import Text.RE.Replace (replaceAll)+import Data.List (nub)++-- | Basic type for manipulating strings.+data InnerWord = InnerWord+  { innerWord :: Text+  , innerReplacedWords :: [Text]+  } deriving (Eq, Show)++toText :: InnerWord -> Text+toText InnerWord{ innerWord } = innerWord++testAndGetReplacingWord :: RE -> Text -> Text -> Text+testAndGetReplacingWord searchValue replaceValue str =+  let matchedItems = str *=~ searchValue in+  if anyMatches matchedItems then+    case listToMaybe $ matches matchedItems of+      Nothing -> str+      Just hd -> Data.Text.Lazy.replace hd replaceValue str+  else+    str++containsReplacedWords :: InnerWord -> RE -> Text -> Bool+containsReplacedWords InnerWord { innerReplacedWords } searchValue replaceValue =+  any (\s -> let matchedItems = s *=~ searchValue in+    anyMatches matchedItems && (+    let replacedWord = Data.Text.Lazy.replace (head $ matches matchedItems) replaceValue s in+    replacedWord == s)) innerReplacedWords++buildCollection :: RE -> Text -> [Text]+buildCollection searchValue str = matches $ str *=~ searchValue++buildReplacedWords :: Functor f => Text -> f Text -> f Text+buildReplacedWords replaceValue texts = (\s -> Data.Text.Lazy.replace s replaceValue s) <$> texts++-- | Match the `word` against `searchValue` and replace matched strings with `replaceValue`.+innerReplace :: InnerWord -> RE -> Text -> Bool -> InnerWord+innerReplace word@InnerWord { innerWord, innerReplacedWords } searchValue replaceValue replaceReplacedWords+  | not replaceReplacedWords && containsReplacedWords word searchValue replaceValue = word+  | otherwise = if replacingWord == innerWord then word else InnerWord { innerWord = replacingWord, innerReplacedWords = nub $ innerReplacedWords <> replacedWords }+    where+      matchedItems = innerWord *=~ searchValue+      collection = matches matchedItems+      replacingWord = case listToMaybe collection of+        Nothing -> innerWord+        Just _ -> strip $ replaceAll replaceValue matchedItems+      replacedWords = buildReplacedWords replaceValue collection++-- | Match the `word` against `searchValue` and replace matched strings with the string resulting from invoking `f`.+innerReplaceWithFuncSingle :: InnerWord -> RE -> (() -> Text) -> Bool -> InnerWord+innerReplaceWithFuncSingle word@InnerWord { innerWord, innerReplacedWords } searchValue f replaceReplacedWords+  | not replaceReplacedWords && containsReplacedWords word searchValue replaceValue = word+  | replacingWord == innerWord = word+  | otherwise = InnerWord { innerWord = replacingWord, innerReplacedWords = nub $ innerReplacedWords <> replacedWords }+  where+      replaceValue = f ()+      replacingWord+        = strip+            $ testAndGetReplacingWord searchValue replaceValue innerWord+      collection = buildCollection searchValue replaceValue+      replacedWords = buildReplacedWords replaceValue collection++-- | Match the `word` against `searchValue` and replace matched strings with the string resulting from invoking `f`.+-- +-- The difference between this and `replaceWithFuncSingle` is that the `f` here takes two `String` arguments.+innerReplaceWithFuncMultiple :: InnerWord -> RE -> (Text -> Text -> Text) -> Bool -> InnerWord+innerReplaceWithFuncMultiple word@InnerWord { innerWord, innerReplacedWords } searchValue f replaceReplacedWords+  | not $ anyMatches matchedItems = word+  | otherwise =+    if (not replaceReplacedWords && containsReplacedWords word searchValue replaceValue) || (replacingWord == innerWord) then word+    else InnerWord { innerWord = replacingWord, innerReplacedWords = nub $ innerReplacedWords <> replacedWords }+  where+    matchedItems = innerWord *=~ searchValue+    collection = matches matchedItems+    (s1 : s2 : s3 : _) = if length collection == 3 then collection else [innerWord, innerWord, innerWord]+    replaceValue = f s2 s3+    replacingWord = strip $ Data.Text.Lazy.replace s1 replaceValue innerWord+    replacedWords = buildReplacedWords replaceValue collection
+ src/Data/Owoify/Internal/Parser/OwoifyParser.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE RankNTypes #-}+module Data.Owoify.Internal.Parser.OwoifyParser+  ( count+  , OError(..)+  , OwoifyError+  , OwoifyParser+  , runParser+  )+  where++import Prelude++import Control.Monad ((>=>), foldM, replicateM)+import Data.Function ((&))+import Data.List (uncons)+import Data.Owoify.Internal.Entity.Word (InnerWord(InnerWord, innerWord, innerReplacedWords)) +import Data.Text.Lazy (Text)++-- | Represents those types denoting errors when owoifying.+class OwoifyError e where+  -- | Representing that the source collection of strings has been exhausted.+  eof :: e+  -- | Representing general parser error. Currently not used.+  parseError :: Text -> e++-- | A simple type representing errors that occur during owoification.+data OError = EOF | ParseError Text deriving (Show)++instance OwoifyError OError where+  eof = EOF+  parseError = ParseError++type OwoifyResult a = ([Text], a)++type OwoifyFunction e a = OwoifyError e => [Text] -> Either e (OwoifyResult a)++newtype OwoifyParser e a = OwoifyParser (OwoifyFunction e a)++instance Functor (OwoifyParser e) where+  fmap f (OwoifyParser g) = OwoifyParser (fmap (fmap f) . g)++instance Applicative (OwoifyParser e) where+  (<*>) (OwoifyParser f) (OwoifyParser g) = OwoifyParser (f >=> \(s', ab) -> g s' >>= \(s'', a) -> pure (s'', ab a))+  pure x = OwoifyParser (\s -> pure (s, x))++instance Monad (OwoifyParser e) where+  (>>=) (OwoifyParser f) g = OwoifyParser (f >=> \(s', a) -> runParser (g a) s')++-- | Executes (unwraps) the parser inside the monad.+runParser :: OwoifyError e => OwoifyParser e a -> [Text] -> Either e (OwoifyResult a)+runParser (OwoifyParser f) = f++word ::+  (Foldable t, Monad m, OwoifyError e)+  => t (InnerWord -> m InnerWord)+  -> OwoifyParser e (m InnerWord)+word mappings = OwoifyParser (\s ->+  case uncons s of+    Nothing -> Left eof+    Just (head, tail) -> do+      let w = InnerWord { innerWord = head, innerReplacedWords = [] }+      let result = foldM (&) w mappings+      Right (tail, result))++-- | Replicate owoify parser according to the specified length (`n`) and a collection of owoify functions. +count ::+  (Foldable t, Monad m, OwoifyError e)+  => Int+  -> t (InnerWord -> m InnerWord)+  -> OwoifyParser e [m InnerWord]+count n p | n <= 0 = pure []+          | otherwise = replicateM n $ word p
+ src/Data/Owoify/Internal/Util/Interleave.hs view
@@ -0,0 +1,12 @@+module Data.Owoify.Internal.Util.Interleave where++import Prelude++-- | Utility function to interleave two lists.+interleave :: [a] -> [a] -> [a]+interleave a b = go [] a b 0+  where+    go result [] other round        | even round = if not $ null other then other <> result else result+    go result (x : xs) other round  | even round = go (x : result) xs other (round + 1)+    go result _ [] _                             = result+    go result arr (x : xs) round                 = go (x : result) arr xs (round + 1)
+ src/Data/Owoify/Owoify.hs view
@@ -0,0 +1,53 @@+module Data.Owoify.Owoify+  ( owoify+  , OwoifyLevel(..)+  )+  where++import Prelude hiding (words)++import Data.Functor ((<&>))+import Data.Text.Lazy (Text, intercalate, pack)+import Text.RE.PCRE.Text.Lazy ((*=~), compileRegex, Matches, matches, RE)+import Data.Owoify.Internal.Parser.OwoifyParser (count, OError, OwoifyParser, runParser)+import Data.Owoify.Internal.Data.Presets (owoMappingList, specificWordMappingList, uvuMappingList, uwuMappingList)+import Data.Owoify.Internal.Entity.Word (InnerWord(InnerWord), toText)+import Data.Owoify.Internal.Util.Interleave (interleave)++-- | Levels to denote owoness.+data OwoifyLevel = Owo | Uwu | Uvu++extractWords :: MonadFail f => String -> Text -> f [Text]+extractWords pattern s =+  compileRegex pattern <&> (s *=~) <&> matches++words :: Text -> IO [Text]+words = extractWords "[^\\s]+"++spaces :: Text -> IO [Text]+spaces = extractWords "\\s+"++-- | Owoify source text using the specified level and turn text into nonsensical babyspeaks.+--+-- Examples:+--+-- >>> owoify (Data.Text.Lazy.pack "Hello World!") Owo+-- Hewwo World+owoify :: Text -> OwoifyLevel -> IO Text+owoify source level = do+  w <- words source+  s <- spaces source+  let n = length w+  let parsers = count n $ specificWordMappingList <> (case level of+        Owo -> owoMappingList+        Uwu -> uwuMappingList <> owoMappingList+        Uvu -> uvuMappingList <> uwuMappingList <> owoMappingList) :: OwoifyParser OError [IO InnerWord]+  let result = runParser parsers w+  case result of+    Left e -> do+      error $ show e+      pure $ pack ""+    Right (_, transformedWords) -> do+      wordsList <- sequence transformedWords <&> fmap toText+      let interleaved = reverse $ interleave wordsList s+      pure $ intercalate (pack "") interleaved
+ test/Spec.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE BlockArguments #-}+import Prelude++import Data.Text.Lazy (pack, Text)+import Test.HUnit (Test(TestCase, TestLabel, TestList), Assertable (assert), runTestTT)+import Data.Owoify.Owoify (owoify, OwoifyLevel (Owo, Uwu, Uvu))+import Data.Foldable (traverse_)++source :: Text+source = pack "Hello World! This is the string to owo! Kinda cute, isn't it?"++pokemonNamesListPath :: String+pokemonNamesListPath = "assets/pokemons.txt"++warAndPeacePath :: String+warAndPeacePath = "assets/war_and_peace_chapter01-20.txt"++testOwoify :: Test+testOwoify = TestCase (do+  result <- owoify source Owo+  assert $ result /= source)++testOwo :: Test+testOwo = TestCase (do+  result <- owoify source Owo+  assert $ result /= mempty)++testUwu :: Test+testUwu = TestCase (do+  result <- owoify source Uwu+  assert $ result /= mempty)++testUvu :: Test+testUvu = TestCase (do+  result <- owoify source Uvu+  assert $ result /= mempty)++testOwoNotEqualToUwu :: Test+testOwoNotEqualToUwu = TestCase (do+  owoResult <- owoify source Owo+  uwuResult <- owoify source Uwu+  assert $ owoResult /= uwuResult)++testOwoNotEqualToUvu :: Test+testOwoNotEqualToUvu = TestCase (do+  owoResult <- owoify source Owo+  uvuResult <- owoify source Uvu+  assert $ owoResult /= uvuResult)++testUwuNotEqualToUvu :: Test+testUwuNotEqualToUvu = TestCase (do+  uwuResult <- owoify source Uwu+  uvuResult <- owoify source Uvu+  assert $ uwuResult /= uvuResult)++testPokemonNameWithLevel :: Text -> OwoifyLevel -> IO ()+testPokemonNameWithLevel name level = do+  result <- owoify name level+  assert $ result /= pack ""++testPokemonNames :: Test+testPokemonNames = TestCase (do+  pokemonNames <- readFile pokemonNamesListPath+  let pokemonNameList = pack <$> concat (words <$> lines pokemonNames)+  putStrLn "\n---- Test Pokemon Names with Owo"+  traverse_ (`testPokemonNameWithLevel` Owo) pokemonNameList+  putStrLn "\n---- Test Pokemon Names with Uwu"+  traverse_ (`testPokemonNameWithLevel` Uwu) pokemonNameList+  putStrLn "\n---- Test Pokemon Names with Uvu"+  traverse_ (`testPokemonNameWithLevel` Uvu) pokemonNameList)++testLongText :: Test+testLongText = TestCase (do+  longText <- pack <$> readFile warAndPeacePath+  putStrLn "\n---- Test Long Text with Owo"+  owoLongText <- owoify longText Owo+  assert $ owoLongText /= longText+  assert $ owoLongText /= mempty++  putStrLn "\n---- Test Long Text with Uwu"+  uwuLongText <- owoify longText Uwu+  assert $ uwuLongText /= longText+  assert $ uwuLongText /= mempty++  putStrLn "\n---- Test Long Text with Uvu"+  uvuLongText <- owoify longText Uvu+  assert $ uvuLongText /= longText+  assert $ uvuLongText /= mempty)++main :: IO ()+main = do+  let testList = TestList+        [ TestLabel "Test Owoify" testOwoify+        , TestLabel "Test Owo" testOwo+        , TestLabel "Test Uwu" testUwu+        , TestLabel "Test Uvu" testUvu+        , TestLabel "Test Uvu" testUvu+        , TestLabel "Test Owo Not Equal To Uwu" testOwoNotEqualToUwu+        , TestLabel "Test Owo Not Equal To Uvu" testOwoNotEqualToUvu+        , TestLabel "Test Uwu Not Equal To Uvu" testUwuNotEqualToUvu+        , TestLabel "Test Pokemon Names" testPokemonNames+        , TestLabel "Test Long Text" testLongText+        ]+  count <- runTestTT testList+  pure ()