This C program interprets the Mouse language while dumping out the internal interpreter state after each line.
/*
MOUSE - A Language for Microcomputers (interpreter)
as described by Peter Grogono in July 1979 BYTE Magazine
*/
#include <stdio.h>
enum tagtype { MACRO, PARAM, LOOP };
struct frame {
enum tagtype tag;
int pos, off;
};
FILE *infile;
char prog[5000];
int definitions[26];
int calstack[256], data[256], cal, chpos, level, offset, parnum, parbal, temp;
struct frame stack[256];
char ch;
int end;
#define num(ch) (ch - 'A')
#define val(ch) (ch - '0')
#define nextchar() (ch = prog[chpos++])
void dumpline()
{
int p = chpos, l = 0, a = 0, i;
char buf[256];
while (p >= 0 && prog[p] != '\n' && prog[p] != '\r') p--;
p++;
printf("\n###data ");
for (i=0; i<13; i++) printf("%c=%d ",i+'A',data[i + offset]);
printf("\n ");
for (; i<26; i++) printf("%c=%d ",i+'A',data[i + offset]);
printf("\n###stack ");
if (cal > 0)
{
for (i=0; i<cal; i++) printf("%d ", calstack[i]);
}
else printf("<empty>");
printf("\n###");
do {
printf("%c", prog[p]);
if (p == chpos) a = l;
p++; l++;
} while (prog[p] != '\n' && prog[p] != '\r' && p < end);
printf("\n###");
for (i=0; i<l; i++) if (i == a) printf("^"); else printf(" ");
printf("?");
fgets(buf, 256, stdin);
}
void pushcal(int datum) {
calstack[cal++] = datum;
}
int popcal() {
return calstack[--cal];
}
void push(enum tagtype tagval) {
stack[level].tag = tagval;
stack[level].pos = chpos;
stack[level++].off = offset;
}
void pop() {
chpos = stack[--level].pos;
offset = stack[level].off;
}
void skip(char lch, char rch) {
int cnt = 1;
do {
nextchar();
if (ch == lch) cnt++;
else if (ch == rch) cnt--;
} while (cnt != 0);
}
void load() {
char this, last;
int charnum;
for (charnum = 0; charnum < 26; charnum++)
definitions[charnum] = 0;
charnum = 0;
this = ' ';
do {
last = this;
this = fgetc(infile);
if (this == '\'') {
do {
this = fgetc(infile);
} while (this != '\n');
prog[charnum++] = this;
}
else {
prog[charnum] = this;
if (this >= 'A' && this <= 'Z' && last == '$')
{
definitions[num(this)] = charnum + 1;
printf("defined $%c\n", this);
}
charnum++;
}
} while (this != '$' || last != '$');
end = charnum;
}
void main(int argc, char *argv[]) {
if (argc < 2) infile = stdin;
else {
infile = fopen(argv[1], "r");
if (infile == NULL) {
puts("Error: cannot load program file\n");
return;
}
}
load();
if (infile != stdin) fclose(infile);
chpos = level = offset = cal = 0;
do {
if (!strchr("\n\t\r ", ch)) dumpline();
nextchar();
switch (ch) {
case ' ': case ']': case '$': break;
case '0': case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
temp = 0;
while (ch >= '0' && ch <= '9') {
temp = 10 * temp + val(ch);
nextchar();
}
pushcal(temp);
chpos--;
break;
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F': case 'G': case 'H': case 'I': case 'J':
case 'L': case 'M': case 'N': case 'O': case 'P':
case 'Q': case 'R': case 'S': case 'T': case 'U':
case 'V': case 'W': case 'X': case 'Y': case 'Z':
pushcal(num(ch) + offset);
break;
case '?':
scanf("%d", &temp);
pushcal(temp);
break;
case '!': printf("%d", popcal()); fflush(stdout); break;
case '+': pushcal(popcal() + popcal()); break;
case '-': pushcal(popcal() - popcal()); break;
case '*': pushcal(popcal() * popcal()); break;
case '/': pushcal(popcal() / popcal()); break;
case '.':
pushcal(data[popcal()]);
break;
case '=':
temp = popcal();
data[popcal()] = temp;
break;
case '"':
do {
nextchar();
if (ch == '!') putchar('\n');
else if (ch != '"') putchar(ch);
} while (ch != '"');
break;
case '[':
if (popcal() <= 0) skip('[', ']');
break;
case '(': push(LOOP); break;
case '^':
if (popcal() <= 0) {
pop();
skip('(', ')');
}
break;
case ')': chpos = stack[level - 1].pos; break;
case '#':
nextchar();
if (definitions[num(ch)] > 0) {
push(MACRO);
chpos = definitions[num(ch)];
offset += 26;
}
else skip('#', ';');
break;
case '@': case '}': pop(); skip('#', ';'); break;
case '%':
nextchar();
parnum = num(ch);
push(PARAM);
parbal = 1;
temp = level - 1;
do {
temp--;
switch (stack[temp].tag) {
case MACRO: parbal--; break;
case PARAM: parbal--; break;
case LOOP: break;
}
} while (parbal != 0);
chpos = stack[temp].pos;
offset = stack[temp].off;
do {
if (!strchr("\n\t\r ", ch)) dumpline();
nextchar();
if (ch == '#') {
skip('#', ';');
if (!strchr("\n\t\r ", ch)) dumpline();
nextchar();
}
if (ch == ',') parnum--;
} while (parnum >= 0 && ch != ';');
if (ch == ';') pop();
break;
case ',': case ';': pop(); break;
}
} while (ch != '$');
}