cathy@comp.lancs.ac.uk (Cathy) (07/10/86)
The following source forms the xref program for syntactically correct Yacc files and runs on UN*X 4.2. All tokens and non-terminals in the grammar are located, together with a list of the line numbers at which they occur. The output is a numbered listing of the Yacc grammar upto the second '%%' or EOF, followed by the xref table. Should a syntax error occur before normal termin- ation, the grammar listing ends and as much of the table as has been constructed is printed. All comments /* .. */ , %{ .. %} and actions { .. } are ignored, although they appear in the listing. Table data is stored in "table[]", which holds MAXINTS terminal / non-terminal names, each of length <= MAXCHARS. A total of MAXDEFS defining (rhs of rule) occurances can be held, and a total of MAXOCCS lhs occurances. Each identifier which may be the start token has the message held in "start_maybe[]" printed - likewise all possible tokens are accompanied by "token_maybe[]". All defining occurances are printed following "declared_at_mark[]", and all other occurances following "occurs_at_mark[]". An example of the output is given below. 1 : %{ 2 : # include <ctype.h> 3 : %} 4 : 5 : 6 : /*******************************************************\ 7 : * * 8 : * Date : Fri Jul 4 00:50:04 BST 1986 * 9 : * * 10 : \*******************************************************/ 11 : 12 : %token IDENTIFIER START_TOKEN NUMBER 13 : 14 : %start small 15 : 16 : %% 17 : 18 : small 19 : : start middle 20 : { 21 : printf("\n\nMiddle"); 22 : yyclearin; 23 : return(0); 24 : } 25 : end postamble 26 : ; 27 : 28 : start 29 : : /* empty */ 30 : | START_TOKEN 31 : ; 32 : 33 : middle 34 : : 35 : ; 36 : 37 : middle 38 : : MID_TOKEN 39 : | /* empty */ 40 : ; 41 : 42 : %% 'small' - ~~~~~~~ *18 , never occurs on rhs of rule - start rule? 'start' - ~~~~~~~ *28 , 19 'middle' - ~~~~~~~ *33 , *37, 19 'end' - ~~~~~~~ is not declared - token??, 25 'postamble' - ~~~~~~~ is not declared - token??, 25 'START_TOKEN' - ~~~~~~~ is not declared - token??, 30 'MID_TOKEN' - ~~~~~~~ is not declared - token??, 38 End of X-ref ~~~~~~~~~~~~ The text below should be stored in the file pack.out and can be converted from the single file to the three files xref-grammar, xref-regular_expr, xref-line.h by issuing the command sh pack.out Issue the commands lex xref-regular_expr yacc -vd xref-grammar cc y.tab.c -o xref to produce an executable version and use as any UN*X command. <---------------------------------- Cut here ----------------------------------> #!/bin/sh echo 'Start of pack.out, part 01 of 01:' echo 'x - xref-grammar' sed 's/^X//' > xref-grammar << '/' X%{ X X# include <ctype.h> X# include <stdio.h> X X%} X X X /*******************************************************\ X * * X * X_reference program for YACC files * X * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * X * * X * Cathy Taylor, * X * c/o Department of Computing, * X * University of Lancaster, * X * Bailrigg, Lancaster, England. * X * * X * Date : Fri Jul 4 00:50:04 BST 1986 * X * * X \*******************************************************/ X X X /***********************************************\ X * * X * Yacc Input Syntax * X * ~~~~~~~~~~~~~~~~~ * X * * X * Adapted from the document * X * 'YACC - Yet Another Compiler Compiler' * X * by * X * S. C. Johnson * X * * X * Date: Tue Jul 1 02:40:18 BST 1986 * X * * X \***********************************************/ X X X%token IDENTIFIER CHARACTER NUMBER X%token LEFT RIGHT NONASSOC TOKEN PREC TYPE START UNION X%token PER PERCURL ACT X%token COLON SEMICOLON COMMA OR LESS GREATER X X%start spec X X%% X Xspec X : defs PER rules tail X { X printf("\n\n"); X yyclearin; X return(0); X } X ; X Xtail X : /* empty */ X | PER X ; X Xdefs X : /* empty */ X | def_bk X ; X Xdef_bk X : def X | def_bk def X ; X Xdef X : START IDENTIFIER X | UNION X | PERCURL X | rword tag nlist X ; X Xrword X : TOKEN X | LEFT X | RIGHT X | NONASSOC X | TYPE X ; X Xtag X : /* empty */ X | LESS IDENTIFIER GREATER X ; X Xnlist X : nmno X | nlist opt_comma nmno X ; X Xopt_comma X : /* empty */ X | COMMA X ; X Xnmno X : IDENTIFIER opt_num X ; X Xopt_num X : /* empty */ X | NUMBER X ; Xrules X : rule X | rules rule X ; X Xrule X : IDENTIFIER X { X yyaction(ON_C_IDENT,line); X } X COLON body SEMICOLON X ; X Xbody X : body_block X | body OR body_block X ; X Xbody_block X : /* empty */ X | body_block body_entity X ; X Xbody_entity X : opt_prec id_ent X | ACT X ; X Xid_ent X : IDENTIFIER X { X yyaction(ON_IDENT,line); X } X | CHARACTER X ; X Xopt_prec X : /* empty */ X | PREC X ; X X X%% X X# include <stdio.h> X# include "lex.yy.c" X# include "xref-line.h" X X#define ON_C_IDENT 000 X#define ON_IDENT 001 X X#define MAXIDENTS 1000 X#define MAXCHARS 100 X#define MAXDEFS 20 X#define MAXOCCS 1000 X Xstruct IREC { X char ident[MAXCHARS]; X int desc[MAXDEFS]; X int nextdesc; X int occ[MAXOCCS]; X int nextocc; X } idents[MAXIDENTS]; X X Xyyaction (action,ln) Xint action; Xint ln; X{ X int id; X X id = 0; X while ( strcmp(idents[id].ident,yytext) != 0 && strcmp(idents[id].ident,"") != 0 ) X id++; X X if ( strcmp(idents[id].ident, yytext) != 0 ) X { X X /*******************************************************\ X * * X * New non-terminal to be stored. * X * No distinction is made here between tokens * X * and (non) terminals. * X * * X \*******************************************************/ X X strcpy(idents[id].ident,yytext); X idents[id].nextdesc = 0; X idents[id].nextocc = 0; X } /* fi */ X X switch (action) { X case ON_C_IDENT: X X /*******************************************************\ X * * X * Add to list of definition lines. * X * * X \*******************************************************/ X X idents[id].desc[idents[id].nextdesc++] = ln; X break; X X case ON_IDENT: X X /*******************************************************\ X * * X * Add to list of occurance lines. * X * * X \*******************************************************/ X X idents[id].occ[idents[id].nextocc++] = ln; X break; X X default : X fprintf (stdout, "yyaction: invalid action\n"); X return (-1); X } /* hctiws */ X return (0); X} /* corp */ X Xnline(ln) Xint ln; X{ X printf("%4d :\t",ln); X} X X X char declared_at_mark[] = "*"; X char occurs_at_mark[] = ""; X char token_maybe[] = "is not declared - token??"; X char start_maybe[] = "never occurs on rhs of rule - start rule?"; X X/* X* Strings for output X*/ X Xmain () X{ X int ind,id; X X strcpy(idents[0].ident,""); X X line = 0; X nline(++line); X X yyparse (); X X id = 0; X while( strcmp(idents[id].ident,"") != 0 ) X { X printf("\n'%s' -\n~~~~~~~\t\t",idents[id].ident); X if (idents[id].nextdesc == 0 ) X printf("%s",token_maybe); X else X { X ind = 0; X printf("*%d ",idents[id].desc[ind++]); X for ( ind=1; ind < idents[id].nextdesc ; ind++) X printf(", %s%d",declared_at_mark,idents[id].desc[ind]); X } X if (idents[id].occ[0] == 0) X printf(", %s",start_maybe); X else X { X for ( ind = 0; ind < idents[id].nextocc ; ind++ ) X printf(", %s%d",occurs_at_mark,idents[id].occ[ind]); X } X id++; X } X printf("\n\n\tEnd of X-ref\n\t~~~~~~~~~~~~\n"); X} /* niam */ X Xyyerror(mess) Xchar *mess; X{ X printf("\n\t%s\n",mess); X} /* corp */ / echo 'x - xref-regular_expr' sed 's/^X//' > xref-regular_expr << '/' X X /*******************************************************\ X * * X * X_ref for YACC - LEX file * X * ~~~~~~~~~~~~~~~~~~~~~~~~~ * X * * X * Date: Tue Jul 1 03:36:21 BST 1986 * X * * X * Cathy Taylor, * X * c/o Department of Computing, * X * University of Lancaster, * X * Bailrigg, Lancaster, England. * X * * X \*******************************************************/ X X%{ X X# include <stdio.h> X# include "xref-line.h" X X#define TRUE 1 X#define FALSE 0 X Xint recognised; Xchar c; X X%} X X /* abbreviations */ X Xdigit [0-9] Xu_case [A-Z] Xl_case [a-z] Xid_char [A-Za-z0-9_] Xletter [A-Za-z] Xwhite [\t ] X X X%% X X"/*" { X ECHO; X recognised = FALSE; X c = nextchar(); X while (recognised == FALSE) X { X while (c != '*') X c = nextchar(); X c = nextchar(); X if (c == '\/') X recognised = TRUE; X } X } X"%{" { X ECHO; X recognised = FALSE; X c = nextchar(); X while (recognised == FALSE) X { X while (c != '\%') X c = nextchar(); X c = nextchar(); X if (c == '\}') X recognised = TRUE; X } X return(PERCURL); X } X"{" { X X/* X* Although LEX can cope with the full definition, X* ( "{"[^\}]*"}" ) this may overrun the lex buffer (200 chars). X* Thus this routine. X*/ X X ECHO; X c = nextchar(); X while (c != '\}') X c = nextchar(); X return(ACT); X } X{letter}{id_char}* { X ECHO; X return(IDENTIFIER); X } X"'"\\?[^']+"'" { X ECHO; X return(CHARACTER); X } X{white}+ { X ECHO; X } X{digit}+ { X ECHO; X return(NUMBER); X } X"%"{white}*"left" { X ECHO; X return(LEFT); X } X"%"{white}*"right" { X ECHO; X return(RIGHT); X } X"%"{white}*"nonassoc" { X ECHO; X return(NONASSOC); X } X"%"{white}*"token" { X ECHO; X return(TOKEN); X } X"%"{white}*"prec" { X ECHO; X return(PREC); X } X"%"{white}*"type" { X ECHO; X return(TYPE); X } X"%"{white}*"start" { X ECHO; X return(START); X } X"%"{white}*"union" { X ECHO; X return(UNION); X } X"%%" { X ECHO; X return(PER); X } X":" { X ECHO; X return(COLON); X } X";" { X ECHO; X return(SEMICOLON); X } X"," { X ECHO; X return(COMMA); X } X"|" { X ECHO; X return(OR); X } X"<" { X ECHO; X return(LESS); X } X">" { X ECHO; X return(GREATER); X } X"\n" { X ECHO; X nline(++line); X } X X%% X Xyywrap() X{ X /* wrap-up procedure */ X return(1); X} X Xnextchar() X{ X char c; X X c = input(); X printf("%c",c); X if (c == '\n') X nline(++line); X return(c); X} / echo 'x - xref-line.h' sed 's/^X//' > xref-line.h << '/' X X int line; / echo 'Part 01 of pack.out complete.' exit <---------------------------------- Cut here ----------------------------------> -- - Marxist unite! - Comrades unite! - Bad-spellers of the world untie! UUCP: ...!seismo!mcvax!ukc!dcl-cs!cathy DARPA: cathy%lancs.comp@ucl-cs | Post: University of Lancaster, JANET: cathy@uk.ac.lancs.comp | Department of Computing, Phone: +44 524 65201 ext ???? | Bailrigg, Lancaster, LA1 4YR, UK.
cathy@dcl-cs.UUCP (07/14/86)
( Apologies for sending the wrong version here and to mod.sources a few days ago. Correct version is listed below. All flames to above address. ) The following source forms the xref program for syntactically correct Yacc files and runs on UN*X 4.2. All tokens and non-terminals in the grammar are located, together with a list of the line numbers at which they occur. The output is a numbered listing of the Yacc grammar upto the second '%%' or EOF, followed by an alphabetical listing of the xref table. Should a syntax error occur before normal termination, the grammar listing ends and as much of the table as has been constructed is printed. All comments /* .. */ , %{ .. %} and actions { .. } are ignored, although they appear in the listing. Overflow in any field of the data structure holding the table information is reported as it occurs and execution terminates without printing any xref information. Table data is stored in "table[]", which holds MAXINTS terminal/non- terminal names, each of length <= MAXCHARS. A total of MAXDEFS defining (rhs of rule) occurances can be held, and a total of MAXOCCS lhs occurances. Each ident- ifier which may be the start token has the message held in "start_maybe[]" printed - likewise all possible tokens are accompanied by "token_maybe[]". All defining occurances are printed following "declared_at_mark[]", and all other occurances following "occurs_at_mark[]". An example of the output is given below. 1 : %{ 2 : 3 : 4 : %} 5 : 6 : /*******************************************************\ 7 : * * 8 : * Trivial Yacc grammar. * 9 : * ~~~~~~~~~~~~~~~~~~~~~ * 10 : * * 11 : \*******************************************************/ 12 : 13 : %token IDENTIFIER CHARACTER NUMBER 14 : 15 : %start start 16 : 17 : %% 18 : 19 : start 20 : : begin middle end 21 : ; 22 : 23 : begin 24 : : BEGIN 25 : ; 26 : 27 : middle 28 : : 29 : ; 30 : 31 : begin 32 : : 33 : | ALT_BEGIN 34 : ; 35 : 36 : ' ALT_BEGIN ' - is not declared - token?? 1 occurances at lines , 33 ' BEGIN ' - is not declared - token?? 1 occurances at lines , 24 ' begin ' - *23 , *31 1 occurances at lines , 20 ' end ' - is not declared - token?? 1 occurances at lines , 20 ' middle ' - *27 1 occurances at lines , 20 ' start ' - *19 , never occurs on rhs of rule - start rule? End of X-ref ~~~~~~~~~~~~ The text below should be stored in the file pack.out and can be converted from the single file to the three files xref-grammar xref-regular_expr xref-line.h by issuing the command sh pack.out Issue the commands lex xref-regular_expr yacc -vd xref-grammar cc y.tab.c -o xref to produce an executable version and use as any UN*X command. <---------------------------------- Cut here ----------------------------------> #!/bin/sh echo 'Start of pack.out, part 01 of 01:' echo 'x - xref-grammar' sed 's/^X//' > xref-grammar << '/' X%{ X X# include <ctype.h> X# include <stdio.h> X X X /***************************************************************\ X * * X * Error messages. * X * * X \***************************************************************/ X Xchar *charroom = " MAXCHARS TOO SMALL - no room to store "; Xchar *occroom = " MAXOCCS TOO SMALL - no room to store occurance of "; Xchar *defroom = " MAXDEFS TOO SMALL - no room to store definition of "; Xchar *idroom = " MAXIDENTS TOO SMALL - no space in table for new identifier "; X X%} X X X /*******************************************************\ X * * X * X_reference program for YACC files * X * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * X * * X * Cathy Taylor, * X * c/o Department of Computing, * X * University of Lancaster, * X * Bailrigg, Lancaster, England. * X * * X * Date : Mon Jul 14 01:24:34 BST 1986 * X * * X \*******************************************************/ X X X /***********************************************\ X * * X * Yacc Input Syntax * X * ~~~~~~~~~~~~~~~~~ * X * * X * Adapted from the document * X * 'YACC - Yet Another Compiler Compiler' * X * by * X * S. C. Johnson * X * * X * Date: Tue Jul 1 02:40:18 BST 1986 * X * * X \***********************************************/ X X X%token IDENTIFIER CHARACTER NUMBER X%token LEFT RIGHT NONASSOC TOKEN PREC TYPE START UNION X%token PER PERCURL ACT X%token COLON SEMICOLON COMMA OR LESS GREATER X X%start spec X X%% X Xspec X : defs PER rules tail X { X printf("\n\n"); X yyclearin; X return(0); X } X ; X Xtail X : /* empty */ X | PER X ; X Xdefs X : /* empty */ X | def_bk X ; X Xdef_bk X : def X | def_bk def X ; X Xdef X : START IDENTIFIER X | UNION X | PERCURL X | rword tag nlist X ; X Xrword X : TOKEN X | LEFT X | RIGHT X | NONASSOC X | TYPE X ; X Xtag X : /* empty */ X | LESS IDENTIFIER GREATER X ; X Xnlist X : nmno X | nlist opt_comma nmno X ; X Xopt_comma X : /* empty */ X | COMMA X ; X Xnmno X : IDENTIFIER opt_num X ; X Xopt_num X : /* empty */ X | NUMBER X ; Xrules X : rule X | rules rule X ; X Xrule X : IDENTIFIER X { X yyaction(ON_C_IDENT,line); X } X COLON body SEMICOLON X ; X Xbody X : body_block X | body OR body_block X ; X Xbody_block X : /* empty */ X | body_block body_entity X ; X Xbody_entity X : opt_prec id_ent X | ACT X ; X Xid_ent X : IDENTIFIER X { X yyaction(ON_IDENT,line); X } X | CHARACTER X ; X Xopt_prec X : /* empty */ X | PREC X ; X X X%% X X# include <stdio.h> X# include "lex.yy.c" X# include "sto.h" X X#define ON_C_IDENT 000 X#define ON_IDENT 001 X X#define MAXIDENTS 200 X#define MAXCHARS 50 X#define MAXDEFS 5 X#define MAXOCCS 80 X Xstruct IREC { X char symbol[MAXCHARS]; X int description[MAXDEFS]; X int next_d; X int occurance[MAXOCCS]; X int next_o; X } table[MAXIDENTS]; X X Xyyaction (action,ln) Xint action; Xint ln; X{ X int id; X X id = 0; X while ( strcmp(table[id].symbol,yytext) != 0 && strcmp(table[id].symbol,"") != 0 ) X { X if (id== MAXIDENTS) X { X yyerror(strcat(idroom,yytext),ln); X exit(3); X } X id++; X } X X if ( strcmp(table[id].symbol, yytext) != 0 ) X { X X /*******************************************************\ X * * X * New non-terminal to be stored. * X * No distinction is made here between tokens * X * and (non) terminals. * X * * X \*******************************************************/ X X if (strlen(yytext) == MAXCHARS) X { X yyerror(strcat(charroom,yytext),ln); X exit(3); X } /* fi */ X strcpy(table[id].symbol,yytext); X table[id].next_d = 0; X table[id].next_o = 0; X } /* fi */ X X switch (action) { X case ON_C_IDENT: X X /*******************************************************\ X * * X * Add to list of definition lines. * X * * X \*******************************************************/ X X if (table[id].next_d == MAXDEFS ) X { X yyerror(strcat(defroom,yytext),ln); X exit(3); X } /* fi */ X table[id].description[table[id].next_d++] = ln; X break; X X X case ON_IDENT: X X /*******************************************************\ X * * X * Add to list of occurance lines. * X * * X \*******************************************************/ X if (table[id].next_o == MAXOCCS ) X { X yyerror(strcat(occroom,yytext),ln); X exit(3); X } /* fi */ X table[id].occurance[table[id].next_o++] = ln; X break; X X default : X fprintf (stdout, "yyaction: invalid action\n"); X return (-1); X } /* hctiws */ X return (0); X} /* corp */ X Xnline(ln) Xint ln; X{ X printf("%4d :\t",ln); X} X X X char declared_at_mark[] = "*"; X char occurs_at_mark[] = ""; X char token_maybe[] = "is not declared - token??"; X char start_maybe[] = "never occurs on rhs of rule - start rule?"; X X/* X* Strings for output X*/ X Xmain () X{ X int ind,id,numids; X int i, j, gap, temp; X int pos[MAXIDENTS]; X X strcpy(table[0].symbol,""); X X line = 0; X nline(++line); X X yyparse (); X X id = 0; X while ( strcmp(table[id].symbol,"") != 0 ) X pos[id] = id++; X X X /***************************************************************\ X * * X * Grubby sort. Pointers to records held in "pos[]". * X * * X \***************************************************************/ X X numids = id; X for (gap = numids/2; gap > 0; gap /= 2) X for (i=gap; i<numids; i++) X for (j=i-gap; j>=0; j-=gap) X if (strcmp(table[pos[j]].symbol,table[pos[j+gap]].symbol) > 0) X { X temp = pos[j]; X pos[j] = pos[j+gap]; X pos[j+gap] = temp; X } X X X for ( i=0; i<numids; i++ ) X { X id = pos[i]; X printf("\n' %s ' -\n\t\t\t",table[id].symbol); X if (table[id].next_d == 0 ) X printf("%s",token_maybe); X else X { X ind = 0; X printf("%s%d ",declared_at_mark,table[id].description[ind++]); X for ( ind=1; ind < table[id].next_d ; ind++) X printf(", %s%d",declared_at_mark,table[id].description[ind]); X } X if (table[id].occurance[0] == 0) X printf(", %s",start_maybe); X else X { X printf(" %d occurances at lines ",table[id].next_o); X for ( ind = 0; ind < table[id].next_o ; ind++ ) X printf(", %s%d",occurs_at_mark,table[id].occurance[ind]); X } X id++; X } X printf("\n\n\tEnd of X-ref\n\t~~~~~~~~~~~~\n"); X} /* niam */ X Xyyerror(mess,errline) Xchar *mess; X{ X printf("\n\n\n\t%s at line %d\n",mess,errline); X} /* corp */ / echo 'x - xref-regular_expr' sed 's/^X//' > xref-regular_expr << '/' X X /*******************************************************\ X * * X * X_ref for YACC - LEX file * X * ~~~~~~~~~~~~~~~~~~~~~~~~~ * X * * X * Date: Tue Jul 1 03:36:21 BST 1986 * X * * X \*******************************************************/ X X%{ X X# include <stdio.h> X# include "sto.h" X X#define TRUE 1 X#define FALSE 0 X Xint recognised; Xchar c; X X%} X X /* abbreviations */ X Xdigit [0-9] Xu_case [A-Z] Xl_case [a-z] Xid_char [A-Za-z0-9_] Xletter [A-Za-z] Xwhite [\t ] X X X%% X X"/*" { X ECHO; X recognised = FALSE; X c = nextchar(); X while (recognised == FALSE) X { X while (c != '*') X c = nextchar(); X c = nextchar(); X if (c == '\/') X recognised = TRUE; X } X } X"%{" { X ECHO; X recognised = FALSE; X c = nextchar(); X while (recognised == FALSE) X { X while (c != '\%') X c = nextchar(); X c = nextchar(); X if (c == '\}') X recognised = TRUE; X } X return(PERCURL); X } X"{" { X X/* X* Although LEX can cope with the full definition, X* ( "{"[^\}]*"}" ) this may overrun the lex buffer (200 chars). X* Thus this routine. X*/ X X ECHO; X c = nextchar(); X while (c != '\}') X c = nextchar(); X return(ACT); X } X{letter}{id_char}* { X ECHO; X return(IDENTIFIER); X } X"'"\\?[^']+"'" { X ECHO; X return(CHARACTER); X } X{white}+ { X ECHO; X } X{digit}+ { X ECHO; X return(NUMBER); X } X"%"{white}*"left" { X ECHO; X return(LEFT); X } X"%"{white}*"right" { X ECHO; X return(RIGHT); X } X"%"{white}*"nonassoc" { X ECHO; X return(NONASSOC); X } X"%"{white}*"token" { X ECHO; X return(TOKEN); X } X"%"{white}*"prec" { X ECHO; X return(PREC); X } X"%"{white}*"type" { X ECHO; X return(TYPE); X } X"%"{white}*"start" { X ECHO; X return(START); X } X"%"{white}*"union" { X ECHO; X return(UNION); X } X"%%" { X ECHO; X return(PER); X } X":" { X ECHO; X return(COLON); X } X";" { X ECHO; X return(SEMICOLON); X } X"," { X ECHO; X return(COMMA); X } X"|" { X ECHO; X return(OR); X } X"<" { X ECHO; X return(LESS); X } X">" { X ECHO; X return(GREATER); X } X"\n" { X ECHO; X nline(++line); X } X X%% X Xyywrap() X{ X /* wrap-up procedure */ X return(1); X} X Xnextchar() X{ X char c; X X c = input(); X printf("%c",c); X if (c == '\n') X nline(++line); X return(c); X} / echo 'x - xref-line.h' sed 's/^X//' > xref-line.h << '/' X X int line; / echo 'Part 01 of pack.out complete.' exit <---------------------------------- Cut here ----------------------------------> -- - Marxist unite! - Comrades unite! - Bad-spellers of the world untie! UUCP: ...!seismo!mcvax!ukc!dcl-cs!cathy DARPA: cathy%lancs.comp@ucl-cs | Post: University of Lancaster, JANET: cathy@uk.ac.lancs.comp | Department of Computing, Phone: +44 524 65201 ext ???? | Bailrigg, Lancaster, LA1 4YR, UK.