jtm@spock.UUCP (Jan Miksovsky '86 off) (11/16/85)
Last year, I wrote an interpreter for the Karel language to help introduce students here taking structured programming courses. The language is very simple, but is more fun for new programmers to use than Pascal since it is more visually-oriented. I'm running this on a VAX 11/750 under 4.2 BSD, and I haven't found any problems (yet :-) . Mail comments, bug reports, suggestions, etc. to me, *please*. A demo program, maze.k, is enclosed. To run it, type "karel maze.k" and press the return key after the maze is loaded in. Enjoy. Jan Miksovsky Choate Rosemary Hall P.O. Box 788 Wallingford, CT 06492 ...{decvax}!yale!spock!jtm # -----------------CUT HERE--------------------- #!/bin/sh # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # wrapped by spock!jtm on Wed Nov 6 12:03:55 EST 1985 echo x - README sed 's/^X//' > README << !SHAR!EOF! XThis is version 1.0 of an interpreter for Karel's programming language as Xdescribed in "Karel the Robot" by Richard E. Pattis (John Wiley & Sons, 1981). XThe language is described as a "gentle introduction to Pascal"; while very Xsimple, it teaches the basics of structured programming. XN.B. This is *not* the interpreter that Pattis wrote (the one described in the Xbook), which is copyright. This version was written independently; approval Xwas obtained from him for this posting. Right is hereby given for unlimited Xnon-profit use of this particular package. X XThe structure of this interpreter is based heavily on that of hoc(1), described Xin "The Unix Programming Environment" by Brian Kernighan and Rob Pike; anyone Xinterested in the "guts" of this program should be familiar with that program. X X****************************************************************************** X XFile Contents X Xcode.c program executor and run-time routines Xdoc short BNF-like language description Xhelp.h help-screen information Xkarel.1 manual page Xkarel.h global declarations Xkarel.y syntactical language definition Xklex.c lexical analyzer Xmain.c startup routines, error handlers, etc. Xscr.c screen routines Xsymbol.c symbol-table manipulator Xwords.h Karel keywords and built-in procedures X X****************************************************************************** X X X Jan Miksovsky X Choate Rosemary Hall X P.O. Box 788 X Wallingford, CT 06492 X X ...{decvax}!yale!spock!jtm X !SHAR!EOF! echo x - makefile sed 's/^X//' > makefile << !SHAR!EOF! X# makefile for karel 1.0 11/6/85 X XOBJS = main.o klex.o symbol.o code.o y.tab.o scr.o X Xkarel: $(OBJS) X cc $(CFLAGS) $(OBJS) -o karel -lcurses -ltermcap X X$(OBJS): karel.h X Xklex.o: words.h X Xwords.h: y.tab.h X Xy.tab.c y.tab.h: karel.y X yacc -d karel.y X Xscr.o: help.h !SHAR!EOF! echo x - code.c sed 's/^X//' > code.c << !SHAR!EOF! X#include <stdio.h> X#include "karel.h" X#include "y.tab.h" X X/* NPROG is size of program; no one would ever write a program this large */ X#define NPROG 500 X X#define advpc ++pc X#define nextinst prog[advpc] X XInst prog[NPROG]; /* the machine */ Xint progp; /* next spot for code */ Xint pc; /* program counter */ Xint startaddr; /* start of main block */ Xint flag; /* flag for logic tests */ Xint dest; /* branch destination */ X Xinitcode() /* set up counters, etc., for execution */ X{ X progp = 0; X flag = 0; X pc = 0; X} X Xsetcode(addr, n) /* install one program instruction */ Xint addr; XInst n; X{ X prog[addr] = n; X} X Xsetcodeint(addr, n) /* install one int */ Xint addr, n; X{ X prog[addr] = (Inst) n; X} X Xcode(n) /* install next instruction */ XInst n; X{ X if (progp >= NPROG) X severe("program too big", (char *) 0); X setcode(progp++, n); X} X Xcodeint(n) /* install a int as next instruction */ Xint n; X{ X code((Inst) n); X} X Xexecute(n) /* execute machine */ Xint n; X{ X int temp; X X temp = pc; X for (pc = n; (prog[pc] != RETURN) && state; pc++) { X (*(prog[pc]))(); X update(); X } X pc = temp; X} X Xturnleft() /* turn karel 90 degrees left */ X{ X if (--dir < 0) X dir = 3; X placekarel(y, x); X} X Xbranch() /* jump to another instruction */ X{ X dest = (int) nextinst; X pc = dest - 1; X} X Xcondbranch() /* jump of last logic test was false */ X{ X if (!flag) X branch(); X else X advpc; X} X Xcall() /* call a user-defined instruction */ X{ X execute(nextinst); X} X Xloopexec() /* execute block of code a number of times */ X{ X int k, limit, loopbody; X X limit = (int) nextinst; X loopbody = pc + 2; X for (k = 0; k < limit; k++) X execute(loopbody); X branch(); X} X Xturnoff() /* end program execution */ X{ X state = OFF; X} X X/* code for built-in logical test */ X Xanybeepers() { flag = beepers; } Xfacingeast() { flag = (dir == 1); } Xfacingnorth() { flag = (dir == 0); } Xfacingsouth() { flag = (dir == 2); } Xfacingwest() { flag = (dir == 3); } Xfrontblocked() { flag = !sideclear(dir); } Xfrontclear() { flag = sideclear(dir); } Xleftblocked() { flag = !sideclear(dir-1); } Xleftclear() { flag = sideclear(dir-1); } Xnobeepers() { flag = !beepers; } Xnotfacingeast() { flag = (dir != 1); } Xnotfacingnorth() { flag = (dir != 0); } Xnotfacingsouth() { flag = (dir != 2); } Xnotfacingwest() { flag = (dir != 3); } Xrightblocked() { flag = !sideclear(dir+1); } Xrightclear() { flag = sideclear(dir+1); } X Xnexttobeeper() X{ X flag = (oldch == '*' || (oldch >= '0' && oldch <= '9')); X} X Xnotnexttobeeper() X{ X nexttobeeper(); X flag = !flag; X} !SHAR!EOF! echo x - doc sed 's/^X//' > doc << !SHAR!EOF! X X XThe following is a list of statements available in the Karel language... X X Xprimitive instructions: X move Karel moves one block forward X turnleft pivots 90 degrees left X pickbeeper puts beeper in beeper-bag X putbeeper puts beeper on corner X turnoff turns off X Xblock structure words: X BEGIN X <stmt>; X <stmt>; X <stmt>; X ... X END; X X IF <test> X THEN <stmt> X [ ELSE <stmt> ] X X ITERATE <positive-integer> TIMES X <stmt> X X WHILE <test> DO X <stmt> X X DEFINE-NEW-INSTRUCTION <name> AS X <stmt> X X BEGINNING-OF-PROGRAM X <definitions> X BEGINNING-OF-EXECUTION X <stmt>; X <stmt>; X <stmt>; X ... X END-OF-EXECUTION X END-OF-PROGRAM X Xtests: X front-is-clear, front-is-blocked, X left-is-clear, left-is-blocked, X right-is-clear, right-is-blocked, X next-to-a-beeper, not-next-to-a-beeper, X facing-north, not-facing-north, X facing-south, not-facing-south, X facing-east, not-facing-east, X facing-west, not-facing-west, X any-beepers-in-beeper-bag, X no-beepers-in-beeper-bag X Xcomments: X This version of karel uses Pascal-style "{" and "}" comment X delimiters; this is the only addition to the language described X in "Karel the Robot" !SHAR!EOF! echo x - help.h sed 's/^X//' > help.h << !SHAR!EOF! X/* messages for help screen */ X Xchar *helpmsg[] = { X "Key Commands:\n", X "i\tmove north", X "j\t west", X "k\t east", X "m\t south\n", X "I\tplace Karel facing north", X "J\t west", X "K\t east", X "M\t south\n", X "o\tplace wall section", X "q\tquit", X "s\tsave screen", X "S\tsave snapshot\n", X "*\tplace beeper", X "?\tprint this screen", X "return\trun program", X "space\terase an object\n", X 0 X}; !SHAR!EOF! echo x - karel.1 sed 's/^X//' > karel.1 << !SHAR!EOF! X.TH KAREL 1 "Choate Rosemary Hall" X.UC 4 X.SH NAME Xkarel \- interpreter for Karel's programming language X.SH SYNOPSIS X.br X.B karel X[ X.B \-n X] X[ X.B \-b Xbeepers X] Xfile.k X.br X.SH DESCRIPTION X.I Karel Xis an interpreter for Karel's programming language, an introductory language Xbased on the structure of Pascal, which incorporates some of the visual Xfeedback possible from languages like Logo. X.PP XAfter reading the specified program (with a name ending in ".k"), a Xscreen with a corresponding name (ending in ".scr") is read if it exists. XThe screen editor is then called; from there, you can place Karel, the Xrobot, on the screen, and then run the program by pressing the return key. X.PP XThe following flags are recognized by X.I karel: X.TP 8 X.B \-b XSets the number of beepers Karel will be holding at the start of program Xexecution (default is zero). X.br X.TP 8 X.B \-n XSuppress loading of screen file X.br X.TP 0 X.nf XThe following is a list of the screen editor commands: X Xi move cursor north (up) Xj west (left) Xk east (right) Xm south (down) X XI place Karel facing north XJ west XK east XM south X Xo place wall section Xq quit; end execution of karel Xs save screen as file with suffix '.scr' XS save picture of screen with borders, saved as 'snapshot' X X* place beeper X? print help screen Xreturn run program Xspace erase an object X.fi X.PP XNote: after running program, press ESC to leave screen setup as it is, or press Xany other key to reset screen to setup before program execution. X.br X.SH FILES Xfile.k source program X.br Xfile.scr picture of screen, without borders X.br Xsnapshot picture of screen, with borders X.br X.SH SEE\ ALSO XPattis, Richard E.; X.ul 1 XKarel the Robot X.br X.SH AUTHOR XJan Miksovsky X.SH BUGS XSome of the "features" of this interpreter are superfluous: e.g., the Xdashed lines which serve no purpose other than to make the screen look Xlike the pictures in the book. !SHAR!EOF! echo x - karel.h sed 's/^X//' > karel.h << !SHAR!EOF! X/* file name suffixes */ X#define PROGSUFFIX "k" X#define SCRSUFFIX "scr" X X/* interpreter states */ X#define OFF 0 X#define COMPILE 1 X#define EDIT 2 X#define RUN 3 X Xtypedef int (*Inst)(); /* pseudo-compiled instruction */ X X/* marks end of procedure or main block */ X#define RETURN (Inst) 0 X Xtypedef struct Bltintype { /* built-in procedure entry */ X char *name; X Inst func; X int type; X} Bltintype; X Xtypedef struct Symbol { /* symbol table entry */ X char *name; X int addr; X struct Symbol *next; X} Symbol; X X/* in main.c */ Xextern nflg, state; Xextern severe(), err(), interupt(), screrror(); Xextern char *progname, basename[]; X X/* in words.h */ Xextern Bltintype bltins[]; X X/* in symbol.c */ Xextern Symbol *symtab, *lookup(); Xextern install(); X X/* in klex.c */ Xextern nkeys, gotsemcolon, gotturnoff, linecount, tokenid, yyval; Xextern yylex(), printdump(); Xextern char yytext[]; X X/* in code.c */ Xextern progp, startaddr, initcode(), setcode(), code(), execute(); Xextern anybeepers(), facingeast(), facingnorth(), facingsouth(); Xextern facingwest(), frontblocked(), frontclear(), leftblocked(); Xextern leftclear(), nexttobeeper(), nobeepers(); Xextern notfacingeast(), notfacingnorth(), notfacingsouth(), notfacingwest(); Xextern notnexttobeeper(), pickbeeper(), putbeeper(), rightblocked(); Xextern rightclear(), turnleft(), turnoff(); X X/* in scr.c */ Xextern beepers, dir, x, y, editscr(), reset(), placekarel(), putbeeper(); Xextern pickbeeper(), movekarel(), sideclear(), update(), finish(); Xextern readscrn(), initialize(), shutoff(); Xextern char oldch; !SHAR!EOF! echo x - karel.y sed 's/^X//' > karel.y << !SHAR!EOF! X%{ X X#include <stdio.h> X#include "karel.h" X Xextern int condbranch(), branch(), call(), bltin(), loopexec(); Xchar instname[BUFSIZ]; XSymbol *sp; X X%} X X%start prog X /* Karel keywords */ X X%token AS BEGEXEC BEGIN BEGPROG DEFINST DO ELSE END ENDEXEC X%token ENDPROG IF ITERATE THEN TIMES WHILE X X /* interpreter types */ X X%token KEY BLTIN TEST NUMBER NAME X X X%% /* beginning of rules */ X X Xprog : BEGPROG deflist begexec stmtlist ENDEXEC ENDPROG { X startaddr = \$3; X code(RETURN); X } X | prog error X { yyerrok; } X ; X Xbegexec : BEGEXEC { X strcpy(instname, ""); X fprintf(stderr, "main block:\n"); X \$\$ = progp; X } X ; X Xdeflist : def X | deflist ';' def X ; X Xdef : /* nothing */ X | definst AS stmt X { code(RETURN); } X ; X Xdefinst : DEFINST NAME { X strcpy(instname, yytext); X fprintf(stderr, "%s:\n", instname); X install(instname); X } X | DEFINST BLTIN X { err("tried to redefine primitive instruction:", X yytext); } X | DEFINST TEST X { err("tried to redefine logical test:", yytext); } X ; X Xstmtlist : stmt X | stmtlist ';' stmt X ; X Xstmt : BEGIN stmtlist END X | IF logictest THEN stmt { X setcode(\$2 + 1, condbranch); X setcodeint(\$2 + 2, progp); X } X | IF logictest THEN stmt else stmt { X setcode(\$2 + 1, condbranch); X setcodeint(\$2 + 2, \$5 + 1); X setcodeint(\$5, progp); X } X | iterate TIMES stmt { X code(RETURN); X setcodeint(\$1, progp); X } X | WHILE logictest DO stmt { X setcode(\$2 + 1, condbranch); X setcodeint(\$2 + 2, progp + 2); X code(branch); X codeint(\$2); X } X | NAME { X if ((sp = lookup(yytext)) == (Symbol *) 0) X err(yytext, "undefined"); X else { X if (strcmp(yytext, instname) == 0) X err("recursive procedure call:", X yytext); X else { X code(call); X codeint(sp->addr); X } X } X } X | BLTIN { X if (strcmp(yytext, "turnoff") == 0) X gotturnoff = 1; X code(bltins[tokenid].func); X } X | error X ; X Xlogictest : TEST { X \$\$ = progp; X code(bltins[tokenid].func); X codeint(0); /* leave room for branch */ X codeint(0); /* instruction and address */ X } X | NAME X { err("invalid logical test:", yytext); } X | BLTIN X { err("invalid logical test:", yytext); } X ; X Xelse : ELSE { X code(branch); X \$\$ = progp; X codeint(0); X }; X Xiterate : ITERATE NUMBER { X code(loopexec); X codeint(atoi(yytext)); X \$\$ = progp; X codeint(0); X } X ; !SHAR!EOF! echo x - klex.c sed 's/^X//' > klex.c << !SHAR!EOF! X#include <stdio.h> X#include <ctype.h> X#include "karel.h" X#include "words.h" X Xint nkeys, nbltins; /* number of keywords and bltins */ Xint gotsemcolon; Xint gotturnoff; Xint tokenid; /* token number */ Xint linecount; /* no. of lines in input */ Xint yyval; Xchar yytext[BUFSIZ]; Xstatic char c; /* character being handled */ Xextern FILE *fp; /* source file; found in main.c */ X Xinitlex() /* prepare the lexical analyzer */ X{ X /* count the number of keywords */ X for (nkeys = 0; keywords[nkeys].name; nkeys++) X ; X for (nbltins = 0; bltins[nbltins].name; nbltins++) X ; X c = ' '; X} X Xchar egetc(fp) /* get a character, checking for EOF */ XFILE *fp; X{ X char c; X X if ((c = getc(fp)) == EOF) X severe("unexpected end of program"); X else X return(c); X} X Xyylex() /* lexical analyzer */ X{ X int len; /* length of word */ X int n; /* temporary */ X X skipwhite(); X while (c == '{') { /* skip over comment */ X while (c != '}') X c = egetc(fp); X c = getc(fp); X skipwhite(); X } X len = 0; X while (isalnum(c) || c == '-') { /* read one word */ X yytext[len++] = c; X c = getc(fp); X } X yytext[len] = '\0'; /* mark end of word */ X if (len > 0 && c != EOF) { X tokenid = getkeyid(yytext); X if (tokenid >= 0) X yyval = keywords[tokenid].keyid; X else { X tokenid = getbltinid(yytext); X if (tokenid >= 0) X yyval = bltins[tokenid].type; X else X yyval = sscanf(yytext, "%d", &n) ? NUMBER:NAME; X } X } X else { X yyval = c; X c = getc(fp); X } X return(yyval); X} X Xgetkeyid(s) /* find s in keyword array; return -1 if not found */ Xchar *s; X{ X int cmp, lower, upper, guess, found; X X /* use a binary search */ X found = lower = 0; X upper = nkeys; X while (lower <= upper && !found) { X if (!(cmp = strcmp(s, keywords[guess=(lower+upper)/2].name))) X found = 1; X else X if (cmp > 0) X lower = guess + 1; X else X upper = guess - 1; X } X return(found ? guess : -1); X} X X X/* this is sort of redundant, but I didn't want to pass two kinds of */ X/* arrays to the same search routine */ X X Xgetbltinid(s) /* find s in built-in array; return -1 if not found */ Xchar *s; X{ X int cmp, lower, upper, guess, found; X X /* use a binary search */ X found = lower = 0; X upper = nbltins; X while (lower <= upper && !found) { X if (!(cmp = strcmp(s, bltins[guess=(lower+upper)/2].name))) X found = 1; X else X if (cmp > 0) X lower = guess + 1; X else X upper = guess - 1; X } X return(found ? guess : -1); X} X Xskipwhite() /* skip over white space (tabs, etc.) */ X{ X while (isspace(c)) { X if (c == '\n') X linecount++; X c = getc(fp); X } X} !SHAR!EOF! echo x - main.c sed 's/^X//' > main.c << !SHAR!EOF! X X/* X * karel: interpreter for Karel's programming language X * X * version 1.0, completed November 6th, 1985 X * by J.T.Miksovsky @ Choate Rosemary Hall X * X * N.B. This is *not* the interpreter that Richard E. Pattis wrote (the one X * described in his book, "Karel the Robot"). Approval from the Pattis was X * obtained for this distribution. X * X */ X X#include <stdio.h> X#include <signal.h> X#include "karel.h" X Xint nflg; /* -n (no screen load) option flag */ Xint errfound; /* 1 if a syntax or lexical error found */ Xint state; /* tells if compiling, running, etc. */ Xchar *progname; /* name of this program */ Xchar filename[BUFSIZ]; /* name of the source file */ Xchar basename[BUFSIZ]; /* name of source without suffix */ Xchar *usage = "usage: karel [-n] [-b beepers] file.k\n"; XFILE *fp; /* source file */ X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X progname = *argv; X state = COMPILE; X beepers = 0; X /* process arguments */ X nflg = 0; X argc--; X argv++; X while (argc > 0 && **argv == '-') { X (*argv)++; X while (**argv) X switch (*(*argv)++) { X case 'b': X if (**argv == '\0') { X argc--; X argv++; X } X if (sscanf(*argv, "%d",&beepers) != 1) X syserr("bad beeper number",0); X if (beepers < 0) X syserr("bad beeper number",0); X **argv = '\0'; X break; X case 'n': X nflg = 1; X break; X default: X fprintf(stderr, usage); X exit(1); X break; X } X argc--; X argv++; X } X if (argc != 1) { X fprintf(stderr, usage); X exit(1); X } X dofiles(*argv); X /* set up for parsing */ X errfound = 0; X gotsemcolon = 0; X gotturnoff = 0; X linecount = 1; X initlex(); X initcode(); X yyparse(); X fclose(fp); X if (!gotturnoff && !errfound) X severe("no turnoff instruction found", NULL); X if (!errfound) { X fprintf(stderr, "\tno lexical or syntatic errors\n"); X /* prepare to call screen editor */ X state = EDIT; X initialize(); X if (!nflg) X readscrn(); X editscr(); X } X else X fprintf(stderr, X "Execution suppressed due to compilation errors\n"); X} X X/* there are many error handlers to handle many type of errors */ X Xsyserr(s, t) /* system error: print error message and die */ Xchar *s, *t; X{ X if (state != COMPILE) X finish(); X fprintf(stderr, "%s: %s", progname, s); X if (t) X fprintf(stderr, "%s", t); X fputc('\n', stderr); X exit(1); X} X Xsevere(s, t) /* print error message and die */ Xchar *s, *t; X{ X fprintf(stderr, "%s", s); X if (t) X fprintf(stderr, " %s", t); X fprintf(stderr, "\ncannot recover from previous errors -- QUIT\n"); X exit(1); X} X Xerr(s, t) /* print error message */ Xchar *s, *t; X{ X errfound = 1; X fprintf(stderr, "\t%s", s); X if (t) X fprintf(stderr, " %s", t); X fprintf(stderr, ", line %d\n", linecount); X} X Xyyerror(s) /* handle parser error */ Xchar *s; X{ X errfound = 1; X fprintf(stderr, "\t%s, line %d", s, linecount); X if (yytext[0]) X fprintf(stderr, " near %s", yytext); X fputc('\n', stderr); X} X Xinterupt() /* handle interupts, die */ X{ X signal(SIGINT, SIG_IGN); X finish(); X fprintf(stderr, "interupt\n"); X exit(0); X} X Xscrerror(s) /* reset terminal modes, die */ Xchar *s; X{ X reset(); X fprintf(stderr, "\nscreen error: %s\n", s); X exit(1); X} X Xdofiles(s) /* get filename, open files, etc. */ Xchar *s; X{ X int suffstart; X char suffix[10]; X X /* check suffix */ X strcpy(suffix, "."); X strcat(suffix, PROGSUFFIX); X suffstart = strlen(s) - strlen(suffix); X if (strcmp(s + suffstart, suffix) != 0) X syserr("bad filename: ", s); X s[suffstart] = '\0'; /* delete suffix */ X strcpy(basename, s); X sprintf(filename, "%s.%s", basename, PROGSUFFIX); X if ((fp = fopen(filename, "r")) == NULL) X syserr("can't open file: ", s); X} !SHAR!EOF! echo x - scr.c sed 's/^X//' > scr.c << !SHAR!EOF! X#include <stdio.h> X#include <signal.h> X#include <curses.h> X#include "karel.h" X#include "help.h" X X#define scrnchr(t, s) (stdscr->_y[t][s] & 0177) X Xint cx, cy; /* cursor coordinates */ Xint x, y; /* Karel's x, y coord's */ Xint dir; /* Karel's heading */ X /* 0 = north */ X /* 1 = east */ X /* 2 = south */ X /* 3 = west */ Xint beepers; /* no. of beepers held */ Xint placed; /* 1 if Karel is placed */ Xint clearln; /* 1 if bottom line */ X /* should be cleared */ Xchar oldch; /* Karel is on top of */ Xchar scrnname[80]; /* name of screen */ XWINDOW *helpscrn; /* editor help screen */ XWINDOW *begscrn; /* screen at start */ X Xeditscr() /* main interactive loop */ X{ X int c; /* keyboard input char */ X int newx, newy; /* coord's to move to */ X int done; /* signal to quit */ X X done = 0; X clearln = 0; X c = getch(); X while (c != EOF && c != '\004' && !done) { X if (c == ERR) X screrror("getting character"); X /* clear bottom line if necessary */ X if (clearln) X clearline(); X /* get current location */ X getyx(stdscr, cy, cx); X newx = cx; X newy = cy; X switch (c) { X case 'i': X newy--; X break; X case 'j': X newx--; X break; X case 'k': X newx++; X break; X case 'm': X newy++; X break; X case 'I': X dir = 0; X placekarel(cy, cx); X break; X case 'J': X dir = 3; X placekarel(cy, cx); X break; X case 'K': X dir = 1; X placekarel(cy, cx); X break; X case 'M': X dir = 2; X placekarel(cy, cx); X break; X case 'O': X case 'o': X placeobj('O'); X break; X case 'Q': X case 'q': X done = 1; X break; X case '\r': X case '\n': X startprog(); X break; X case 'S': X snapshot(); X break; X case 's': X savescrn(); X break; X case '8': X case ':': X case '*': X putbeeper(); X break; X case ' ': X placeobj(' '); X break; X case '\014': /* control-L */ X redraw(); X break; X case '?': X case '/': X help(); X break; X } X /* check to see if cursor moved off screen */ X if (newx>=1 && newx<COLS && newy>=0 && newy < LINES-2) { X cy = newy; X cx = newx; X move(cy, cx); X } X update(); X if (!done) X c = getch(); X } X finish(); X} X Xinitialize() /* prepare for screen editing */ X{ X int k; X X if (initscr() == ERR || strcmp(ttytype, "unknown") == 0) { X fprintf(stderr, "TERM variable not set\n"); X exit(0); X } X sprintf(scrnname, "%s.%s", basename, SCRSUFFIX); X signal(SIGINT, interupt); X scrollok(stdscr, FALSE); X crmode(); X noecho(); X clear(); X /* set up help screen */ X helpscrn = newwin(0, 0, 0, 0); X wclear(helpscrn); X wmove(helpscrn, 0, 0); X for (k = 0; helpmsg[k]; k++) { X wprintw(helpscrn, helpmsg[k]); X waddch(helpscrn, '\n'); X } X wprintw(helpscrn, "\npress any key to continue: "); X /* make a second screen to save beginning screen for execution */ X begscrn = newwin(0, 0, 0, 0); X /* draw the boundaries */ X for (k = 0; k < LINES - 2; k++) X mvaddch(k, 0, '|'); X mvaddch(LINES - 2, 0, '+'); X for (k = 1; k < COLS; k++) X mvaddch(LINES - 2, k, '-'); X /* place the cursor in the center of the screen */ X cx = COLS / 2; X cy = LINES / 2; X placed = 0; X oldch = ' '; X move(cy, cx); X redraw(); X} X Xreset() /* reset the screen to normal mode */ X{ X echo(); X nocrmode(); X endwin(); X} X Xredraw() /* redraw the screen */ X{ X /* force curses to redraw screen by copying screen, then copying back*/ X cpscrn(stdscr, begscrn); X clear(); X cpscrn(begscrn, stdscr); X touchwin(stdscr); /* make sure everything is drawn */ X move(cy, cx); X update(); X} X Xupdate() /* refresh the screen */ X{ X if (refresh() == ERR) X screrror("refreshing screen"); X} X Xplaceobj(c) /* place a wall, beeper, or space */ Xchar c; X{ X if (cy == y && cx == x) X placed = 0; X addch(c); X} X Xplacekarel(newy, newx) /* put karel on screen at new location */ Xint newy, newx; X{ X char c; /* symbol karel will be shown as */ X X if (placed) X mvaddch(y, x, oldch); X else X placed = 1; X move(newy, newx); X oldch = inch(); X switch (dir) { X case 0: X c = '^'; X break; X case 1: X c = '>'; X break; X case 2: X c = 'v'; X break; X case 3: X c = '<'; X break; X default: X screrror("weird direction"); X } X addch(c); X y = newy; X x = newx; X} X Xputbeeper() /* put down one beeper */ X{ X char c; /* char that cursor is on top of */ X X if (state == RUN) X c = oldch; X else X c = inch(); X if (c == '*') X c = '2'; X else { X if (c >= '0' && c <= '8') X c++; X else { X if (c != '9') X c = '*'; X else { X shutoff("stacked beepers too high"); X return; X } X } X } X if (state == RUN) { X if (--beepers < 0) X shutoff("don't have enough beepers"); X else X oldch = c; X } X else X placeobj(c); X} X Xhelp() /* print editor help screen */ X{ X touchwin(helpscrn); X if (wrefresh(helpscrn) == ERR) X screrror("refreshing help screen"); X if (wgetch(helpscrn) == ERR) X screrror("getting character"); X redraw(); X} X Xsavescrn() /* save the screen to a file */ X{ X int k, j; /* loop indexes */ X FILE *fp; /* output file */ X FILE *fopen(); X X if ((fp = fopen(scrnname, "w")) == NULL) X syserr("can't open file: ", scrnname); X for (k = 0; k < LINES - 2; k++) { X for (j = 1; j < COLS; j++) X fputc(scrnchr(k, j), fp); X fputc('\n', fp); X } X fclose(fp); X prbotln("screen saved"); X clearln = 1; X move(cy, cx); X} X Xsnapshot() /* save screen with the borders */ X{ X int k, j; /* loop indexes */ X FILE *fp; /* output file */ X FILE *fopen(); X X if ((fp = fopen("snapshot", "w")) == NULL) X syserr("can't open file: snapshot"); X for (k = 0; k < LINES - 2; k++) { X fputc('|', fp); X for (j = 1; j < COLS; j++) X fputc(scrnchr(k, j), fp); X fputc('\n', fp); X } X fputc('+', fp); X for (k = 1; k < COLS; k++) X fputc('-', fp); X fputc('\n', fp); X fclose(fp); X prbotln("snapshot saved"); X clearln = 1; X move(cy, cx); X} X Xsideclear(n) /* return 1 if side n is clear, 0 otherwise */ Xint n; X{ X int retval; /* value to be returned */ X X retval = 1; X /* make sure side-number is between 0 and 3 */ X while (n < 0) X n += 4; X n = n % 4; X switch (n) { X case 0: X if (y == 0) X retval = 0; X else X if (scrnchr(y - 1, x) == 'O') X retval = 0; X break; X case 1: X if (x == COLS - 1) X retval = 0; X else X if (scrnchr(y, x + 1) == 'O') X retval = 0; X break; X case 2: X if (y == LINES - 3) X retval = 0; X else X if (scrnchr(y + 1, x) == 'O') X retval = 0; X break; X case 3: X if (x == 1) X retval = 0; X else X if (scrnchr(y, x - 1) == 'O') X retval = 0; X break; X } X return(retval); X} X Xmovekarel() /* move karel one character forward */ X{ X int newx, newy; X X newx = x; X newy = y; X if (sideclear(dir)) { X switch (dir) { X case 0: X newy--; X break; X case 1: X newx++; X break; X case 2: X newy++; X break; X case 3: X newx--; X break; X } X placekarel(newy, newx); X cy = newy; X cx = newx; X } X else X shutoff("hit a wall"); X} X Xfinish() /* clean up; reset terminal modes, etc. */ X{ X scrollok(stdscr, TRUE); X if (move(LINES - 1, 0) == ERR) X screrror("moving to bottom of screen"); X if (clrtoeol() == ERR) X screrror("clearing bottom line"); X if (refresh() == ERR) X screrror("refreshing screen"); X reset(); X} X Xpickbeeper() /* pick up one beeper underneath karel */ X{ X if (oldch == ' ') X shutoff("tried to pick non-existent beeper"); X else { X if (oldch == '*') X oldch = ' '; X else X if (oldch == '0') X oldch = '*'; X else X oldch--; X beepers++; X } X} X Xreadscrn() /* read screen in from a file */ X{ X int k, j; /* loop indexes */ X FILE *fp; /* screen file */ X FILE *fopen(); X char c; /* char being read */ X char *kar; /* these two used to */ X char *kars = "^>v<"; /* place karel */ X char *index(); X X if ((fp = fopen(scrnname, "r")) != NULL) { X for (k = 0; k < LINES - 2; k++) { X for (j = 1; j < COLS; j++) { X if ((c = getc(fp)) == EOF) X break; X if (c != ' ') { X if (kar = index(kars, c)) { X dir = 4 - strlen(kar); X placekarel(k, j); X } X else X mvaddch(k, j, c); X } X } X getc(fp); /* ignore newline */ X } X fclose(fp); X } X move(cy, cx); X update(); X} X Xshutoff(s) /* print s on bottom of screen */ Xchar *s; X{ X char mesg[BUFSIZ]; X X state = OFF; X sprintf(mesg, "\007error shutoff: %.45s. press any key: ", s); X prbotln(mesg); X update(); X if (getch() == ERR) X screrror("getting character"); X if (move(cy, cx) == ERR) X screrror("moving cursor to karel"); X clearln = 1; X} X Xclearline() /* clear bottom line of screen */ X{ X mvbot(); X if (clrtoeol() == ERR) X screrror("clearing bottom line"); X if (move(cy, cx) == ERR) X screrror("moving cursor to karel"); X clearln = 0; X} X Xstartprog() /* prepare for execution, then call program */ X{ X int begx, begy; /* these are all temporary */ X int begdir; /* variables to save conditions */ X int begoldch; /* at start of execution */ X int begbeepers; X int c; /* key user presses */ X X /* save current status */ X begx = x; X begy = y; X begdir = dir; X begoldch = oldch; X begbeepers = beepers; X cpscrn(stdscr, begscrn); X if (!placed) X shutoff("karel has not been placed"); X else { X state = RUN; X execute(startaddr); X if (state != OFF) X shutoff("turnoff instruction never reached"); X if (clearln == 0) { X mvbot(); X printw("press any key: "); X update(); X if ((c = getch()) == ERR) X screrror("getting character"); X } X else X clearln = 0; X clearline(); X /* restore previous status if ESC was not pressed */ X if (c != '\033') { X x = begx; X y = begy; X dir = begdir; X oldch = begoldch; X beepers = begbeepers; X cpscrn(begscrn, stdscr); X } X update(); X } X state = EDIT; X} X Xcpscrn(a, b) /* copy screen a to screen b */ XWINDOW *a, *b; X{ X int k, j; /* loop indexes */ X X for (k = 0; k < LINES - 1; k++) X for (j = 0; j < COLS; j++) X b->_y[k][j] = a->_y[k][j]; X touchwin(b); X} X Xmvbot() /* move to bottom of screen */ X{ X if (move(LINES - 1, 0) == ERR) X screrror("moving cursor to bottom of screen"); X} X Xprbotln(s) /* print a line on the bottom of screen */ Xchar *s; X{ X mvbot(); X if (clrtoeol() == ERR) X screrror("clearing bottom line"); X printw("%s", s); X update(); X} !SHAR!EOF! echo x - symbol.c sed 's/^X//' > symbol.c << !SHAR!EOF! X#include "karel.h" X Xstatic Symbol *symtab = 0; /* the symbol table */ X XSymbol *lookup(s) /* find s in symbol table */ Xchar *s; X{ X Symbol *sp; /* loop index */ X X for (sp = symtab; sp != (Symbol *) 0; sp = sp->next) X if (strcmp(s, sp->name) == 0) X return(sp); X return(0); /* not found */ X} X Xinstall(s) /* install s in symbol table */ Xchar *s; X{ X char *emalloc(); X Symbol *sp; /* new symbol table entry */ X X sp = (Symbol *) emalloc(sizeof(Symbol)); X sp->name = emalloc(strlen(s) + 1); X strcpy(sp->name, s); X sp->addr = progp; X sp->next = symtab; X symtab = sp; X} X Xchar *emalloc(n) /* do malloc with error checking */ Xint n; X{ X char *p; /* pointer to free memory */ X char *malloc(); X X if ((p = malloc(n)) == (char *) 0) X syserr("out of memory", (char *) 0); X return(p); X} !SHAR!EOF! echo x - words.h sed 's/^X//' > words.h << !SHAR!EOF! X#include "y.tab.h" X X/* these lists must be sorted alphabetically in order for the */ X/* binary search routine to work */ X X X/* keywords */ X Xstruct { X char *name; X int keyid; X} keywords[] = { X "AS", AS, X "BEGIN", BEGIN, X "BEGINNING-OF-EXECUTION", BEGEXEC, X "BEGINNING-OF-PROGRAM", BEGPROG, X "DEFINE-NEW-INSTRUCTION", DEFINST, X "DO", DO, X "ELSE", ELSE, X "END", END, X "END-OF-EXECUTION", ENDEXEC, X "END-OF-PROGRAM", ENDPROG, X "IF", IF, X "ITERATE", ITERATE, X "THEN", THEN, X "TIMES", TIMES, X "WHILE", WHILE, X 0, 0 X}; X X X/* built-in procedures and tests */ X XBltintype bltins[] = { X "any-beepers-in-beeper-bag", anybeepers, TEST, X "facing-east", facingeast, TEST, X "facing-north", facingnorth, TEST, X "facing-south", facingsouth, TEST, X "facing-west", facingwest, TEST, X "front-is-blocked", frontblocked, TEST, X "front-is-clear", frontclear, TEST, X "left-is-blocked", leftblocked, TEST, X "left-is-clear", leftclear, TEST, X "move", movekarel, BLTIN, X "next-to-a-beeper", nexttobeeper, TEST, X "no-beepers-in-beeper-bag", nobeepers, TEST, X "not-facing-east", notfacingeast, TEST, X "not-facing-north", notfacingnorth, TEST, X "not-facing-south", notfacingsouth, TEST, X "not-facing-west", notfacingwest, TEST, X "not-next-to-a-beeper", notnexttobeeper, TEST, X "pickbeeper", pickbeeper, BLTIN, X "putbeeper", putbeeper, BLTIN, X "right-is-blocked", rightblocked, TEST, X "right-is-clear", rightclear, TEST, X "turnleft", turnleft, BLTIN, X "turnoff", turnoff, BLTIN, X 0, 0, 0 X}; !SHAR!EOF! echo x - maze.k sed 's/^X//' > maze.k << !SHAR!EOF! X X{ karel maze demo: robot will follow the right wall until he finds a beeper } X XBEGINNING-OF-PROGRAM X DEFINE-NEW-INSTRUCTION turnright AS X ITERATE 3 TIMES X turnleft; X X BEGINNING-OF-EXECUTION X WHILE not-next-to-a-beeper DO X BEGIN X IF right-is-clear X THEN turnright X ELSE X WHILE front-is-blocked DO X turnleft; X move X END; X turnoff X END-OF-EXECUTION X XEND-OF-PROGRAM !SHAR!EOF! echo x - maze.scr sed 's/^X//' > maze.scr << !SHAR!EOF! X X X X X X X X X X XOOOOOOOOOOOOOOOOOOOOOOO X O O O O X OOOOOO O OOOOO OOO O O X O O O O O XOOO OOOOOOO O OOO O * X OOOOOOO O O O X OOOOOOOO O O O OOO X O O O OOO O O X OOO OOOOOOOO O O O O X O OOO O O O X O OOOOOO O OOO O OOO O X^O O O O !SHAR!EOF!