Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 8/23/84; site ucbcad.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!genrad!decvax!ucbvax!ucbcad!faustus From: faustus@ucbcad.UUCP (Wayne A. Christopher) Newsgroups: net.sources.games Subject: Source for insult, flame, babble (Part 3 of 3) Message-ID: <234@ucbcad.UUCP> Date: Sat, 11-May-85 03:25:35 EDT Article-I.D.: ucbcad.234 Posted: Sat May 11 03:25:35 1985 Date-Received: Mon, 13-May-85 02:42:30 EDT References: <399@ho95b.UUCP> Organization: UC Berkeley CAD Group, Berkeley, CA Lines: 1008 I am posting the source for a few programs I wrote some time ago to generate random insults, flames, and sentences. They are written using a pre-processor called "kafka", which is also included. To create them collect all the parts of this posting, pipe them to sh, then run "make". Wayne echo x - etc.c cat >etc.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. * * Some useful things. */ #include #define DEFAULT 3 int nflag = 0; main(ac, av) char **av; { /* The main function for babble. */ int i, j; extern int lpos; srandom(getpid()); if (ac > 1) { j = atoi(av[1]); if (j < 0) { j = -j; nflag = 1; } if (j == 0) { for (i = 0; i < 100; i++) { maketext("start"); kkoutput("@"); lpos = 0; putchar('\n'); putchar('\n'); } exit(0); } for (i = 0; i < j; i++) { maketext("start"); kkoutput("@"); } putchar('\n'); exit(0); } i = DEFAULT; while (i--) { srandom(random()); maketext("start"); kkoutput("@"); } putchar('\n'); exit(0); } !Funky!Stuff! echo x - funwords.c cat >funwords.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. */ #include #define maxlen 8 char rword[20]; char vow[] = {'a','e','i','o','u','y'}; char con[] = {'t','n','s','h','r','d','l','b','c','f','g','j','k', 'm','p','w','v','z','x','q'}; char dip[][2] = {'t','h','s','h','s','s','r','d','q','u', 'l','l','r','n','s','p','s','t','r','z'}; char *fword() { char nl, a, b, c, d; nl = (rand() % maxlen + rand() % maxlen)/2 + 1; for (b = 0; b < nl; ){ switch (rand() % 8) { case 0 : case 1 : case 2 : case 3 : case 4 : case 5 : case 6 : rword[b++] = con[abs(rand() % 20 + rand() % 20 - 20)]; break; case 7: c = abs(rand() % 10 + rand() % 10 - 10); rword[b++] = dip[c][0]; rword[b++] = dip[c][1]; break; } rword[b++] = vow[abs(rand() % 6 + rand() % 6 - 6) ]; } if (rand() % 10 < 7)rword[b++] = con[rand() % 20]; rword[b++]='\0'; return (rword); } main() { char *fword(); srand(getpid()); printf ("%s\n",fword()); } !Funky!Stuff! echo x - init.c cat >init.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. */ #include FILE *textp, *specp, *inp; extern char *sourcefile; init() { /* These should be only defaults here... */ if ((textp = fopen("kaf.text.c", "w")) == NULL) { perror("kaf.text.c"); exit(1); } if ((specp = fopen("kaf.spec.c", "w")) == NULL) { perror("kaf.spec.c"); exit(1); } if (!sourcefile) inp = stdin; else if ((inp = fopen(sourcefile, "r")) == NULL) { perror(sourcefile); exit(1); } fprintf(textp, "/* The user routine file. */\n\n"); fprintf(specp, "/* The specification file. */\n\n"); fprintf(specp, "#include \"kafgraf.h\"\n\n"); return (0); } !Funky!Stuff! echo x - main.c cat >main.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. * * The kafka main function. Usage: kafka [sourcefile] [-v]. */ #include #define USAGE { printf("Usage: kafka [sourcefile] [-v].\n"); exit(1); } int vflag = 0; char *sourcefile = NULL; main(ac, av) char **av; { int ch; for (ch = 1; ch < ac; ch++) { if (!strcmp(av[ch], "-v")) { vflag++; continue; } if (!strcmp(av[ch], "-t")) { /* Do textfile option. (Unimplemented...) */ ch++; continue; } if (!strcmp(av[ch], "-s")) { /* Do specfile option. (Unimplemented...) */ ch++; continue; } /* Sourcefile. */ if (sourcefile) USAGE; sourcefile = av[ch]; } if (!sourcefile) USAGE; /* Set up the files, */ init(); /* read in the information, */ yyparse(); /* and write it out. */ dstrans(); /* That's all... */ exit(0); } /* The error handler. This could use work. */ yyerror() { extern int lineno, errp; fprintf(stderr, "Syntax error in line %d.\n", lineno); fflush(stderr); errp = 1; } !Funky!Stuff! echo x - maketext.c cat >maketext.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. */ #include #include "kafgraf.h" #include "defs.h" struct kkelt kkstack[STACKSIZE]; /* The stack. */ struct kkelt *kksp; /* The stack pointer. */ extern struct kknode *start; extern struct kknode *nodelist; /* This routine is the one that generates a string in the grammer, * beginning with the non-terminal s. Returns -1 if s is not the * name of a non-terminal. */ maketext(s) char *s; { struct kknode *stnode, *kknn; int kkcv; /* First find the start node. */ /* dumpnlist(); Diagnostic. */ for (stnode = nodelist; stnode != NULL; stnode = stnode->kk_link) if (!strcmp(stnode->kk_nodename, s)) break; if (stnode == NULL) { /* Error condition. */ return (-1); } /* Now to generate the text. The generator works like this: first * select one of the rules for the given non-terminal to follow. * If the non-terminal is actually a computed terminal, compute * the value and output it. Otherwise, then push a pointer to the * it onto the stack along with a pointer to the arc that we are * currently following and follow it. If it is a terminal arc, * output the terminal and return, otherwise repeat for the * new nt. When returning, pop the stack and go on to the next * arc, or return if there is none. */ kksp = &kkstack[0]; kksp->ke_node = stnode; kksp->ke_arc = stnode->kk_arc; nextarc: if (kksp->ke_arc == NULL) { /* We have reached the end of the * current rule. */ kksp--; if (kksp < &kkstack[0]) { /* Done? */ return (0); } /* There are more rules to go. */ kksp->ke_arc = kksp->ke_arc->kc_narc; goto nextarc; } /* There are yet arcs to be followed in the current rule. */ if (kksp->ke_arc->kc_to) { /* The arc is to a non-term. Follow it. */ kknn = kksp->ke_arc->kc_to; if (kknn->kk_type == KTCOMP) { /* Compute the value of the terminal. Note that we * don't bother declaring these functions to be * char *. */ kkcv = (int) (*kknn->kk_func) (); if (kkcv == NULL) { /* Doesn't like this rule. */ kksp->ke_arc = kksp->ke_arc->kc_narc; goto nextarc; } else { /* Cool. */ kkoutput((char *) kkcv); kksp->ke_arc = kksp->ke_arc->kc_narc; goto nextarc; } } /* It must be a real non-terminal then. Now decide * which rule to use. */ while (kknn) { if ((kknn->kk_func == NULL) || (*kknn->kk_func) () ) break; kknn = kknn->kk_next; } if (kknn == NULL) { fprintf(stderr, "Error: no rules accepted for %s\n", kksp->ke_node->kk_nodename); exit(1); } kksp++; kksp->ke_node = kknn; kksp->ke_arc = kknn->kk_arc; goto nextarc; } /* It is a terminal. */ kkoutput(kksp->ke_arc->kc_toname); kksp->ke_arc = kksp->ke_arc->kc_narc; goto nextarc; } !Funky!Stuff! echo x - output.c cat >output.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. * * The output function for babble. Well, there has to be a special control * character for newlines, spaces, and tabs... This is %. %n, %t, %s. */ #include extern int nflag; #define vowel(c) ((c == 'a') || (c == 'e') || (c == 'i') || (c == 'o') \ || (c == 'u') || (c == 'A') || (c == 'E') \ || (c == 'I') || (c == 'O') || (c == 'U')) #define wspace(c) ((c == ' ') || (c == '\n') || (c == '\t')) int lpos = 0; kkoutput(string) char *string; { static char text[BUFSIZ]; static int tpos = 0; int a, b; int vow, x; char c; /* Save up text until it is time to write out the stuff. */ if (*string != '@') { while (*string) text[tpos++] = *(string++); text[tpos++] = ' '; return; } else { a = 0; if (text[0] >= 'a' && text[0] <= 'z') text[0] = text[0] - 'a' + 'A'; if (lpos > 64) { putchar('\n'); lpos = 0; } loop: if (a >= tpos) { tpos = 0; return; } if (text[a] != ' ' && text[a] != '\t' && text[a] != '!' && text[a] != '\n' && text[a] != ',' && text[a] != '.' && text[a] != '?') { if ((text[a] == 'a') && ((a == 0) || wspace(text[a - 1])) && a < tpos && wspace(text[a + 1])) { for (x = a + 1; x < tpos; x++) if (!wspace(text[x])) break; if ((x != tpos) && vowel(text[x])) { putchar('a'); putchar('n'); a++; lpos += 2; goto loop; } } if (text[a] == '#') { a++; goto loop; } if (text[a] == '%') { switch(text[++a]) { case 'n': { putchar('\n'); lpos = 0; a++; break; } case 's': { putchar(' '); lpos++; a++; break; } case 't': { putchar('\t'); lpos = (lpos / 8 + 1) * 8; a++; break; } default: { putchar(text[a++]); lpos++; break; } } goto loop; } putchar(text[a]); lpos++; a++; goto loop; } if (text[a] == '.' || text[a] == ',' || text[a] == '!' || text[a] == '?') { putchar(text[a++]); if (*(string + 1) == 'P') { putchar('\n'); putchar('\t'); lpos = 8; goto loop; } while (text[a] == ' ' || text[a] == '\t' || text[a] == '\n') a++; goto space; } for (b = a; b < tpos; b++) { if (text[b] != ' ' && text[b] != '\t' && text[b] != '\n') break; } if (text[b] == '.' || text[b] == ',' || text[b] == '!' || text[b] == '?') { a = b; goto loop; } space: for (x = a + 1; x < tpos; x++) if (!wspace(text[x])) { if (text[x] == '#') { a = x + 1; goto loop; } break; } if ((lpos > 64) && !nflag) { if (tpos > a) { putchar('\n'); lpos = 0; } else { /* tpos = 1; lpos = 0; text[0] = '\n'; return; */ } } else { putchar(' '); lpos++; } while (text[a] == ' ' || text[a] == '\t' || text[a] == '\n') a++; goto loop; } } !Funky!Stuff! echo x - psubr.c cat >psubr.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. * * Routines for the parser. These create and maintain the graph that the * parser is responsible for. */ #include "kafgraf.h" #include #include struct knode *n; struct knode *hashtab[256]; struct karc *avarc; int i, c; /* Enter a new rule. */ newrule(name) char *name; { struct knode *prev; unsigned char hashind, phash(); hashind = phash(name); if (hashtab[hashind]) { for (n = hashtab[hashind]; n != NULL; n = n->kn_nnt) { if (!strcmp(n->kn_nodename, name)) { while (n->kn_next) n = n->kn_next; n->kn_next = (struct knode *) malloc(sizeof (struct knode)); n = n->kn_next; goto gotit; } prev = n; } if (n == NULL) { n = prev->kn_nnt = (struct knode *) malloc(sizeof (struct knode)); } } else { hashtab[hashind] = (struct knode *) malloc(sizeof (struct knode)); n = hashtab[hashind]; } gotit: strcpy(n->kn_nodename, name); n->kn_nnt = n->kn_next = NULL; n->kn_type = KTNTERM; n->kn_nodenumber = newnum2(); avarc = n->kn_arc = 0; /* Set n->kn_arc to the arc when you * find one. */ /* The new node is now in place. Now to collect the arcs... */ } /* Enter a computed terminal. */ docompterm() { int fnum; n->kn_type = KTCOMP; n->kn_next = n->kn_nnt = NULL; n->kn_arc = NULL; fnum = newnum(); n->kn_fnum = fnum; /* Now we have to read in the actual function * definition. This is kept in "kaf.text.c", along * with the rest of the user-supplied C code. */ if (transcribe(fnum)) { fprintf(stderr, "Premature end of file.\n"); exit(1); } } /* Enter a nonterminal in a rule. */ dononterm(name) char *name; { /* Found a non-terminal. Note that throughout * this process n will point to the main functor * and avarc will point the last used arc. Thus * when the end is found avarc->narc must be set = NULL. * (This will also occur immediately if the rule * is determined to be a computed terminal.) * Also references in arcs cannot be resolved * until all rules are in... */ if (avarc) { avarc->ka_narc = (struct karc *) malloc(sizeof (struct karc)); avarc = avarc->ka_narc; } else { /* This is the first one. */ avarc = (struct karc *) malloc(sizeof (struct karc)); n->kn_arc = avarc; } strcpy(avarc->ka_toname, name); avarc->ka_type = KTNTERM; avarc->ka_arcnumber = newnum2(); } /* Enter a terminal. */ doterm(name) char *name; { /* Just set ka_toname to the name of the terminal and set ka_type * to be KTTERM. */ if (avarc) { avarc->ka_narc = (struct karc *) malloc(sizeof (struct karc)); avarc = avarc->ka_narc; } else { /* This is the first arc. */ avarc = (struct karc *) malloc(sizeof (struct karc)); n->kn_arc = avarc; } strcpy(avarc->ka_toname, name); avarc->ka_type = KTTERM; avarc->ka_arcnumber = newnum2(); } /* Copy C code in defs section. */ copyccode() { int c; extern FILE *textp; for (;;) { while ((c = input()) != '\n') { if (c == '\0') { fprintf(stderr, "Unexpected EOF.\n"); exit(1); } putc(c, textp); } c = input(); if (c == '%') { /* { boo hiss */ if ((c = input()) == '}') return; putc('\n', textp); putc('%', textp); putc(c, textp); continue; } putc('\n', textp); unput(c); } } !Funky!Stuff! echo x - stuff.c cat >stuff.c <<'!Funky!Stuff!' /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. * * Assorted things. */ #include extern FILE *specp, *textp; /* This should exist somewhere. It should be first in the file... */ char *itoa(num) { static char buf[32]; int p; p = 31; do { buf[p] = (char) num % 10 - '\0'; num /= 10; p--; } while (num); return (buf + p); } /* The hashing function. Note that hashtables have to be of size 256. * They probably don't need to be very big, and the fn is simple... */ phash(string) char *string; { unsigned char rv = 0; while (*string) rv += *(string++); return (rv); } /* This function reads a C function from the input file. It is kept in * the file pointed to by outp. It takes the function number as an argument * (the function will not have arguments). Note that this will be messed * up by unbalanced brackets in comments. */ transcribe(num) int num; { /* Note that we must add the first bracket... */ extern FILE *textp; extern char input(); char c; int brct; fprintf(textp, "\n_kkFunc%d ()\n{\n", num); brct = 1; while (c = input()) { /* This is the lex input function. */ if (c == '{') brct++; if (c == '}') brct--; putc(c, textp); if (!brct) return (0); } /* Reached EOF. */ return (1); } /* "Gensym" for user supplied C code names. */ newnum() { static int n = 0; n++; return (n); } /* "Gensym" for arcnumbers and nodenumbers. */ newnum2() { static int n = 0; n++; return (n); } !Funky!Stuff! echo x - parse.y cat >parse.y <<'!Funky!Stuff!' %{ /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. * * The Kafka parser. It is called once, and builds the * necessary data structures for the specification. */ #include #include #include "kafgraf.h" char buf[BUFSIZ]; extern FILE *inp; extern struct karc *avarc; %} %start spec %token LBRACE RBRACE NONTERM TERM RULEOP SSEP KESC %token COPEN CCLOSE SEMI %% /* The following definition is much like the one for * yacc itself described in the user's guide. */ spec : defs SSEP rules tail ; tail : /* Empty. */ | SSEP { /* Deal with program section here. */ int n; extern FILE *textp; do { n = fread(buf, 1, BUFSIZ, inp); fwrite(buf, 1, n, textp); } while (n == BUFSIZ); } ; defs : /* Empty. */ | defs def ; def : KESC { /* Deal with Kafka escape here. * (None yet implemented.) */ } | COPEN { /* Copy C code here. */ copyccode(); } ; rules : /* Empty. */ | rules rule ; rule : NONTERM { /* Got the NT name. Create a node. */ newrule($1); } RULEOP rtail SEMI ; rtail : LBRACE { /* A computed terminal. */ docompterm(); } | stuff LBRACE { /* We have to copy the C code and clean up. */ int fnum; extern struct knode *n; fnum = newnum(); transcribe(fnum); n->kn_fnum = fnum; avarc->ka_narc = NULL; } | stuff { /* Just clean up. */ n->kn_fnum = 0; avarc->ka_narc = NULL; } ; stuff : /* Empty. */ | stuff thing ; thing : NONTERM { dononterm($1); } | TERM { doterm($1); } ; %% !Funky!Stuff! echo x - lexical.l cat >lexical.l <<'!Funky!Stuff!' %{ /* RCS Info: $Revision: $ on $Date: $ * $Source: $ * Copyright (c) 1985 Wayne A. Christopher * Permission is granted to do anything with this code except sell it * or remove this message. * * The lexical analyzer for kafka. */ #include "y.tab.h" char lookahead[100]; /* For pushback of characters. */ int nlk = 0; /* # characters pushed back. */ extern FILE *inp; /* The input file. */ extern int yylval; %} %% \<[^ \t\n]*\> { /* A non-terminal. */ yytext[yyleng - 1] = '\0'; yylval = (int) (yytext + 1); return (NONTERM); } [ \t\n]* { /* White space. */ } ^#.*$ { /* A comment. Ignore. */ } [^ \"\t\n=\<\>;\{\}\%]* { /* An unquoted terminal string. */ yylval = (int) yytext; return (TERM); } \"[^\"]*\" { /* A quoted terminal. */ yytext[yyleng - 1] = '\0'; yylval = (int) (yytext + 1); return (TERM); } = { /* The rule operator. */ return (RULEOP); } ^\%\% { /* The section seperator. */ return (SSEP); } ^\%[A-Za-z].*$ { /* A kafka escape. */ yylval = (int) yytext[1]; return (KESC); } ^\%\{ { /* Begin def section C code. */ return (COPEN); } ^\%\} { /* End def section C code. */ return (CCLOSE); } \{ { /* Open brace. */ return (LBRACE); } \} { /* Close brace. */ return (RBRACE); } \; { /* Semicolon. */ return (SEMI); } %% /* The redefined input and unput functions -- they must be usable by * other routines. */ int lineno = 0; char input() { int c; if (nlk == 0) { c = getc(inp); if (c == '\n') lineno++; if (c == EOF) return (0); else return (c); } else { c = lookahead[--nlk]; return (c); } } unput(c) char c; { lookahead[nlk++] = c; return; } /* Don't need this thing... */ yywrap() { return (1); } !Funky!Stuff!