commit 0f032db0cdeb5c57a201e2ff94eb189bf2a877be
Author: Andrew Kloet <andrew@kloet.net>
Date: Thu, 16 Apr 2026 02:18:42 -0400
initial commit
Diffstat:
| A | LICENSE | | | 13 | +++++++++++++ |
| A | Makefile | | | 26 | ++++++++++++++++++++++++++ |
| A | thy.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;
+}