From: Eric S. Raymond Date: Sat, 12 May 1990 20:24:39 +0000 (-0400) Subject: Intercal 0.3 prerelease, Sat May 12 12:24:39 EDT 1990. X-Git-Tag: 0.3 X-Git-Url: http://thyrsus.com/gitweb/?a=commitdiff_plain;h=3493a5a8f6ce9a5e524107c870c171f4073af987;p=intercal.git Intercal 0.3 prerelease, Sat May 12 12:24:39 EDT 1990. This was the first publicly visible version. (This is a retrospective comment.) --- 3493a5a8f6ce9a5e524107c870c171f4073af987 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