这就是Basic解释器的主代码,其中用到上节讲的词法提取get_token()和代数式求值get_exp(int *result)函数.
这一节的代码更简单,就是随心所欲地将得到的token组装.
譬如在get_token后如果token装PRINT,你就调用一次get_token将下一个token答应出来就是了,很简单的,或许你自己也能搞定的.
在下一节你,我会给你完整的C++封装好了的源代码.
/* A tiny BASIC interpreter */
#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>
#define NUM_LAB 100
#define LAB_LEN 10
#define FOR_NEST 25
#define SUB_NEST 25
#define PROG_SIZE 10000
#define DELIMITER 1
#define VARIABLE 2
#define NUMBER 3
#define COMMAND 4
#define STRING 5
#define QUOTE 6
#define PRINT 1
#define INPUT 2
#define IF 3
#define THEN 4
#define FOR 5
#define NEXT 6
#define TO 7
#define GOTO 8
#define EOL 9
#define FINISHED 10
#define GOSUB 11
#define RETURN 12
#define END 13
char *prog; /* holds expression to be analyzed */
jmp_buf e_buf; /* hold environment for longjmp() */
int variables[26]= { /* 26 user variables,A-Z */
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0
};
struct commands { /* keyword lookup table */
char command[20];
char tok;
} table[] = { /* command must be entered lowercase */
"print",PRINT, /* in this table */
"input",INPUT,
"if",IF,
"then",THEN,
"goto",GOTO,
"for",FOR,
"next",NEXT,
"to",TO,
"gosub",GOSUB,
"return",RETURN,
"end",END,
NULL,END
};
char token[80];
char token_type,tok;
struct label {
char name [LAB_LEN];
char *p; /* point to place to go in source */
};
struct label label_table[NUM_LAB];
char *find_label(),*gpop();
struct for_stack {
int var; /* counter variable */
int target; /* target value */
char *loc;
} fstack[FOR_NEST]; /* stack for FOR/NEXT loop */
struct for_stack fpop();
char *gstack[SUB_NEST]; /* stack for gosub */
int ftos; /* index to top of FOR stack */
int gtos; /* index to top of GOSUB */
void print(),scan_labels(),find_eol(),exec_goto();
void gosub(),greturn(),gpush(),label_init(),fpush();
/* Load a program */
load_program (char *p,char *fname)
{
FILE *fp;
int i=0;
if (!(fp=fopen(fname,"rb"))) return 0;
i=0;
do {
*p = getc(fp);
p++;i++;
} while (!feof(fp)&&i<PROG_SIZE);
*(p-2) = '\0'; /* null terminate the program */
fclose (fp);
return 1;
}
/* assign a variable a value */
assignment()
{
int var,value;
/* getthe variable name */
get_token();
if (!isalpha(*token)) {
serror(4);
return;
}
var = toupper(*token)-'A';
/* get the equals sign */
get_token();
if (*token!='=') {
serror(3);
return;
}
/* get the value to assign to var */
get_exp(&value);
/* assign the value */
variables[var] = value;
}
/* execute a simple version of the BASIC PRINT statement */
void print()
{
int answer;
int len=0,spaces;
char last_delim;
do {
get_token(); /* get next list item */
if (tok==EOL||tok==FINISHED) break;
if (token_type==QUOTE) { /* is string */
printf ("%s",token);
len+=strlen(token);
get_token();
}
else { /* is expression */
putback();
get_exp(&answer);
get_token();
len += printf ("%d",answer);
}
last_delim = *token;
if (*token==',') {
/* compute number of move to next tab */
spaces = 8-(len%8);
len += spaces; /* add in the tabbing position */
while (spaces) {
printf (" ");
spaces--;
}
}
else if (*token==';') {
printf (" ");
}
else if (tok != EOL && tok != FINISHED) serror (0);
} while (*token==';'||*token==',');
if (tok==EOL||tok==FINISHED) {
if (last_delim != ';' && last_delim != ',') printf ("\n");
}
else serror(0); /* error is not, or ; */
}
/* find all labels */
void scan_labels()
{
int addr;
char *temp;
label_init(); /* zero all labels */
temp = prog; /* save poiter to top of program */
/* if the first token in the fike is a label */
get_token();
if (token_type==NUMBER) {
strcpy (label_table[0].name,token);
label_table[0].p=prog;
}
find_eol();
do {
get_token();
if (token_type==NUMBER) {
addr = get_next_label(token);
if (addr==-1||addr==-2) {
(addr==-1) ? serror(5):serror(6);
}
strcpy (label_table[addr].name,token);
label_table[addr].p = prog; /* current point in program */
}
/* if not on a blank line , find next line */
if (tok!=EOL) find_eol();
} while (tok!=FINISHED);
prog = temp; /* restore to original */
}
/* find the start of next line */
void find_eol()
{
while (*prog!='\n'&&*prog!='\0') ++prog;
if (*prog) prog++;
}
/* return index of next free posion in the label array
-1 is returned if the array is full.
-2 is returned when duplicate label is found.
*/
get_next_label(char *s)
{
register int t;
for (t=0;t<NUM_LAB;++t) {
if (label_table[t].name[0]==0) return t;
if (!strcmp(label_table[t].name,s)) return -2; /* dup */
}
return -1;
}
/* find location of given label. A null is returned if
label is not found; ohtherwise a pointer to the position
of the label is returned.
*/
char *find_label(char *s)
{
register int t;
for (t=0;t<NUM_LAB;++t)
if (!strcmp(label_table[t].name,s)) return label_table[t].p;
return '\0'; /* error condition */
}
/* execute a GOTO statement. */
void exec_goto()
{
char *loc;
get_token(); /* get label to go to */
/* find the location of label */
loc = find_label (token);
if (loc=='\0')
serror(7); /* label not defined */
else prog=loc; /* start program running at that time */
}
/* initialize the array that holds the labels.
by convention , a null label name indicates that
array posiiton is unused.
*/
void label_init()
{
register int t;
for (t=0;t<NUM_LAB;++t) label_table[t].name[0]='\0';
}
/* execute an IF statement */
void exec_if()
{
int x,y,cond;
char op;
get_exp(&x); /* get left exapression */
get_token(); /* get the operator */
if (!strcmp("<>",*token)) {
serror(0); /* not a leagal oprator */
return;
}
op = *token;
get_exp(&y); /* get right expression */
/* determine the outcome */
cond = 0;
switch(op) {
case '<':
if (x<y) cond=1;
break;
case '>':
if (x>y) cond=1;
break;
case '==':
if (x==y) cond=1;
break;
}
if (cond) { /* is true so process target of IF */
get_token();
if (tok != THEN) {
serror(8);
return;
} /* else program execution starts on next line */
}
else find_eol(); /* find start of next line */
}
/* execute a FOR loop */
void exec_for()
{
struct for_stack i;
int value;
get_token(); /* read the control variable */
if (!isalpha(*token)) {
serror(4);
return;
}
i.var = toupper(*token) - 'A'; /* save its index */
get_token(); /* read the equal sign */
if (*token!='=') {
serror(3);
return;
}
get_exp(&value); /* get initial value */
variables[i.var]=value;
get_token();
if (tok != TO) serror(9); /* read an discard the TO */
get_exp(&i.target); /* get target value */
/* if loop can execute at least once, push into on stack */
if (value<=i.target) {
i.loc = prog;
fpush(i);
}
else /* otherwise, skip loop code altogether */
while (tok!=NEXT) get_token();
}
/* execute a NEXT statement */
void next()
{
struct for_stack i;
i = fpop(); /*read the loop info */
variables[i.var]++; /* increment control variable */
if (variables[i.var]>i.target) return; /* all done */
fpush(i); /* otherwise,return the info */
prog = i.loc; /* loop */
}
/* push function for the FOR stack */
void fpush(struct for_stack i)
{
if (ftos>FOR_NEST)
serror(10);
fstack[ftos]=i;
ftos++;
}
struct for_stack fpop()
{
ftos--;
if (ftos<0) serror(11);
return (fstack[ftos]);
}
/* exec a simple form of BASIC INPUT command */
void input()
{
char str[80],var;
int i;
get_token(); /* see if prompt string id=s present */
if (token_type == QUOTE) {
printf (token); /* if so , print it and check for command */
get_token();
if (*token != ',') serror(1);
get_token();
}
else printf ("? "); /* otherwise, prompt with / */
var = toupper(*token) - 'A'; /* get the input var */
scanf ("%d",&i); /* read input */
variables[var] = i; /* store it */
}
/* execute a GOSUB command */
void gosub()
{
char *loc;
get_token();
/* find the label to call */
loc = find_label(token);
if (loc=='\0')
serror(7); /* label not defined */
else {
gpush(prog); /* save place to return to */
prog = loc; /* start program running at that loc */
}
}
/* return from GOSUB */
void greturn()
{
prog = gpop();
}
/* GOSUB stack push function */
void gpush(char *s)
{
gtos++;
if (gtos==SUB_NEST) {
serror(12);
return;
}
gstack[gtos] = s;
}
/* GOSUB stack pop function */
char *gpop()
{
if (gtos==0) {
serror(13);
return 0;
}
return gstack[gtos--];
}
main (int argc,char *argv[])
{
char in[80];
int answer;
char *p_buf;
char *t;
if (argc!=2) {
printf ("usage: run <filename>\n");
exit (1);
}
/* allocate memory for the program */
if (!(p_buf=(char *)malloc(PROG_SIZE))) {
printf ("allocation failure");
exit (1);
}
/* load the program to execute */
if (!load_program(p_buf,argv[1])) exit(1);
if (setjmp(e_buf)) exit(1); /* initialize the long jump */
prog = p_buf;
scan_labels(); /* find the labels in the program */
ftos = 0; /* initialize the FOR stack index */
gtos = 0; /* initialize the GOSUB stack index */
do {
token_type = get_token();
/* check for assignment stack */
if (token_type==VARIABLE) {
putback(); /* return the varto the input stream */
assignment(); /* must 1: assignment statemnet */
}
else /* is command */
switch (tok) {
case PRINT:
print();
break;
case GOTO:
exec_goto();
break;
case IF:
exec_if();
break;
case FOR:
exec_for();
break;
case NEXT:
next();
break;
case INPUT:
input();
break;
case GOSUB:
gosub();
break;
case RETURN:
greturn();
break;
case END:
exit(0);
}
}while (tok != FINISHED);
}