From 3493a5a8f6ce9a5e524107c870c171f4073af987 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Sat, 12 May 1990 16:24:39 -0400 Subject: [PATCH 1/1] Intercal 0.3 prerelease, Sat May 12 12:24:39 EDT 1990. This was the first publicly visible version. (This is a retrospective comment.) --- BUGS | 57 +++ Makefile | 48 ++ READ.ME | 50 ++ THEORY | 51 ++ cesspool.c | 404 +++++++++++++++ cesspool.h | 18 + feh.c | 936 +++++++++++++++++++++++++++++++++++ fiddle.c | 124 +++++ ick-wrapper.c | 49 ++ ick.h | 50 ++ ick.y | 273 +++++++++++ intercal.man | 1294 +++++++++++++++++++++++++++++++++++++++++++++++++ intercal.sty | 137 ++++++ intercal.tex | 1074 ++++++++++++++++++++++++++++++++++++++++ lexer.l | 171 +++++++ lose.c | 20 + lose.h | 52 ++ sample.i | 27 ++ 18 files changed, 4835 insertions(+) create mode 100644 BUGS create mode 100644 Makefile create mode 100644 READ.ME create mode 100644 THEORY create mode 100644 cesspool.c create mode 100644 cesspool.h create mode 100644 feh.c create mode 100644 fiddle.c create mode 100644 ick-wrapper.c create mode 100644 ick.h create mode 100644 ick.y create mode 100644 intercal.man create mode 100644 intercal.sty create mode 100644 intercal.tex create mode 100644 lexer.l create mode 100644 lose.c create mode 100644 lose.h create mode 100644 sample.i diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..56d50c4 --- /dev/null +++ b/BUGS @@ -0,0 +1,57 @@ + NEW FEATURES IN C-INTERCAL + +1. As a convenience to all you junior birdmen out there, `NINER' is accepted as + a synonym for `NINE' in INTERCAL input. + +2. The COME FROM statement is now compiled. You may write + + PLEASE COME FROM (n) + + and the effect will be that whenever execution reaches statement label n, + it will immediately transfer control to the statement following the + COME FROM. Conditional COME FROM is possible; ABSTAIN FROM COMING FROM and + REINSTATE COMING FROM are both valid. DON'T COME FROM is a no-op (until + reinstated). Finally; NEXTING to the label `target' of an un-abstained + COME FROM *will* cause control to be transferred to the statement following + the COME FROM. + + BUGS + +1) INTERCAL would be intrinsically a crock even if it worked right. + +2) The INTERCAL-72 handling of invalid statements isn't implemented; instead + they raise a normal compile-time error. This is because I haven't figured + out how to force YACC to do the needed thing yet. It means C-INTERCAL + can't compile the INTERCAL-72 system library yet. Isn't compatibility + wonderful? I may have to go to a handcrafted LL(1) parser to fix this... + +3) Error-checking could be improved. Not all the errors listed in the + manual are actually detected (of those listed in lose.h, E123, E621, E632, + E562, E579, E436, E017, E275, E182, E129, E139, and E778 are implemented). + In this respect C-INTERCAL follows nobly in the tradition of many production + compilers. + +4) Proper C code is generated for array operations, but the runtime-library + hooks for the dynamic memory management needed to make the code work are + stubbed out. I'm poor at array-bashing. Rachel Millian has promised to + implement this for the next release. + +5) The Roman-numeral output from INTERCAL isn't quite as butchered as it ought + to be, because neither I nor the implementor could figure out what the frotz + the note in the INTERCAL-72 manual about modulo 4000 and I and M and all + that was really supposed to mean. + +6) The manual is mangled in spots. I'm in touch with the INTERCAL-72 authors + and we'll try to fix this in the next release, assuming there *is* a next + release. + + TO DO + +1. Test this loser on something other than the sample program. + +2. Add more optimization templates, esp. the idioms for &, |, ^, ~. + +3. Input format internationalization -- allow WRITE IN input digits in + major languages such as Nahuatl, Tagalog, Sanskrit, and Basque. + +4. Forget this @!%$#! crock and take a long vacation. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..451f2a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# Makefile for the INTERCAL compiler + +YDEBUG = -v -t +SYSTYPE = -DATT # -DBSD for Berkeley systems +CFLAGS = -I. $(SYSTYPE) -g +LDFLAGS = -lg + +DOCS = READ.ME intercal.man intercal.tex intercal.sty BUGS THEORY +SOURCES = cesspool.[ch] feh.c fiddle.c ick.[yh] lose.[ch] lexer.l + +all: ick libick.a + +ick: y.tab.o lexer.o feh.o lose.o fiddle.o + $(CC) y.tab.o lexer.o feh.o lose.o fiddle.o $(LDFLAGS) -o ick + +y.tab.h y.tab.c: ick.y + @echo "expect 13 shift/reduce conflicts" + yacc -d $(YDEBUG) ick.y + +lextest: y.tab.h lexer.c ick.h + $(CC) -DMAIN lexer.c -ll -o lextest + +y.tab.o: y.tab.c ick.h lose.h +lexer.o: lexer.c y.tab.h ick.h +feh.o: feh.c y.tab.h ick.h lose.h +cesspool.o: cesspool.c cesspool.h lose.h +lose.o: lose.h + +libick.a: cesspool.o lose.o fiddle.o + ar cr libick.a cesspool.o lose.o fiddle.o + +install: + cp cesspool.h /usr/include + cp libick.a /usr/lib + cp ick /usr/bin + +clean: + rm -f yacc.* y.tab.* y.output *~ *.[oa] lexer.c lextest ick + rm -f sample.c sample ick.shar Part1.sh Part2.sh + +shar: + shar $(DOCS) Makefile $(SOURCES) ick-wrapper.c sample.i >ick.shar + +mailable: + shar $(DOCS) Makefile >Part1.sh + shar $(SOURCES) ick-wrapper.c sample.i >Part2.sh + + diff --git a/READ.ME b/READ.ME new file mode 100644 index 0000000..bf70684 --- /dev/null +++ b/READ.ME @@ -0,0 +1,50 @@ + C-INTERCAL (v 0.3) + + NOTE! THIS IS A PRE-RELEASE VERSION WITH SOME KNOWN BUGS! + SEE THE `BUGS' FILE FOR DETAILS, AND EARN THE ETERNAL GRATITUDE + OF THE HUGE, BUSTLING INTERCAL COMMUNITY BY FIXING THEM! + +This package is an implementation of the language INTERCAL designed by Don +Woods and James Lyon, who have since spent most of twenty years trying to +live it down. + +The implementation was created by Eric S. Raymond (...!uunet!snark!eric) +during a fit of lunacy from which he has since mostly recovered. The files +included are: + +READ.ME -- this file +intercal.man -- The INTERCAL manual (read this next!) +intercal.tex -- TeX source form +intercal.sty -- TeX style file +THEORY -- some notes on the internals of the INTERCAL compiler +BUGS -- notes pertaining to this release + +Makefile -- makefile for the INTERCAL compiler +lexer.l -- the lexical analyzer specification (in LEX) +ick.y -- the grammar specification (in YACC) +ick.h -- compilation types and defines +feh.c -- INTERCAL-to-C code generator +fiddle.c -- the INTERCAL operators +lose.[ch] -- INTERCAL compile- and run-time error handling +ick-wrapper.c -- the driver for generated C-from-INTERCAL code +cesspool.c -- the INTERCAL runtime support code +cesspool.h -- interface fr. generated code to the INTERCAL runtime support + +sample.i -- a sample INTERCAL program (from the manual) + +You want a man page? Man pages are for wimps. To compile an INTERCAL +program `foo.i' to executable code, just do + + ick foo.i + +There's a -d option that leaves the generated `foo.c' in place for +inspection (suppressing compilation to machine code), and an -O option +that enables the (hah!) optimizer. Other than that, yer on yer own. + +One other switch affects C-intercal's runtime behavior. The `-C' +option forces output in "clockface" mode, for superstitious users who +believe writing "IV" upside-down offends IVPITER and would rather +see IIII. + +Report bugs, if you absolutely must, to the author. Or post them to +alt.lang.intercal. Or something. diff --git a/THEORY b/THEORY new file mode 100644 index 0000000..f08b592 --- /dev/null +++ b/THEORY @@ -0,0 +1,51 @@ + INTERCAL IMPLEMENTOR'S NOTES + +This C-INTERCAL compiler is a very conventional implementation using YACC and +LEX. Each line of INTERCAL is translated into a C if()-then; the guard part +is used to implement abstentions and RESUMES, and the arm part translates the +`body' of the corresponding INTERCAL statement. + +The generated C code is plugged into the template file ick-wrapper.c inside +main(). It needs to be linked with cesspool.o, fiddle.o and lose.o. Cesspool.o +is the code that implements the storage manager; fiddle.o implements the +INTERCAL operators; and lose.o is the code that generates INTERCAL's error +messages. + +The abstain[] array in the generated C is used to track line and label +abstentions; if member i is on, the statement on line i is being abstained +from. Labels are mapped to line numbers in the code checker, just before +optimization. + +The gerund variables at the top of the template file are used to implement +gerund abstentions (ABSTAIN FROM CALCULATING etc). The guard part of each +generated C statement checks the appropriate variable. + +RESUMES are implemented by setting skipto to the location at which execution +is to resume, then jumping to the top of the program and skipping all the +guard/arm pairs before the proper NEXT. + +The parser builds an array of tuples, one for each INTERCAL statement. Most +tuples have node trees attached. Once all tuples have been generated, +the compile-time checker and optimizer phases can do consistency checks +and expression-tree rewrites. Finally, the tuples are ground out as C code +by the emit() function. + +The optimizer does constant folding for all five operators. It also checks +for the idiom for `test for nonzeroness'. + +Calculations are fully type-checked at compile time; they have to be because +(as I read the manual) the 16- and 32-bit versions of the unary ops do +different things. The only potential problem here is that the typechecker +has to assume that :m ~ :n has the type of :n (32-bit) even though the +result might fit in 16 bits. At run-time everything is calculated in 32 +bits. When INTERCAL-72 was designed 32 bits was expensive; now it's cheap. +Really, the only reason for retaining a 16-bit type at all is for the +irritation value of it (yes, C-INTERCAL *does* enforce the 16-bit limit +on constants). + +Note that the spectacular ugliness of INTERCAL syntax requires that the lexical +analyzer have two levels. One, embedded in the input() function, handles +logical-line continuation and the ! abbrev, and stashes each logical line away +in a buffer accessible to the code generator (this is necessary for the * +construct to be compiled correctly). The upper level is generated by lex(1) and +does normal tokenizing for YACC. diff --git a/cesspool.c b/cesspool.c new file mode 100644 index 0000000..94fbcb9 --- /dev/null +++ b/cesspool.c @@ -0,0 +1,404 @@ +/* cesspool.c -- storage management and runtime support for INTERCAL */ +/* LINTLIBRARY */ +#include +#include "cesspool.h" +#include "lose.h" + +/********************************************************************** + * + * The following functions manipulate the nexting stack + * + *********************************************************************/ + +#define MAXNEXT 80 + +int next[MAXNEXT]; +int nextindex = 0; + +void pushnext(n) +int n; +{ + if (nextindex < MAXNEXT) + next[nextindex++] = n; + else + lose(lineno, E123); +} + +unsigned int popnext(n) +int n; +{ + while (n--) + if (nextindex > 0) + nextindex--; + return(nextindex ? next[nextindex - 1] : -1); +} + +unsigned int resume(n) +unsigned int n; +{ + if (n == 0) + lose(lineno, E621); + else if ((n = popnext(n)) == -1) + lose(lineno, E632); + else + return(n); +} + +/********************************************************************** + * + * The following functions implement the INTERCAL I/O model + * + *********************************************************************/ + +unsigned int pin(width) +int width; +{ + char buf[BUFSIZ], *cp, *strtok(); + unsigned int result = 0; + + if (fgets(buf, BUFSIZ, stdin) == (char *)NULL) + lose(lineno, E562); + + cp = strtok(buf, " "); + do { + int digit; + + if (strcmp(cp, "OH") == 0 || strcmp(cp, "ZERO") == 0) + digit = 0; + else if (strcmp(cp, "ONE") == 0) + digit = 1; + else if (strcmp(cp, "TWO") == 0) + digit = 2; + else if (strcmp(cp, "THREE") == 0) + digit = 3; + else if (strcmp(cp, "FOUR") == 0) + digit = 4; + else if (strcmp(cp, "FIVE") == 0) + digit = 5; + else if (strcmp(cp, "SIX") == 0) + digit = 6; + else if (strcmp(cp, "SEVEN") == 0) + digit = 7; + else if (strcmp(cp, "EIGHT") == 0) + digit = 8; + else if (strcmp(cp, "NINE") == 0 || strcmp(cp, "NINER") == 0) + digit = 9; + else + lose(lineno, E579); + + result = result * 10 + digit; + } while + (cp = strtok((char *)NULL, " ")); + + if (width = 16 && result > 0xffff) + lose(lineno, E579); +} + +/********************************************************************** + * + * Butchered Roman numerals implemented by + * Michael Ernst, mernst@theory.lcs.mit.edu. May 7, 1990 + * + * The INTERCAL manual hints that 3999 should translate to MMMIM + * (compare MMMCMXCIX) without specifying what the translation is. + * That may be a typo; in any case, this implementation isn't that + * butchered. + * + *********************************************************************/ + +#define MAXDIGITS 10 /* max base 10 digits */ +#define MAXROMANS (MAXDIGITS*4+1) /* max chars in translation */ + +/* + * The first column tells how many of the succeeding columns are used. + * The other columns refer to the columns of br_equiv and br_overbar. + */ +static int br_trans[10][5] = +{ + {0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {2, 0, 0, 0, 0}, + {3, 0, 0, 0, 0}, + {2, 1, 2, 0, 0}, /* or use {4, 0, 0, 0, 0} */ + {1, 2, 0, 0, 0}, + {2, 2, 1, 0, 0}, + {3, 2, 1, 1, 0}, + {4, 2, 1, 1, 1}, + {2, 1, 3, 0, 0} +}; + +/* + * butcher places in the string result the "butchered" Roman numeral for val. + * This string should be printed at the beginning of a line; it spans two + * lines and already contains newlines. + */ + +static void butcher(val, result) +unsigned long val; +char *result; +{ + int i, j; + int digitsig, digitval; + char res[MAXROMANS], ovb[MAXROMANS]; + + /* We need FOUR columns because of the odd way that M and I interact. */ + static char br_equiv[MAXDIGITS][4] = + { + {'I', 'I', 'V', 'X'}, {'X', 'X', 'L', 'C'}, + {'C', 'C', 'D', 'M'}, {'M', 'I', 'V', 'X'}, + {'X', 'X', 'L', 'C'}, {'C', 'C', 'D', 'M'}, + {'M', 'i', 'v', 'x'}, {'x', 'x', 'l', 'c'}, + {'c', 'c', 'd', 'm'}, {'m', 'i', 'v', 'x'}, + }; + + static char br_overbar[MAXDIGITS][4] = + { + {' ', ' ', ' ', ' '}, + {' ', ' ', ' ', ' '}, + {' ', ' ', ' ', ' '}, + {' ', '_', '_', '_'}, + {'_', '_', '_', '_'}, + {'_', '_', '_', '_'}, + {'_', ' ', ' ', ' '}, + {' ', ' ', ' ', ' '}, + {' ', ' ', ' ', ' '}, + {' ', '_', '_', '_'}, + }; + + if (val == 0) + (void) strcpy(result, "_\n \n"); + else + { + res[MAXROMANS-1] = 0; + ovb[MAXROMANS-1] = 0; + i = MAXROMANS-1; + + /* the significance of the current digit is 10 ** digitsig */ + for (digitsig = 0; (digitsig < MAXDIGITS) && (val > 0); digitsig++) + { + digitval = val % 10; + for (j = br_trans[digitval][0]; j > 0; j--) + { + /* printf("In j loop: %d %d\n", j, i); */ + res[--i] = br_equiv[digitsig][br_trans[digitval][j]]; + ovb[i] = br_overbar[digitsig][br_trans[digitval][j]]; + } + val = val / 10; + } + + j = i; + while (*result++ = ovb[j++]) + continue; + *--result = '\n'; + + j = i; + while (*++result = res[j++]) + continue; + *result++ = '\n'; + *result = '\0'; + } +} + +void clockface(mode) +/* enable or disable clockface mode (output IIII instead of IV) */ +bool mode; +{ + if (mode) + { + /* clockface mode */ + br_trans[4][0] = 4; + br_trans[4][1] = 0; + br_trans[4][2] = 0; + } + else + { + /* normal mode */ + br_trans[4][0] = 2; + br_trans[4][1] = 1; + br_trans[4][2] = 2; + } +} + +void pout(val) +/* output in `butchered' Roman numerals; see manual, part 4.4.13 */ +unsigned int val; +{ + char result[2*MAXROMANS+1]; + + butchar(val, result); + (void) puts(result); +} + +/********************************************************************** + * + * The following functions implement the INTERCAL array model + * + *********************************************************************/ + +/*VARARGS3*/ +unsigned int aget(type, index, dim) +/* return the value of the array location given by the 4th-nth args */ +unsigned int type, index; +int dim; +{ + /* + * The function may abort if no resize() has been executed for the + * array, or if any member of the tuple is zero. + */ +} + +/*VARARGS4*/ +void aput(value, type, index, dim) +/* set the value of an array location given by the 5th-nth args */ +unsigned int value, type, index; +int dim; +{ + /* + * The function may abort if no resize() has been executed for the + * array, or if any member of the tuple is zero. + */ +} + +/*VARARGS3*/ +void resize(type, index, dim) +/* resize an array to the shape of the 4th-nth args */ +unsigned int type, index; +int dim; +{ + /* + * The function may abort if any member of the tuple is zero. + * If the array being resized exists, it should preserve the contents + * of whatever sub-volume remains in the resized version. + */ +} + +static void arraycopy(type, index, target, source) +/* stash away an array variable's value */ +unsigned int type, index; +char **target, *source; +{ +} + +/********************************************************************** + * + * The following functions implement save/retrieve + * + * If you want a more sophisticated dynamic allocation strategy, + * go ahead and implement one! *I'm* sure not stopping you... + * + *********************************************************************/ + +#define MAXSAVE 100 + +typedef struct /* this is a save-stack element */ +{ + int type; /* variable type */ + int index; /* variable's index within the type */ + int depth; /* depth of stash */ + union /* the data itself */ + { + short onespot; + int twospot; + short *tail; + int *hybrid; + } save; +} +stashbox; + +static stashbox stashes[MAXSAVE]; + +void stashinit() +{ + memset(stashes, sizeof(stashes)); +} + +static stashbox *fetch(type, index) +/* restore the value of a variable from the save stack */ +unsigned int type, index; +{ + stashbox *sp, *tp = (stashbox *)NULL; + int depthfound = 0; + + for (sp = stashes; sp < stashes + MAXSAVE; sp++) + if (sp->type == type && sp->index == index && sp->depth > depthfound) + { + depthfound = sp->depth; + tp = sp; + } + + return(tp); +} + +void stash(type, index, from) +/* stash away the variable's value */ +char *from; +unsigned int type, index; +{ + stashbox *sp, *op = fetch(type, index); + + for (sp = stashes; sp < stashes + MAXSAVE; sp++) + if (sp->type == 0) + { + if (op) + sp->depth = op->depth + 1; + else + sp->depth = 0; + if (type == ONESPOT) + memcpy((char *)&sp->save.onespot, from,sizeof(unsigned short)); + else if (type == TWOSPOT) + memcpy((char *)&sp->save.twospot, from, sizeof(unsigned int)); + else if (type == TAIL) + arraycopy((char **)&sp->save.tail, from); + else if (type == HYBRID) + arraycopy((char **)&sp->save.hybrid, from); + return; + } + lose(E222); +} + +void retrieve(to, type, index) +char **to; +unsigned int type, index; +{ + stashbox *sp; + + if ((sp = fetch(type, index)) == (stashbox *)NULL) + lose(E436); + else + { + if (type == ONESPOT) + memcpy(to, (char *)&sp->save.onespot, sizeof(unsigned short)); + else if (type == TWOSPOT) + memcpy(to, (char *)&sp->save.twospot, sizeof(unsigned int)); + else if (type == TAIL) + { + free(to); /* free storage allocated for array copy */ + *to = (char *)&sp->save.tail; + } + else if (type == HYBRID) + { + free(to); /* free storage allocated for array copy */ + *to = (char *)&sp->save.hybrid; + } + sp->type = 0; /* mark this stashbox free */ + } +} + +/********************************************************************** + * + * The following function is used for random error generation + * + *********************************************************************/ + +unsigned int roll(n) +/* return TRUE on n% chance, FALSE otherwise */ +unsigned int n; +{ +#ifdef USG + return(lrand48() % 100 < n); +#else + return(rand() % 100 < n); +#endif /* UNIX */ +} + +/* cesspool.c ends here */ diff --git a/cesspool.h b/cesspool.h new file mode 100644 index 0000000..44251a4 --- /dev/null +++ b/cesspool.h @@ -0,0 +1,18 @@ +/* cesspool.h -- interface to INTERCAL storage manager and runtime support */ + +typedef int bool; +#define TRUE 1 +#define FALSE 0 + +extern unsigned int aget(), pin(), roll(), popnext(), resume(); +extern void pushnext(), resize(), stashinit(), stash(), retrieve(), aput(); +extern void clockface(), pout(); + +extern int lineno; + +#define ONESPOT 0 +#define TWOSPOT 1 +#define TAIL 2 +#define HYBRID 3 + +/* cesspool.h ends here */ diff --git a/feh.c b/feh.c new file mode 100644 index 0000000..83597ed --- /dev/null +++ b/feh.c @@ -0,0 +1,936 @@ +/**************************************************************************** + +NAME + feh.c -- code-generator back-end for ick parser + +DESCRIPTION + This module provides storage manglement and code degeneration +for the INTERCAL compiler. + +****************************************************************************/ +/*LINTLIBRARY */ +#include +#include +#include "ick.h" +#include "y.tab.h" +#include "lose.h" + +extern unsigned int mingle(), select(); +extern unsigned int and16(), or16(), xor16(), and32(), or32(), xor32(); + +extern char linebuf[]; +extern int yydebug, yylineno; + +/* compilation options */ +static bool dooptimize; /* do optimizations? (controlled by -O) */ +static bool clockface; /* set up output to do IIII for IV */ + +#define SKELETON "ick-wrapper.c" + +int lineno; /* current source line number; lose() uses it */ + +int yyerror() +{ + lose(yylineno, E017); +} + +/************************************************************************* + * + * Node allocation functions. + * + * Nodes are used to represent expresion trees. The emit() function + * deallocates them. + * + **************************************************************************/ + +node *newnode() +/* allocate and zero out a new expression node */ +{ + return((node *)calloc(sizeof(node), 1)); +} + +node *cons(type, car, cdr) +int type; +node *car, *cdr; +{ + node *np = newnode(); + + np->opcode = type; + np->lval = car; + np->rval = cdr; +} + +/************************************************************************* + * + * Variable-name mapping + * + * This permits us to optimize use of variable storage at runtime + * + **************************************************************************/ + +/* this maps the `external' name of a variable to an internal array index */ +typedef struct +{ + int type; + int extindex; + int intindex; +} +atom; + +static atom oblist[MAXSTASHES], *obdex; +static int nonespots, ntwospots, ntails, nhybrids; + +unsigned int intern(type, index) +{ + atom *x; + + /* if it's already on the oblist, return its intindex */ + for (x = oblist; x < obdex; x++) + if (x->type == type && x->extindex == index) + return(x->intindex); + + /* else we must intern a new symbol */ + if (obdex >= oblist + MAXSTASHES) + lose(yylineno, E333); + obdex->type = type; + obdex->extindex = index; + if (type == ONESPOT) + obdex->intindex = nonespots++; + if (type == TWOSPOT) + obdex->intindex = ntwospots++; + if (type == TAIL) + obdex->intindex = ntails++; + if (type == HYBRID) + obdex->intindex = nhybrids++; + ++obdex; + + return(obdex[-1].intindex); +} + +/************************************************************************* + * + * Tuple allocation functions. + * + **************************************************************************/ + +static tuple tuples[MAXLINES]; + +static void treset() +{ + memset(tuples, '\0', sizeof(tuple) * MAXLINES); + nonespots = ntwospots = ntails = nhybrids = 0; + obdex = oblist; + lineno = 0; +} + +tuple *newtuple() +/* allocate and zero out a new expression tuple */ +{ + if (lineno >= MAXLINES) + lose(yylineno, E666); + else + return(tuples + lineno++); +} + +/************************************************************************* + * + * The typecaster + * + * The theory here is that we associate a type with each node in order to + * know what widths of unary-logical operator to use. + * + **************************************************************************/ + +static void typecast(np) +node *np; +{ + /* recurse so we typecast each node after all its subnodes */ + if (np == (node *)NULL) + return; + else if (np->lval != (node *)NULL) + typecast(np->lval); + if (np->rval != (node *)NULL) + typecast(np->rval); + + /* + * This is an entire set of type-deducing machinery right here. + */ + if (np->opcode == MESH || np->opcode == ONESPOT || np->opcode == TAIL) + np->width = 16; + else if (np->opcode==TWOSPOT ||np->opcode==HYBRID ||np->opcode==MINGLE) + np->width = 32; + else if (np->opcode == AND || np->opcode == OR || np->opcode == XOR) + np->width = np->rval->width; + else if (np->opcode == SELECT) + np->width = np->rval->width; /* n-bit select has an n-bit result */ + else if (np->opcode == SUB) + np->width = np->rval->width; /* tpe of the array */ +} + +/************************************************************************* + * + * The codechecker + * + * This checks for nasties like mismatched types in assignments that + * can be detected at compile time -- also for errors that could cause + * the compilation of the generated C to fail, like generated gotos to + * nonexistent labels or duplicate labels. + * + **************************************************************************/ + +static void codecheck() +{ + tuple *tp, *up; + + /* check for assignment type mismatches */ + for (tp = tuples; tp < tuples + lineno; tp++) + if (tp->type == GETS) + if (tp->u.node->lval->width == 16 && tp->u.node->lval->width == 32) + lose(tp - tuples + 1, E275); + + /* check for duplicate labels */ + for (tp = tuples; tp < tuples + lineno; tp++) + if (tp->label) + for (up = tuples; up < tuples + lineno; up++) + if (tp != up && tp->label == up->label) + lose(tp - tuples + 1, E182); + + /* + * Check that every NEXT, ABSTAIN, REINSTATE and COME_FROM actually has a + * legitimate target label. + */ + for (tp = tuples; tp < tuples + lineno; tp++) + if (tp->type == NEXT + || tp->type == ABSTAIN || tp->type == REINSTATE + || tp->type == COME_FROM) + { + bool foundit = FALSE; + + for (up = tuples; up < tuples + lineno; up++) + if (tp->u.target == up->label) + { + foundit = TRUE; + break; + } + if (!foundit) + { + if (tp->type == NEXT) + lose(tp - tuples + 1, E129); + else if (tp->type == COME_FROM) + lose(tp - tuples + 1, E444); + else + lose(tp - tuples + 1, E139); + } + /* this substitutes line numbers for label numbers */ + else if (tp->type != NEXT) + { + if (tp->type == COME_FROM) + if (up->comefrom) + lose(yylineno, E555); + else + up->comefrom = tp - tuples + 1; + tp->u.target = up - tuples + 1; + } + } +} + +/************************************************************************* + * + * Optimizer code. + * + * It's not a very good optimizer, is it? + * + **************************************************************************/ + +/* optimizer node codes -- must make sure these don't collide with y.tab.h */ +#define TESTNZ 300 /* test for non-zero */ + +#define ISCONSTANT(np, v) (np->opcode == MESH && np->constant == v) + +static void rfree(np) +/* recursively free the given node and all nodes underneath */ +node *np; +{ + if (np->lval != (node *)NULL) + rfree(np->lval); + if (np->rval != (node *)NULL) + rfree(np->rval); + free(np); +} + +static int requal(mp, np) +/* do two node trees represent the same expression? */ +node *mp, *np; +{ + if (mp == (node *)NULL && np == (node *)NULL) + return(TRUE); + else if (mp == (node *)NULL || np == (node *)NULL) + return(FALSE); + else if (mp->opcode != np->opcode || mp->constant != np->constant) + return(FALSE); + else if (!requal(mp->lval, np->lval) || !requal(mp->lval, np->lval)) + return(FALSE); + else + return(TRUE); +} + +static void optimize(np) +node *np; +{ + /* recurse so we simplify each node after all its subnodes */ + if (np == (node *)NULL) + return; + else if (np->lval != (node *)NULL) + optimize(np->lval); + if (np->rval != (node *)NULL) + optimize(np->rval); + + /* check for non-zeroness test */ + if (np->opcode == SELECT && ISCONSTANT(np->rval, 1) && requal(np->lval->rval, np->lval->lval)) + { + rfree(np->rval); + np->rval = np->lval->rval; + rfree(np->lval->lval); + np->lval = (node *)NULL; + np->opcode = TESTNZ; + np->width = 16; + } + + /* fold MINGLE operations on constants */ + if (np->opcode==MINGLE && (np->lval->opcode==MESH&&np->rval->opcode==MESH)) + { + np->opcode = MESH; + np->constant = mingle(np->lval->constant, np->rval->constant); + free(np->lval); + free(np->rval); + } + + /* fold SELECT operations on constants */ + if (np->opcode == SELECT && (np->lval->opcode==MESH && np->rval->opcode==MESH)) + { + np->opcode = MESH; + np->constant = select(np->lval->constant, np->rval->constant); + free(np->lval); + free(np->rval); + } + + /* fold AND operations on constants */ + if (np->opcode == AND && np->rval->opcode == MESH) + { + np->opcode = MESH; + np->constant = and16(np->lval->constant, np->rval->constant); + free(np->rval); + } + + /* fold OR operations on constants */ + if (np->opcode == OR && np->rval->opcode == MESH) + { + np->opcode = MESH; + np->constant = or16(np->lval->constant, np->rval->constant); + free(np->rval); + } + + /* fold XOR operations on constants */ + if (np->opcode == AND && np->rval->opcode == MESH) + { + np->opcode = MESH; + np->constant = xor16(np->lval->constant, np->rval->constant); + free(np->rval); + } +} + +/************************************************************************* + * + * Code generation + * + * The theory behind this crock is that we've been handed a pointer to + * a tuple representing a single INTERCAL statement, possibly with an + * expression tree hanging off it and twisting slowly, slowly in the wind. + * + * Our mission, should we choose to accept it, is to emit C code which, + * when linked to the INTERCAL run-time support, will do something + * resembling the right thing. + * + **************************************************************************/ + +typedef struct +{ + int value; + char *name; +} +assoc; + +/* + * If the order of statement-token defines in ick.y ever changes, + * this will need to be resolved. + */ +static char *enablers[] = +{ + /* GETS */ "calculating", + /* RESIZE */ "calculating", + /* NEXT */ "nexting", + /* FORGET */ "forgetting", + /* RESUME */ "resuming", + /* STASH */ "stashing", + /* RETRIEVE */ "retrieving", + /* IGNORE */ "ignoring", + /* REMEMBER */ "remembering", + /* ABSTAIN */ "abstaining", + /* REINSTATE */ "reinstating", + /* DISABLE */ "abstaining", + /* ENABLE */ "reinstating", + /* GIVE_UP */ "givingup", /* never used */ + /* READ_OUT */ "readingout", + /* WRITE_IN */ "writingin", + /* COME_FROM */ "comingfrom", +}; + +static assoc vartypes[] = +{ + ONESPOT, "ONESPOT", + TWOSPOT, "TWOSPOT", + TAIL, "TAIL", + HYBRID, "HYBRID", + 0, (char *)NULL +}; + +static assoc forgetbits[] = +{ + ONESPOT, "oneforget", + TWOSPOT, "twoforget", + TAIL, "tailforget", + HYBRID, "hyforget", + 0, (char *)NULL +}; + +static assoc varstores[] = +{ + ONESPOT, "onespot", + TWOSPOT, "twospot", + TAIL, "tails", + HYBRID, "hybrids", + 0, (char *)NULL +}; + +static assoc typedefs[] = +{ + ONESPOT, "short", + TWOSPOT, "int", + TAIL, "unsigned short *", + HYBRID, "unsigned int *", + 0, (char *)NULL +}; + +static char *nameof(value, table) +/* return string corresponding to value in table */ +int value; +assoc table[]; +{ + assoc *ap; + + for (ap = table; ap->name; ap++) + if (ap->value == value) + return(ap->name); + return((char *)NULL); +} + +static void prvar(np, fp) +/* print out args to pass to storage manager for reference */ +node *np; +FILE *fp; +{ + node *sp; + int dim; + + switch (np->opcode) + { + case ONESPOT: + (void) fprintf(fp, "onespots[%d]", np->constant); + break; + + case TWOSPOT: + (void) fprintf(fp, "twospots[%d]", np->constant); + break; + + case TAIL: + (void) fprintf(fp, "TAIL, %d", np->constant); + break; + + case HYBRID: + (void) fprintf(fp, "HYBRID, %d", np->constant); + break; + + case SUB: + dim = 0; + for (sp = np->rval; sp; sp = sp->rval) + dim++; + (void) fprintf(fp, "%s, %d, %d, ", + nameof(np->lval->opcode, vartypes), + np->lval->constant, + dim); + for (np = np->rval; np; np = np->rval) + { + extern void prexpr(); + + prexpr(np->lval, fp); + (void) fprintf(fp, ", "); + } + (void) fprintf(" 0"); + break; + } +} + +static void prexpr(np, fp) +/* print out C-function equivalent of an expression */ +FILE *fp; +node *np; +{ + switch (np->opcode) + { + case MINGLE: + (void) fprintf(fp, "mingle("); + prexpr(np->lval, fp); + (void) fprintf(fp, ", "); + prexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case SELECT: + (void) fprintf(fp, "select("); + prexpr(np->lval, fp); + (void) fprintf(fp, ", "); + prexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case AND: + (void) fprintf(fp, "and%d(", np->width); + prexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case OR: + (void) fprintf(fp, "or%d(", np->width); + prexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case XOR: + (void) fprintf(fp, "or%d(", np->width); + prexpr(np->rval, fp); + (void) fprintf(fp, ")"); + break; + + case MESH: + (void) fprintf(fp, "0x%x", np->constant); + break; + + case ONESPOT: + case TWOSPOT: + prvar(np, fp); + break; + + case SUB: + (void) fprintf(fp, "aget(%s"); + prvar(np, fp); + (void) fprintf(fp, ")"); + break; + + /* cases from here down are generated by the optimizer */ + case TESTNZ: + (void) fprintf(fp, "("); + prexpr(np->rval, fp); + (void) fprintf(fp, " != 0)"); + break; + } + + (void) free(np); +} + +void emit(tn, fp) +/* emit code for the given tuple */ +tuple *tn; +FILE *fp; +{ + node *np, *sp; + int dim; + + if (yydebug) + (void) fprintf(fp, " /* line %03d: %s */\n", + tn - tuples + 1, + textlines[tn-tuples+1]); + + if (tn->label) + (void) fprintf(fp, "L%d:\n", tn->label); + + if (tn->type == COME_FROM) + { + (void) fprintf(fp, "C%d:\n", tn - tuples + 1); + return; + } + + /* emit conditional-execution prefixes */ + if (tn->type == NEXT) + { + (void) fprintf(fp, " if (skipto == %d) skipto = 0; else\n", tn-tuples+1); + (void) fprintf(fp, " if (!skipto && "); + } + else + (void) fprintf(fp, " if (!skipto && ", tn - tuples + 1); + if (tn->exechance && tn->exechance < 100) + (void) fprintf(fp, "roll(%d) && ", tn->exechance); + if (tn->type == GIVE_UP) + (void) fprintf(fp, "!abstained[lineno = %d])\n", tn - tuples + 1); + else + (void) fprintf(fp, "!abstained[lineno = %d] && %s)\n", + tn-tuples + 1, enablers[tn->type-GETS]); + (void) fprintf(fp, " {\n"); + + if (tn->comefrom) + (void) fprintf(fp, + "\tif (!abstained[%d] && comingfrom)\t/* COME FROM */\n\t goto C%d;\n", + tn->comefrom, tn->comefrom); + + /* now emit the code for the statement body */ + switch(tn->type) + { + case GETS: + np = tn->u.node; + (void) fprintf(fp,"\tif (!%s[%d])\n\t ", + nameof(np->lval->opcode, forgetbits), + np->constant); + if (np->opcode == ONESPOT || np->opcode == TWOSPOT) + { + prvar(np->lval, fp); + (void) fprintf(fp, " = "); + prexpr(tn->u.node->rval, fp); + } + else + { + (void) fprintf("aput("); + prexpr(tn->u.node->rval, fp); + (void) fprintf(", "); + prvar(np->lval, fp); + (void) fprintf(fp, ")"); + } + (void) fprintf(fp, ";\n"); + break; + + case RESIZE: + (void) fprintf(fp, "\tresize("); + prvar(tn->u.node->lval, fp); + dim = 0; + for (sp = np->rval; sp; sp = sp->rval) + dim++; + (void) fprintf(fp, " %d,", dim); + for (np = np->rval; np; np = np->rval) + { + prexpr(np->lval, fp); + fprintf(fp, ", "); + } + (void) fprintf(fp, "0"); + (void) fprintf(fp, ");\n"); + break; + + case NEXT: + (void) fprintf(fp, "\tpushnext(%d); goto L%d;\n",tn - tuples + 1,tn->u.target); + break; + + case RESUME: + (void) fprintf(fp, "\tskipto = resume("); + prexpr(tn->u.node, fp); + (void) fprintf(fp, "); goto top;\n"); + break; + + case FORGET: + (void) fprintf(fp, "\tpopnext("); + prexpr(tn->u.node, fp); + (void) fprintf(fp, ");\n"); + break; + + case STASH: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp, "\tstash(%s, %d, %s[%d]);\n", + nameof(np->opcode, vartypes), + np->constant, + nameof(np->opcode, varstores), np->constant); + break; + + case RETRIEVE: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp, "\tretrieve(&%s[%d], %s, %d);\n", + nameof(np->opcode, varstores), np->constant, + nameof(np->opcode, vartypes), + np->constant); + break; + + case IGNORE: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp,"\t%s[%d] = TRUE;\n", + nameof(np->lval->opcode, forgetbits), + np->constant); + break; + + case REMEMBER: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp,"\t%s[%d] = FALSE;\n", + nameof(np->lval->opcode, forgetbits), + np->constant); + break; + + case ABSTAIN: + (void) fprintf(fp, "\tabstained[%d] = FALSE;\n", tn->u.target); + break; + + case REINSTATE: + (void) fprintf(fp, "\tabstained[%d] = TRUE;\n", tn->u.target); + break; + + case ENABLE: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp, "\t%s = TRUE;\n", enablers[np->constant-GETS]); + break; + + case DISABLE: + for (np = tn->u.node; np; np = np->rval) + (void) fprintf(fp, "\t%s = FALSE;\n", enablers[np->constant-GETS]); + break; + + case GIVE_UP: + (void) fprintf(fp, "\treturn(0);\n"); + break; + + case WRITE_IN: + for (np = tn->u.node; np; np = np->rval) + { + np = tn->u.node; + (void) fprintf(fp,"\tif (!%s[%d])\n\t ", + nameof(np->lval->opcode, forgetbits), + np->constant); + if (np->opcode == ONESPOT || np->opcode == TWOSPOT) + { + prvar(np->lval, fp); + (void) fprintf(fp, " = pin()"); + } + else + { + (void) fprintf("aput(pin(), "); + prvar(np->lval, fp); + (void) fprintf(fp, ")"); + } + (void) fprintf(fp, ";\n"); + } + break; + + case READ_OUT: + for (np = tn->u.node; np; np = np->rval) + { + (void) fprintf(fp, "\tpout("); + prexpr(np->lval, fp); + (void) fprintf(fp, ");\n"); + } + break; + + case SPLATTERED: + (void) fprintf(fp, + "(void) puts(\"%s\");\n", textlines[tn - tuples]); + (void) fprintf(fp, + "exit(%d);\n", tn - tuples); + break; + + default: + lose(tn - tuples + 1, E778); + break; + } + + (void) fprintf(fp, " }\n"); +} + +/************************************************************************* + * + * Driver code + * + **************************************************************************/ + +void abend() +{ + lose(yylineno, E778); +} + +main(argc, argv) +int argc; +char *argv[]; +{ + extern char *optarg; /* set by getopt */ + extern int optind; /* set by getopt */ + char buf[BUFSIZ], buf2[BUFSIZ]; + tuple *tp; + atom *op; + int c; + + while ((c = getopt(argc, argv, "dOC")) != EOF) + { + switch (c) + { + case 'd': + yydebug = 1; + break; + + case 'C': + clockface = TRUE; + break; + + case 'O': + dooptimize = TRUE; + break; + } + } + + (void) signal(SIGSEGV, abend); + (void) signal(SIGBUS, abend); + + for (; optind < argc; optind++) + if (freopen(argv[optind], "r", stdin) == (FILE *)NULL) + lose(0, E777); + else + { + FILE *ifp, *ofp; + int maxabstain; + + /* zero out tuple and oblist storage */ + treset(); + + /* compile tuples from current input source */ + yyparse(); + + /* + * Now propagate type information up the expression tree. + * We need to do this because the unary-logical operations + * are sensitive to the type widths of their operands, so + * we have to generate different code depending on the + * deducible type of the operand. + */ + for (tp = tuples; tp < tuples + lineno; tp++) + if (tp->type == GETS || tp->type == RESIZE) + typecast(tp->u.node); + + codecheck(); /* check for compile-time errors */ + + /* perform optimizations */ + if (dooptimize) + for (tp = tuples; tp < tuples + lineno; tp++) + if (tp->type == GETS || tp->type == RESIZE) + optimize(tp->u.node); + + /* strip off the file extension */ + argv[optind][strlen(argv[optind]) - 2] = '\0'; + + /* set up the generated C output file name */ + (void) strcpy(buf, argv[optind]); + (void) strcat(buf, ".c"); + if ((ofp = fopen(buf, "w")) == (FILE *)NULL) + lose(0, E888); + + /* now substitute in tokens in the skeleton */ + if ((ifp = fopen(SKELETON, "r")) == (FILE *)NULL) + lose(0, E999); + buf[strlen(buf) - 2] = '\0'; + + while ((c = fgetc(ifp)) != EOF) + if (c != '$') + (void) fputc(c, ofp); + else switch(fgetc(ifp)) + { + case 'A': /* source name stem */ + (void) fputs(buf, ofp); + break; + + case 'B': /* # of source lines */ + (void) fprintf(ofp, "%d", lineno); + break; + + case 'C': /* initial abstentions */ + maxabstain = 0; + for (tp = tuples; tp < tuples + lineno; tp++) + if (tp->exechance == 0 && tp - tuples + 1 > maxabstain) + maxabstain = tp - tuples + 1; + if (maxabstain) + { + (void) fprintf(ofp, " = {"); + for (tp = tuples; tp < tuples + maxabstain; tp++) + (void) fprintf(ofp, "%d, ", !tp->exechance); + (void) fprintf(ofp, "}"); + } + break; + + case 'D': /* extern to intern map */ + if (nonespots) + { + (void) fprintf(ofp, + "static unsigned int onespots[%d];\n", + nonespots); + (void) fprintf(ofp, + "static bool oneforget[%d];\n", + nonespots); + } + if (ntwospots) + { + (void) fprintf(ofp, + "static unsigned int twospots[%d];\n", + ntwospots); + (void) fprintf(ofp, + "static bool twoforget[%d];\n", + ntwospots); + } + if (ntails) + { + (void) fprintf(ofp, + "static unsigned int *tails[%d];\n", + ntails); + (void) fprintf(ofp, + "static bool tailforget[%d];\n", + ntails); + } + if (nhybrids) + { + (void) fprintf(ofp, + "static unsigned int *hybrids[%d];\n", + nhybrids); + (void) fprintf(ofp, + "static bool hyforget[%d];\n", + nhybrids); + } + if (yydebug) + for (op = oblist; op < obdex; op++) + (void) fprintf(ofp, "/* %s %d -> %d */\n", + nameof(op->type, vartypes), + op->extindex, + op->intindex); + break; + + case 'E': + if (clockface) + (void) fprintf(ofp, "clockface(TRUE);"); + break; + + case 'F': /* degenerated code */ + for (tp = tuples; tp < tuples + lineno; tp++) + emit(tp, ofp); + break; + } + + (void) fclose(ifp); + (void) fclose(ofp); + + /* OK, now sic the C compiler on the results */ + if (!yydebug) + { + (void) sprintf(buf2, "cc %s.c -I. -L. -lick -o %s", buf, buf); + (void) system(buf2); + (void) strcat(buf, ".c"); + (void) unlink(buf); + } + } +} + +/* feh.c ends here */ diff --git a/fiddle.c b/fiddle.c new file mode 100644 index 0000000..0e51670 --- /dev/null +++ b/fiddle.c @@ -0,0 +1,124 @@ +/* + * fiddle.c -- functions that implement the five INTERCAL operators + * + * We link these to the compiler, too, in order to do constant folding + */ + +#define BIT(n, q) (((q) & (1 << (n))) != 0) + +unsigned int mingle(r, s) +unsigned int r, s; +{ + return ( + (BIT(15, r) << 31) | + (BIT(15, s) << 30) | + (BIT(14, r) << 29) | + (BIT(14, s) << 28) | + (BIT(13, r) << 27) | + (BIT(13, s) << 26) | + (BIT(12, r) << 25) | + (BIT(12, s) << 24) | + (BIT(11, r) << 23) | + (BIT(11, s) << 22) | + (BIT(10, r) << 21) | + (BIT(10, s) << 20) | + (BIT( 9, r) << 19) | + (BIT( 9, s) << 18) | + (BIT( 8, r) << 17) | + (BIT( 8, s) << 16) | + (BIT( 7, r) << 15) | + (BIT( 7, s) << 14) | + (BIT( 6, r) << 13) | + (BIT( 6, s) << 12) | + (BIT( 5, r) << 11) | + (BIT( 5, s) << 10) | + (BIT( 4, r) << 9) | + (BIT( 4, s) << 8) | + (BIT( 3, r) << 7) | + (BIT( 3, s) << 6) | + (BIT( 2, r) << 5) | + (BIT( 2, s) << 4) | + (BIT( 1, r) << 3) | + (BIT( 1, s) << 2) | + (BIT( 0, r) << 1) | + (BIT( 0, s) << 0)); +} + +unsigned int select(r, s) +unsigned int r, s; +{ + int i, n = 0, result = 0; + + for (i = 0; i < 32; i++) + if (BIT(i, s)) + result |= ((1 << n++) & r); + return(result); +} + +unsigned int and16(n) +unsigned int n; +{ + unsigned int m = (n >> 1); + + if (n & 1) + m |= 0x8000; + + return(m & n); +} + +unsigned int or16(n) +unsigned int n; +{ + unsigned int m = (n >> 1); + + if (n & 1) + m |= 0x8000; + + return(m | n); +} + +unsigned int xor16(n) +unsigned int n; +{ + unsigned int m = (n >> 1); + + if (n & 1) + m |= 0x8000; + + return(m ^ n); +} + +unsigned int and32(n) +unsigned int n; +{ + unsigned int m = (n >> 1); + + if (n & 1) + m |= 0x80000000; + + return(m & n); +} + +unsigned int or32(n) +unsigned int n; +{ + unsigned int m = (n >> 1); + + if (n & 1) + m |= 0x80000000; + + return(m | n); +} + +unsigned int xor32(n) +unsigned int n; +{ + unsigned int m = (n >> 1); + + if (n & 1) + m |= 0x80000000; + + return(m ^ n); +} + +/* fiddle.c */ diff --git a/ick-wrapper.c b/ick-wrapper.c new file mode 100644 index 0000000..915eedd --- /dev/null +++ b/ick-wrapper.c @@ -0,0 +1,49 @@ +/* $A.c -- generated C-code file for INTERCAL program $A.i */ + +#include + +extern unsigned int interleave(), select(); +extern unsigned int and16(), or16(), xor16(), and32(), or32(), xor32(); + +int lineno; + +static int abstained[$B]$C; +$D + +main() +{ + /* declare and initialize gerund variables */ + int calculating = TRUE; + int nexting = TRUE; + int forgetting = TRUE; + int resuming = TRUE; + int stashing = TRUE; + int retrieving = TRUE; + int ignoring = TRUE; + int remembering = TRUE; + int abstaining = TRUE; + int reinstating = TRUE; + int readingout = TRUE; + int writingin = TRUE; + int comingfrom = TRUE; + + int skipto = 0; + + /* set seed for random error generation */ +#ifdef USG + srand48(time((long *)0) + getpid()); +#else + srand(time((long *)0)); +#endif /* UNIX */ + + /* set up stash storage */ + stashinit(); + + $E + +top: + /* degenerated code */ +$F + lose($B+1,"633 Execution has passed beyond the last statment of the program."); +} + diff --git a/ick.h b/ick.h new file mode 100644 index 0000000..09af25b --- /dev/null +++ b/ick.h @@ -0,0 +1,50 @@ +/* ick.h -- compilation types for intercal parser */ + +typedef int bool; +#define TRUE 1 +#define FALSE 0 + +/* + * We choose this value for maximum number of possible source lines on + * the theory that no human mind could grok more lines of INTERCAL than + * this and still retain any vestige of sanity. + */ +#define MAXLINES 1000 + +/* + * Similarly for maximum number of variable stashes + */ +#define MAXSTASHES 100 + +typedef struct node_t +{ + int opcode; /* operator or type code */ + unsigned int constant; /* constant data attached */ + int width; /* is this 32-bit data? */ + struct node_t *lval, *rval; /* attached expression nodes */ +} node; + +/* unary-op types -- values for token of type UNARY */ +#define AND 0 +#define OR 1 +#define XOR 2 + +typedef struct tuple_t +{ + unsigned int label; /* label # of this statement */ + unsigned int comefrom; /* if NZ, a COME FROM touches this */ + unsigned int exechance; /* chance of execution */ + unsigned int type; /* statement type */ +#define SPLATTERED -1 /* it's undecodeable */ + union + { + unsigned int target; /* for NEXT statements */ + node *node; /* attached expression node(s) */ + } u; +} tuple; + +/* the lexical analyzer keeps copies of the source logical lines */ +extern char *textlines[MAXLINES]; +extern int yylineno; + +/* ick.h ends here */ diff --git a/ick.y b/ick.y new file mode 100644 index 0000000..9041032 --- /dev/null +++ b/ick.y @@ -0,0 +1,273 @@ +/***************************************************************************** + +NAME + ick.y -- grammar for the INTERCAL language + +DESCRIPTION + This YACC grammar parses the INTERCAL language by Don R. Woods and +James M. Lyon. + +*****************************************************************************/ + +%{ +#include +#include "ick.h" +#include "lose.h" + +extern node *newnode(), *cons(); +extern tuple *newtuple(); +extern unsigned int intern(); +extern int yylineno; + +static node *rlist; /* pointer to current right-hand node list */ +static node *llist; /* pointer to current left-hand node list */ +static node *np; /* variable for building node lists */ + +#define ACTION(x, nt, nn) x = newtuple(); x->type = nt; x->u.node = nn +#define TARGET(x, nt, nn) x = newtuple(); x->type = nt; x->u.target = nn +%} + +%start program + +%union +{ + int numval; /* a numeric value */ + tuple *tuple; /* a code tuple */ + node *node; /* an expression-tree node */ +} + +/* + * Don't change this statement token list gratuitously! + * Some code in feh.c depends on GETS being the least + * statement type and on the order of the ones following. + */ +%token GETS RESIZE NEXT FORGET RESUME STASH RETRIEVE IGNORE REMEMBER ABSTAIN +%token REINSTATE DISABLE ENABLE GIVE_UP READ_OUT WRITE_IN COME_FROM + +%token DO PLEASE NOT MESH ONESPOT TWOSPOT TAIL HYBRID +%token MINGLE SELECT SPARK EARS SUB BY GERUND BADCHAR + +%token NUMBER UNARY OHOHSEVEN GERUND LABEL +%token INTERSECTION + +%token NEWLINE + +%type expr varlist outlist variable constant lvalue inlist outlist +%type subscr byexpr scalar array initem outitem sublist +%type perform +%type please + +%% /* beginning of rules section */ + +/* A program description consists of a sequence of statements */ +program : /* EMPTY */ + | program statemnt NEWLINE + ; + +statemnt: command + | error + {splat();} + ; + +/* + * Each command consists of an optional label, followed by a preamble, + * followed by the statement body + */ +command : /* EMPTY */ /* blank line is OK */ + | please perform + {$2->label = 0; $2->exechance = $1;} + | please OHOHSEVEN perform + {$3->label = 0; $3->exechance = $2;} + | LABEL please perform + {$3->label = $1; $3->exechance = $2;} + | LABEL please OHOHSEVEN perform + {$4->label = $1; $4->exechance = $2;} + ; + +/* There are several forms of preamble */ +please : DO {$$ = 100;} + | NOT {$$ = 0;} + | DO NOT {$$ = 0;} + | PLEASE {$$ = 100;} + | DO PLEASE {$$ = 100;} + | PLEASE DO {$$ = 100;} + | PLEASE NOT {$$ = 0;} + ; + +/* Here's how to parse statement bodies */ +perform : lvalue GETS expr {ACTION($$, GETS,cons(GETS,$1,$3));} + | array GETS byexpr {ACTION($$,RESIZE,cons(RESIZE,$1,$3));} + | LABEL NEXT {TARGET($$, NEXT, $1);} + | FORGET expr {ACTION($$, RESUME, $2);} + | RESUME expr {ACTION($$, RESUME, $2);} + | STASH varlist {ACTION($$, STASH, rlist);} + | RETRIEVE varlist {ACTION($$, RETRIEVE, rlist);} + | IGNORE varlist {ACTION($$, IGNORE, rlist);} + | REMEMBER varlist {ACTION($$, REMEMBER, rlist);} + | ABSTAIN LABEL {TARGET($$, ABSTAIN, $2);} + | ABSTAIN gerunds {ACTION($$, DISABLE, rlist);} + | REINSTATE LABEL {TARGET($$, REINSTATE, $2);} + | REINSTATE gerunds {ACTION($$, ENABLE, rlist);} + | WRITE_IN inlist {ACTION($$, WRITE_IN, $2);} + | READ_OUT outlist {ACTION($$, READ_OUT, $2);} + | GIVE_UP {ACTION($$, GIVE_UP, 0);} + | COME_FROM LABEL {TARGET($$, COME_FROM, $2);} + | error {splat();} + ; + +/* gerund lists are used by ABSTAIN and REINSTATE */ +gerunds : GERUND + {rlist = np = newnode(); np->constant = $1;} + | gerunds INTERSECTION GERUND + { + np->rval = newnode(); + np = np->rval; + np->constant = $3; + } + ; + +/* OK, here's what a variable reference looks like */ +variable: scalar | array; + +lvalue : scalar | subscr; + +scalar : ONESPOT NUMBER + { + $$ = newnode(); + $$->opcode = ONESPOT; + $$->constant = intern(ONESPOT, $2); + } + | TWOSPOT NUMBER + { + $$ = newnode(); + $$->opcode = TWOSPOT; + $$->constant = intern(TWOSPOT, $2); + } + ; + +array : TAIL NUMBER + { + $$ = newnode(); + $$->opcode = TAIL; + $$->constant = intern(TAIL, $2); + } + | HYBRID NUMBER + { + $$ = newnode(); + $$->opcode = HYBRID; + $$->constant = intern(HYBRID, $2); + } + ; + +/* And a constant looks like this */ +constant: MESH NUMBER + { + /* enforce the 16-bit constant constraint */ + if ($2 > 0xffff) + lose(yylineno, E017); + $$ = newnode(); + $$->opcode = MESH; + $$->constant = $2; + } + ; + +/* variable lists are used in STASH, RETRIEVE, IGNORE, REMEMBER */ +varlist : variable {rlist = np = $1;} + | varlist INTERSECTION variable {np = np->rval = newnode();} + ; + +/* scalars and subscript exprs are permitted in WRITE IN lists */ +initem : scalar | subscr; +inlist : initem INTERSECTION inlist {$$=cons(INTERSECTION,$1,$3);} + | initem {$$=cons(INTERSECTION,$1,0);} + +/* scalars, subscript exprs & constants are permitted in READ OUT lists */ +outitem : scalar | subscr | constant; +outlist : outitem INTERSECTION outlist {$$=cons(INTERSECTION,$1,$3);} + | outitem {$$=cons(INTERSECTION,$1,0);} + ; + +/* Now the gnarly part -- expression syntax */ + +/* Support array subscripts (as lvalues) */ +subscr : array SUB sublist {$$ = cons(SUB, $1, $3);} + ; +sublist : expr sublist {$$ = cons(INTERSECTION, $1, $2);} + | expr {$$ = cons(INTERSECTION, $1, 0);} + ; + +/* Support array dimension assignment */ +byexpr : expr BY byexpr {$$ = cons(BY, $1, $3);} + | expr {$$ = cons(BY, $1, 0);} + ; + +/* here goes the general expession syntax */ +preftype: MESH | ONESPOT | TWOSPOT | TAIL | HYBRID; + +expr : variable {$$ = $1;} + | constant {$$ = $1;} + + /* deal with the bizarre unary-op syntax */ + | preftype UNARY NUMBER + { + $$ = newnode(); + $$->opcode = $2; + $$->rval = newnode(); + $$->rval->opcode = MESH; + $$->rval->constant = $3; + } + + /* Now deal with the screwy unary-op interaction with grouping */ + | SPARK UNARY expr SPARK + { + $$ = newnode(); + $$->opcode = $2; + $$->rval = $3; + } + | EARS UNARY expr EARS + { + $$ = newnode(); + $$->opcode = $2; + $$->rval = $3; + } + + | expr SELECT expr {$$ = cons(SELECT, $1, $3);} + | expr MINGLE expr {$$ = cons(MINGLE, $1, $3);} + | SPARK expr SPARK {$$ = $2;} + | EARS expr EARS {$$ = $2;} + | subscr {$$ = $1;} + ; + +%% + +tuple *splat() +/* try to recover from an invalid statement. */ +{ + extern FILE *yyin; + tuple *sp; + + /* we're going to do our own resynchronization */ + yyerrok; + yyclearin; + + /* + * This is called "weird sex with the lexical analyzer". The idea + * here is to skip to the next newline, but to do it with our + * private getc() in order to save the line's characters. + * + * With a bit more effort we could implement the full, hideous + * semantics of splat, i.e. that another statement identifier + * (DO or PLEASE) in the statement takes us back to normal parsing. + * But I'm not feeling that twisted just now. + */ +#undef getc + while (getc(yyin) != '\n') + continue; + ungetc('\n', yyin); + + /* generate a placeholder tuple for the text line */ + TARGET(sp, SPLATTERED, 0); + return(sp); +} + +/* ick.y ends here */ diff --git a/intercal.man b/intercal.man new file mode 100644 index 0000000..88407a0 --- /dev/null +++ b/intercal.man @@ -0,0 +1,1294 @@ + + + + + + + THE INTERCAL PROGRAMMING LANGUAGE + REFERENCE MANUAL + + + Donald R. Woods & James M. Lyon + + Copyright Donald R. Woods & James M. Lyon 1973 + + + + + + + + INTERCAL + +1. INTRODUCTION + +The names you are about to ignore are true. However, the story has been changed +significantly. Any resemblance of the programming language portrayed here +to other programming languages, living or dead, is purely coincidental. + +1.1 ORIGIN AND PURPOSE + +The INTERCAL programming language was designed the morning of May 26, 1972 +by Donald R. Woods and James M. Lyon, at Princeton University. Exactly when +in the morning will become apparent in the course of this manual. It was +inspired by one ambition; to have a compiler language which has nothing at +all in common with any other major language. By 'major' was meant anything +with which the authors were at all familiar, e.g., FORTRAN, BASIC, COBOL, +ALGOL, SNOBOL, SPITBOL, FOCAL, SOLVE, TEACH, APL, LISP, and PI/I. For the +most part, INTERCAL has remained true to this goal, sharing only the basic +elements such as variables, arrays, and the ability to do I/O, and eschewing +all conventional operations other than the assignment statement (FORTRAN +"="). + +1.2 ACRONYM + +The full name of the compiler is "Compiler Language With No Pronounceable +Acronym", which is, for obvious reasons, abbreviated "INTERCAL". + +1.3 ACKNOWLEDGMENTS + +The authors are deeply indebted to Eric M. Van and Daniel J. Warmenhoven, +without whose unwitting assistance this manual would still have been +possible. + + + + + + + + +2. FUNDAMENTAL CONCEPTS + +In this section an attempt is made to describe how and why INTERCAL may be +used; i.e., what it is like and what it is good for. + +2.1 SAMPLE PROGRAM + +Shown below is a relatively simple INTERCAL program which will read in 32-bit +unsigned integers, treat them as signed, 2's-complement numbers, and print out +their absolute values. The program exits if the absolute value is zero. Note +in particular the inversion routine (statements 6 through 14), which could +be greatly simplified if the subroutine library (see section 5) were used. + +A more detailed analysis of a program is made in section 6 of this manual. + + DO (5) NEXT + (5) DO FORGET #1 + PLEASE WRITE IN :1 + DO .1 <- 'V":1~'#32768$#0'"$#1'~#3 + DO (1) NEXT + DO :1 <- "'V":1~'#65535$#0'"$#65535' + ~'#0$#65535'"$"'V":1~'#0$#65535'" + $#65535'~'#0$#65535'" + DO :2 <- #1 + PLEASE DO (4) NEXT + (4) DO FORGET #1 + DO .1 <- "'V":1~'#65535$#0'"$":2~'#65535 + $#0'"'~'#0$#65535'"$"'V":1~'#0 + $#65535'"$":2~'#65535$#0'"'~'#0$#65535'" + DO (1) NEXT + DO :2 <- ":2~'#0$#65535'" + $"'":2~'#65535$#0'"$#0'~'#32767$#1'" + DO (4) NEXT + (2) DO RESUME .1 + (1) PLEASE DO (2) NEXT + PLEASE FORGET #1 + DO READ OUT :1 + PLEASE DO .1 <- 'V"':1~:1'~#1"$#1'~#3 + DO (3) NEXT + PLEASE DO (5) NEXT + (3) DO (2) NEXT + PLEASE GIVE UP + +2.2 USES FOR INTERCAL + +INTERCAL's main advantage over other programming languages is its strict +simplicity. It has few capabilities, and thus there are few restrictions to +be kept in mind. Since it is an exceedingly easy language to learn, one +might expect it would be a good language for initiating novice programmers. +Perhaps surprising, than, is the fact that it would be more likely to +initiate a novice into a search for another line of work. As it turns out, +INTERCAL is more useful (which isn't saying much) as a challenge to +professional programmers. Those who doubt this need only refer back to the +sample program in section 2.1. This 23-statement program took somewhere +from 15 to 30 minutes to write, whereas the same objectives can be achieved +by single-statement programs in either SNOBOL; + + PLEASE INPUT POS(0) ('-' ! '') + + (SPAN('0123456789') $ OUTPUT) + + *NE(OUTPUT) :S(PLEASE)F(END) + +or APL; + + [1] >-0=/?<-? + +Admittedly, neither of these is likely to appear more intelligible to +anyone unfamiliar with the languages involved, but they took roughly 60 +seconds and 15 seconds, respectively, to write. Such is the overwhelming +power of INTERCAL! + +The other major importance of INTERCAL lies in its seemingly inexhaustible +capacity for amazing one's fellow programmers, confounding programming shop +managers, winning friends, and influencing people. It is a well-known and +oft-demonstrated fact that a person whose work is incomprehensible is held +in high esteem. For example, if one were to state that the simplest way to +store a value of 65535 in a 32-bit INTERCAL variable is: + + DO :1 <- #0?#256 + +any sensible programmer would say that that was absurd. Since this is +indeed the simplest method, the programmer would be made to look foolish in +front of his boss, who would of course happened to turn up, as bosses are +wont to do. The effect would be no less devastating for the programmer +having been correct. + + + + + + + +3. DESCRIPTION + +The examples of INTERCAL programming which have appeared in the preceding +sections of this manual have probably seemed highly esoteric to the reader +unfamiliar with the language. With the aim of making them more so, we +present here a description of INTERCAL. + + +3.1 VARIABLES + +INTERCAL allows only 2 different types of variables, the 16-bit integer +and the 32-bit integer. These are represented by a spot (.) or two-spot +(:), respectively, followed by any number between 1 and 65535, inclusive. +These variables may contain only non-negative numbers; thus they have the +respective ranges of values: 0 to 65535 and 0 to 4294967295. Note: .123 and +:123 are two distinct variables. On the other hand, .1 and .0001 are +identical. Furthermore, the latter may NOT be written as 1E-3. + +3.2 CONSTANTS + +Constants are 16-bit values only and may range from 0 to 65535. They are +prefixed by a mesh (#). Caution! Under no circumstances confuse the mesh +with the interleave operator, except under confusing circumstances! + +3.3 ARRAYS + +Arrays are represented by a tail (,) for 16-bit values, or a hybrid (;) for +32-bit values, followed by a number between 1 and 65535, inclusive. The +number is suffixed by the word SUB, followed by the subscripts, separated +optionally by spaces. Subscripts may be any expressions, including those +involving subscripted variables. This occasionally leads to ambiguous +constructions, which are resolved as discussed in section 3.4.3. Definition +of array dimensions will be discussed later in greater detail, since +discussing it in less detail would be difficult. As before, ,123 and ;123 +are distinct. In summary, .123, :123, #123, ,123, and :123 are all +distinct. + +3.4 OPERATORS + +INTERCAL recognizes 5 operators--2 binary and 3 unary. Please be kind to +our operators: they may not be very intelligent, but they're all we've got. +In a sense, all 5 operators are binary, as they are all bit-oriented, but +it is not our purpose here to quibble about bits of trivia. + + + +******* proofread versus the paper copy only this far !!!! ************ + + + + +3.4.1 BINARY OPERATORS + +The binary operators are INTERLEAVE (also called MINGLE) and SELECT, which +are represented by a change (c/) and a sqiggle [sic] (~), respectively. + +The interleave operator takes two 16-bit values and produces a 32-bit result +by alternating the bits of the operands. Thus, #65535c/#0 has the 32-bit +binary form 101010....10 or 2863311530 decimal, while #0c/#65535 = +0101....01 binary = 1431655765 decimal, and #255c/#255 is equivalent to +#65535. + +The select operator takes from the first operand whichever bits correspond +to 1's in the second operand, and packs these bits as the right in the result. +Both operands are automatically padded on the left with zeros to 32 bits +before the selection takes place, so the variable types are unrestricted. +If more than 16 bits are selected, the result is a 32-bit value, otherwise +it is a 16-bit value. For example, #179~#201 (binary value 10110011~11001001) +selects from the first argument the 8th, 7th, 4th, and 1st from last bits, +namely, 1001, which = 9. But #201~#179 selects from binary 11001001 the 8th, +6th, 5th, 2nd, and 1st from last bits, giving 10001 = 17. #179~#179 has the +value 31, while #201~#201 has the value 15. + +Perhaps a simpler way of understanding the operation of the select operator +would be to examine the logic diagram on the following page (Figure 1), which +performs the select operation upon two 8-bit values, A and B. The gates used +are Warmenhovian logic gates, which means the outputs have four possible +values: low, high, undefined (value of an uninitialized flip-flop), and +oscillating (output of a NOR gate with one input low and the other input +connected to the output). These values are represented symbolically by '0', +'1', '2', and 'F'. Note in particular that, while NOT-0 is 1 and NOT-2 +is 0 as in two-valued logic, NOT-? is ? and NOT-F is F. The functions +of the various gates are listed on Table 1. + + + + + + + +3.4.2 UNARY OPERATORS + +The unary operators are & (logical AND), V (logical OR), and V- (logical +XOR). This last character is obtained by overpunching a worm (-) on a V +(V). The operator is inserted between the spot, two-spot, mesh, or +what-have-you, and the integer, thus: .&123, #V123. Multiple unary +operators may not be concatenated, thus the form #V&123 is invalid. This +will be covered later when precedence is discussed. These operators perform +their respective logical operations on all pairs of adjacent bits, the +result from the first and last bits going into the first bit of the result. +The effect is that of rotating the operand one place to the right and +ANDing, ORing, or XORing with its initial value. Thus, #&77 (binary = +1001101) is binary 0000000000000100 = 4, #V77 is binary 1000000001101111 = +32879, and #V77 is binary 1000000001101011 = 32875. + +3.4.3 PRECEDENCE + +Precedence of operators is as follows: + + + + + + + + + + + + [1] +(The remainder of this page intentionally left blank) + + + + + + + + + + + + + +_________________ +1) Keep in mind that the aim in designing INTERCAL was to have no + precedents. + + + + + + + +This precedence (or lack thereof) may be overruled by grouping expressions +between pairs of sparks (') or rabbit-ears ("). Thus '#165c/#203'~#358 +(binary value '10100101c11001011'~101100110) has the value 15, but +#165c/'#203~#358' has the value 34815, and #165c/#203~#358 is invalid +syntax and is completely valueless (except perhaps as an educational tool +to the programmer). A unary operator is applied to a sparked or rabbit-eared +expression by inserting the operator immediately following the opening spark +or ears. Thus, the invalid expression #V&123, which was described earlier, +could be coded as 'V#&123' or 'V"{"'. Note: In the interests of +simplifying the sometimes overly-complex form of expressions, INTERCAL allows +a spark-spot combination ('.) to be replaced with a wow (!). Thus '.1~.2' +is equivalent to !1~.2', and 'V.1c.2' is equivalent to "V!1c.2'". + +Combining a rabbit-ears with a spot to form a rabbit (V) is not permitted, +although the programmer is free to use it should he find an EBCDIC reader +which will properly translate a 12-3-7-8 punch. + +Sparks and/or rabbit-ears must also be used to distinguish among such +otherwise ambiguous subscripted and multiply-subscripted expressions as: + + ,1 SUB #1 ~ #2 + ,1 SUB ,2 SUB #1 #2 #3 + ,1 SUB " ,2 SUB " ,3 SUB #1 " #2 " " #3 " + +The third case may be isolated into either of its possible interpretations +by simply changing some pairs of rabbit-ears to sparks, instead of adding +more ears (which would only confuse the issue further). Ambiguous cases are +defined as those for which the compiler being used finds a legitimate +interpretation which is different from that which the user had in mind. See +also section 8.1. + + + + + + +4. STATEMENTS + +In this section is described the format of INTERCAL statements. + +4.1 GENERAL FORMAT + +Statements may be entered in 'free format'. That is, more than one statement +may occur on a single card, and a statement may begin on one card and end +on a later one. Note that if this is done, all intervening cards and portions +thereof must be part of the same statement. That this restriction is necessary +is immediately apparent from the following example of what might occur if +statements could be interlaced. + + DO .1 <- ".1c/'&:51~"#V1c!12~;&75SUB"V'V.1~ + DO .2 <- '"!1c/"&';V79SUB",&7SUB:173"'~!V9c + .2'c,&1SUB:5~#33578"'"'"~'#65535c/"V'V#&85'"' + #8196'"'~.1"c.2'~'#&5c/"'#1279c/#4351'~#65535"' + +The above statements are obviously meaningless. (For that matter, so are +the statements + + DO .1 <- ".1c/"&:51~"#V1C!12~;&75SUB"V'V.1~ + .2'C,&1SUB:5~/333578"'"'"~#65535c/"V'V#&85'"' + DO .2 <- '"!1c/"&';V79SUB",&7SUB:173"'~!V9c + #8196'"'~.1"c.2'~'#&5c/"'#1279c!4351'~#65535"' + +but this is not of interest here.) + +Spaces may be used freely to enhance program legibility (or at least reduce +program illegibility), with the restriction that no word of a statement +identifier (see section 4.3) may contain any spaces. + +4.2 LABELS + +A statement may begin with a LOGICAL LINE LABEL enclosed in wax-wane pairs +(()). A statement may not have more than one label, although it is possible +to omit the label entirely. A line label is any integer from 1 to 65535, +which must be unique within each program. The user is cautioned, however, +that many line labels between 1000 and 1999 are used in the INTERCAL System +Library functions. + +4.3 IDENTIFIERS AND QUALIFIERS + +After the line label (if any), must follow one of the following statement +identifiers: DO, PLEASE, or PLEASE DO. These may be used interchangeably to +improve the aesthetics of the program. The identifier is then followed by +either, neither, or both of the following optional parameters (qualifiers): +(1) either of the character strings NOT or N'T, which causes the statement +to be automatically abstained from (see section 4.4.9) when execution +begins, and (2) a number between 0 and 100, preceded by a double-oh-seven +(%), which causes the statement to have only the specified percent chance +of being executed each time it is encountered in the course of execution. + + + + + + + +4.4 STATEMENTS + +Following the qualifiers (or, if none are used, the identifier) must occur +one of the 13 valid operations. (Exception: see section 4.5.) These are +described individually in sections 4.4.1 through 4.4.13. + +4.4.1 CALCULATE + +The INTERCAL equivalent of the half-mesh (=) in FORTRAN, BASIC, PL/I, and +others, is represented by an angle (<) followed by a worm (-). This +combination is read 'gets'. 32-bit variables may be assigned 16-bit values, +which are padded on the left with 16 zero bits. 16-bit variables may be +assigned 32-bit values only if the value is less than 65535. Thus, to +invert the least significant bit of the first element of 16-bit +2-dimensional array number 1, one could write: + + ,1SUB#1#1 <- 'V,1SUB#1#1c/#1'~'#0c/#65535' + +Similarly to SNOBOL and SPITBOL, INTERCAL uses the angle-worm to define the +dimensions of arrays. An example will probably best describe the format. +To define 32-bit array number 7 as 3-dimensional, the first dimension being +seven, the second being the current value of 16-bit variable number seven, +and the third being the current value of the seventh element of 16-bit array +number seven (which is one-dimensional) mingled with the last three bits of +32-bit variable number seven, one would write (just before they came to take +him away): + + ;7 <- #7 BY .7 BY ",7SUB#7"c/':7~#7' + +This is, of course, different from the statement: + + ;7 <- #7 BY .7 BY ,7SUB"#7c/':7~#7'" + +INTERCAL also permits the redefining of array dimensioning, which is done +the same way as is the initial dimensioning. All values of items in an array +are lost upon redimensioning, unless they have been STASHed (see section +4.4.5), in which case restoring them also restores the old dimensions. + + + + + + + +4.4.2 NEXT + +The NEXT statement is used both for subroutine calls and for unconditional +transfers. This statement takes the form: + + DO (label) NEXT + +(or, of course, + + PLEASE DO (label) NEXT + +etc.), where (label) represents any logical line label which appears in the +program. The effect of such a statement is to transfer control to the +statement specified, and to store in a push down list (which is initially +empty) the location from which the transfer takes place. Items may be +removed from this list and may be discarded or used to return to the +statement immediately following the NEXT statement. These operations are +described in sections 4.4.3 and 4.4.4 respectively. The programmer is +generally advised to discard any stack entries which he does not intend to +utilize, since the stack has a maximum depth of 79 entries. A program's +attempting to initiate an 80th level of NEXTing will result on the fatal +error message, "PROGRAM HAS DISAPPEARED INTO THE BLACK LAGOON." + +4.4.3 FORGET + +The statement PLEASE FORGET exp, where exp represents any expression +(except colloquial and facial expressions), causes the expression to be +evaluated, and the specified number of entries to be removed from the +NEXTing stack and discarded. An attempt to FORGET more levels of NEXTing +than are currently stacked will cause the stack to be emptied, and no error +condition is indicated. This is because the condition is not considered to +be an error. As described in section 4.4.2, it is good programming practice +to execute a DO FORGET #1 after using a NEXT statement as an unconditional +transfer, so that the stack does not get cluttered up with unused entries: + + DO (123) NEXT + . + . + (123) DO FORGET #1 + +4.4.4 RESUME + +The statement PLEASE RESUME exp has the same effect as FORGET, except that +program control is returned to the statement immediately following the NEXT +statement which stored in the stack the last entry to be removed. Note that +a rough equivalent of the FORTRAN computed GO TO and BASIC ON exp GO TO is +performed by a sequence of the form: + + DO (1) NEXT + . + . + (1) DO (2) NEXT + PLEASE FORGET #1 + . + . + (2) DO RESUME .1 + +Unlike the FORGET statement, an attempt to RESUME more levels of NEXTing than +been stacked will cause program termination. See also section 4.4.11. + + +4.4.5 STASH + +Since subroutines are not explicitly implemented in INTERCAL, the NEXT and +RESUME statements must be used to execute common routines. However, as +these routines might use the same variables as the main program, it is +necessary for them to save the values of any variables whose values they +alter, and later restore them. This process is simplified by the STASH +state ment, which has the form DO STASH list, where list represents a +string of one or more variable or array names, separated by intersections +(+). Thus + + PLEASE STASH .123+:123+,123 + +stashes the values of two variables and one entire array. The values are +left intact, and copies thereof are saved for later retrieval by (what else?) +the RETRIEVE statement (see section 4.4.6). It is not possible to STASH +single array items. + +4.4.6 RETRIEVE + +PLEASE RETRIEVE list restores the previously STASHed values of the variables +and arrays named in the list. If a value has been stashed more than once, +the most recently STASHed values are RETRIEVEd, and a second RETRIEVE will +restore the second most recent values STASHed. Attempting to RETRIEVE a +value which has not been STASHed will result in the error message, "THROW +STICK BEFORE RETRIEVING." + +4.4.7 IGNORE + +The statement DO IGNORE list causes all subsequent statements to have no +effect upon variables and/or arrays named in the list. Thus, for example, +after the sequence + + DO .1 <- #1 + PLEASE IGNORE .1 + DO .1 <- #0 + +16-bit variable number 1 would have the value 1, not 0. Inputting (see +section 4.4.12) into an IGNOREd variable also has no effect. The condition +is annulled via the REMEMBER statement (see section 4.4.8). Note that, when +a variable is being IGNOREd, its value, though immutable, is still +available for use in expressions and the like. + +4.4.8 REMEMBER + +PLEASE REMEMBER list terminates the effect of the IGNORE statement for all +variables and/or arrays named in the list. It does not matter if a variable +has been IGNOREd more than once, nor is it an error if the variable has not +been IGNOREd at all. + +4.4.9 ABSTAIN + +INTERCAL contains no simple equivalent to an IF statement or computed GO +TO, making it difficult to combine similar sections of code into a single +routine which occasionally skips around certain statements. The IGNORE +statement (see section 4.4.7) is helpful in some cases, but a more viable +method is often required. In keeping with the goal of INTERCAL having +nothing in common with any other language, this is made possible via the +ABSTAIN statement. + +This statement takes on one of two forms. It may not take on both at any one +time. DO ABSTAIN FROM (label) causes the statement whose logical line label +is (label) to be abstained form. PLEASE ABSTAIN FROM gerund list causes all +statements of the specified type(s) to be abstained from, as in + + PLEASE ABSTAIN FROM STASHING + PLEASE ABSTAIN FROM IGNORING + FORGETTING + PLEASE ABSTAIN FROM NEXTING + or PLEASE ABSTAIN FROM CALCULATING + +Statements may also be automatically abstained from at the start of +execution via the NOT or N'T parameter (see section 4.3). + + +If, in the course of execution, a statement is encountered which is being +abstained from, it is ignored and control passes to the next statement in +the program (unless it, too, is being abstained from). + +The statement DO ABSTAIN FROM ABSTAINING is perfectly valid, as is DO ABSTAIN +FROM REINSTATING (although this latter is not usually recommended). However, +the statement DO ABSTAIN FROM GIVING UP is not accepted, even though DON'T +GIVE UP is. + +4.4.10 REINSTATE + +The REINSTATE statement, like the ABSTAIN, takes as an argument either a +line label or a gerund list. No other form of argument is permitted. For +example, the following is an invalid argument: + + Given: x=/0, y=/0, Prove: x+y=0 + Since x=/0, then x+1=/1, x+a=/a, x+y=/y. + Thus x+y =/ anything but 0. + Since x+y cannot equal anything but 0, x+y=0. + + Q.E.D. + +REINSTATEment nullifies the effects of an abstention. Either form of +REINSTATEment can be used to "free" a statement, regardless of whether the +statement was abstained from by gerund list, line label, or NOT. Thus, +PLEASE REINSTATE REINSTATING is not necessarily an irrelevant statement, +since it might free a DON'T REINSTATE command or a REINSTATE the line label +of which was abstained from. However, DO REINSTATE GIVING UP is invalid, +and attempting to REINSTATE a GIVE UP statement by line label will have no +effect. Note that this insures that DON'T GIVE UP will always be a +"do-nothing" statement. + +4.4.11 GIVE UP + +PLEASE GIVE UP is used to exit from a program. It has the effect of a PLEASE +RESUME #80. DON'T GIVE UP, as noted in section 4.4.10, is effectively a null +statement. + +4.4.12 Input + +Input is accomplished with the statement DO WRITE IN list, where list +represents a string of variables and/or elements or arrays, separated by +intersections. Numbers are represented on cards, each number on a separate +card, by spelling out each digit (in English) and separating the digits +with one or more spaces. A zero (0) may be spelled as either ZERO or OH. +Thus the range of (32-bit) input values permissible extends from ZERO (or +OH) through FOUR TWO NINE FOUR NINE SIX SEVEN TWO NINE FIVE. + +Attempting to write in a value greater than or equal to SIX FIVE FIVE THREE +SIX for a 16-bit variable will result in the error message, "DON'T BYTE OFF +MORE THAN YOU CAN CHEW." + +4.4.13 Output + +Values may be output to the printer, one value per line, via the statement +DO READ OUT list, where the list contains variables, array elements, and/or +constants. Output is in the form of "extended" Roman numerals (also called + _ +"butchered" Roman numerals), with an overline ( ) indicating the value below +is "times 1000", and lower-case letters indicating "times 1000000". Zero +is indicated by an overline with no character underneath. Thus, the range + _ __ _______ +of (32-bit) output values possible is from through ivccxcivCMLXVIICCXCV. +Note: For values whose residues modulo 1000000 are less than 4000, M is +used to represent 1000; for values whose residues are 4000 or greater, I is + __ +used. Thus #3999 would read out as MMMIM while #4000 would read out as IV. + _ _ _ +Similar rules apply to the use of M and i for 1000000, and to that of m and i +for 1000000000. + + + + + + + +4.5 Comments + +Unrecognizable statements, as noted in section 7, are flagged with a splat +(*) during compilation, and are not considered fatal errors unless they +are encountered during execution, at which time the statement (as input at +compilation time) is printed and execution is terminated. This allows for +an interesting (and, by necessity, unique) means of including comments in +an INTERCAL listing. For example, the statement: + +* PLEASE NOTE THAT THIS LINE HAS NO EFFECT + +will be ignored during execution due to the inclusion of the NOT qualifier. +User-supplied error messages are also easy to implement: + +* DO SOMETHING ABOUT OVERFLOW IN ;3 + +as are certain simple conditional errors: + +* (123) DON'T YOU REALIZE THIS STATEMENT SHOULD ONLY BE ENCOUNTERED + ONCE? + PLEASE REINSTATE (123) + +This pair of statements will cause an error exit the second time they are +encountered. Caution!! The appearance of a statement identifier in an +intended comment will be taken as the beginning of a new statement. Thus, +the first example on the preceding page could not have been: + +* PLEASE NOTE THAT THIS LINE DOES NOTHING + +The third example, however, is valid, despite the appearance of two cases +of D-space-O, since INTERCAL does not ignore extraneous spaces in statement +identifiers. + + + + + + +5. SUBROUTINE LIBRARY + +INTERCAL provides several built-in subroutines to which control can be +transferred to perform various operations. These operations include many +useful functions which are not easily representable in INTERCAL, such as +addition, subtraction, etc. + +5.1 Usage + +In general, the operands are .1, .2, etc., or :1, :2, etc., and the result(s) +are stored in what would have been the next operand(s). For instance, one +routine adds .1 to .2 and store the sum in .3, with .4 being used to indicate +overflow. All variables not used for results are left unchanged. + +5.2 Available Functions + +At the time of this writing, only the most fundamental operations are offered +in the library, as a more complete selection would require prohibitive time +and coree to implement. These functions, along with their corresponding entry +points (entered via DO (entry) NEXT) are listed below. + + (1000) .3 <- .1 plus .2, error exit on overflow + (1009) .3 <- .1 plus .2 + .4 <- #1 if no overflow, else .4 <- #2 + (1010) .3 <- .1 minus .2, no action on overflow + (1020) .1 <- .1 plus #1, no action on overflow + (1030) .3 <- .1 times .2, error exit on overflow + (1039) .3 <- .1 times .2 + .4 <- #1 if no overflow, else .4 <- #2 + (1040) .3 <- .1 divided by .2 + .3 <- #0 if .2 is #0 + (1050) .2 <- :1 divided by .1, error exit on overflow + .2 <- #0 if .1 is #0 + + (1500) :3 <- :1 plus :2, error exit on overflow + (1509) :3 <- :1 plus :2 + :4 <- #1 if no overflow, else :4 <- #2 + (1510) :3 <- :1 minus :2, no action on overflow + (1520) :1 <- .1 concatenated with .2 + (1525) This subroutine is intended solely for internal + use within the subroutine library and is therefore + not described here. Its effect is to shift .3 + logically 8 bits to the left. + (1530) :1 <- .1 times .2 + (1540) :3 <- :1 times :2, error exit on overflow + (1549) :3 <- :1 times :2 + :4 <- #1 if no overflow, else :4 <- #2 + (1550) :3 <- :1 divided by :2 + :3 <- #0 if :2 is #0 + + (1900) .1 <- uniform random no. from #1 to #65535 + (1910) .2 <- normal random no. from #0 to .1, with + standard deviation .1 divided by #12 + + + + + + + +6. PROGRAMMING HINTS + +For the user looking to become more familiar with the INTERCAL language, we +present in this section an analysis of a complex program, as well as some +suggested projects for the ambitious programmer. + +Considering the effort involved in writing an INTERCAL program, it was +decided in putting together this manual to use an already existing program +for instructive analysis. Since there was only one such program available, +we have proceeded to use it. It is known as the "INTERCAL System Library." + +6.1 Description + +The program listing begins on the second page following. It is in the same +format as would be produced by the Princeton INTERCAL compiler in FORMAT +mode with WDITH=62 (see section 8). For a description of the functions +performed by the Library, see section 5.2. + +6.2 Analysis + +We shall not attempt to discuss here the algorithms used, but rather we +shall point out some of the general techniques applicable to a wide range +of problems. + +Statements 10, 14, 15, and 26 make up a virtual "computed GO TO". When +statement 10 is executed, control passes eventually to statement 16 or 11, +depending on whether .5 contains #1 or #2, respectively. The value of .5 +is determined in statement 9, which demonstrates another handy technique. +To turn an expression, exp, with value #0 or #1, into #1 or #2 (for use in +a "GO TO"), use "V'exp'c/#1"~#3. To reverse the condition (i.e., convert #0 +to #2 and leave #1 alone) use "V'exp'c/#2"~#3. + +Certain conditions are easily checked. For example, to test for zero, +select the value from itself and select the bottom bit (see statement 54). +To test for all bits being 1's, select the value from itself and select the +top bit (see statement 261). The test to greater than, performed in +statements 192 and 193 on 32-bit values, employs binary logical operations, +which are performed as follows: + + 'V.1c.2'~'#0c/#65535' + +for 16-bit values or, for 32-bit values: + + "'V":1~'#65535c30'"c/":2~'#65535c/#0'"'~'#0 + c/#65535'"c/"'V":1~'#0c/#65535'"c/":2~'#0 + c/#65535'"'~'#0c/#65535'" + +(The proofs are left as an exercise to the reader.) + +Testing for greater-than with 16-bit values is somewhat simpler and is done +with the pair of statements: + + DO .C <- 'V.Ac.B'~'#0c/#65535' + DO .C <- '&"'.A~.C'~'"V'V.C~.C'c/#32768" + ~"#0c/#65535"'"c/".C~.CZ''ZZZ`#1 + +This sets .C (a dummy variable) to #1 if .A > .B, and #0 otherwise. The +expression may be expanded as described above to instead set .C to #1 or +#2. + +Note also in statement 220 the occurrence of ~"#65535c65535". Although +these operations select the entire value, they are not extraneous, as they +ensure that the forthcoming Vs will be operating on 32-bit values. + +In several virtual computed GO TOs the DO FORGET #1 (statement 15 in the +earlier example) has been omitted, since the next transfer of control would +be a DO RESUME #1. By making this a DO RESUME #2 instead, the FORGET may +be forgotten. + +In statement 64, note that .2 is STASHed twice by a single statement. This +is perfectly legal. + +Lastly, note in statements 243 and 214 respectively, expressions for +shifting 16- and 32-bit variables logically one place to the left. +Statement 231 demonstrates right-shifting for 32-bit variables. + +MIKE GETS TO INSERT THE PROGRAM HERE! + +6.3 Program Listing + + 1 (1000) PLEASE IGNORE .4 + 2 PLEASE ABSTAIN FROM (1005) + 3 (1009) DO STASH .1 + .2 + .5 + .6 + 4 DO .4 <- #1 + 5 DO (1004) NEXT + 6 (1004) PLEASE FORGET #1 + 7 DO .3 <- 'V.1c.2'~'#0c/#65535' + 8 DO .6 <- '&.1c.2'~'#0c/#65535' + 9 PLEASE DO .5 <- "V!6~#32768'c/#1"~#3 + 10 DO (1002) NEXT + 11 DO .4 <- #2 + 12 (1005) DO (1006) NEXT +* 13 (1999) DOUBLE OR SINGLE PRECISION OVERFLOW + 14 (1002) DO (1001) NEXT + 15 (1006) PLEASE FORGET #1 + 16 DO .5 <- 'V"!6~.6'~#1"c/#1'~#3 + 17 DO (1003) NEXT + 18 DO .1 <- .3 + 19 DO .2 <- !6c/#0'~'#32767c/#1' + 20 DO (1004) NEXT + 21 (1003) DO (1001) NEXT + 22 DO REINSTATE (1005) + 23 (1007) PLEASE RETRIEVE .1 + .2 + .5 + .6 + 24 DO REMEMBER .4 + 25 PLEASE RESUME #2 + 26 (1001) DO RESUME .5 + 27 (1010) DO STASH .1 + .2 + .4 + 28 DO .4 <- .1 + 29 DO .1 <- 'V.2c/#65535'~'#0c/#65535' + 30 DO (1020) NEXT + 31 PLEASE DO .2 <- .4 + 32 PLEASE DO (1009) NEXT + 33 DO RETRIEVE .1 + .2 + .4 + 34 PLEASE RESUME #1 + 35 (1020) DO STASH .2 + .3 + 36 DO .2 <- #1 + 37 PLEASE DO (1021) NEXT + 38 (1021) DO FORGET #1 + 39 DO .3 <- "V!1~.2'c/#1"~#3 + 40 PLEASE DO .1 <- 'V.1c.2'~'#0c/#65535' + 41 DO (1022) NEXT + 42 DO .2 <- !2c/#0'~'#32767c/#1' + 43 DO (1021) NEXT + 44 (1023) PLEASE RESUME .3 + 45 (1022) DO (1023) NEXT + 46 PLEASE RETRIEVE .2 + .3 + 47 PLEASE RESUME #2 + 48 (1030) DO ABSTAIN FROM (1033) + 49 PLEASE ABSTAIN FROM (1032) + 50 (1039) DO STASH :1 + .5 + 51 DO (1530) NEXT + 52 DO .3 <- :1~#65535 + 53 PLEASE DO .5 <- :1~'#65280c/#65280' + 54 DO .5 <- 'V"!5~.5'~#1"c/#1'~#3 + 55 DO (1031) NEXT + 56 (1032) DO (1033) NEXT + 57 DO (1999) NEXT + 58 (1031) DO (1001) NEXT + 59 (1033) DO .4 <- .5 + 60 DO REINSTATE (1032) + 61 PLEASE REINSTATE (1033) + 62 DO RETRIEVE :1 + .5 + 63 PLEASE RESUME #2 + 64 (1040) PLEASE STASH .1 + .2 + .2 + :1 + :2 + :3 + 65 DO .2 <- #0 + 66 DO (1520) NEXT + 67 DO STASH :1 + 68 PLEASE RETRIEVE .2 + 69 DO .1 <- .2 + 70 DO .2 <- #0 + 71 PLEASE DO (1520) NEXT + 72 DO :2 <- :1 + 73 DO RETRIEVE .1 + .2 + :1 + 74 DO (1550) NEXT + 75 PLEASE DO .3 <- :3 + 76 DO RETRIEVE :1 + :2 + :3 + 77 DO RESUME #1 + 78 (1050) PLEASE STASH :2 + :3 + .5 + 79 DO :2 <- .1 + 80 PLEASE DO (1550) NEXT + 81 DO .5 <- :3~'#65280c/#65280' + 82 DO .5 <- 'V"!5~.5'~#1"c/#1'~#3 + 83 DO (1051) NEXT + 84 DO (1999) NEXT + 85 (1051) DO (1001) NEXT + 86 DO .2 <- :3 + 87 PLEASE RETRIEVE :2 + :3 + .5 + 88 DO RESUME #2 + 89 (1500) PLEASE ABSTAIN FROM (1502) + 90 PLEASE ABSTAIN FROM (1506) + 91 (1509) PLEASE STASH :1 + .1 + .2 + .3 + .4 + .5 + .6 + 92 DO .1 <- :1~#65535 + 93 PLEASE DO .2 <- :2~#65535 + 94 DO (1009) NEXT + 95 DO .5 <- .3 + 96 PLEASE DO .6 <- .4 + 97 DO .1 <- :1~'#65280c/#65280' + 98 DO .2 <- :2~'#65280c/#65280' + 99 DO (1009) NEXT + 100 DO .1 <- .3 + 101 PLEASE DO (1503) NEXT + 102 DO .6 <- .4 + 103 DO .2 <- #1 + 104 DO (1009) NEXT + 105 DO .1 <- .3 + 106 DO (1501) NEXT + 107 (1504) PLEASE RESUME .6 + 108 (1503) DO (1504) NEXT + 109 (1501) DO .2 <- .5 + 110 DO .5 <- 'V"'&.6c.4'~#1"c/#2'~#3 + 111 DO (1505) NEXT + 112 (1506) DO (1502) NEXT + 113 PLEASE DO (1999) NEXT + 114 (1505) DO (1001) NEXT + 115 (1502) DO :4 <- .5 + 116 DO (1520) NEXT + 117 DO :3 <- :1 + 118 PLEASE RETRIEVE :1 + .1 + .2 + .3 + .4 + .5 + .6 + 119 DO REINSTATE (1502) + 120 DO REINSTATE (1506) + 121 PLEASE RESUME #3 + 122 (1510) DO STASH :1 + :2 + :4 + 123 DO :1 <- "'V/":2~'#65535c/#0'"c/#65535'~'#0c/#6553 + 5'"c/"'V":2~'#0c/#65535'"c/#65535'~'#0c65535 + '" + 124 DO :2 <- #1 + 125 DO (1509) NEXT + 126 PLEASE RETRIEVE :1 + 127 DO :2 <- :3 + 128 PLEASE DO (1509) NEXT + 129 DO RETRIEVE :2 + :4 + 130 PLEASE RESUME #1 + 131 (1520) PLEASE STASH .3 + .4 + 132 DO .3 <- .1~#43690 + 133 DO (1525) NEXT + 134 PLEASE DO .4 <- 'V.3c/".2~#43690"'~'#0c/#65535' + 135 DO .3 <- .1~#21845 + 136 PLEASE DO (1525) NEXT + 137 DO :1 <- .4c/"'V.3c/".2~#21845"'~'#0c/#65535'" + 138 PLEASE RETRIEVE .3 + .4 + 139 DO RESUME #1 + 140 (1525) DO .3 <- '"'"'"!3c/#0'~'#32767c/#1'"c/#0'~'#32767 + c/#1'"c/#0'~'#16383c/#3'"c/#0'~'#4095c/#15' + 141 PLEASE RESUME #1 + 142 (1530) DO STASH :2 + :3 + .3 + .5 + 143 DO :1 <- #0 + 144 DO :2 <- .2 + 145 DO .3 <- #1 + 146 DO (1535) NEXT + 147 (1535) PLEASE FORGET #1 + 148 DO .5 <- "V!1~.3'c/#1"~#3 + 149 DO (1531) NEXT + 150 DO (1500) NEXT + 151 DO :1 <- :3 + 152 PLEASE DO (1533) NEXT + 153 (1531) PLEASE DO (1001) NEXT + 154 (1533) DO FORGET #1 + 155 DO .3 <- !3c/#0'~'#32767c/#1' + 156 DO :2 <- ":2~'#0c/#65535'"c/"'":2~'#32767c/#0'"c/# + 0'~'#32767c/#1'" + 157 PLEASE DO .5 <- "V!3~.3'c/#1"~#3 + 158 DO (1532) NEXT + 159 DO (1535) NEXT + 160 (1532) DO (1001) NEXT + 161 PLEASE RETRIEVE :2 + :3 + .3 + .5 + 162 DO RESUME #2 + 163 (1540) PLEASE ABSTAIN FROM (1541) + 164 DO ABSTAIN FROM (1542) + 165 (1549) PLEASE STASH :1 + :2 + :4 + :5 + .1 + .2 + .5 + 166 DO .1 <- :1~#65535 + 167 PLEASE DO .2 <- :2~'#65280c/#65280' + 168 DO .5 <- :1~'#65280c/#65280' + 169 DO (1530) NEXT + 170 DO :3 <- :1 + 171 DO .2 <- :2~#65535 + 172 PLEASE DO (1530) NEXT + 173 DO :5 <- :1 + 174 DO .1 <- .5 + 175 DO (1530) NEXT + 176 DO :4 <- :1 + 177 PLEASE DO :1 <- ":3~'#65280c/#65280'"c/":5~'652 + 80c/#65280'" + 178 DO .5 <- ':1~:1'~#1 + 179 DO .2 <- :2~'#65280c/#65280' + 180 DO (1530) NEXT + 181 PLEASE DO .5 <- '"':1~:1'~#1"c.5'~#3 + 182 DO .1 <- :3~#65535 + 183 DO .2 <- #0 + 184 DO (1520) NEXT + 185 PLEASE DO :2 <- :1 + 186 PLEASE DO .1 <- :4~#65535 + 187 DO (1520) NEXT + 188 DO (1509) NEXT + 189 DO .5 <- !5c/":4~#3"'~#15 + 190 DO :1 <- :3 + 191 DO :2 <- :5 + 192 DO (1509) NEXT + 193 PLEASE DO .5 <- !5c/"4~#3"'~#63 + 194 DO .5 <- 'V"!5~.5'~#1"c/#1'~#3 + 195 PLEASE RETRIEVE :4 + 196 (1541) DO :4 <- .5 + 197 DO (1543) NEXT + 198 (1542) DO (1544) NEXT + 199 PLEASE DO (1999) NEXT + 200 (1543) DO (1001) NEXT + 201 (1544) DO REINSTATE (1541) + 202 PLEASE REINSTATE (1542) + 203 PLEASE RETRIEVE :1 + :2 + :5 + .1 + .2 + .5 + 204 DO RESUME #2 + 205 (1550) DO STASH :1 + :4 + :5 + .5 + 206 DO :3 <- #0 + 207 DO .5 <- 'V"':2~:2'~#1"c/#1'~#3 + 208 PLEASE DO (1551) NEXT + + + + + + +6.4 Programming Suggestions + +For the novice INTERCAL programmer, we provide here a list of suggested +INTERCAL programming projects: + +Write an integer exponentiation subroutine. :1 <- .1 raised to the .2 power. + +Write a double-precision sorting subroutine. Given 32-bit array ;1 of size +:1, sort the contents into numerically increasing order, leaving the results +in ;1. + +Generate a table of prime numbers. + +Put together a floating-point library, using 32-bit variables to represent +floating-point numbers (let the upper half be the mantissa and the lower +half be the characteristic). The library should be capable of performing +floating-point addition, subtraction, multiplication, and division, as well +as the natural logarithm function. + +Program a Fast Fourier Transform (FFT). This project would probably entail +the writing of the floating-point library as well as sine and cosine functions. + +Calculate, to :1 places, the value of pi. + + + + + + + +7. ERROR MESSAGES + +Due to INTERCAL's implementation of comment lines (see section 4.5), most +error messages are produced during execution instead of during compilation. +All errors except those not causing immediate termination of program execution +are treated as fatal. + +7.1 Format + +All error messages appear in the following form: + + ICLnnnI (error message) + ON THE WAY TO STATEMENT nnnn + CORRECT SOURCE AND RESUBMIT + +The message varies depending upon the error involved. For undecodable +statements the message is the statement itself. The second line tells +which statement would have been executed next had the error not occurred. +Note that if the error is due to 80 attempted levels of NEXTing, the +statement which would have been executed next need not be anywhere near the +statement causing the error. + +7.2 Messages + +Brief descriptions of the different error types are listed below according +to message number. + + 000 An undecodable statement has been encountered in the course of + execution. Note that keypunching errors can be slightly disastrous, + since if 'FORGET' were misspelled F-O-R-G-E-R, the results would + probably not be those desired. Extreme misspellings may have even + more surprising consequences. For example, misspelling 'FORGET' + R-E-S-U-M-E could have drastic results. + + 017 An expression contains a syntax error. + + 079 Improper use has been made of statement identifiers. + + 099 Improper use has been made of statement identifiers. + + 123 Program has attempted 80 levels of NEXTing. + + 129 Program has attempted to transfer to a non-existent line label. + + 139 An ABSTAIN or REINSTATE statement references a non-existent line label. + + 182 A line label has been multiply defined. + + 197 An invalid line label has been encountered. + + 200 An expression involves an unidentified variable. + + 240 An attempt has been made to give an array a dimension of zero. + + 241 Invalid dimensioning information was supplied in defining or using + an array. + + 275 A 32-bit value has been assigned to a 16-bit variable. + + 436 A retrieval has been attempted for an unSTASHed value. + + 533 A WRITE IN statement or interleave (c) operation has produced a + value requiring over 32 bits to represent. + + 562 Insufficient data. + + 579 Input data is invalid. + + 621 The expression of a RESUME statement evaluated to #0. + + 632 Program execution was terminated via a RESUME statement instead of + GIVE UP. + + 633 Execution has passed beyond the last statement of the program. + + 774 A compiler error has occurred (see section 8.1). + + 778 An unexplainable compiler error has occurred (see J. Lyon or B. Woods). + + + + + + + [3] + TONSIL A + + + +The Official INTERCAL Character Set + +Tabulated on page XX are all the characters used in INTERCAL, excepting +letters and digits, along with their names and interpretations. Also +included are several characters not used in INTERCAL, which are presented +for completeness and to allow for future expansion. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +_____________________ +3) Since all other reference manuals have Appendices, it was decided that +the INTERCAL manual should contain some other type of removable organ. + +4) This footnote intentionally unreferenced. + + + + + + +_______________________________________________________________________ +| | +|Character Name Use (if any) | +| | +| . spot identify 16-bit variable | +| : two-spot identify 32-bit variable | +| , tail identify 16-bit array | +| ; hybrid identify 32-bit array | +| # mesh identify constant | +| = half-mesh | +| ' spark grouper | +| ` backspark | +| ! wow equivalent to spark-spot | +| ? what unary exlusive OR (ATARI only)| +| " rabbit-ears grouper | +| !` rabbit equivalent to ears-spot | +| | spike | +| % double-oh-seven percentage qualifier | +| - worm used with angles | +| < angle used with worms | +| > right angle | +| ( wax precedes line label | +| ) wane follows line label | +| [ U turn | +| ] U turn back | +| { embrace | +| } bracelet | +| * splat flags invalid statements | +| & ampersand[5] unary logical AND | +| V V unary logical OR | +| (or book) | +| V- bookworm unary exclusive OR | +| (or universal qualifier) | +| $ big money | +| c| change binary mingle | +| ~ sqiggle binary select | +| _ flat worm | +| overline indicates "times 1000" | +| + intersection separates list items | +| / slat | +| \ backslat | +| @ whirlpool | +| -' hookworm | +| ^ shark | +| (or simply sharkfin) | +| #I[] blotch | +|_____________________________________________________________________| + + + Table 2 (top view). INTERCAL character set. + +__________________________________ + +5) Got any better ideas? + + +NOTES ON THE ATARI IMPLEMENTATION + +The Atari implementation of INTERCAL differs from the original Princeton +version primarily in the use of ASCII rather than EBCDIC. Since there is no +"change" sign (c) in ASCII, we have substituted the "big money" ($) as the +mingle operator. We feel that this correctly represents the increasing cost +of software in relation to hardware. (Consider that in 1970 one could get +RUNOFF for free, to run on a $20K machine, whereas today a not quite as +powerful formatter costs $99 and runs on a $75 machine.) We also feel that +there should be no defensible contention that INTERCAL has any sense. +Also, since overpunches are difficult to read on the average VDT, the +exclusive-or operator may be written ?. This correctly expresses the +average person's reaction on first encountering exclusive-or, especially on +a PDP-11. Note that in both of these cases, the over-punched symbol may +also be used if one is masochistic, or concerned with portability to the +Princeton compiler. The correct over-punch for "change" is "c/" +and the correct over-punch for V is "V-". These codes will be +properly printed if you have a proper printer, and the corresponding EBCDIC +code will be produced by the /IBM option on the LIST command. + diff --git a/intercal.sty b/intercal.sty new file mode 100644 index 0000000..7f1a036 --- /dev/null +++ b/intercal.sty @@ -0,0 +1,137 @@ +% -*- Mode: TeX -*- +% for LaTeX + +% This is the file ick.sty: macros for typesetting the INTERCAL Manual. + +\setlength{\textwidth}{6.5in} +\setlength{\oddsidemargin}{0in} +\setlength{\evensidemargin}{0in} +\setlength{\textheight}{9in} +\setlength{\topmargin}{0in} +\setlength{\headsep}{0in} +\setlength{\headheight}{0in} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SPECIAL INTERCAL CHARACTERS +% + +% Save ``regular'' characters. +\def\backslat{\backslash} +\def\embrace{\verb|{|} +\def\bracelet{\verb|}|} +\def\doubleohseven{\%} +\def\ampersand{\&} +\def\sqiggle{\~{}} +\def\bigmoney{\$} +\def\shark{\verb|^|} +\def\flatworm{\verb|_|} +\def\mesh{\#} +\def\spot{.} +\def\what{?} + +% \newcommand{\mathify}[1]{\ifmmode{#1}\else\mbox{$#1$}\fi} +% \def\bookworm{\mathify{\forall}} +\def\bookworm{\rlap V-} +\def\change{\rlap c/} +%\def\altchange{\rlap /c} +\def\zero{\={}} +\def\spike{\verb+|+} +\def\althookworm{-\llap '} +\def\hookworm{'\llap -} +\def\blotch{\#\llap I\llap [\llap ]} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% INTERCAL CODE ENVIRONMENT +% + +% Just surround the code with vertical bars. + + +\catcode`|=\active % The `|' is active from now on. +\def|{\ifmmode\vert% + \else\bgroup\tt\usespecials\def|{\null\egroup}% + \fi} + +% To get backslat, embrace, or bracelet in a | | environment, use the +% respective macros. +% I might want to translate $ and ? as in the verbatim macros. +\def\activatespecials{% + \catcode`\%=\active + \catcode`\&=\active + \catcode`\~=\active + \catcode`\$=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\#=\active} + +{% Add new comment character + \catcode`+=14 + \activatespecials + \gdef\usespecials{+ + \activatespecials + \def%{\doubleohseven}+ + \def&{\ampersand}+ + \def~{\sqiggle}+ + \def${\bigmoney}+ + \def^{\shark}+ + \def_{\flatworm}+ + \def#{\mesh}}} + +% Typeset a one-line INTERCAL example. This is pretty gross. +\def\IConeline#1{\vspace\topsep\par\noindent{\tt\ \ \ \ \ \ \ \ }#1\vspace\topsep} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% VERBATIM FILE LISTING +% + +% --- The macros are based on those distributed with Sunil Podar's +% --- `EPIC.STY' macro package, attributed to Tim Morgan, +% --- modified to keep internal macros internal by Adrian F. Clark, +% --- alien@uk.ac.essex.ese +% --- ---and further latexification by Chris Rowley, +% --- ca_rowley@uk.ac.open.acs.vax. +% And finally, modified for INTERCAL by Michael Ernst. + +\def\verbatimfile#1{\begingroup + \@verbatim \frenchspacing \@vobeyspaces + \input#1 \endgroup +} + +\newcounter{lineno} + +\newif\ifcontinuationchar +% Say \continuationchartrue if the ends of continued program lines are +% flagged by slash-newline. +% This implementation doesn't allow backslashes elsewhere; it would be +% better to make \, clobbered by \@verbatim, the escape character again, +% and then define a whole wad of escape sequences, with \^^M being one. + +\newif\ifnonumber + +\def\activateverbspecials{% + \catcode`$=\active + \catcode`?=\active} + +{\activateverbspecials + \catcode`|=0 + \catcode`\\=\active + |gdef|useverbspecials{% + |def\{|nonumbertrue}% + |def${|change}% + |def?{|bookworm}% + |activateverbspecials}} + +\def\ICverbatimlisting#1{\setcounter{lineno}{0}% + \begingroup \@verbatim \frenchspacing \@vobeyspaces \parindent=20pt + \everypar{\ifnonumber\nonumberfalse + \else\stepcounter{lineno}% + \llap{{\footnotesize\rm\thelineno\ \ \ }}% + \fi}% + \ifcontinuationchar\catcode`\\=\active\fi + \useverbspecials + \input#1 + \endgroup +} + diff --git a/intercal.tex b/intercal.tex new file mode 100644 index 0000000..31eb276 --- /dev/null +++ b/intercal.tex @@ -0,0 +1,1074 @@ +% -*- Mode: TeX -*- +% for LaTeX +\documentstyle[ick]{article} + +\title{ The INTERCAL Programming Language \\ + Reference Manual} +\author{ Donald R. Woods \& James M. Lyon} +\date{ Copyright Donald R. Woods \& James M. Lyon 1973} + +\begin{document} + +\maketitle +\thispagestyle{empty} +\newpage + +\tableofcontents +\thispagestyle{empty} +\newpage +\setcounter{page}{1} + +\section{Introduction} + +The names you are about to ignore are true. However, the story has been +changed significantly. Any resemblance of the programming language +portrayed here to other programming languages, living or dead, is purely +coincidental. + +\subsection{Origin and Purpose} + +The INTERCAL programming language was designed the morning of May 26, 1972 +by Donald R. Woods and James M. Lyon, at Princeton University. Exactly +when in the morning will become apparent in the course of this manual. It +was inspired by one ambition; to have a compiler language which has nothing +at all in common with any other major language. By 'major' was meant +anything with which the authors were at all familiar, e.g., FORTRAN, BASIC, +COBOL, ALGOL, SNOBOL, SPITBOL, FOCAL, SOLVE, TEACH, APL, LISP, and PI/I. +For the most part, INTERCAL has remained true to this goal, sharing only +the basic elements such as variables, arrays, and the ability to do I/O, +and eschewing all conventional operations other than the assignment +statement (FORTRAN ``|=|''). + +\subsection{Acronym} + +The full name of the compiler is ``Compiler Language With No Pronounceable +Acronym'', which is, for obvious reasons, abbreviated ``INTERCAL''. + +\subsection{Acknowledgments} + +The authors are deeply indebted to Eric M. Van and Daniel J. Warmenhoven, +without whose unwitting assistance this manual would still have been +possible. + + + + + + + + +\section{Fundamental Concepts} + +In this section an attempt is made to describe how and why INTERCAL may be +used; i.e., what it is like and what it is good for. + +\subsection{Sample Program} + +Shown below is a relatively simple INTERCAL program which will read in +32-bit unsigned integers, treat them as signed, 2's-complement numbers, and +print out their absolute values. The program exits if the absolute value +is zero. Note in particular the inversion routine (statements 6 through +14), which could be greatly simplified if the subroutine library (see +section 5) were used. + +A more detailed analysis of a program is made in section 6 of this manual. + +\continuationchartrue % sample.i uses the continuation character convention +\ICverbatimlisting{sample.i} + + +\subsection{Uses For INTERCAL} + +INTERCAL's main advantage over other programming languages is its strict +simplicity. It has few capabilities, and thus there are few restrictions +to be kept in mind. Since it is an exceedingly easy language to learn, one +might expect it would be a good language for initiating novice programmers. +Perhaps surprising, than, is the fact that it would be more likely to +initiate a novice into a search for another line of work. As it turns out, +INTERCAL is more useful (which isn't saying much) as a challenge to +professional programmers. Those who doubt this need only refer back to the +sample program in section 2.1. This 23-statement program took somewhere +from 15 to 30 minutes to write, whereas the same objectives can be achieved +by single-statement programs in either SNOBOL; + +\begin{verbatim} + PLEASE INPUT POS(0) ('-' ! '') + + (SPAN('0123456789') $ OUTPUT) + + *NE(OUTPUT) :S(PLEASE)F(END) +\end{verbatim} +\noindent or APL; + +\IConeline{|[1] >\llap -0=\llap /?<\llap -?|} + +Admittedly, neither of these is likely to appear more intelligible to +anyone unfamiliar with the languages involved, but they took roughly 60 +seconds and 15 seconds, respectively, to write. Such is the overwhelming +power of INTERCAL! + +The other major importance of INTERCAL lies in its seemingly inexhaustible +capacity for amazing one's fellow programmers, confounding programming shop +managers, winning friends, and influencing people. It is a well-known and +oft-demonstrated fact that a person whose work is incomprehensible is held +in high esteem. For example, if one were to state that the simplest way to +store a value of 65535 in a 32-bit INTERCAL variable is: + +\begin{verbatim} + DO :1 <- #0?#256 +\end{verbatim} +any sensible programmer would say that that was absurd. Since this is +indeed the simplest method, the programmer would be made to look foolish in +front of his boss, who would of course happened to turn up, as bosses are +wont to do. The effect would be no less devastating for the programmer +having been correct. + + + + + + + +\section{Description} + +The examples of INTERCAL programming which have appeared in the preceding +sections of this manual have probably seemed highly esoteric to the reader +unfamiliar with the language. With the aim of making them more so, we +present here a description of INTERCAL. + + +\subsection{Variables} + +INTERCAL allows only 2 different types of variables, the 16-bit integer +and the 32-bit integer. These are represented by a spot (|.|) or two-spot +(|:|), respectively, followed by any number between 1 and 65535, inclusive. +These variables may contain only non-negative numbers; thus they have the +respective ranges of values: 0 to 65535 and 0 to 4294967295. Note: |.123| +and |:123| are two distinct variables. On the other hand, |.1| and |.0001| +are identical. Furthermore, the latter may NOT be written as |1E-3|. + +\subsection{Constants} + +Constants are 16-bit values only and may range from 0 to 65535. They are +prefixed by a mesh (|#|). Caution! Under no circumstances confuse the mesh +with the interleave operator, except under confusing circumstances! + +\subsection{Arrays} + +Arrays are represented by a tail (|,|) for 16-bit values, or a hybrid (|;|) +for 32-bit values, followed by a number between 1 and 65535, inclusive. +The number is suffixed by the word |SUB|, followed by the subscripts, +separated optionally by spaces. Subscripts may be any expressions, +including those involving subscripted variables. This occasionally leads +to ambiguous constructions, which are resolved as discussed in section +3.4.3. Definition of array dimensions will be discussed later in greater +detail, since discussing it in less detail would be difficult. As before, +|,123| and |;123| are distinct. In summary, |.123|, |:123|, |#123|, +|,123|, and |:123| are all distinct. + +\subsection{Operators} + +INTERCAL recognizes 5 operators---2 binary and 3 unary. Please be kind to +our operators: they may not be very intelligent, but they're all we've got. +In a sense, all 5 operators are binary, as they are all bit-oriented, but +it is not our purpose here to quibble about bits of trivia. + + + +% ******* proofread versus the paper copy only this far !!!! ************ + + + + +\subsubsection{Binary Operators} + +The binary operators are INTERLEAVE (also called MINGLE) and SELECT, which +are represented by a change (|\change|) and a sqiggle (|~|), respectively. + +The interleave operator takes two 16-bit values and produces a 32-bit +result by alternating the bits of the operands. Thus, |#65535c#0| has the +32-bit binary form 101010....10 or 2863311530 decimal, while |#0c#65535| = +0101....01 binary = 1431655765 decimal, and |#255c#255| is equivalent to +|#65535|. + +The select operator takes from the first operand whichever bits correspond +to 1's in the second operand, and packs these bits as the right in the +result. Both operands are automatically padded on the left with zeros to +32 bits before the selection takes place, so the variable types are +unrestricted. If more than 16 bits are selected, the result is a 32-bit +value, otherwise it is a 16-bit value. For example, |#179~#201| (binary +value 10110011|~|11001001) selects from the first argument the 8th, 7th, +4th, and 1st from last bits, namely, 1001, which = 9. But |#201~#179| +selects from binary 11001001 the 8th, 6th, 5th, 2nd, and 1st from last +bits, giving 10001 = 17. |#179~#179| has the value 31, while |#201~#201| +has the value 15. + +Perhaps a simpler way of understanding the operation of the select operator +would be to examine the logic diagram on the following page (Figure 1), +which performs the select operation upon two 8-bit values, A and B. The +gates used are Warmenhovian logic gates, which means the outputs have four +possible values: low, high, undefined (value of an uninitialized +flip-flop), and oscillating (output of a NOR gate with one input low and +the other input connected to the output). These values are represented +symbolically by '|0|', '|1|', '{\tt ?}', and '|F|'. Note in particular that, +while NOT-|0| is |1| and NOT-|1| is |0| as in two-valued logic, NOT-{\tt ?} is +{\tt ?} and NOT-|F| is |F|. The functions of the various gates are listed in +Table 1. +\stepcounter{table} + + + + + + + + +\subsubsection{Unary Operators} + +The unary operators are |&| (logical AND), |V| (logical OR), and +|\bookworm| (logical XOR). This last character is obtained by overpunching +a worm (|-|) on a V (|V|). The operator is inserted between the spot, +two-spot, mesh, or what-have-you, and the integer, thus: |.&123|, |#V123|. +Multiple unary operators may not be concatenated, thus the form |#V&123| is +invalid. This will be covered later when precedence is discussed. These +operators perform their respective logical operations on all pairs of +adjacent bits, the result from the first and last bits going into the first +bit of the result. The effect is that of rotating the operand one place to +the right and ANDing, ORing, or XORing with its initial value. Thus, +|#&77| (binary = 1001101) is binary 0000000000000100 = 4, |#V77| is binary +1000000001101111 = 32879, and |#V77| is binary 1000000001101011 = 32875. +\typeout{typo here} + +\subsubsection{Precedence} + +Precedence of operators is as follows: + + +\vspace{2 in} +\hspace{4 in} +\footnote{Keep in mind that the aim in designing INTERCAL was to have no +precedents.} +\stepcounter{footnote} +\vspace{1 in} + +(The remainder of this page intentionally left blank.) + +\newpage + + + + + + + + + + + + + + + + + + + + +This precedence (or lack thereof) may be overruled by grouping expressions +between pairs of sparks (|'|) or rabbit-ears (|"|). Thus +|'#165c#203'~#358| (binary value |'|10100101|c|11001011|'~|101100110) has +the value 15, but |#165c'#203~#358'| has the value 34815, and +|#165c#203~#358| is invalid syntax and is completely valueless (except +perhaps as an educational tool to the programmer). A unary operator is +applied to a sparked or rabbit-eared expression by inserting the operator +immediately following the opening spark or ears. Thus, the invalid +expression |#V&123|, which was described earlier, could be coded as +|'V#&123'| or |'V"{"'|. Note: In the interests of simplifying the +sometimes overly-complex form of expressions, INTERCAL allows a spark-spot +combination (|'.|) to be replaced with a wow (|!|). Thus |'.1~.2'| is +equivalent to |!1~.2'|, and |'V.1c.2'| is equivalent to |"V!1c.2'"|. + +Combining a rabbit-ears with a spot to form a rabbit (|V|\typeout{Rabbit?}) +is not permitted, although the programmer is free to use it should he find +an EBCDIC reader which will properly translate a 12-3-7-8 punch. + +Sparks and/or rabbit-ears must also be used to distinguish among such +otherwise ambiguous subscripted and multiply-subscripted expressions as: + +\begin{verbatim} + ,1 SUB #1 ~ #2 + ,1 SUB ,2 SUB #1 #2 #3 + ,1 SUB " ,2 SUB " ,3 SUB #1 " #2 " " #3 " +\end{verbatim} + +The third case may be isolated into either of its possible interpretations +by simply changing some pairs of rabbit-ears to sparks, instead of adding +more ears (which would only confuse the issue further). Ambiguous cases +are defined as those for which the compiler being used finds a legitimate +interpretation which is different from that which the user had in mind. +See also section 8.1. + + + + + + +\section{Statements} + +In this section is described the format of INTERCAL statements. + +\subsection{General Format} + +Statements may be entered in ``free format.'' That is, more than one +statement may occur on a single card, and a statement may begin on one card +and end on a later one. Note that if this is done, all intervening cards +and portions thereof must be part of the same statement. That this +restriction is necessary is immediately apparent from the following example +of what might occur if statements could be interlaced. + +\begin{verbatim} + DO .1 <- ".1c'&:51~"#V1c!12~;&75SUB"V'V.1~ + DO .2 <- '"!1c"&';V79SUB",&7SUB:173"'~!V9c + .2'c,&1SUB:5~#33578"'"'"~'#65535c"V'V#&85'"' + #8196'"'~.1"c.2'~'#&5c"'#1279c#4351'~#65535"' +\end{verbatim} +The above statements are obviously meaningless. (For that matter, so are +the statements + +\begin{verbatim} + DO .1 <- ".1c"&:51~"#V1C!12~;&75SUB"V'V.1~ + .2'C,&1SUB:5~/333578"'"'"~#65535c"V'V#&85'"' + DO .2 <- '"!1c"&';V79SUB",&7SUB:173"'~!V9c + #8196'"'~.1"c.2'~'#&5c"'#1279c!4351'~#65535"' +\end{verbatim} +but this is not of interest here.) + +Spaces may be used freely to enhance program legibility (or at least reduce +program illegibility), with the restriction that no word of a statement +identifier (see section 4.3) may contain any spaces. + +\subsection{Labels} + +A statement may begin with a LOGICAL LINE LABEL enclosed in wax-wane pairs +(|()|). A statement may not have more than one label, although it is +possible to omit the label entirely. A line label is any integer from 1 to +65535, which must be unique within each program. The user is cautioned, +however, that many line labels between 1000 and 1999 are used in the +INTERCAL System Library functions. + +\subsection{Identifiers and Qualifiers} + +After the line label (if any), must follow one of the following statement +identifiers: |DO|, |PLEASE|, or |PLEASE DO|. These may be used +interchangeably to improve the aesthetics of the program. The identifier +is then followed by either, neither, or both of the following optional +parameters (qualifiers): +\begin{enumerate} +\item either of the character strings |NOT| or |N'T|, which causes the statement +to be automatically abstained from (see section 4.4.9) when execution +begins, and +\item a number between 0 and 100, preceded by a double-oh-seven +(|%|), which causes the statement to have only the specified percent chance +of being executed each time it is encountered in the course of execution. +\end{enumerate} + + + + + + + +\subsection{Statements} + +Following the qualifiers (or, if none are used, the identifier) must occur +one of the 13 valid operations. (Exception: see section 4.5.) These are +described individually in sections 4.4.1 through 4.4.13. + +\subsubsection{Calculate} + +The INTERCAL equivalent of the half-mesh (|=|) in FORTRAN, BASIC, PL/I, and +others, is represented by an angle (|<|) followed by a worm (|-|). This +combination is read ``gets.'' 32-bit variables may be assigned 16-bit +values, which are padded on the left with 16 zero bits. 16-bit variables +may be assigned 32-bit values only if the value is less than 65535. Thus, +to invert the least significant bit of the first element of 16-bit +2-dimensional array number 1, one could write: + +\begin{verbatim} + ,1SUB#1#1 <- 'V,1SUB#1#1c#1'~'#0c#65535' +\end{verbatim} + +Similarly to SNOBOL and SPITBOL, INTERCAL uses the angle-worm to define the +dimensions of arrays. An example will probably best describe the format. +To define 32-bit array number 7 as 3-dimensional, the first dimension being +seven, the second being the current value of 16-bit variable number seven, +and the third being the current value of the seventh element of 16-bit +array number seven (which is one-dimensional) mingled with the last three +bits of 32-bit variable number seven, one would write (just before they +came to take him away): + +\begin{verbatim} + ;7 <- #7 BY .7 BY ",7SUB#7"c':7~#7' +\end{verbatim} +This is, of course, different from the statement: + +\begin{verbatim} + ;7 <- #7 BY .7 BY ,7SUB"#7c':7~#7'" +\end{verbatim} + +INTERCAL also permits the redefining of array dimensioning, which is done +the same way as is the initial dimensioning. All values of items in an +array are lost upon redimensioning, unless they have been |STASH|ed (see +section 4.4.5), in which case restoring them also restores the old +dimensions. + + + + + + + +\subsubsection{Next} + +The |NEXT| statement is used both for subroutine calls and for +unconditional transfers. This statement takes the form: + +\IConeline{|DO ({\em label}) NEXT|} + +\noindent +(or, of course, +\IConeline{|PLEASE DO ({\em label}) NEXT|} + +\noindent +etc.), where |({\em label})| represents any logical line label which +appears in the program. The effect of such a statement is to transfer +control to the statement specified, and to store in a push down list (which +is initially empty) the location from which the transfer takes place. +Items may be removed from this list and may be discarded or used to return +to the statement immediately following the |NEXT| statement. These +operations are described in sections 4.4.3 and 4.4.4 respectively. The +programmer is generally advised to discard any stack entries which he does +not intend to utilize, since the stack has a maximum depth of 79 entries. +A program's attempting to initiate an 80th level of |NEXT|ing will result +on the fatal error message, ``PROGRAM HAS DISAPPEARED INTO THE BLACK +LAGOON.'' + +\subsubsection{Forget} + +The statement |PLEASE FORGET| {\em exp}, where {\em exp} represents any +expression (except colloquial and facial expressions), causes the +expression to be evaluated, and the specified number of entries to be +removed from the |NEXT|ing stack and discarded. An attempt to |FORGET| +more levels of |NEXT|ing than are currently stacked will cause the stack to +be emptied, and no error condition is indicated. This is because the +condition is not considered to be an error. As described in section 4.4.2, +it is good programming practice to execute a |DO FORGET #1| after using a +|NEXT| statement as an unconditional transfer, so that the stack does not +get cluttered up with unused entries: + +\begin{verbatim} + DO (123) NEXT + . + . + (123) DO FORGET #1 +\end{verbatim} + +\subsubsection{Resume} + +The statement |PLEASE RESUME| {\em exp} has the same effect as |FORGET|, +except that program control is returned to the statement immediately +following the |NEXT| statement which stored in the stack the last entry to +be removed. Note that a rough equivalent of the FORTRAN computed |GO TO| +and BASIC |ON| {\em exp} |GO TO| is performed by a sequence of the form: + +\begin{verbatim} + DO (1) NEXT + . + . + (1) DO (2) NEXT + PLEASE FORGET #1 + . + . + (2) DO RESUME .1 +\end{verbatim} + +Unlike the |FORGET| statement, an attempt to |RESUME| more levels of +|NEXT|ing than been stacked will cause program termination. See also +section 4.4.11. + + +\subsubsection{Stash} + +Since subroutines are not explicitly implemented in INTERCAL, the |NEXT| +and |RESUME| statements must be used to execute common routines. However, +as these routines might use the same variables as the main program, it is +necessary for them to save the values of any variables whose values they +alter, and later restore them. This process is simplified by the |STASH| +state ment, which has the form |DO STASH| {\em list}, where {\em list} +represents a string of one or more variable or array names, separated by +intersections (|+|). Thus + +\begin{verbatim} + PLEASE STASH .123+:123+,123 +\end{verbatim} +stashes the values of two variables and one entire array. The values are +left intact, and copies thereof are saved for later retrieval by (what +else?) the |RETRIEVE| statement (see section 4.4.6). It is not possible +to |STASH| single array items. + +\subsubsection{Retrieve} + +|PLEASE RETRIEVE| {\em list} restores the previously |STASH|ed values of +the variables and arrays named in the list. If a value has been +stashed more than once, the most recently |STASH|ed values are |RETRIEVE|d, +and a second |RETRIEVE| will restore the second most recent values +|STASH|ed. Attempting to |RETRIEVE| a value which has not been |STASH|ed +will result in the error message, ``THROW STICK BEFORE RETRIEVING.'' + +\subsubsection{Ignore} + +The statement |DO IGNORE| {\em list} causes all subsequent statements to +have no effect upon variables and/or arrays named in the list. Thus, for +example, after the sequence + +\begin{verbatim} + DO .1 <- #1 + PLEASE IGNORE .1 + DO .1 <- #0 +\end{verbatim} +16-bit variable number 1 would have the value 1, not 0. Inputting (see +section 4.4.12) into an |IGNORE|d variable also has no effect. The +condition is annulled via the |REMEMBER| statement (see section 4.4.8). +Note that, when a variable is being |IGNORE|d, its value, though immutable, +is still available for use in expressions and the like. + +\subsubsection{Remember} + +|PLEASE REMEMBER| {\em list} terminates the effect of the |IGNORE| +statement for all variables and/or arrays named in the list. It does not +matter if a variable has been |IGNORE|d more than once, nor is it an error +if the variable has not been |IGNORE|d at all. + +\subsubsection{Abstain} + +INTERCAL contains no simple equivalent to an |IF| statement or computed |GO +TO|, making it difficult to combine similar sections of code into a single +routine which occasionally skips around certain statements. The |IGNORE| +statement (see section 4.4.7) is helpful in some cases, but a more viable +method is often required. In keeping with the goal of INTERCAL having +nothing in common with any other language, this is made possible via the +|ABSTAIN| statement. + +This statement takes on one of two forms. It may not take on both at any +one time. |DO ABSTAIN FROM ({\em label})| causes the statement whose +logical line label is |({\em label})| to be abstained form. |PLEASE +ABSTAIN FROM| {\em gerund list} causes all statements of the specified +type(s) to be abstained from, as in + +\begin{verbatim} + PLEASE ABSTAIN FROM STASHING + PLEASE ABSTAIN FROM IGNORING + FORGETTING + PLEASE ABSTAIN FROM NEXTING + PLEASE ABSTAIN FROM CALCULATING +\end{verbatim} + + +Statements may also be automatically abstained from at the start of +execution via the |NOT| or |N'T| parameter (see section 4.3). + + +If, in the course of execution, a statement is encountered which is being +abstained from, it is ignored and control passes to the next statement in +the program (unless it, too, is being abstained from). + +The statement |DO ABSTAIN FROM ABSTAINING| is perfectly valid, as is |DO +ABSTAIN FROM REINSTATING| (although this latter is not usually +recommended). However, the statement |DO ABSTAIN FROM GIVING UP| is not +accepted, even though |DON'T GIVE UP| is. + +\subsubsection{Reinstate} +\newlength{\QEDlen}\settowidth{\QEDlen}{Q.E.D.}\newlength{\lastlinelen} +\settowidth{\lastlinelen}{Since $x+y$ cannot equal anything but 0, $x+y=0$.} +\addtolength{\lastlinelen}{-1\QEDlen} + +The |REINSTATE| statement, like the |ABSTAIN|, takes as an argument either +a line label or a gerund list. No other form of argument is permitted. +For example, the following is an invalid argument: +\begin{quote} + Given: $x\neq0$, $y\neq0$, Prove: $x+y=0$. \\ + Since $x\neq0$, then $x+1\neq1$, $x+a\neq a$, $x+y\neq y$. \\ + Thus $x+y \neq$ anything but 0. \\ + Since $x+y$ cannot equal anything but 0, $x+y=0$. \\ + \hspace*{\lastlinelen} Q.E.D. +\end{quote} + +|REINSTATE|ment nullifies the effects of an abstention. Either form of +|REINSTATE|ment can be used to ``free'' a statement, regardless of whether +the statement was abstained from by gerund list, line label, or |NOT|. +Thus, |PLEASE REINSTATE REINSTATING| is not necessarily an irrelevant +statement, since it might free a |DON'T REINSTATE| command or a |REINSTATE| +the line label of which was abstained from. However, |DO REINSTATE GIVING +UP| is invalid, and attempting to |REINSTATE| a |GIVE UP| statement by line +label will have no effect. Note that this insures that |DON'T GIVE UP| +will always be a ``do-nothing'' statement. + +\subsubsection{Give Up} + +|PLEASE GIVE UP| is used to exit from a program. It has the effect of a +|PLEASE RESUME #80|. |DON'T GIVE UP|, as noted in section 4.4.10, is +effectively a null statement. + +\subsubsection{Input} + +Input is accomplished with the statement |DO WRITE IN| {\em list}, where +{\em list} represents a string of variables and/or elements or arrays, +separated by intersections. Numbers are represented on cards, each number +on a separate card, by spelling out each digit (in English) and separating +the digits with one or more spaces. A zero (0) may be spelled as either +|ZERO| or |OH|. Thus the range of (32-bit) input values permissible +extends from |ZERO| (or |OH|) through |FOUR TWO NINE FOUR NINE SIX SEVEN +TWO NINE FIVE|. + +Attempting to write in a value greater than or equal to |SIX FIVE FIVE +THREE SIX| for a 16-bit variable will result in the error message, ``DON'T +BYTE OFF MORE THAN YOU CAN CHEW.'' + +\subsubsection{Output} + +Values may be output to the printer, one value per line, via the statement +|DO READ OUT| {\em list}, where the list contains variables, array +elements, and/or constants. Output is in the form of ``extended'' Roman +numerals (also called ``butchered'' Roman numerals), with an overline +(\zero) indicating the value below is ``times 1000'', and lower-case +letters indicating ``times 1000000''. Zero is indicated by an overline +with no character underneath. Thus, the range of (32-bit) output values +possible is from \zero\ through \=i\=vccxciv\=C\=M\=L\=X\=V\=I\=ICCXCV. +Note: For values whose residues modulo 1000000 are less than 4000, M is +used to represent 1000; for values whose residues are 4000 or greater, \=I +is used. Thus |#3999| would read out as MMMIM while |#4000| would read out +as \=I\=V. Similar rules apply to the use of \=M and i for 1000000, and to +that of \=m and \=i for 1000000000. +\typeout{Do they really mean MMMIM and not MMMCMXCIX??} + + + + + + + +\subsection{Comments} + +Unrecognizable statements, as noted in section 7, are flagged with a splat +(|*|) during compilation, and are not considered fatal errors unless they +are encountered during execution, at which time the statement (as input at +compilation time) is printed and execution is terminated. This allows for +an interesting (and, by necessity, unique) means of including comments in +an INTERCAL listing. For example, the statement: + +\begin{verbatim} +* PLEASE NOTE THAT THIS LINE HAS NO EFFECT +\end{verbatim} +will be ignored during execution due to the inclusion of the NOT qualifier. +User-supplied error messages are also easy to implement: + +\begin{verbatim} +* DO SOMETHING ABOUT OVERFLOW IN ;3 +\end{verbatim} +as are certain simple conditional errors: + +\begin{verbatim} +* (123) DON'T YOU REALIZE THIS STATEMENT SHOULD ONLY BE ENCOUNTERED + ONCE? + PLEASE REINSTATE (123) +\end{verbatim} +This pair of statements will cause an error exit the second time they are +encountered. Caution!! The appearance of a statement identifier in an +intended comment will be taken as the beginning of a new statement. Thus, +the first example on the preceding page could not have been: + +\begin{verbatim} +* PLEASE NOTE THAT THIS LINE DOES NOTHING +\end{verbatim} +The third example, however, is valid, despite the appearance of two cases +of |D|-space-|O|, since INTERCAL does not ignore extraneous spaces in +statement identifiers. + + + + + + +\section{Subroutine Library} + +INTERCAL provides several built-in subroutines to which control can be +transferred to perform various operations. These operations include many +useful functions which are not easily representable in INTERCAL, such as +addition, subtraction, etc. + +\subsection{Usage} + +In general, the operands are |.1|, |.2|, etc., or |:1|, |:2|, etc., and the +result(s) are stored in what would have been the next operand(s). For +instance, one routine adds |.1| to |.2| and store the sum in |.3|, with +|.4| being used to indicate overflow. All variables not used for results +are left unchanged. + +\subsection{Available Functions} + +At the time of this writing, only the most fundamental operations are +offered in the library, as a more complete selection would require +prohibitive time and coree to implement. These functions, along with their +corresponding entry points (entered via |DO ({\em entry}) NEXT|) are listed +below. + +\begin{verbatim} + (1000) .3 <- .1 plus .2, error exit on overflow + (1009) .3 <- .1 plus .2 + .4 <- #1 if no overflow, else .4 <- #2 + (1010) .3 <- .1 minus .2, no action on overflow + (1020) .1 <- .1 plus #1, no action on overflow + (1030) .3 <- .1 times .2, error exit on overflow + (1039) .3 <- .1 times .2 + .4 <- #1 if no overflow, else .4 <- #2 + (1040) .3 <- .1 divided by .2 + .3 <- #0 if .2 is #0 + (1050) .2 <- :1 divided by .1, error exit on overflow + .2 <- #0 if .1 is #0 + + (1500) :3 <- :1 plus :2, error exit on overflow + (1509) :3 <- :1 plus :2 + :4 <- #1 if no overflow, else :4 <- #2 + (1510) :3 <- :1 minus :2, no action on overflow + (1520) :1 <- .1 concatenated with .2 + (1525) This subroutine is intended solely for internal + use within the subroutine library and is therefore + not described here. Its effect is to shift .3 + logically 8 bits to the left. + (1530) :1 <- .1 times .2 + (1540) :3 <- :1 times :2, error exit on overflow + (1549) :3 <- :1 times :2 + :4 <- #1 if no overflow, else :4 <- #2 + (1550) :3 <- :1 divided by :2 + :3 <- #0 if :2 is #0 + + (1900) .1 <- uniform random no. from #1 to #65535 + (1910) .2 <- normal random no. from #0 to .1, with + standard deviation .1 divided by #12 +\end{verbatim} + + + + + + + +\section{Programming Hints} + +For the user looking to become more familiar with the INTERCAL language, we +present in this section an analysis of a complex program, as well as some +suggested projects for the ambitious programmer. + +Considering the effort involved in writing an INTERCAL program, it was +decided in putting together this manual to use an already existing program +for instructive analysis. Since there was only one such program available, +we have proceeded to use it. It is known as the ``INTERCAL System Library.'' + +\subsection{Description} + +The program listing begins on the second page following. It is in the same +format as would be produced by the Princeton INTERCAL compiler in FORMAT +mode with WDITH=62 (see section 8). For a description of the functions +performed by the Library, see section 5.2. + +\subsection{Analysis} + +We shall not attempt to discuss here the algorithms used, but rather we +shall point out some of the general techniques applicable to a wide range +of problems. + +Statements 10, 14, 15, and 26 make up a virtual ``computed |GO TO|''. When +statement 10 is executed, control passes eventually to statement 16 or 11, +depending on whether |.5| contains |#1| or |#2|, respectively. The value +of |.5| is determined in statement 9, which demonstrates another handy +technique. To turn an expression, {\em exp}, with value |#0| or |#1|, into +|#1| or |#2| (for use in a ``|GO TO|''), use |"V'{\em exp}'c#1"~#3|. To +reverse the condition (i.e., convert |#0| to |#2| and leave |#1| alone) use +|"V'{\em exp}'c#2"~#3|. + +Certain conditions are easily checked. For example, to test for zero, +select the value from itself and select the bottom bit (see statement 54). +To test for all bits being 1's, select the value from itself and select the +top bit (see statement 261). The test to greater than, performed in +statements 192 and 193 on 32-bit values, employs binary logical operations, +which are performed as follows: + +\begin{verbatim} + 'V.1c.2'~'#0c#65535' +\end{verbatim} +for 16-bit values or, for 32-bit values: + +\begin{verbatim} + "'V":1~'#65535c30'"c":2~'#65535c#0'"'~'#0 + c#65535'"c"'V":1~'#0c#65535'"c":2~'#0 + c#65535'"'~'#0c#65535'" +\end{verbatim} +(The proofs are left as an exercise to the reader.) + +Testing for greater-than with 16-bit values is somewhat simpler and is done +with the pair of statements: + +\begin{verbatim} + DO .C <- 'V.Ac.B'~'#0c#65535' + DO .C <- '&"'.A~.C'~'"V'V.C~.C'c#32768" + ~"#0c#65535"'"c".C~.CZ''ZZZ`#1 +\end{verbatim} +This sets |.C| (a dummy variable) to |#1| if |.A| $>$ |.B|, and |#0| +otherwise. The expression may be expanded as described above to instead +set |.C| to |#1| or |#2|. + +Note also in statement 220 the occurrence of |~"#65535c65535"|. Although +these operations select the entire value, they are not extraneous, as they +ensure that the forthcoming |V|s will be operating on 32-bit values. + +In several virtual computed |GO TO|s the |DO FORGET #1| (statement 15 in the +earlier example) has been omitted, since the next transfer of control would +be a |DO RESUME #1|. By making this a |DO RESUME #2| instead, the |FORGET| may +be forgotten. + +In statement 64, note that .2 is |STASH|ed twice by a single statement. This +is perfectly legal. + +Lastly, note in statements 243 and 214 respectively, expressions for +shifting 16- and 32-bit variables logically one place to the left. +Statement 231 demonstrates right-shifting for 32-bit variables. + +% MIKE GETS TO INSERT THE PROGRAM HERE! + +\subsection{Program Listing} + +\continuationchartrue % syslib.i uses the continuation character convention +\ICverbatimlisting{syslib.i} +\typeout{Where's line 1551? 1900? 1910?} + + + + + + +\subsection{Programming Suggestions} + +For the novice INTERCAL programmer, we provide here a list of suggested +INTERCAL programming projects: + +Write an integer exponentiation subroutine. |:1 <- .1| raised to the |.2| power. + +Write a double-precision sorting subroutine. Given 32-bit array |;1| of +size |:1|, sort the contents into numerically increasing order, leaving the +results in |;1|. + +Generate a table of prime numbers. + +Put together a floating-point library, using 32-bit variables to represent +floating-point numbers (let the upper half be the mantissa and the lower +half be the characteristic). The library should be capable of performing +floating-point addition, subtraction, multiplication, and division, as well +as the natural logarithm function. + +Program a Fast Fourier Transform (FFT). This project would probably entail +the writing of the floating-point library as well as sine and cosine functions. + +Calculate, to |:1| places, the value of pi. + + + + + + + +\section{Error Messages} + +Due to INTERCAL's implementation of comment lines (see section 4.5), most +error messages are produced during execution instead of during compilation. +All errors except those not causing immediate termination of program execution +are treated as fatal. + +\subsection{Format} + +All error messages appear in the following form: + +\begin{verbatim} + ICLnnnI (error message) + ON THE WAY TO STATEMENT nnnn + CORRECT SOURCE AND RESUBMIT +\end{verbatim} + +The message varies depending upon the error involved. For undecodable +statements the message is the statement itself. The second line tells +which statement would have been executed next had the error not occurred. +Note that if the error is due to 80 attempted levels of |NEXT|ing, the +statement which would have been executed next need not be anywhere near the +statement causing the error. + +\subsection{Messages} + +Brief descriptions of the different error types are listed below according +to message number. + +\begin{description} +\def\ttitem#1{\item[{\tt #1}]} + +\ttitem{000} An undecodable statement has been encountered in the course of + execution. Note that keypunching errors can be slightly disastrous, + since if `|FORGET|' were misspelled F-O-R-G-E-R, the results would + probably not be those desired. Extreme misspellings may have even + more surprising consequences. For example, misspelling `|FORGET|' + R-E-S-U-M-E could have drastic results. + +\ttitem{017} An expression contains a syntax error. + +\ttitem{079} Improper use has been made of statement identifiers. + +\ttitem{099} Improper use has been made of statement identifiers. + +\ttitem{123} Program has attempted 80 levels of |NEXT|ing. + +\ttitem{129} Program has attempted to transfer to a non-existent line label. + +\ttitem{139} An |ABSTAIN| or |REINSTATE| statement references a non-existent line label. + +\ttitem{182} A line label has been multiply defined. + +\ttitem{197} An invalid line label has been encountered. + +\ttitem{200} An expression involves an unidentified variable. + +\ttitem{240} An attempt has been made to give an array a dimension of zero. + +\ttitem{241} Invalid dimensioning information was supplied in defining or using + an array. + +\ttitem{275} A 32-bit value has been assigned to a 16-bit variable. + +\ttitem{436} A retrieval has been attempted for an un|STASH|ed value. + +\ttitem{533} A |WRITE IN| statement or interleave (|\change|) operation has +produced a value requiring over 32 bits to represent. + +\ttitem{562} Insufficient data. + +\ttitem{579} Input data is invalid. + +\ttitem{621} The expression of a |RESUME| statement evaluated to |#0|. + +\ttitem{632} Program execution was terminated via a |RESUME| statement instead of + |GIVE UP|. + +\ttitem{633} Execution has passed beyond the last statement of the program. + +\ttitem{774} A compiler error has occurred (see section 8.1). + +\ttitem{778} An unexplainable compiler error has occurred (see J. Lyon or B. Woods). +\end{description} + + + + +\section*{Tonsil\footnote{Since all other reference manuals +have Appendices, it was decided that the INTERCAL manual should contain +some other type of removable organ.} A: The Official INTERCAL Character Set} + + + + + + +Tabulated on page 18 are all the characters used in INTERCAL, excepting +letters and digits, along with their names and interpretations. Also +included are several characters not used in INTERCAL, which are presented +for completeness and to allow for future expansion. +\stepcounter{footnote} +\footnotetext{This footnote intentionally unreferenced.} + +{\catcode`.=\active \catcode`|=12 % Use . instead of | for code mode +\def.{\bgroup\tt\usespecials\def.{\null\egroup}} +\begin{table} +\begin{minipage}{\textwidth} % so the footnote works out. +\begin{tabular}{|r|l|p{3in}|} +\hline +Character & Name & Use (if any) \\ +\hline +.\spot. & spot & identify 16-bit variable \\ +.:. & two-spot & identify 32-bit variable \\ +.,. & tail & identify 16-bit array \\ +.;. & hybrid & identify 32-bit array \\ +.\#. & mesh & identify constant \\ +.=. & half-mesh & \\ +.'. & spark & grouper \\ +.`. & backspark & \\ +.!. & wow & equivalent to spark-spot \\ +\verb|?|& what & unary exlusive OR (ASCII) \\ +.". & rabbit-ears & grouper \\ +{\verb|!`|}& rabbit & equivalent to ears-spot \\ +{\verb+|+}& spike & \\ +.\%. & double-oh-seven & percentage qualifier \\ +.-. & worm & used with angles \\ +.<. & angle & used with worms \\ +.>. & right angle & \\ +.(. & wax & precedes line label \\ +.). & wane & follows line label \\ +.[. & U turn & \\ +.]. & U turn back & \\ +.\{. & embrace & \\ +.\}. & bracelet & \\ +.*. & splat & flags invalid statements \\ +.\&. & ampersand\footnotemark & unary logical AND \\ +.V. & V (or book) & unary logical OR \\ +.\bookworm. & bookworm (or universal qualifier) & unary exclusive OR \\ +.\$. & big money & binary mingle (ASCII) \\ +.\change.& change & binary mingle \\ +.\~{}. & sqiggle & binary select \\ +.\_. & flat worm & \\ +.\zero. & overline & indicates ``times 1000'' \\ +.+. & intersection & separates list items \\ +./. & slat & \\ +{\verb|\|}& backslat & \\ +.@. & whirlpool & \\ +.\hookworm.& hookworm & \\ +{\verb|^|}& shark (or simply sharkfin) & \\ +.\blotch.& blotch \\ +\hline +\end{tabular} +\catcode`.=12 +\caption{ INTERCAL character set (top view).} +\typeout{What does Rabbit really look like?} +\def\thempfootnote{\arabic{mpfootnote}} +\footnotetext[\thefootnote]{Got any better ideas?} +\label{character-set} +\end{minipage} +\end{table} +} + + +\section*{Tonsil B: +Notes On The Atari Implementation} + +The Atari implementation of INTERCAL differs from the original Princeton +version primarily in the use of ASCII rather than EBCDIC. Since there is +no ``change'' sign (|\change|) in ASCII, we have substituted the ``big +money'' (|$|) as the mingle operator. We feel that this correctly +represents the increasing cost of software in relation to hardware. +(Consider that in 1970 one could get RUNOFF for free, to run on a \$20K +machine, whereas today a not quite as powerful formatter costs \$99 and +runs on a \$75 machine.) We also feel that there should be no defensible +contention that INTERCAL has any sense. Also, since overpunches are +difficult to read on the average VDT, the exclusive-or operator may be +written ?. This correctly expresses the average person's reaction on first +encountering exclusive-or, especially on a PDP-11. Note that in both of +these cases, the over-punched symbol may also be used if one is +masochistic, or concerned with portability to the Princeton compiler. The +correct over-punch for ``change'' is ``c{\sc +$\langle$backspace$\rangle$}/'' and the correct over-punch for |\bookworm| +is "V{\sc $\langle$backspace$\rangle$}-". These codes will be properly +printed if you have a proper printer, and the corresponding EBCDIC code +will be produced by the |/IBM| option on the |LIST| command. + +\end{document} + diff --git a/lexer.l b/lexer.l new file mode 100644 index 0000000..fabdc4e --- /dev/null +++ b/lexer.l @@ -0,0 +1,171 @@ +%{ +#include +#include +#include "ick.h" +#include "y.tab.h" + +#ifdef MAIN +YYSTYPE yylval; +#endif /* MAIN */ + +char *textlines[MAXLINES]; + +static char linebuf[YYLMAX]; +static char *lineptr = linebuf; + +/* + * The spectacular ugliness of INTERCAL syntax requires that the lexical + * analyzer have two levels. One, embedded in the getc() function, handles + * logical-line continuation and the ! abbrev, and stashes each logical + * line away in a buffer accessible to the code generator (this is necessary + * for the * construct to be interpreted correctly). The upper level is + * generated by lex(1) and does normal tokenizing. + */ + +#undef getc +int getc(fp) +FILE *fp; +{ + bool bangflag = FALSE; + + if (bangflag) + { + bangflag = FALSE; + *lineptr++ = '!'; + return('.'); + } + else + { + int c = fgetc(fp); + + if (feof(yyin)) + { + *lineptr = '\0'; + return(EOF); + } + else if (c == '\\') + { + do { + c = fgetc(fp); + } while + (c != '\\' && isspace(c)); + } + + if (c == '!') + { + *lineptr++ = '!'; + bangflag = TRUE; + return(c = '\''); + } + else if (c == '\n') + { + extern char *strdup(); + + *lineptr = '\0'; + lineptr = linebuf; + textlines[yylineno] = strdup(linebuf); + return('\n'); + } + else + { + if (islower(c)) + c = toupper(c); + return(*lineptr++ = c); + } + } +} + +%} + +W [\ \t\n]* +D [0-9][0-9]* + +%% + +[0-9][0-9]* {yylval.numval = atoi(yytext); return(NUMBER);} +\. {return(ONESPOT);} +\: {return(TWOSPOT);} +\, {return(TAIL);} +\; {return(HYBRID);} +\# {return(MESH);} + +\$ {return(MINGLE);} +\~ {return(SELECT);} + +\& {yylval.numval = AND; return(UNARY);} +V {yylval.numval = OR; return(UNARY);} +\? {yylval.numval = XOR; return(UNARY);} + +\' {return(SPARK);} +\" {return(EARS);} + +\({D}\) {yylval.numval = atoi(yytext + 1); return(LABEL);} + +DO {return(DO);} +PLEASE {return(DO);} +PLEASE{W}DO {return(DO);} +NOT {return(NOT);} +DON\'T {return(NOT);} + +\%{D} {yylval.numval = atoi(yytext + 1); return(OHOHSEVEN);} +SUB {return(SUB);} +BY {return(BY);} + +\<- {return(GETS);} +CALCULATING {yylval.numval = GETS; return(GERUND);} + +NEXT {return(NEXT);} +NEXTING {yylval.numval = NEXT; return(GERUND);} +FORGET {return(FORGET);} +FORGETTING {yylval.numval = FORGET; return(GERUND);} +RESUME {return(RESUME);} +RESUMING {yylval.numval = RESUME; return(GERUND);} +STASH {return(STASH);} +STASHING {yylval.numval = STASH; return(GERUND);} +RETRIEVE {return(RETRIEVE);} +RETRIEVING {yylval.numval = RETRIEVE; return(GERUND);} +IGNORE {return(IGNORE);} +IGNORING {yylval.numval = IGNORE; return(GERUND);} +REMEMBER {return(REMEMBER);} +REMEMBERING {yylval.numval = REMEMBER; return(GERUND);} +ABSTAIN{W}FROM {return(ABSTAIN);} +ABSTAINING {yylval.numval = ABSTAIN; return(GERUND);} +REINSTATE {return(REINSTATE);} +REINSTATING {yylval.numval = REINSTATE; return(GERUND);} +\+ {return(INTERSECTION);} +GIVE{W}UP {return(GIVE_UP);} +READ{W}OUT {return(READ_OUT);} +WRITE{W}IN {return(WRITE_IN);} +COME{W}FROM {return(COME_FROM);} +COMING{W}FROM {yylval.numval = COME_FROM; return(GERUND);} + +[\ \t][\ \t]* ; +\n {return(NEWLINE);} +. {yylval.numval = yytext[0]; return(BADCHAR);} + + +%% + +#ifdef MAIN +int yyerror() +{ + (void) printf("lextest: lexer error.\n"); +} + +main() +{ + int t; + + while ((t = yylex()) > 0) + { + (void) printf("%03d %09d\n", t, yylval.numval); + yylval.numval = 0; + } +} +#endif /* MAIN */ + +static int yywrap() +{ + return(EOF); +} + diff --git a/lose.c b/lose.c new file mode 100644 index 0000000..ef9f043 --- /dev/null +++ b/lose.c @@ -0,0 +1,20 @@ +/* + * lose.c -- report INTERCAL compile- or run-time error + */ +/*LINTLIBRARY*/ +#include + +void lose(n, m) +int n; +char *m; +{ + (void) fprintf(stderr, + "ICL%c%c%cI %s\n ON THE WAY TO STATEMENT %04d\n", + m[0], m[1], m[2], m+4, n); + (void) fprintf(stderr, " CORRECT SOURCE AND RESUBMIT\n"); + exit(atoi(m)); +} + +/* lose.c ends here */ + + diff --git a/lose.h b/lose.h new file mode 100644 index 0000000..b77c0a4 --- /dev/null +++ b/lose.h @@ -0,0 +1,52 @@ +/* lose.h --- error message strings + +/* + * These error messages are documented in the INTERCAL-72 manual. + */ +#define E000 "000 An undecodable statement has been encountered in the course of execution." +#define E017 "017 CAN'T YOU SPELL, YOU HOPELESS DWEEB?\n\ + (An expression contains a syntax error.)" +#define E079 "079 Improper use has been made of statement identifiers." +#define E099 "099 Improper use has been made of statement identifiers." +#define E123 "123 YOUR PROGRAM HAS VANISHED INTO THE BLACK LAGOON!\n\ + (Program has attempted 80 levels of NEXTing.)" +#define E129 "129 Program has attempted to transfer to a non-existent line label." +#define E139 "139 An ABSTAIN or REINSTATE statement references a non-existent line label." +#define E182 "182 A line label has been multiply defined." +#define E197 "197 An invalid line label has been encountered." +#define E200 "200 An expression involves an unidentified variable." +#define E240 "240 An attempt has been made to give an array a dimension of zero." +#define E241 "241 Invalid dimensioning information was supplied in defining or using an array." +#define E275 "275 DON'T BYTE OFF MORE THAN YOU CAN CHEW\n\ + A 32-bit value has been assigned to a 16-bit variable." +#define E436 "436 THROW STICK BEFORE RETRIEVING!\n\ + (A retrieval has been attempted for an unSTASHed value)." +#define E533 "533 A WRITE IN statement or interleave ($) operation has produced value requiring over 32 bits to represent." +#define E562 "562 Insufficient data." +#define E579 "579 Input data is invalid." +#define E621 "621 The expression of a RESUME statement evaluated to #0." +#define E632 "632 THE NEXT STACK RUPTURES. ALL DIE. OH, THE EMBARRASSMENT!\n\ + (Program execution terminated via a RESUME statement instead of GIVE UP)." +#define E633 "633 YOU HAVE FALLEN OFF THE EDGE OF THE WORLD. BYE-BYE!\n\ + (Execution has passed beyond the last statement of the program.)" +#define E774 "774 A compiler error has occurred (see section 8.1)." +#define E778 "778 An unexplainable compiler error has occurred (see J. Lyon or B. Woods)." + +/* + * These errors are unique to C-INTERCAL + */ +#define E222 "222 Out of stash space." +#define E333 "333 Too many variables." +#define E444 "444 A COME FROM statement references a non-existent line label." +#define E555 "555 More than one COME FROM references the same label." +#define E666 "666 Too many source lines." +#define E777 "777 No such source file." +#define E888 "888 Can't open C output file" +#define E999 "999 Can't open C skeleton file." + +extern void lose(); + +/* lose.h ends here */ + + + diff --git a/sample.i b/sample.i new file mode 100644 index 0000000..364eec7 --- /dev/null +++ b/sample.i @@ -0,0 +1,27 @@ + DO (5) NEXT + (5) DO FORGET #1 + PLEASE WRITE IN :1 + DO .1 <- 'V":1~'#32768$#0'"$#1'~#3 + DO (1) NEXT + DO :1 <- "'V":1~'#65535$#0'"$#65535' \ + ~'#0$#65535'"$"'V":1~'#0$#65535'" \ + $#65535'~'#0$#65535'" + DO :2 <- #1 + PLEASE DO (4) NEXT + (4) DO FORGET #1 + DO .1 <- "'V":1~'#65535$#0'"$":2~'#65535 \ + $#0'"'~'#0$#65535'"$"'V":1~'#0 \ + $#65535'"$":2~'#65535$#0'"'~'#0$#65535'" + DO (1) NEXT + DO :2 <- ":2~'#0$#65535'" \ + $"'":2~'#65535$#0'"$#0'~'#32767$#1'" + DO (4) NEXT + (2) DO RESUME .1 + (1) PLEASE DO (2) NEXT + PLEASE FORGET #1 + DO READ OUT :1 + PLEASE DO .1 <- 'V"':1~:1'~#1"$#1'~#3 + DO (3) NEXT + PLEASE DO (5) NEXT + (3) DO (2) NEXT + PLEASE GIVE UP -- 2.43.0