yum-mirror/UwwwuPP
A rehost of leonetienne's UwwwuPP, a simple text filter to uwu-ify English text.
git clone https://git.yummers.dev/yum-mirror/UwwwuPP
b5eac77
master
1#ifndef UWWWU_LIBUWU_H 2#define UWWWU_LIBUWU_H 3 4#include <StringTools.h> 5#include <CharTools.h> 6#include <string> 7#include <sstream> 8#include <functional> 9#include <random> 10#include "Util.h" 11 12// This validator will only replace findings, if they are a complete word, and not just part of a word. 13static inline autoValidatorFindingIsCompleteWord (const std ::string & original ,const std ::string & finding ,const std ::size_t index )-> bool { 14// Quick-accept: Original-string length matches finding-string length 15if (original .length ()== finding .length ()) 16return true; 17 18bool lastCharBreaksWord = true;// Default is true, because this value stays in case there is no last/next char. 19bool nextCharBreaksWord = true;// In this case, "no character" would imply the word to be broken. 20 21// Assign surrounding chars, if possible 22if (index > 0 ) 23lastCharBreaksWord = !CharTools ::IsLetter (original [index - 1 ]); 24if (index + finding .length ()< original .length ()) 25nextCharBreaksWord = !CharTools ::IsLetter (original [index + finding .length ()]); 26 27// If both the last and the next character are word-breaking, replace. 28if (lastCharBreaksWord && nextCharBreaksWord ) 29return true; 30// Else: don't 31else 32return false; 33} 34 35 36//! Will make a boring string look sooper dooper kawaii and cute :3 37static inline std ::string MakeUwu (std ::string boringString ) { 38// Easy ones first 39// none, lol 40 41// Slightly more complex... Multichar replacements, but we have to keep capitalization... 42boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"th" ,"tw" ); 43boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"ove" ,"uv" ); 44boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"have" ,"haf" ); 45boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"tr" ,"tw" ); 46boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"up" ,"uwp" ); 47 48// Let's do some language adjustments 49boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"twank you" ,"you're twe best <3333 xoxo" ,ValidatorFindingIsCompleteWord ); 50boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"good" ,"sooper dooper" ,ValidatorFindingIsCompleteWord ); 51boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"suwper" ,"sooper dooper" ,ValidatorFindingIsCompleteWord ); 52boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"well" ,"sooper dooper" ,ValidatorFindingIsCompleteWord ); 53boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"emacs" ,"vim" ,ValidatorFindingIsCompleteWord ); 54boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"twanks" ,"you're twe best :33 xoxo" ,ValidatorFindingIsCompleteWord ); 55boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"hello" ,"hiiiiiii" ,ValidatorFindingIsCompleteWord ); 56boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"dear" ,"hiiiiiii" ,ValidatorFindingIsCompleteWord ); 57 58// Let's extend some phonetics 59boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"hi" ,"hiiiiiii" ); 60boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"ay" ,"aaay" ); 61boringString = Util ::ConditionalReplaceButKeepSigns (boringString ,"ey" ,"eeey" ); 62 63// Replace N with Ny, but only if succeeded by a vowel, and not (preceded by an o and succeeded by an "e{nonletter}"): "one" has such a niche pronunciation... 64boringString = Util ::ConditionalReplaceButKeepSigns ( 65boringString , 66"n" , 67"ny" , 68 [](const std ::string & original ,const std ::string & finding ,const std ::size_t index ) { 69// Don't replace, if we are on the last char 70if (index + finding .length ()== original .length ()) 71return false; 72 73const char nextChar = CharTools ::MakeLower (original [index + finding .length ()]); 74const bool haveLastchar = index > 0 ;// Do we even have a last char? 75const char lastChar = haveLastchar ?CharTools ::MakeLower (original [index - 1 ]) :'\0' ; 76 77// Apply the complex "one\b"-rule: 78// (don't replace if 'n' is preceded by 'o' and succeeded by 'e', which is succeeded by a word break) 79 { 80bool nextNextCharIsNotLetter = false; 81char nextNextChar ; 82 83// How much length is left including `nextChar`? 84const std ::size_t sizeLeft = original .length ()- (index + finding .length ()); 85 86// We have room to pick the nextNext char... 87if (sizeLeft > 1 ) 88 { 89nextNextChar = CharTools ::MakeLower (original [index + finding .length ()+ 1 ]); 90nextNextCharIsNotLetter = !CharTools ::IsLetter (nextNextChar ); 91 } 92 93const bool nextNextCharBreaksWord = (sizeLeft == 1 )|| (nextNextCharIsNotLetter ); 94 95// Don't replace if: 96// (lastChar == o) && (nextChar == e) && (nextNextCharBreaksWord) 97if ((haveLastchar )&& (lastChar == 'o' )&& (nextChar == 'e' )&& (nextNextCharBreaksWord )) 98return false; 99 } 100 101// Is this a vowel? 102if (CharTools ::IsVowel (nextChar )) 103return true; 104 105// Else, don't replace 106return false; 107 } 108 ); 109 110// Replace R with W, but only if not succeeded by a non-vowel, and if it's not the first character of a word 111boringString = Util ::ConditionalReplaceButKeepSigns ( 112boringString , 113"r" , 114"w" , 115 [](const std ::string & original ,const std ::string & finding ,const std ::size_t index ) { 116// Don't replace, if we are on the last char 117if (index + finding .length ()== original .length ()) 118return false; 119 120// Don't replace if we're at index 0 121if (index == 0 ) 122return false; 123 124const char nextChar = CharTools ::MakeLower (original [index + finding .length ()]); 125const char lastChar = CharTools ::MakeLower (original [index - 1 ]); 126 127// Is this a non-vowel? 128if (!CharTools ::IsVowel (nextChar )) 129return false; 130 131// Don't replace if the last char is not a letter 132if (!CharTools ::IsLetter (lastChar )) 133return false; 134 135// Else, replace 136return true; 137 } 138 ); 139 140// Replace C with W, but only if succeeded and preceeded by a vowel 141boringString = Util ::ConditionalReplaceButKeepSigns ( 142boringString , 143"c" , 144"w" , 145 [](const std ::string & original ,const std ::string & finding ,const std ::size_t index ) { 146// Don't replace, if we are on the last char 147if (index + finding .length ()== original .length ()) 148return false; 149 150// Don't replace if we're at index 0 151if (index == 0 ) 152return false; 153 154const char nextChar = CharTools ::MakeLower (original [index + finding .length ()]); 155const char lastChar = CharTools ::MakeLower (original [index - 1 ]); 156 157// Don't replace, if the next char is not a vowel 158if (!CharTools ::IsVowel (nextChar )) 159return false; 160 161// Don't replace if the last char is not a vowel 162if (!CharTools ::IsVowel (lastChar )) 163return false; 164 165// Else, replace 166return true; 167 } 168 ); 169 170// Replace L with W, but only if not followed or preceded by another L, and if it's not the first character of a word 171boringString = Util ::ConditionalReplaceButKeepSigns ( 172boringString , 173"l" , 174"w" , 175 [](const std ::string & original ,const std ::string & finding ,const std ::size_t index ) { 176// Our segment has to be at least two characters long 177if (original .length ()< finding .length ()+ 2 ) 178return false; 179 180// Don't replace if we're at index o 181if (index == 0 ) 182return false; 183 184const char lastChar = CharTools ::MakeLower (original [index - 1 ]); 185const char nextChar = CharTools ::MakeLower (original [index + finding .length ()]); 186 187// Don't replace if the last char is not a letter 188if (!CharTools ::IsLetter (lastChar )) 189return false; 190 191return (lastChar != 'l' )&& (nextChar != 'l' ); 192 } 193 ); 194 195// Replace LL with WW, but only if followed by a vowel 196boringString = Util ::ConditionalReplaceButKeepSigns ( 197boringString , 198"ll" , 199"ww" , 200 [](const std ::string & original ,const std ::string & finding ,const std ::size_t index ) { 201// Don't replace, if we are on the last char 202if (index + finding .length ()== original .length ()) 203return false; 204 205const char nextChar = CharTools ::MakeLower (original [index + finding .length ()]); 206 207return CharTools ::IsVowel (nextChar ); 208 } 209 ); 210 211// Replace ER with A, but only if it's the last two letters of a word 212boringString = Util ::ConditionalReplaceButKeepSigns ( 213boringString , 214"er" , 215"a" , 216 [](const std ::string & original ,const std ::string & finding ,const std ::size_t index ) { 217// Replace if we're at the end of this line/segment 218if (index + finding .length ()== original .length ()) 219return true; 220 221// Fetch the next char 222const char nextChar = CharTools ::MakeLower (original [index + finding .length ()]); 223 224// Replace if the next char is not a letter 225return !CharTools ::IsLetter (nextChar ); 226 } 227 ); 228 229// Replace R with W, but only (if it's preceeded by a vowel, 230// or preceeded by another 'r', 231// or if it's the first character of a word) 232// and if it's not the last character of a word 233boringString = Util ::ConditionalReplaceButKeepSigns ( 234boringString , 235"r" , 236"w" , 237 [](const std ::string & original ,const std ::string & finding ,const std ::size_t index ) { 238// Don't replace if it's the last character 239if (index + finding .length ()== original .length ()) 240return false; 241 242// Do blindly replace if it's the first character 243if (index == 0 ) 244return true; 245 246// Fetch the last character 247const char lastChar = CharTools ::MakeLower (original [index - 1 ]); 248 249// Fetch the next char 250const char nextChar = CharTools ::MakeLower (original [index + finding .length ()]); 251 252// Don't replace, if the last char is not a letter 253if (!CharTools ::IsLetter (lastChar )) 254return false; 255 256// Don't replace, if the next char is not a letter 257if (!CharTools ::IsLetter (nextChar )) 258return false; 259 260// Replace, if the last character is an 'r' aswell 261if (lastChar == 'r' ) 262return true; 263 264// Replace, if the last character is a vowel. 265return CharTools ::IsVowel (lastChar ); 266 } 267 ); 268 269// Replace y with y-y (imitates shy stuttering), but only sometimes (random change), 270// and if it is the first character of a word, 271// and if it is followed by a vowel 272boringString = Util ::ConditionalReplaceButKeepSigns ( 273boringString , 274"y" , 275"y-y" , 276[boringString](const std::string & original, const std::string & finding, const std:: size_t index) { 277// Don't replace, if we're at the end of our string 278if (index + finding. length () == original. length ()) 279return false; 280 281// This is a bit tricky, because we can't abort if we're on the first char 282// but we can can't not check the previous char either. Running big risk 283// of causing a segfault. So we have to not check the previous character, 284// if we are on the first char. 285 286// Are we on the first char? 287const bool isFirstChar = index == 0 ; 288 289// Fetch the last character 290const char lastChar = isFirstChar ? '\0' : CharTools:: MakeLower (original[index - 1 ]); 291 292// Fetch the next character 293const char nextChar = CharTools:: MakeLower (original[index + finding. length ()]); 294 295// Don't replace, if the last char is a letter 296if ((!isFirstChar) && (CharTools:: IsLetter (lastChar))) 297return false; 298 299// Don't replace, if the next char is not a vowel 300if (!CharTools:: IsVowel (nextChar)) 301return false; 302 303// Initialize deterministic prng 304// Can't initialize one for all scopes, they don't seem to play 305// nicely with being passed to lambda functions... 306static std ::mt19937 rng ( std ::hash < std::string > ()(boringString)); // Seed rng based on string 307 308// Chance (out of a 100) for the mutation to take place 309// if all preconditions are met 310constexpr int chance = 40 ; 311 312// Roll the dice, baby! 313return ( rng () % 100 ) < chance; 314} 315); 316 317// Replace random punctuation with uwwwwu cute symbols 318// About evewy fifteenth symbol 319std ::stringstream ss; 320for ( const char c : boringString ) 321{ 322// Initialize deterministic prng 323std :: mt19937 rng( std ::hash < std::string > ()(boringString)); // Seed rng based on string 324 325if ((c == '.' ) && ( rng () % 15 == 0 )) 326{ 327ss << " <3333 ^.^ " ; 328} 329else if ((c == '!' ) && ( rng () % 15 == 0 )) 330{ 331ss << "!! Thadws impowtant! <3 " ; 332} 333else if ((c == ',' ) && ( rng () % 15 == 0 )) 334{ 335ss << " <3 aaaaaand " ; 336} 337else if ((c == '?' ) && ( rng () % 15 == 0 )) 338{ 339ss << "?? now tell me! >:( " ; 340} 341else 342ss << c; 343} 344boringString = ss. str (); 345 346// Also replace some ascii-"emojis' 347boringString = StringTools:: Replace (boringString, ":)" , "UwU :D" ); 348boringString = StringTools:: Replace (boringString, ":D" , ":3" ); 349boringString = StringTools:: Replace (boringString, ":-)" , "UwwwU :3" ); 350boringString = StringTools:: Replace (boringString, "^^" , "^.^ UwU" ); 351 352// Some language replacement should happen after these more complex rules 353boringString = Util:: ConditionalReplaceButKeepSigns (boringString, "c++" , "c++ (rust is hella cutewr btw ^^)" ); 354 355 356return boringString; 357} 358 359#endif //UWWWU_LIBUWU_H