/*
* rube.c v1.6, Oct 2014, Chris Pressey
* Interpreter/Debugger for the RUBE programming language
*
* (c)1997-2014 Chris Pressey, Cat's Eye Technologies. All rights reserved.
* Covered under a BSD-style license; see LICENSE for more information.
*
* Usage :
*
* rube [-d] [-q] [-i] [-y delay] [-f frame-skip] [-o offset] <rube-source>
*
* -d: disable debugging output
* -q: produce no output but program output
* -i: run interactively (single step with lines from stdin)
* -y: specify debugging delay in milliseconds (default 0)
* -f: specify debugging frame skip in frames (default 1)
*
* Compilation :
*
* MS-DOS: tested with Turbo C++ v3.0, Borland C++ v3.1, DGJPP gcc v4.7.3.
* Load ANSI.SYS or compatible ANSI driver before using.
* Windows: tested with GCC 4.8.3 under Cygwin.
* Linux: tested with GCC 4.8.2 under Ubuntu 14.04 LTS.
* NetBSD: tested with GCC 4.5.3 under NetBSD 6.1.4
* AmigaOS: tested with DICE C 3.16 under AmigaOS 1.3.
*
* History :
*
* v1.00: May/Jun 97 original, minimal implementation
* v1.01: Jun 97 added K gate, AV swinches, + packer, - unpacker
* v1.02: Jul 97 fixed bug in WM winches, - unpacker, added . and C
* doubled height of playfield and improved debugger.
* added Ben Olmstead's cursor-turner-offer-thingy.
* added -q option.
* v1.3: Feb 110 made compilable in POSIX and strict ANSI C89.
* screen is cleared before drawing initial playfield.
* v1.4: Apr 110 really made compilable in strict ANSI C89.
* made command-line options case-senstive.
* added GNU Makefile.
* v1.5: Jan 111 simplified and cleaned up code, allowing it to
* build on AmigaOS 1.3 using DICE C. Added -i option.
* v1.6: Oct 114 cleaned up functioning under DOS, Turbo C, DJGPP
* use kbhit() instead of (less reliable?) setcbrk()
* use _setcursortype() instead of inline assembly
* properly clear ANSI terminal screen (esp. Amiga console)
* reorganized build system and converted tabs to spaces
*/
/********************************************************* #INCLUDE'S */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#if defined(__TURBOC__) || defined(__BORLANDC__) || defined(__DJGPP__)
#define MSDOS 1
#endif
#ifdef MSDOS
#include <dos.h>
#include <conio.h> /* for _setcursortype() and kbhit() */
#endif
#ifdef __GNUC__
#include <unistd.h>
#endif
#ifdef _POSIX_C_SOURCE
#include <sys/time.h>
#endif
/********************************************************** #DEFINE'S */
#define LINEWIDTH 80
#define PAGEHEIGHT 50
#define SCREENWIDTH 79
#define SCREENHEIGHT 22
#define nex pg2[y * LINEWIDTH + x].c
#define nexd(dx,dy) pg2[(y+dy) * LINEWIDTH + (x+dx)].c
#define shrink(s) s[strlen(s)-1]=0
typedef struct cellstruct
{
char c;
} cell;
/*************************************************** GLOBAL VARIABLES */
static cell pg[LINEWIDTH * PAGEHEIGHT]; /* playfield */
static cell pg2[LINEWIDTH * PAGEHEIGHT];/* playfield' */
static int x = 0, y = 0; /* x and y looping */
static int debug = 1; /* flag: display ANSI debugging? */
static int interactive = 0; /* flag: ask for input each frame? */
static int deldur = 0; /* debugging delay in milliseconds */
static int debskip = 1; /* frame skip in debug view */
static int debopos = 1; /* output column in debugger */
static int frame = 1;
static int quiet = 0;
/********************************************************* PROTOTYPES */
int curd(int, int);
int isramp(char);
int isblock(char);
int issupport(char);
int iscrate(char);
int ctoh(char);
char htoc(int);
void rube_delay(int);
/******************************************************* MAIN PROGRAM */
int main (int argc, char **argv)
{
FILE *f;
int i;
int done=0;
int maxy=0; int maxx=0;
srand (time (0));
if (argc < 2)
{
printf ("USAGE: rube [-d] [-q] [-i] [-y delay] [-f skip] foo.rub\n");
exit (0);
}
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "-d")) { debug = 0; }
if (!strcmp(argv[i], "-q")) { quiet = 1; debug = 0; }
if (!strcmp(argv[i], "-i")) { interactive = 1; debug = 1; }
if (!strcmp(argv[i], "-y")) { deldur = atoi(argv[i + 1]); }
if (!strcmp(argv[i], "-f")) { debskip = atoi(argv[i + 1]); }
}
if (!quiet) printf ("Cat's Eye Technologies' RUBE Interpreter v1.5\n");
f = fopen (argv[argc - 1], "r");
if (f == NULL) {
printf ("Error : couldn't open '%s' for input.\n", argv[argc - 1]);
exit (0);
}
while (!feof (f))
{
int cur = fgetc(f);
pg[y * LINEWIDTH + x].c = cur;
if (cur == '\n')
{
pg[y * LINEWIDTH + x].c = ' ';
x = 0;
y++;
if (y >= PAGEHEIGHT) break;
} else
{
x++;
if (x > maxx) maxx = x;
if (x >= LINEWIDTH)
{
x = 0;
y++;
if (y >= PAGEHEIGHT) break;
}
}
}
fclose (f);
maxy = y;
#if MSDOS
_setcursortype(_NOCURSOR);
#endif
if (debug)
{
printf ("%c[1;1H%c[2J", 27, 27);
}
while (!done) /*** Intepreting Phase */
{
if ((debug) && (!(frame++ % debskip) || (!frame)))
{
printf ("%c[1;1H", 27);
for(y = 0; (y <= maxy) && (y <= SCREENHEIGHT); y++)
{
for(x = 0; (x <= maxx) && (x <= SCREENWIDTH); x++)
{
int cur = pg[y * LINEWIDTH + x].c;
putc(isprint(cur) ? cur : ' ', stdout);
}
printf("\n");
}
} else
{
/* putc('.', stdout); */
}
fflush (stdout);
fflush (stdin);
for (x=0; x<=(maxx); x++)
{
for (y=0; y<=(maxy); y++)
{
int cur = pg[y * LINEWIDTH + x].c;
if (cur <= 32) {
if (iscrate(curd(0,-1))) nex = curd(0,-1); /* falling in from above */
if (curd(0,-1) == '(') nex = '(';
if (curd(0,-1) == ')') nex = ')';
if (curd(1,1) == 'W') nex = curd(2,2);
if (curd(-1,1) == 'W') nex = curd(-2,2);
if ((curd(1,1) == 'V') && iscrate(curd(2,1))) nex = curd(2,1);
if ((curd(-1,1) == 'V') && iscrate(curd(-2,1))) nex = curd(-2,1);
if (curd(1,-1) == 'M') nex = curd(2,-2);
if (curd(-1,-1) == 'M') nex = curd(-2,-2);
if ((curd(1,-1) == 'A') && iscrate(curd(2,-1))) nex = curd(2,-1);
if ((curd(-1,-1) == 'A') && iscrate(curd(-2,-1))) nex = curd(-2,-1);
if (curd(0,-1) == '~') nex = '~';
if ((curd(-1,0) == '~') && (issupport(curd(-1,1)))) nex = '~';
if ((curd(1,0) == '~') && (issupport(curd(1,1)))) nex = '~';
if (curd(1,-1) == '+')
{
if (iscrate(curd(1,0)) && iscrate(curd(2,0)))
{
nex = htoc((ctoh(curd(1,0))+ctoh(curd(2,0))) % 16);
}
}
if (curd(-1,-1) == '+')
{
if (iscrate(curd(-1,0)) && iscrate(curd(-2,0)))
{
nex = htoc((ctoh(curd(-1,0))+ctoh(curd(-2,0))) % 16);
}
}
if (curd(1,-1) == '-')
{
if (iscrate(curd(1,0)) && iscrate(curd(2,0)))
{
int z;
z = ctoh(curd(2,0)) - ctoh(curd(1,0));
while (z < 0) z += 16;
nex = htoc(z);
}
}
if (curd(-1,-1) == '-')
{
if (iscrate(curd(-1,0)) && iscrate(curd(-2,0)))
{
int z;
z = ctoh(curd(-2,0)) - ctoh(curd(-1,0));
while (z < 0) z += 16;
nex = htoc(z);
}
}
if ((curd(1,-1) == 'K') && (iscrate(curd(1,-2))))
{
if(ctoh(curd(1,-2)) < ctoh(curd(1,0))) nex = curd(1,-2);
}
if ((curd(-1,-1) == 'K') && (iscrate(curd(-1,-2))))
{
if(ctoh(curd(-1,-2)) >= ctoh(curd(-1,0))) nex = curd(-1,-2);
}
if ((iscrate(curd(-1,0))) && (curd(-1,1) == '>')) nex = curd(-1,0);
if ((iscrate(curd(1,0))) && (curd(1,1) == '<')) nex = curd(1,0);
if (curd(0,-1) == ':') nex = curd(0,-2);
if ((curd(0,-1) == ';') && (iscrate(curd(0,-2)))) nex = curd(0,-2);
if ((curd(0,1) == '.') && (iscrate(curd(0,2)))) nex = curd(0,2);
if ((curd(-1,0) == '(') && (curd(1,0) == ')')) /* collision */
{
nex = ' ';
} else
{
if ((curd(-1,0) == '(') && (issupport(curd(-1,1)))) nex = '(';
if ((curd(1,0) == ')') && (issupport(curd(1,1)))) nex = ')';
if ((curd(0,1) == '/') || (curd(0,1) == '\\'))
{
if ((curd(-1,1) == '(') && (issupport(curd(-1,2)))) nex = '(';
if ((curd(1,1) == ')') && (issupport(curd(1,2)))) nex = ')';
}
}
if (iscrate(curd(-1,0)))
{ /* shift crates */
int bx=-1;
while ((iscrate(curd(bx,0))) && (issupport(curd(bx,1))))
{
if (curd(bx-1,0) == '(')
{
nex = curd(-1,0);
}
bx--;
}
}
if (iscrate(curd(1,0)))
{
int bx=1;
while ((iscrate(curd(bx,0))) && (issupport(curd(bx,1))))
{
if (curd(bx+1,0) == ')')
{
nex = curd(1,0);
}
bx++;
}
}
} else switch (cur)
{
case '(':
if (((curd(1,0) == '(') ||
(curd(1,0) <= ' ') ||
(curd(0,1) <= ' ') ||
(curd(0,1) == '('))) nex = ' ';
if (isramp(curd(0,1))) nex = ' ';
if (isramp(curd(1,0))) nex = ' ';
if (isramp(curd(-1,0))) nex = ' ';
if ((isblock(curd(1,0))) ||
(curd(1,-1) == ',') ||
(curd(1,0) == '*')) nex = ')';
if (iscrate(curd(1,0)))
{
int bx=1;
while ((iscrate(curd(bx,0))) && (issupport(curd(bx,1))))
{
if (isblock(curd(bx+1,0)))
{
nex = ')';
}
bx++;
}
}
break;
case ')':
if (((curd(-1,0) == ')') ||
(curd(-1,0) <= ' ') ||
(curd(0,1) <= ' ') ||
(curd(0,1) == ')'))) nex = ' ';
if (isramp(curd(0,1))) nex = ' ';
if (isramp(curd(1,0))) nex = ' ';
if (isramp(curd(-1,0))) nex = ' ';
if ((isblock(curd(-1,0))) ||
(curd(-1,-1) == ',') ||
(curd(-1,0) == '*')) nex = '(';
if (iscrate(curd(-1,0)))
{
int bx=-1;
while ((iscrate(curd(bx,0))) && (issupport(curd(bx,1))))
{
if (isblock(curd(bx-1,0)))
{
nex = '(';
}
bx--;
}
}
break;
case 'O':
if ((iscrate(curd(0,-1))) && (iscrate(curd(0,-2))))
{
int d;
d = ctoh(curd(0,-1)) + ctoh(curd(0,-2)) * 16;
if (curd(0, 1) == 'b')
{
if (debug)
{
char s[80];
printf ("%c[%d;%dH", 27, 25, debopos);
sprintf(s, "%d ", (int)d);
debopos += strlen(s);
if (debopos > SCREENWIDTH)
{
debopos = 1;
printf ("%c[%d;%dH%c[K", 27, 25, 1, 27);
debopos += strlen(s);
}
printf("%s", s);
} else
{
printf("%d ", (int)d);
}
}
if (curd(0, 1) == 'c')
{
if (debug)
{
printf ("%c[%d;%dH", 27, 25, debopos++);
if (debopos > SCREENWIDTH)
{
debopos = 1;
printf ("%c[%d;%dH%c[K", 27, 25, 1, 27);
debopos++;
}
printf ("%c", (char)d);
} else
{
putc((char)d, stdout);
}
}
}
case 'A':
if (iscrate(curd(-1,0)) || iscrate(curd(1,0))) nex = 'V'; else nex = cur;
break;
case 'V':
if (iscrate(curd(-1,0)) || iscrate(curd(1,0))) nex = 'A'; else nex = cur;
break;
default: nex = cur;
}
if (iscrate(cur))
{
if (issupport(curd(0,1))) nex = cur; else nex = ' ';
if ((curd(1,0) <= ' ') && (curd(0,1) == '>')) nex = ' ';
if ((curd(-1,0) <= ' ') && (curd(0,1) == '<')) nex = ' ';
if ((curd(1,-1) == 'W') && (curd(2,-2) == cur)) nex = ' ';
if ((curd(-1,-1) == 'W') && (curd(2,-2) == cur)) nex = ' ';
if ((curd(1,1) == 'M') && (curd(2,2) == cur)) nex = ' ';
if ((curd(-1,1) == 'M') && (curd(-2,2) == cur)) nex = ' ';
if (curd(1,0) == 'V') nex = ' ';
if (curd(-1,0) == 'V') nex = ' ';
if (curd(1,0) == 'A') nex = ' ';
if (curd(-1,0) == 'A') nex = ' ';
if (iscrate(curd(-1,0)) && ((curd(-1,-1) == '+')
|| (curd(-1,-1) == '-'))) nex = ' ';
if (iscrate(curd(1,0)) && ((curd(1,-1) == '+')
|| (curd(1,-1) == '-'))) nex = ' ';
if ((iscrate(curd(-1,0)) || iscrate(curd(1,0))) && ((curd(0,-1) == '+')
|| (curd(0,-1) == '-'))) nex = ' ';
}
}
}
/* fix nex array */
for (x=0; x<=(maxx); x++)
{
for (y=0; y<=(maxy); y++)
{
int cur = pg[y * LINEWIDTH + x].c;
switch (cur)
{
case '*':
if (curd(-1,0) == ')') nex = ' ';
if (curd(1,0) == '(') nex = ' ';
break;
case 'O':
if ((iscrate(curd(0,-1))) && (iscrate(curd(0,-2))))
{
nexd(0,-1)=' ';
nexd(0,-2)=' ';
}
break;
}
if (iscrate(cur))
{
if (curd(1,0) == ')')
{
int bx=0; int flag=0;
while ((iscrate(curd(bx,0))) && (issupport(curd(bx,1))))
{
if (curd(bx-1,0) <= ' ')
{
flag = 1;
}
bx--;
}
if (flag)
{
bx=0;
while ((iscrate(curd(bx,0))) && (issupport(curd(bx,1))))
{
nexd(bx-1,0) = curd(bx,0);
bx--;
}
nex = ')'; nexd(1,0) = ' ';
}
}
if (curd(-1,0) == '(')
{
int bx=0; int flag=0;
while (iscrate(curd(bx,0)) && (issupport(curd(bx,1))))
{
if (curd(bx+1,0) <= ' ')
{
flag=1;
}
bx++;
}
if (flag)
{
bx=0;
while ((iscrate(curd(bx,0))) && (issupport(curd(bx,1))))
{
nexd(bx+1,0) = curd(bx,0);
bx++;
}
nex = '('; nexd(-1,0)= ' ';
}
}
if ((curd(-1,0)=='C') ||
(curd(1,0)=='C') ||
(curd(0,-1)=='C') ||
(curd(0,1)=='C')) nex = ' ';
}
if ((curd(-1,0)=='F') ||
(curd(1,0)=='F') ||
(curd(0,-1)=='F') ||
(curd(0,1)=='F')) nex = ' ';
}
}
if (interactive) {
char s[80];
fgets(s, 79, stdin);
if (s[0] == 'q') done = 1;
} else {
if (deldur > 0) {
rube_delay (deldur);
}
#ifdef MSDOS
if (kbhit()) {
char c;
c = getch();
if (c == 27) done = 1;
}
#endif
}
memcpy(pg, pg2, LINEWIDTH * PAGEHEIGHT * sizeof(cell));
}
if (debug) printf ("%c[22;1H", 27);
#if MSDOS
_setcursortype(_NORMALCURSOR);
#endif
exit (0);
}
int curd(int zdx, int zdy)
{
int r = (y+zdy) * LINEWIDTH + (x+zdx);
if (r < 0 || r >= LINEWIDTH * PAGEHEIGHT)
return 0;
return pg[r].c;
}
int isramp(char c)
{
return ((c=='/')||(c=='\\'));
}
int isblock(char c)
{
return ((c=='='));
}
int issupport(char c)
{
return ((c=='=')||iscrate(c)||(c=='(')||(c==')')||(c==';')||
(c=='/')||(c=='\\')||(c==':')||(c=='*')||(c==',')||
(c=='>')||(c=='<')||(c=='O')||(c=='W')||(c=='M')||
(c=='A')||(c=='V')||(c=='~')||(c=='.'));
}
int iscrate(char c)
{
return ((c=='0')||(c=='1')||(c=='2')||(c=='3')||
(c=='4')||(c=='5')||(c=='6')||(c=='7')||
(c=='8')||(c=='9')||(c=='a')||(c=='b')||
(c=='c')||(c=='d')||(c=='e')||(c=='f'));
}
int ctoh(char c)
{
if((c>='0') && (c<='9')) return (c-'0'); else return ((c-'a')+10);
}
char htoc(int i)
{
if((i>=0) && (i<=9)) return ((char)(i+'0')); else return ((char)(i+'a')-10);
}
void rube_delay(int msec)
{
#if MSDOS
delay (msec);
#elif _POSIX_C_SOURCE
struct timespec d;
d.tv_sec = msec / 1000;
msec %= 1000;
d.tv_nsec = msec * 1000000;
nanosleep(&d, NULL);
#else
sleep(msec / 1000);
#endif
}