/ namegen.py
namegen.py
1 #!/usr/bin/python3 2 """Generate names (or other strings) based on specialized templates""" 3 4 # SPDX-FileCopyrightText: 2021 Jeff Epler 5 # 6 # SPDX-License-Identifier: GPL-3.0-only 7 8 import sys 9 import random 10 11 symbols = { 12 "'": ["'"], 13 "-": ["-"], 14 " ": [" "], 15 "s": [ 16 "ach", 17 "ack", 18 "ad", 19 "age", 20 "ald", 21 "ale", 22 "an", 23 "ang", 24 "ar", 25 "ard", 26 "as", 27 "ash", 28 "at", 29 "ath", 30 "augh", 31 "aw", 32 "ban", 33 "bel", 34 "bur", 35 "cer", 36 "cha", 37 "che", 38 "dan", 39 "dar", 40 "del", 41 "den", 42 "dra", 43 "dyn", 44 "ech", 45 "eld", 46 "elm", 47 "em", 48 "en", 49 "end", 50 "eng", 51 "enth", 52 "er", 53 "ess", 54 "est", 55 "et", 56 "gar", 57 "gha", 58 "hat", 59 "hin", 60 "hon", 61 "ia", 62 "ight", 63 "ild", 64 "im", 65 "ina", 66 "ine", 67 "ing", 68 "ir", 69 "is", 70 "iss", 71 "it", 72 "kal", 73 "kel", 74 "kim", 75 "kin", 76 "ler", 77 "lor", 78 "lye", 79 "mor", 80 "mos", 81 "nal", 82 "ny", 83 "nys", 84 "old", 85 "om", 86 "on", 87 "or", 88 "orm", 89 "os", 90 "ough", 91 "per", 92 "pol", 93 "qua", 94 "que", 95 "rad", 96 "rak", 97 "ran", 98 "ray", 99 "ril", 100 "ris", 101 "rod", 102 "roth", 103 "ryn", 104 "sam", 105 "say", 106 "ser", 107 "shy", 108 "skel", 109 "sul", 110 "tai", 111 "tan", 112 "tas", 113 "ther", 114 "tia", 115 "tin", 116 "ton", 117 "tor", 118 "tur", 119 "um", 120 "und", 121 "unt", 122 "urn", 123 "usk", 124 "ust", 125 "ver", 126 "ves", 127 "vor", 128 "war", 129 "wor", 130 "yer", 131 ], 132 "v": ["a", "e", "i", "o", "u", "y"], 133 "V": [ 134 "a", 135 "e", 136 "i", 137 "o", 138 "u", 139 "y", 140 "ae", 141 "ai", 142 "au", 143 "ay", 144 "ea", 145 "ee", 146 "ei", 147 "eu", 148 "ey", 149 "ia", 150 "ie", 151 "oe", 152 "oi", 153 "oo", 154 "ou", 155 "ui", 156 ], 157 "c": [ 158 "b", 159 "c", 160 "d", 161 "f", 162 "g", 163 "h", 164 "j", 165 "k", 166 "l", 167 "m", 168 "n", 169 "p", 170 "q", 171 "r", 172 "s", 173 "t", 174 "v", 175 "w", 176 "x", 177 "y", 178 "z", 179 ], 180 "B": [ 181 "b", 182 "bl", 183 "br", 184 "c", 185 "ch", 186 "chr", 187 "cl", 188 "cr", 189 "d", 190 "dr", 191 "f", 192 "g", 193 "h", 194 "j", 195 "k", 196 "l", 197 "ll", 198 "m", 199 "n", 200 "p", 201 "ph", 202 "qu", 203 "r", 204 "rh", 205 "s", 206 "sch", 207 "sh", 208 "sl", 209 "sm", 210 "sn", 211 "st", 212 "str", 213 "sw", 214 "t", 215 "th", 216 "thr", 217 "tr", 218 "v", 219 "w", 220 "wh", 221 "y", 222 "z", 223 "zh", 224 ], 225 "C": [ 226 "b", 227 "c", 228 "ch", 229 "ck", 230 "d", 231 "f", 232 "g", 233 "gh", 234 "h", 235 "k", 236 "l", 237 "ld", 238 "ll", 239 "lt", 240 "m", 241 "n", 242 "nd", 243 "nn", 244 "nt", 245 "p", 246 "ph", 247 "q", 248 "r", 249 "rd", 250 "rr", 251 "rt", 252 "s", 253 "sh", 254 "ss", 255 "st", 256 "t", 257 "th", 258 "v", 259 "w", 260 "y", 261 "z", 262 ], 263 "i": [ 264 "air", 265 "ankle", 266 "ball", 267 "beef", 268 "bone", 269 "bum", 270 "bumble", 271 "bump", 272 "cheese", 273 "clod", 274 "clot", 275 "clown", 276 "corn", 277 "dip", 278 "dolt", 279 "doof", 280 "dork", 281 "dumb", 282 "face", 283 "finger", 284 "foot", 285 "fumble", 286 "goof", 287 "grumble", 288 "head", 289 "knock", 290 "knocker", 291 "knuckle", 292 "loaf", 293 "lump", 294 "lunk", 295 "meat", 296 "muck", 297 "munch", 298 "nit", 299 "numb", 300 "pin", 301 "puff", 302 "skull", 303 "snark", 304 "sneeze", 305 "thimble", 306 "twerp", 307 "twit", 308 "wad", 309 "wimp", 310 "wipe", 311 ], 312 "m": [ 313 "baby", 314 "booble", 315 "bunker", 316 "cuddle", 317 "cuddly", 318 "cutie", 319 "doodle", 320 "foofie", 321 "gooble", 322 "honey", 323 "kissie", 324 "lover", 325 "lovey", 326 "moofie", 327 "mooglie", 328 "moopie", 329 "moopsie", 330 "nookum", 331 "poochie", 332 "poof", 333 "poofie", 334 "pookie", 335 "schmoopie", 336 "schnoogle", 337 "schnookie", 338 "schnookum", 339 "smooch", 340 "smoochie", 341 "smoosh", 342 "snoogle", 343 "snoogy", 344 "snookie", 345 "snookum", 346 "snuggy", 347 "sweetie", 348 "woogle", 349 "woogy", 350 "wookie", 351 "wookum", 352 "wuddle", 353 "wuddly", 354 "wuggy", 355 "wunny", 356 ], 357 "M": [ 358 "boo", 359 "bunch", 360 "bunny", 361 "cake", 362 "cakes", 363 "cute", 364 "darling", 365 "dumpling", 366 "dumplings", 367 "face", 368 "foof", 369 "goo", 370 "head", 371 "kin", 372 "kins", 373 "lips", 374 "love", 375 "mush", 376 "pie", 377 "poo", 378 "pooh", 379 "pook", 380 "pums", 381 ], 382 "D": [ 383 "b", 384 "bl", 385 "br", 386 "cl", 387 "d", 388 "f", 389 "fl", 390 "fr", 391 "g", 392 "gh", 393 "gl", 394 "gr", 395 "h", 396 "j", 397 "k", 398 "kl", 399 "m", 400 "n", 401 "p", 402 "th", 403 "w", 404 ], 405 "d": [ 406 "elch", 407 "idiot", 408 "ob", 409 "og", 410 "ok", 411 "olph", 412 "olt", 413 "omph", 414 "ong", 415 "onk", 416 "oo", 417 "oob", 418 "oof", 419 "oog", 420 "ook", 421 "ooz", 422 "org", 423 "ork", 424 "orm", 425 "oron", 426 "ub", 427 "uck", 428 "ug", 429 "ulf", 430 "ult", 431 "um", 432 "umb", 433 "ump", 434 "umph", 435 "un", 436 "unb", 437 "ung", 438 "unk", 439 "unph", 440 "unt", 441 "uzz", 442 ], 443 "z": ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], 444 } 445 446 447 def namegen( 448 pattern, rng=random.randrange 449 ): # pylint: disable=too-many-branches, too-many-statements 450 """Generate a name from the given pattern, with the given random number source""" 451 depth = silent = literal = capstack = capitalize = 0 452 reset = [0] 453 n = [1] 454 result = [] 455 456 for c in pattern: 457 if c == "<": 458 depth += 1 459 bit = 1 << depth 460 n.append(1) 461 reset.append(len(result)) 462 literal &= ~bit 463 silent &= ~bit 464 silent |= (silent << 1) & bit 465 capstack &= ~bit 466 capstack |= capitalize << depth 467 468 elif c == "(": 469 depth += 1 470 bit = 1 << depth 471 n.append(1) 472 reset.append(len(result)) 473 literal |= bit 474 silent &= ~bit 475 silent |= (silent << 1) & bit 476 capstack &= ~bit 477 capstack |= capitalize << depth 478 479 elif c == ">": 480 n.pop() 481 reset.pop() 482 if depth == 0: 483 raise ValueError("Invalid pattern") 484 bit = 1 << depth 485 if literal & bit: 486 raise ValueError("Invalid pattern") 487 depth -= 1 488 489 elif c == ")": 490 n.pop() 491 reset.pop() 492 if depth == 0: 493 raise ValueError("Invalid pattern") 494 bit = 1 << depth 495 if not (literal & bit): 496 raise ValueError("Invalid pattern") 497 depth -= 1 498 499 elif c == "|": 500 bit = 1 << depth 501 if not (silent & (bit >> 1)): 502 nd = n[depth] = n[depth] + 1 503 if rng(nd) == 0: 504 del result[reset[depth] :] 505 silent &= ~bit 506 capitalize = bool(capstack & bit) 507 else: 508 silent |= bit 509 510 elif c == "!": 511 capitalize = True 512 513 else: 514 bit = 1 << depth 515 if not (silent & bit): 516 if not (literal & bit): 517 s = symbols.get(c) 518 if s is None: 519 raise ValueError(f"Invalid metacharacter {c}") 520 c = s[rng(len(s))] 521 if capitalize: 522 c = c[0].capitalize() + c[1:] 523 capitalize = 0 524 result.append(c) 525 526 if depth != 0: 527 raise ValueError("Invalid pattern") 528 return "".join(result) 529 530 531 if __name__ == "__main__": 532 for template in sys.argv[1:]: 533 print(end=f"{template!r}:") 534 for _ in range(8): 535 print(end=f" {namegen(template)!r}") 536 print()