thy

pseudo-elizabethan context-free grammar
git clone git@git.kloet.net/thy.git
Download | Log | Files | Refs | LICENSE

commit 0f032db0cdeb5c57a201e2ff94eb189bf2a877be
Author: Andrew Kloet <andrew@kloet.net>
Date:   Thu, 16 Apr 2026 02:18:42 -0400

initial commit

Diffstat:
ALICENSE | 13+++++++++++++
AMakefile | 26++++++++++++++++++++++++++
Athy.c | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 322 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2026 Andrew Kloet <andrew@kloet.net> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,26 @@ +CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700L +CFLAGS = -std=c99 -pedantic -Wall -Os + +SRC = thy.c +OBJ = ${SRC:.c=.o} + +all: thy + +.c.o: + ${CC} -c ${CPPFLAGS} ${CFLAGS} $< + +thy: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f thy ${OBJ} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f thy ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/thy + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/thy + +.PHONY: all clean install uninstall diff --git a/thy.c b/thy.c @@ -0,0 +1,283 @@ +/* See LICENSE file for copyright and license details. + * + * thy: a pseudo-elizabethan context-free grammar + * This program constructs fragments using a "Mad Libs"-like text engine. + */ +#include <stdarg.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#define W(s, l) do { if (write(1, s, l)) {} } while (0) + +void +tiny_printf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + + while (*fmt) { + if (*fmt == '%' && *(fmt + 1) == 's') { + const char *s = va_arg(args, const char *); + if (s) { + int len = 0; + while (s[len]) len++; + W(s, len); + } + fmt += 2; + } else { + W(fmt++, 1); + } + } + va_end(args); +} + +const char *Noun[] = { + "mirror", "furnace", "skeleton", "biscuit", "mountain", "delusion", + "microcosm", "jelly", "ocean", "truth", "science", "product", "insect", + "tyranny", "creature", "theory", "life", "beast", "breast", "hope", "gland", + "convexity", "concavity", "doom", "season", "corrosion", "passion", "fate", + "vessel", "pain", "chemical", "mandible", "ray", "spirit", "feeling", + "radiance", "dirt", "vacuum", "art", "homunculus", "engine", "text", + "wheel", "vent", "lard", "memory", "apparel", "domicile", "behavior", + "government", "sound", "prison", "being", "sustenance", "death", "worm", + "stone", "color", "crystal", "gastropod", "flesh", "fire" +}; +const char *Verb1[] = { + "indulge", "lament", "caress", "exalt", "destroy", "avenge", "batter", + "stretch", "deceive", "shun", "manifest", "witness", "actualize", "embrace", + "levitate", "pardon", "sniff", "banish", "falsify", "pilot", "hark", + "consider", "render", "forget", "remember", "approach", "castigate", + "liquify", "debase", "sing", "hydrate", "chill", "rescue", "hoist", + "extrude", "desire", "impede", "thrum", "suffer", "texturize", "bungle", + "dance", "beat", "bleed", "soak", "work", "calm", "tether", "blast", + "digest", "smear", "rock", "roll", "sustain", "gather", "exhume", + "rehydrate", "prepare", "purify" +}; +const char *Verb2[] = { + "yearns for", "is like", "appears as", "reveals", "extracts", "is" +}; +const char *Adj[] = { + "mortal", "shining", "joyful", "austere", "melancholy", "small", "lordly", + "massive", "effulgent", "wandering", "gentle", "weaponized", "sentimental", + "correct", "motile", "youthful", "incomplete", "haughty", "furtive", + "vital", "offending", "pungent", "shriveled", "noisome", "hallowed", + "flabby", "zesty", "mechanized", "little", "burning", "fatal", "elusive", + "merry", "plastic", "juicy", "televised", "sphere-like", "modified", + "augmented", "noble", "pneumatic", "hylic", "psychic", "bad", "pupigerous", + "nodulous", "cycloid", "forbidden", "lurid", "fine", "platinum", "blue", + "monochrome", "spreading", "mutant", "revolving", "corporeal", "hideous", + "mild", "shrunken", "glum", "petty", "clean", "mutable", "leather", "scaly", + "spongy", "tendrillar", "refulgent", "porcelein" +}; +const char *Adv[] = { + "thinly", "strongly", "invisibly", "slowly", "woozily", "grimly", "greatly", + "mostly", "partially", "totally", "blatantly", "pleasantly", "luxuriously", + "expansively", "utterly", "rapidly", "crudely", "mercifully", "delicately", + "wrongfully", "arrogantly", "horribly", "wonderfully", "ostensibly", + "remarkably", "miraculously", "accidentally" +}; +const char *Prep[] = { + "above", "below", "amidst", "near", "far beyond", "inside", "thanks to", + "outside", "out of", "despite", "astride", "in", "beside", "of" +}; +const char *Sense[] = { + "shiver", "pressure", "texture", "friction", "warmth", "chill", "weight", + "echo", "resonance", "murmur", "thrum", "discord", "silence", "cadence", + "glow", "shadow", "radiance", "glimmer", "shimmer", "haze", "aura", "scent", + "stink", "perfume", "savor", "aftertaste", "pungency", "breath", + "intuition", "dread", "premonition", "vertigo", "ache", "yearning", "sting" +}; +const char *Det[] = { + "his", "her", "its", "our", "their", "the", "thy", "this", "that", "thine", + "another", "a", "an", "some", "every", "my", "mine", "those", "these" +}; +const char *Question[] = { + "who", "what", "where" +}; +const char *SubjPronoun[] = { + "he", "she", "it", "they", "we", "who", "naught", "someone" +}; +const char *ObjPronoun[] = { + "him", "her", "it", "them", "us", "whom", "thee", "nothing", "thou" +}; +const char *SortOf[] = { + "sort of", "kind of", "like", "almost" +}; +const char *Condition[] = { + "if", "when", "though", "while", "whenever", "since", "unless", "once" +}; +const char *Vocative[] = { + "o", "alas,", "behold,", "hail,", "farewell,", "stay,", "hark," +}; +const char *Coord[] = { + "and", "but", "for", "yet", "or", "nor", "so" +}; +const char *WillDoth[] = { + "doth", "may", "can", "will", "shall", "must", "hath", "might" +}; +const char *PosPronoun[] = { + "hers", "his", "yours", "theirs", "ours", "its" +}; +const char *Conj2[] = { + "but", "though", "yet" +}; +const char *REEZ[] = { + "and ease", "and chronologies", "and grease", "and debris", + "with expertise", "and TVs", "and bourgeoisies", "and industries", + "without cease", "in three", "in twos and threes", "by degrees", "and ZZZs", + "and memories", "breeze", "trees", "seas", "crease", "lees", "fleece", + "police", "freeze" +}; +const char *IRE[] = { + "fire", "ire", "wire", "mire", "things expire'd", "desire", "spire", + "attire", "pyre", "higher", "liar", "dire", "prior", "cypher" +}; +const char *ACE[] = { + "face", "grace", "suitcase", "pace", "solace", "base", "case", "trace" +}; +const char *ISS[] = { + "terrace", "space", "populace", "surface", "waste", "place" +}; +const char *AND[] = { + "land", "hand", "sand", "strand", "brand", "unmanned", "planned", + "command", "grand", "unplanned", "fanned", "bland", "banned", + "contraband", "expand", "reprimand", "unhand", "demand" +}; + +#define PICK(arr) (arr[rand() % (sizeof(arr) / sizeof(arr[0]))]) + +void +generate_prose(void) { + int template_choice = rand() % 17; + + switch (template_choice) { + case 0: /* Transitive Metaphor */ + tiny_printf("%s %s %s %s %s\n", + PICK(Det), PICK(Noun), PICK(Verb2), + PICK(Adj), PICK(Noun)); + break; + + case 1: /* Auxiliary Question */ + tiny_printf("%s %s %s %s %s %s?\n", + PICK(Question), PICK(WillDoth), PICK(Adv), + PICK(Verb1),PICK(Det), PICK(Noun)); + break; + + case 2: /* Attributive Approximation */ + tiny_printf("%s %s %s %s %s\n", + PICK(SubjPronoun), PICK(Verb2), PICK(SortOf), + PICK(Adj), PICK(Noun)); + break; + + case 3: /* Vocative */ + tiny_printf("%s %s, %s %s %s %s\n", + PICK(Vocative), PICK(Noun), PICK(Det), + PICK(Noun), PICK(Verb1), PICK(ObjPronoun)); + break; + + case 4: /* Relative */ + tiny_printf("%s %s %s, %s %s %s %s?\n", + PICK(Question), PICK(Adj), PICK(Noun), + PICK(Verb1), PICK(SortOf), PICK(Det), + PICK(Noun)); + break; + + case 5: /* Paradox */ + tiny_printf("%s %s %s %s %s %s\n", + PICK(Det), PICK(Noun), PICK(Verb2), + PICK(Det), PICK(Adj), PICK(Noun)); + break; + + case 6: /* Modal Adverbial */ + tiny_printf("%s %s %s %s %s %s\n", + PICK(SubjPronoun), PICK(WillDoth), PICK(Adv), + PICK(Verb1), PICK(Det), PICK(Noun)); + break; + + case 7: /* Prepositional Opener */ + tiny_printf("%s %s %s, %s %s %s\n", + PICK(Prep), PICK(Det), PICK(Noun), + PICK(Det), PICK(Noun), PICK(Verb1)); + break; + + case 8: /* Conditional */ + tiny_printf("%s %s %s %s, %s %s %s %s\n", + PICK(Condition), PICK(SubjPronoun), PICK(WillDoth), + PICK(Verb1), PICK(Det), PICK(Noun), PICK(WillDoth), + PICK(Verb1)); + break; + + case 9: /* Inverse Conditional */ + tiny_printf("%s %s %s %s %s %s %s %s\n", + PICK(Det), PICK(Noun), PICK(WillDoth), + PICK(Verb1), PICK(Condition), PICK(SubjPronoun), + PICK(WillDoth), PICK(Verb1)); + break; + + case 10: /* Possessive Existential */ + tiny_printf("%s %s's %s %s %s %s\n", + PICK(Det), PICK(Noun), PICK(Noun), + PICK(Verb2), PICK(Det), PICK(Noun)); + break; + + case 11: /* Sensory Attestation */ + tiny_printf("%s %s %s of %s %s %s %s\n", + PICK(Det), PICK(Adj), PICK(Sense), + PICK(Noun), PICK(Verb2), PICK(Det), + PICK(Noun)); + break; + + case 12: /* Negative Sensory */ + tiny_printf("no %s %s %s %s %s %s %s %s\n", + PICK(Sense), PICK(Prep), PICK(Det), + PICK(Noun), PICK(WillDoth), PICK(Verb1), + PICK(Det), PICK(Noun)); + break; + + case 13: + tiny_printf("%s %s %s %s\n", + PICK(Det), PICK(Noun), PICK(Verb2), + PICK(AND)); + tiny_printf("%s %s %s %s\n", + PICK(Det), PICK(Adj), PICK(Noun), + PICK(AND)); + break; + + case 14: + tiny_printf("%s %s %s %s\n", + PICK(Det), PICK(Noun), PICK(Verb1), + PICK(REEZ)); + tiny_printf("%s %s %s %s %s\n", + PICK(Prep), PICK(Det), PICK(Adj), + PICK(Noun), PICK(REEZ)); + break; + + case 15: + tiny_printf("%s %s %s %s\n", + PICK(Vocative), PICK(Noun), PICK(Verb2), + PICK(IRE)); + tiny_printf("%s %s %s %s\n", + PICK(Det), PICK(Noun), PICK(WillDoth), + PICK(IRE)); + break; + + case 16: + tiny_printf("%s %s %s %s %s\n", + PICK(SubjPronoun), PICK(Adv), PICK(Verb1), + PICK(Det), PICK(ACE)); + tiny_printf("%s %s %s %s\n", + PICK(Coord), PICK(Verb1), PICK(Det), + PICK(ISS)); + break; + default: break; + } +} + +int +main(void) { + srand((unsigned int)time(NULL)); + int lines = (rand() % 2) + 1; + for (int i = 0; i < lines; i++) + generate_prose(); + return 0; +}