这就是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); }

|