关于Basic程序解释器及编译原理的简单化(1)---Basic器的语法分析及主要代码

原创 2001年05月18日 21:14:00

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

自己动手写basic解释器(一)

自己动手写basic解释器刺猬@http://blog.csdn.net/littlehedgehog注: 文章basic解释源码摘自梁肇新先生的《编程高手箴言》(据他所说这个代码也是网上摘录的),源...

关于Basic程序解释器及编译原理的简单化(2)---C++封装好的Basic解释器

这是CMake的源代码.主要负责词汇的提取你可以调用它的CMake::get_token(),返回个CToken的类./////////////////////////////////////////...

关于Basic程序解释器及编译原理的简单化(1)--词法分析和代数式求值

   在网上,看到还是有部分程序爱好者希望能编出自己的编译器.当然,这的确是件难事,许多人都说要去看什么编译原理和精通汇编语言,结果让这些爱好者都望而却步.但是,当我们亲手去做做后,发现要做一个简单的...

STM32 深入浅出 (新手必看)

作者:tangwei039 转自:http://www.amobbs.com/thread-4197396-1-1.html STM32学前班教程之一:为什么是它 经过几天的学习,基本掌握了S...
  • whw8007
  • whw8007
  • 2013年04月27日 09:32
  • 2967

Basic脚本解释器移植到STM32

上次讲了LUA移植到STM32,这次讲讲Basic脚本解释器移植到STM32。在STM32上跑Basic脚本,同样可以跟穿戴设备结合,也可以作为初学者学习MCU的入门工具,当然前提是有人做好Basic...
  • hellogv
  • hellogv
  • 2014年05月23日 18:33
  • 8952

吉首大学_编译原理实验题_基于预测方法的语法分析程序的设计【通过代码】

一、实验要求 实验二  基于预测方法的语法分析程序的设计 一、实验目的 了解预测分析器的基本构成及用自顶向下的预测法对表达式进行语法分析的方法,掌握预测语法分析程序的手工构造方法。 二、实验内容 1...

编译原理丨第十三周 ——1000. 输入输出LL(1)语法分析程序

Description  输入开始符号,非终结符,终结符,产生式,LL(1)分析表 输出LL(1)分析表 G[E]:E →E+T | E-T | T T →T*F | T/F | F F →(E...
  • xxhi008
  • xxhi008
  • 2017年11月29日 13:24
  • 53

编译原理程序设计实践(七)解释器的相关代码

/* 目标代码生成过程gen */ /* 参数:x:要生成的一行代码的助记符 */ /* y, z:代码的两个操作数 */ /* 本过程用于把生成的目标代码写入目标代码数组,供后面的...
  • laomai
  • laomai
  • 2013年03月13日 21:54
  • 1518

atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结

atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结   1. 建立AST 抽象语法树 Abstract Syntax Tree,AS...
  • attilax
  • attilax
  • 2014年12月01日 21:27
  • 2941
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于Basic程序解释器及编译原理的简单化(1)---Basic器的语法分析及主要代码
举报原因:
原因补充:

(最多只允许输入30个字)