Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1292164
  • 博文数量: 196
  • 博客积分: 4141
  • 博客等级: 中将
  • 技术积分: 2253
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-21 20:04
文章存档

2019年(31)

2016年(1)

2014年(16)

2011年(8)

2010年(25)

2009年(115)

分类:

2009-03-22 14:18:33

////////////////////////////////////////////////////////////////////////////////

//recursive descent parser for integer expression

//which may include variables

////////////////////////////////////////////////////////////////////////////////


#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>

#define DELIMITER 1
#define VARIABLE 2
#define NUMBER 3
#define COMMAND 4
#define STRING 5
#define QUOTE 6

#define EOL 9
#define FINISHED 10

extern char *prog; // holds expression to be analyzed


extern jmp_buf e_buf; // hold enviroment

extern int variables[26]; // variables

extern struct commands
{
    char command[20];
    char tok;
} table[];

extern char token[80]; // holds string representation of token

extern char token_type; // contains type of token

extern char tok; // holds the internal representation of token


void get_exp(),level2(),level3(),level4(),level5();
void level6(),primitive(),arith(),unary();
void serror(),putback();


// entry point into parser

void get_exp(int *result)
{
    get_token();
    if (!*token)
        {
        serror(2);
        return;
        }
    level2(result);
    putback(); //return last token read to input stream

}


// add or subtract two terms

void level2(int *result)
{
    register char op;
    int hold;
    
    level3(result);
    while ((op = *token) =='+' || op == '-')
        {
        get_token();
        level3(&hold);
        arith(op,result,&hold);
        }
}


// multiply or divide two factors

void level3(int *result)
{
    register char op;
    int hold;
    
    level4(result);
    while ((op = *token) == '*' || op == '/' || op == '%')
        {
        get_token();
        level3(&hold);
        arith(op,result,&hold);
        }
}


// process integer exponent

void level4(int *result)
{
    register char op;
    int hold;
    
    level5(result);
    if (*token == '-')
        {
        get_token();
        level4(&hold);
        arith(op,result,&hold);
        }
}


// is a unary + or -

void level5(int *result)
{
    register char op;
    
    op = 0;
    if ((token_type==DELIMITER) && *token == '+' || *token == '-' )
        {
        op = *token;
        get_token();
        }
    level6(result);
    if (op) unary(op,result);
}


// process parenthesized expression

void level6(int *result)
{
    if ((*token == '(') && (token_type == DELIMITER))
        {
        get_token();
        level2(result);
        if (*token!=')')
            serror(1);
        get_token();
        }
    else
        primitive(result);
}


// find value of number or variable

void primitive(int *result)
{
    switch (token_type)
        {
        case VARIABLE:
            *result = find_var(token);
            get_token();
            return;
        case NUMBER:
            *result = atoi(token);
            get_token();
            return;
        default:
            serror(0);
        }
}


// perform the specified arithmetic

void arith(char o,int *r,int *h)
{
    register int t,ex;
    
    switch (o)
        {
        case '-':
            *r = *r-*h;
            break;
        case '+':
            *r = *r+*h;
            break;
        case '*':
            *r = *r**h;
            break;
        case '/':
            *r = (*r)/(*h);
            break;
        case '%':
            *r = (*r)%(*h);
            break;
        case '^':
            ex = *r;
            if (*h==0)
                {
                *r = 1;
                break;
                }
            for (t=*h-1;t>0;--t) *r=(*r)*ex;
            break;
        }
}


// reverse the sign

void unary(char o,int *r)
{
    if (o=='-') *r = -(*r);
}


// find the value of a variable

int find_var(char *s)
{
    if (!isalpha(*s))
        {
        serror(4); // not a variable

        return 0;
        }
    return variables[toupper(*token)-'A'];
}


// display an error message

void serror(int error)
{
    char *e[] =
        {
        "syntax error",
        "unbalanced parentheses",
        "no expression present",
        "equal sign expected",
        "not a variable",
        "label table full",
        "duplicate label",
        "undefined label",
        "THEN expected",
        "TO expected",
        "too many nested FOR loops",
        "NEXT without FOR",
        "too many nested GOSUB",
        "RETURN without GOSUB"
        };
    
    printf ("%s\n",e[error]);
    longjmp(e_buf,1); // return to save point

}


// get a token

get_token()
{
    register char *temp;
    
    token_type = 0;
    tok = 0;
    temp = token;
    
    if (*prog == '\0')
        { // end of file

        *token = 0;
        tok = FINISHED;
        return (token_type = DELIMITER);
        }
    
    while (iswhite(*prog)) ++prog; // skip over white space

    
    if (*prog == '\r')
        { // CR LF

        ++prog;
        ++prog;
        tok = EOL;
        *token = '\r';
        token[1] = '\n';
        token[2] = 0;
        return (token_type = DELIMITER);
        }
    
    if (strchr("+-*^/%=;(),><",*prog))
        { // delimiter

        *temp = *prog;
        prog++; // advance to next position

        temp++;
        *temp=0;
        return (token_type = DELIMITER);
        }
    
    if (*prog == '"')
        { // quote string

        prog++;
        while (*prog!='"'&&*prog!='\r') *temp++=*prog++;
        if (*prog=='\r') serror(1);
        prog++;
        *temp=0;
        return (token_type = QUOTE);
        }
    
    if (isdigit(*prog))
        { // number

        while (!isdelim(*prog)) *temp++=*prog++;
        *temp = '\0';
        return (token_type = NUMBER);
        }
    
    if (isalpha(*prog))
        { // var or command

        while (!isdelim(*prog)) *temp++=*prog++;
        token_type = STRING;
        }
    
    *temp = '\0';
    
    // see if a string is a command or a variable

    if (token_type == STRING)
        {
        tok = look_up(token); // convert to internal rep

        if (!tok) token_type = VARIABLE;
        else token_type = COMMAND; // is a command

        }
    return token_type;
}


// return a token to input stream

void putback()
{
    char *t;
    t = token;
    for (;*t;t++) prog--;
}


look_up(char *s)
{
    register int i,j;
    char *p;
    
    // convert to lowercase

    p = s;
    while (*p) { *p = tolower(*p); p++; }
    
    // see if token is in table

    for (i=0;*table[i].command;i++)
        if (!strcmp(table[i].command,s)) return table[i].tok;
    return 0; // unknown command

}


// return true if c is a delimiter

isdelim(char c)
{
    if (strchr(";,+-<>/*%^=() ",c)||c==9||c=='\r'||c==0)
    return 1;
    return 0;
}


iswhite (char c)
{
    if (c==' '||c=='\t') return 1;
    else return 0;
}


// 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 \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);
}

阅读(4583) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~