Jackiesteed

www.github.com/jackiesteed

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

来自<C++编程艺术>.

Herbert Schildt已经把很多基本的程序解析的技巧表达了出来, 不过现在的源代码比较脆弱, 经过我改进的代码也很脆弱, 所以, 如果你感兴趣, 请使用简单的代码来测试它.

书中MiniCPP(理解为MiniCpp1.0)支持的特性如下:

具有局部变量的参数化函数.

嵌套的作用域.

递归.

if语句.

switch语句.

do-while, while及for循环.

break语句.

int, char类型的局部及全局变量.

整型和字符型的函数参数.

字符串常量(部分实现).

return语句, 可以带或不带返回值.

少量的标准函数.

基本的运算符.

返回整型的函数.

/*和//注释

使用cin和cout的控制台I/O

作者的源代码如下:

View Code
listing 1
// Recursive descent parser for integer expressions.
//
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include "mccommon.h"

using namespace std;

// Keyword lookup table.
// Keywords must be entered lowercase.
struct commands
{
    char command[20];
    token_ireps tok;
} com_table[] =
{
    "if", IF,
    "else", ELSE,
    "for", FOR,
    "do", DO,
    "while", WHILE,
    "char", CHAR,
    "int", INT,
    "return", RETURN,
    "switch", SWITCH,
    "break", BREAK,
    "case", CASE,
    "cout", COUT,
    "cin", CIN,
    "", END  // mark end of table
};

// This structure links a library function name
// with a pointer to that function.
struct intern_func_type
{
    char *f_name; // function name
    int (*p)();   // pointer to the function
} intern_func[] =
{
    "getchar", call_getchar,
    "putchar", call_putchar,
    "abs", call_abs,
    "rand", call_rand,
    "", 0  // null terminate the list
};

// Entry point into parser.
void eval_exp(int &value)
{
    get_token();

    if(!*token)
    {
        throw InterpExc(NO_EXP);
    }

    if(*token == ';')
    {
        value = 0; // empty expression
        return;
    }

    eval_exp0(value);

    putback(); // return last token read to input stream
}

// Process an assignment expression.
void eval_exp0(int &value)
{
    // temp holds name of var receiving the assignment.
    char temp[MAX_ID_LEN+1];

    tok_types temp_tok;

    if(token_type == IDENTIFIER)
    {
        if(is_var(token))   // if a var, see if assignment
        {
            strcpy(temp, token);
            temp_tok = token_type;
            get_token();
            if(*token == '=')   // is an assignment
            {
                get_token();
                eval_exp0(value); // get value to assign
                assign_var(temp, value); // assign the value
                return;
            }
            else   // not an assignment
            {
                putback(); // restore original token
                strcpy(token, temp);
                token_type = temp_tok;
            }
        }
    }
    eval_exp1(value);
}

// Process relational operators.
void eval_exp1(int &value)
{
    int partial_value;
    char op;
    char relops[] =
    {
        LT, LE, GT, GE, EQ, NE, 0
    };

    eval_exp2(value);

    op = *token;
    if(strchr(relops, op))
    {
        get_token();
        eval_exp2(partial_value);

        switch(op)   // perform the relational operation
        {
        case LT:
            value = value < partial_value;
            break;
        case LE:
            value = value <= partial_value;
            break;
        case GT:
            value = value > partial_value;
            break;
        case GE:
            value = value >= partial_value;
            break;
        case EQ:
            value = value == partial_value;
            break;
        case NE:
            value = value != partial_value;
            break;
        }
    }
}

// Add or subtract two terms.
void eval_exp2(int &value)
{
    char  op;
    int partial_value;
    char okops[] =
    {
        '(', INC, DEC, '-', '+', 0
    };

    eval_exp3(value);

    while((op = *token) == '+' || op == '-')
    {
        get_token();

        if(token_type == DELIMITER &&
                !strchr(okops, *token))
            throw InterpExc(SYNTAX);

        eval_exp3(partial_value);

        switch(op)   // add or subtract
        {
        case '-':
            value = value - partial_value;
            break;
        case '+':
            value = value + partial_value;
            break;
        }
    }
}

// Multiply or divide two factors.
void eval_exp3(int &value)
{
    char  op;
    int partial_value, t;
    char okops[] =
    {
        '(', INC, DEC, '-', '+', 0
    };

    eval_exp4(value);

    while((op = *token) == '*' || op == '/'
            || op == '%')
    {
        get_token();

        if(token_type == DELIMITER &&
                !strchr(okops, *token))
            throw InterpExc(SYNTAX);

        eval_exp4(partial_value);

        switch(op)   // mul, div, or modulus
        {
        case '*':
            value = value * partial_value;
            break;
        case '/':
            if(partial_value == 0)
                throw InterpExc(DIV_BY_ZERO);
            value = (value) / partial_value;
            break;
        case '%':
            t = (value) / partial_value;
            value = value - (t * partial_value);
            break;
        }
    }
}

// Is a unary +, -, ++, or --.
void eval_exp4(int &value)
{
    char  op;
    char temp;

    op = '\0';
    if(*token == '+' || *token == '-' ||
            *token == INC || *token == DEC)
    {
        temp = *token;
        op = *token;
        get_token();
        if(temp == INC)
            assign_var(token, find_var(token)+1);
        if(temp == DEC)
            assign_var(token, find_var(token)-1);
    }

    eval_exp5(value);
    if(op == '-') value = -(value);
}

// Process parenthesized expression.
void eval_exp5(int &value)
{

    if((*token == '('))
    {
        get_token();

        eval_exp0(value); // get subexpression

        if(*token != ')')
            throw InterpExc(PAREN_EXPECTED);
        get_token();
    }
    else
        atom(value);
}

// Find value of number, variable, or function.
void atom(int &value)
{
    int i;
    char temp[MAX_ID_LEN+1];

    switch(token_type)
    {
    case IDENTIFIER:
        i = internal_func(token);
        if(i != -1)
        {
            // Call "standard library" function.
            value = (*intern_func[i].p)();
        }
        else if(find_func(token))
        {
            // Call programmer-created function.
            call();
            value = ret_value;
        }
        else
        {
            value = find_var(token); // get var's value
            strcpy(temp, token); // save variable name

            // Check for ++ or --.
            get_token();
            if(*token == INC || *token == DEC)
            {
                if(*token == INC)
                    assign_var(temp, find_var(temp)+1);
                else
                    assign_var(temp, find_var(temp)-1);
            }
            else putback();
        }

        get_token();
        return;
    case NUMBER: // is numeric constant
        value = atoi(token);
        get_token();

        return;
    case DELIMITER: // see if character constant
        if(*token == '\'')
        {
            value = *prog;
            prog++;
            if(*prog!='\'')
                throw InterpExc(QUOTE_EXPECTED);

            prog++;
            get_token();

            return ;
        }
        if(*token==')') return; // process empty expression
        else throw InterpExc(SYNTAX);  // otherwise, syntax error
    default:
        throw InterpExc(SYNTAX); // syntax error
    }
}

// Display an error message.
void sntx_err(error_msg error)
{
    char *p, *temp;
    int linecount = 0;

    static char *e[]=
    {
        "Syntax error",
        "No expression present",
        "Not a variable",
        "Duplicate variable name",
        "Duplicate function name",
        "Semicolon expected",
        "Unbalanced braces",
        "Function undefined",
        "Type specifier expected",
        "Return without call",
        "Parentheses expected",
        "While expected",
        "Closing quote expected",
        "Division by zero",
        "{ expected (control statements must use blocks)",
        "Colon expected"
    };

    // Display error and line number.
    cout << "\n" << e[error];
    p = p_buf;
    while(p != prog)   // find line number of error
    {
        p++;
        if(*p == '\r')
        {
            linecount++;
        }
    }
    cout << " in line " << linecount << endl;

    temp = p;
    while(p > p_buf && *p != '\n') p--;

    // Display offending line.
    while(p <= temp)
        cout << *p++;

    cout << endl;
}

// Get a token.
tok_types get_token()
{

    char *temp;

    token_type = UNDEFTT;
    tok = UNDEFTOK;

    temp = token;
    *temp = '\0';

    // Skip over white space.
    while(isspace(*prog) && *prog) ++prog;

    // Skip over newline.
    while(*prog == '\r')
    {
        ++prog;
        ++prog;
        // Again, skip over white space.
        while(isspace(*prog) && *prog) ++prog;
    }

    // Check for end of program.
    if(*prog == '\0')
    {
        *token = '\0';
        tok = END;
        return (token_type = DELIMITER);
    }

    // Check for block delimiters.
    if(strchr("{}", *prog))
    {
        *temp = *prog;
        temp++;
        *temp = '\0';
        prog++;
        return (token_type = BLOCK);
    }

    // Look for comments.
    if(*prog == '/')
        if(*(prog+1) == '*')   // is a /* comment
        {
            prog += 2;
            do   // find end of comment
            {
                while(*prog != '*') prog++;
                prog++;
            }
            while (*prog != '/');
            prog++;
            return (token_type = DELIMITER);
        }
        else if(*(prog+1) == '/')     // is a // comment
        {
            prog += 2;
            // Find end of comment.
            while(*prog != '\r' && *prog != '\0') prog++;
            if(*prog == '\r') prog +=2;
            return (token_type = DELIMITER);
        }

    // Check for double-ops.
    if(strchr("!<>=+-", *prog))
    {
        switch(*prog)
        {
        case '=':
            if(*(prog+1) == '=')
            {
                prog++;
                prog++;
                *temp = EQ;
                temp++;
                *temp = EQ;
                temp++;
                *temp = '\0';
            }
            break;
        case '!':
            if(*(prog+1) == '=')
            {
                prog++;
                prog++;
                *temp = NE;
                temp++;
                *temp = NE;
                temp++;
                *temp = '\0';
            }
            break;
        case '<':
            if(*(prog+1) == '=')
            {
                prog++;
                prog++;
                *temp = LE;
                temp++;
                *temp = LE;
            }
            else if(*(prog+1) == '<')
            {
                prog++;
                prog++;
                *temp = LS;
                temp++;
                *temp = LS;
            }
            else
            {
                prog++;
                *temp = LT;
            }
            temp++;
            *temp = '\0';
            break;
        case '>':
            if(*(prog+1) == '=')
            {
                prog++;
                prog++;
                *temp = GE;
                temp++;
                *temp = GE;
            }
            else if(*(prog+1) == '>')
            {
                prog++;
                prog++;
                *temp = RS;
                temp++;
                *temp = RS;
            }
            else
            {
                prog++;
                *temp = GT;
            }
            temp++;
            *temp = '\0';
            break;
        case '+':
            if(*(prog+1) == '+')
            {
                prog++;
                prog++;
                *temp = INC;
                temp++;
                *temp = INC;
                temp++;
                *temp = '\0';
            }
            break;
        case '-':
            if(*(prog+1) == '-')
            {
                prog++;
                prog++;
                *temp = DEC;
                temp++;
                *temp = DEC;
                temp++;
                *temp = '\0';
            }
            break;
        }

        if(*token) return(token_type = DELIMITER);
    }

    // Check for other delimiters.
    if(strchr("+-*^/%=;:(),'", *prog))
    {
        *temp = *prog;
        prog++;
        temp++;
        *temp = '\0';
        return (token_type = DELIMITER);
    }

    // Read a quoted string.
    if(*prog == '"')
    {
        prog++;
        while(*prog != '"' && *prog != '\r' && *prog)
        {
            // Check for \n escape sequence.
            if(*prog == '\\')
            {
                if(*(prog+1) == 'n')
                {
                    prog++;
                    *temp++ = '\n';
                }
            }
            else if((temp - token) < MAX_T_LEN)
                *temp++ = *prog;

            prog++;
        }
        if(*prog == '\r' || *prog == 0)
            throw InterpExc(SYNTAX);
        prog++;
        *temp = '\0';
        return (token_type = STRING);
    }

    // Read an integer number.
    if(isdigit(*prog))
    {
        while(!isdelim(*prog))
        {
            if((temp - token) < MAX_ID_LEN)
                *temp++ = *prog;
            prog++;
        }
        *temp = '\0';
        return (token_type = NUMBER);
    }

    // Read identifier or keyword.
    if(isalpha(*prog))
    {
        while(!isdelim(*prog))
        {
            if((temp - token) < MAX_ID_LEN)
                *temp++ = *prog;
            prog++;
        }
        token_type = TEMP;
    }

    *temp = '\0';

    // Determine if token is a keyword or identifier.
    if(token_type == TEMP)
    {
        tok = look_up(token); // convert to internal form
        if(tok) token_type = KEYWORD; // is a keyword
        else token_type = IDENTIFIER;
    }

    // Check for unidentified character in file.
    if(token_type == UNDEFTT)
        throw InterpExc(SYNTAX);

    return token_type;
}

// Return a token to input stream.
void putback()
{
    char *t;

    t = token;
    for(; *t; t++) prog--;
}

// Look up a token's internal representation in the
// token table.
token_ireps look_up(char *s)
{
    int i;
    char *p;

    // Convert to lowercase.
    p = s;
    while(*p)
    {
        *p = tolower(*p);
        p++;
    }

    // See if token is in table.
    for(i=0; *com_table[i].command; i++)
    {
        if(!strcmp(com_table[i].command, s))
            return com_table[i].tok;
    }

    return UNDEFTOK; // unknown command
}

// Return index of internal library function or -1 if
// not found.
int internal_func(char *s)
{
    int i;

    for(i=0; intern_func[i].f_name[0]; i++)
    {
        if(!strcmp(intern_func[i].f_name, s))  return i;
    }
    return -1;
}

// Return true if c is a delimiter.
bool isdelim(char c)
{
    if(strchr(" !:;,+-<>'/*%^=()", c) || c == 9 ||
            c == '\r' || c == 0) return true;
    return false;
}

listing 2
// A Mini C++ interpreter.

#include <iostream>
#include <fstream>
#include <new>
#include <stack>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include "mccommon.h"

using namespace std;

char *prog;  // current execution point in source code
char *p_buf; // points to start of program buffer

// This structure encapsulates the info
// associated with variables.
struct var_type
{
    char var_name[MAX_ID_LEN+1]; // name
    token_ireps v_type; // data type
    int value; // value
};

// This vector holds info for global variables.
vector<var_type> global_vars;

// This vector holds info for local variables
// and parameters.
vector<var_type> local_var_stack;

// This structure encapsulates function info.
struct func_type
{
    char func_name[MAX_ID_LEN+1]; // name
    token_ireps ret_type; // return type
    char *loc; // location of entry point in program
};

// This vector holds info about functions.
vector<func_type> func_table;

// Stack for managing function scope.
stack<int> func_call_stack;

// Stack for managing nested scopes.
stack<int> nest_scope_stack;

char token[MAX_T_LEN+1]; // current token
tok_types token_type; // token type
token_ireps tok; // internal representation

int ret_value; // function return value

bool breakfound = false; // true if break encountered

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        cout << "Usage: minicpp <filename>\n";
        return 1;
    }

    // Allocate memory for the program.
    try
    {
        p_buf = new char[PROG_SIZE];
    }
    catch (bad_alloc exc)
    {
        cout << "Could Not Allocate Program Buffer\n";
        return 1;
    }

    // Load the program to execute.
    if(!load_program(p_buf, argv[1])) return 1;

    // Set program pointer to start of program buffer.
    prog = p_buf;

    try
    {
        // Find the location of all functions
        // and global variables in the program.
        prescan();

        // Next, set up the call to main().

        // Find program starting point.
        prog = find_func("main");

        // Check for incorrect or missing main() function.
        if(!prog)
        {
            cout << "main() Not Found\n";
            return 1;
        }

        // Back up to opening (.
        prog--;

        // Set the first token to main
        strcpy(token, "main");

        // Call main() to start interpreting.
        call();
    }
    catch(InterpExc exc)
    {
        sntx_err(exc.get_err());
        return 1;
    }
    catch(bad_alloc exc)
    {
        cout << "Out Of Memory\n";
        return 1;
    }

    return ret_value;
}

// Load a program.
bool load_program(char *p, char *fname)
{
    int i=0;

    ifstream in(fname, ios::in | ios::binary);
    if(!in)
    {
        cout << "Cannot Open file.\n";
        return false;
    }

    do
    {
        *p = in.get();
        p++;
        i++;
    }
    while(!in.eof() && i < PROG_SIZE);

    if(i == PROG_SIZE)
    {
        cout << "Program Too Big\n";
        return false;
    }

    // Null terminate the program. Skip any EOF
    // mark if present in the file.
    if(*(p-2) == 0x1a) *(p-2) = '\0';
    else *(p-1) = '\0';

    in.close();

    return true;
}

// Find the location of all functions in the program
// and store global variables.
void prescan()
{
    char *p, *tp;
    char temp[MAX_ID_LEN+1];
    token_ireps datatype;
    func_type ft;

    // When brace is 0, the current source position
    // is outside of any function.
    int brace = 0;

    p = prog;

    do
    {
        // Bypass code inside functions
        while(brace)
        {
            get_token();
            if(tok == END) throw InterpExc(UNBAL_BRACES);
            if(*token == '{') brace++;
            if(*token == '}') brace--;
        }

        tp = prog; // save current position
        get_token();

        // See if global var type or function return type.
        if(tok==CHAR || tok==INT)
        {
            datatype = tok; // save data type
            get_token();

            if(token_type == IDENTIFIER)
            {
                strcpy(temp, token);
                get_token();

                if(*token != '(')   // must be global var
                {
                    prog = tp; // return to start of declaration
                    decl_global();
                }
                else if(*token == '(')   // must be a function
                {

                    // See if function already defined.
                    for(unsigned i=0; i < func_table.size(); i++)
                        if(!strcmp(func_table[i].func_name, temp))
                            throw InterpExc(DUP_FUNC);

                    ft.loc = prog;
                    ft.ret_type = datatype;
                    strcpy(ft.func_name, temp);
                    func_table.push_back(ft);

                    do
                    {
                        get_token();
                    }
                    while(*token != ')');
                    // Next token will now be opening curly
                    // brace of function.
                }
                else putback();
            }
        }
        else
        {
            if(*token == '{') brace++;
            if(*token == '}') brace--;
        }
    }
    while(tok != END);
    if(brace) throw InterpExc(UNBAL_BRACES);
    prog = p;
}

// Interpret a single statement or block of code. When
// interp() returns from its initial call, the final
// brace (or a return) in main() has been encountered.
void interp()
{
    int value;
    int block = 0;

    do
    {
        // Don't interpret until break is handled.
        if(breakfound) return;

        token_type = get_token();

        // See what kind of token is up.
        if(token_type == IDENTIFIER ||
                *token == INC || *token == DEC)
        {
            // Not a keyword, so process expression.
            putback();  // restore token to input stream for
            // further processing by eval_exp()
            eval_exp(value); // process the expression
            if(*token != ';') throw InterpExc(SEMI_EXPECTED);
        }
        else if(token_type==BLOCK)   // block delimiter?
        {
            if(*token == '{')   // is a block
            {
                block = 1; // interpreting block, not statement
                // Record nested scope.
                nest_scope_stack.push(local_var_stack.size());
            }
            else   // is a }, so reset scope and return
            {
                // Reset nested scope.
                local_var_stack.resize(nest_scope_stack.top());
                nest_scope_stack.pop();
                return;
            }
        }
        else // is keyword
            switch(tok)
            {
            case CHAR:
            case INT:     // declare local variables
                putback();
                decl_local();
                break;
            case RETURN:  // return from function call
                func_ret();
                return;
            case IF:      // process an if statement
                exec_if();
                break;
            case ELSE:    // process an else statement
                find_eob(); // find end of else block
                // and continue execution
                break;
            case WHILE:   // process a while loop
                exec_while();
                break;
            case DO:      // process a do-while loop
                exec_do();
                break;
            case FOR:     // process a for loop
                exec_for();
                break;
            case BREAK:   // handle break
                breakfound = true;

                // Reset nested scope.
                local_var_stack.resize(nest_scope_stack.top());
                nest_scope_stack.pop();
                return;
            case SWITCH:  // handle a switch statement
                exec_switch();
                break;
            case COUT:    // handle console output
                exec_cout();
                break;
            case CIN:     // handle console input
                exec_cin();
                break;
            case END:
                exit(0);
            }
    }
    while (tok != END && block);
    return;
}

// Return the entry point of the specified function.
// Return NULL if not found.
char *find_func(char *name)
{
    unsigned i;

    for(i=0; i < func_table.size(); i++)
        if(!strcmp(name, func_table[i].func_name))
            return func_table[i].loc;

    return NULL;
}

// Declare a global variable.
void decl_global()
{
    token_ireps vartype;
    var_type vt;

    get_token(); // get type

    vartype = tok; // save var type

    // Process comma-separated list.
    do
    {
        vt.v_type = vartype;
        vt.value = 0; // init to 0
        get_token(); // get name

        // See if variable is a duplicate.
        for(unsigned i=0; i < global_vars.size(); i++)
            if(!strcmp(global_vars[i].var_name, token))
                throw InterpExc(DUP_VAR);

        strcpy(vt.var_name, token);
        global_vars.push_back(vt);

        get_token();
    }
    while(*token == ',');

    if(*token != ';') throw InterpExc(SEMI_EXPECTED);
}

// Declare a local variable.
void decl_local()
{
    var_type vt;

    get_token(); // get var type
    vt.v_type = tok; // store type

    vt.value = 0; // init var to 0

    // Process comma-separated list.
    do
    {
        get_token(); // get var name

        // See if variable is already the name
        // of a local variable in this scope.
        if(!local_var_stack.empty())
            for(int i=local_var_stack.size()-1;
                    i >= nest_scope_stack.top(); i--)
            {
                if(!strcmp(local_var_stack[i].var_name, token))
                    throw InterpExc(DUP_VAR);
            }

        strcpy(vt.var_name, token);
        local_var_stack.push_back(vt);
        get_token();
    }
    while(*token == ',');

    if(*token != ';') throw InterpExc(SEMI_EXPECTED);
}

// Call a function.
void call()
{
    char *loc, *temp;
    int lvartemp;

    // First, find entry point of function.
    loc = find_func(token);

    if(loc == NULL)
        throw InterpExc(FUNC_UNDEF); // function not defined
    else
    {
        // Save local var stack index.
        lvartemp = local_var_stack.size();

        get_args(); // get function arguments
        temp = prog; // save return location

        func_call_stack.push(lvartemp); // push local var index

        prog = loc; // reset prog to start of function
        get_params(); // load the function's parameters with
        // the values of the arguments

        interp(); // interpret the function

        prog = temp; // reset the program pointer

        if(func_call_stack.empty()) throw InterpExc(RET_NOCALL);

        // Reset local_var_stack to its previous state.
        local_var_stack.resize(func_call_stack.top());
        func_call_stack.pop();
    }
}

// Push the arguments to a function onto the local
// variable stack.
void get_args()
{
    int value, count, temp[NUM_PARAMS];
    var_type vt;

    count = 0;
    get_token();
    if(*token != '(') throw InterpExc(PAREN_EXPECTED);

    // Process a comma-separated list of values.
    do
    {
        eval_exp(value);
        temp[count] = value; // save temporarily
        get_token();
        count++;
    }
    while(*token == ',');
    count--;

    // Now, push on local_var_stack in reverse order.
    for(; count>=0; count--)
    {
        vt.value = temp[count];
        vt.v_type = ARG;
        local_var_stack.push_back(vt);
    }
}

// Get function parameters.
void get_params()
{
    var_type *p;
    int i;

    i = local_var_stack.size()-1;

    // Process comma-separated list of parameters.
    do
    {
        get_token();
        p = &local_var_stack[i];
        if(*token != ')' )
        {
            if(tok != INT && tok != CHAR)
                throw InterpExc(TYPE_EXPECTED);

            p->v_type = tok;
            get_token();

            // Link parameter name with argument already on
            // local var stack.
            strcpy(p->var_name, token);
            get_token();
            i--;
        }
        else break;
    }
    while(*token == ',');

    if(*token != ')') throw InterpExc(PAREN_EXPECTED);
}

// Return from a function.
void func_ret()
{
    int value;

    value = 0;

    // Get return value, if any.
    eval_exp(value);

    ret_value = value;
}

// Assign a value to a variable.
void assign_var(char *vname, int value)
{
    // First, see if it's a local variable.
    if(!local_var_stack.empty())
        for(int i=local_var_stack.size()-1;
                i >= func_call_stack.top(); i--)
        {
            if(!strcmp(local_var_stack[i].var_name,
                       vname))
            {
                if(local_var_stack[i].v_type == CHAR)
                    local_var_stack[i].value = (char) value;
                else if(local_var_stack[i].v_type == INT)
                    local_var_stack[i].value = value;
                return;
            }
        }

    // Otherwise, try global vars.
    for(unsigned i=0; i < global_vars.size(); i++)
        if(!strcmp(global_vars[i].var_name, vname))
        {
            if(global_vars[i].v_type == CHAR)
                global_vars[i].value = (char) value;
            else if(global_vars[i].v_type == INT)
                global_vars[i].value = value;
            return;
        }

    throw InterpExc(NOT_VAR); // variable not found
}

// Find the value of a variable.
int find_var(char *vname)
{
    // First, see if it's a local variable.
    if(!local_var_stack.empty())
        for(int i=local_var_stack.size()-1;
                i >= func_call_stack.top(); i--)
        {
            if(!strcmp(local_var_stack[i].var_name, vname))
                return local_var_stack[i].value;
        }

    // Otherwise, try global vars.
    for(unsigned i=0; i < global_vars.size(); i++)
        if(!strcmp(global_vars[i].var_name, vname))
            return global_vars[i].value;

    throw InterpExc(NOT_VAR); // variable not found
}

// Execute an if statement.
void exec_if()
{
    int cond;

    eval_exp(cond); // get if expression.

    if(cond)   // if true, process target of IF
    {
        // Confirm start of block.
        if(*token != '{')
            throw InterpExc(BRACE_EXPECTED);

        interp();
    }
    else
    {
        // Otherwise skip around IF block and
        // process the ELSE, if present.

        find_eob(); // find start of next line
        get_token();

        if(tok != ELSE)
        {
            // Restore token if no ELSE is present.
            putback();
            return;
        }

        // Confirm start of block.
        get_token();
        if(*token != '{')
            throw InterpExc(BRACE_EXPECTED);
        putback();

        interp();
    }
}

// Execute a switch statement.
void exec_switch()
{
    int sval, cval;
    int brace;

    eval_exp(sval); // Get switch expression.

    // Check for start of block.
    if(*token != '{')
        throw InterpExc(BRACE_EXPECTED);

    // Record new scope.
    nest_scope_stack.push(local_var_stack.size());

    // Now, check case statements.
    for(;;)
    {
        brace = 1;
        // Find a case statement.
        do
        {
            get_token();
            if(*token == '{') brace++;
            else if(*token == '}') brace--;
        }
        while(tok != CASE && tok != END && brace);

        // If no matching case found, then skip.
        if(!brace) break;

        if(tok == END) throw InterpExc(SYNTAX);

        // Get value of the case statement.
        eval_exp(cval);

        // Read and discard the :
        get_token();

        if(*token != ':')
            throw InterpExc(COLON_EXPECTED);

        // If values match, then interpret.
        if(cval == sval)
        {
            brace = 1;
            do
            {
                interp();

                if(*token == '{') brace++;
                else if(*token == '}') brace--;
            }
            while(!breakfound && tok != END && brace);

            // Find end of switch statement.
            while(brace)
            {
                get_token();
                if(*token == '{') brace++;
                else if(*token == '}') brace--;
            }
            breakfound = false;

            break;
        }
    }
}

// Execute a while loop.
void exec_while()
{
    int cond;
    char *temp;

    putback(); // put back the while
    temp = prog; // save location of top of while loop

    get_token();
    eval_exp(cond); // check the conditional expression

    // Confirm start of block.
    if(*token != '{')
        throw InterpExc(BRACE_EXPECTED);

    if(cond)
        interp(); // if true, interpret
    else   // otherwise, skip to end of loop
    {
        find_eob();
        return;
    }

    prog = temp; // loop back to top

    // Check for break in loop.
    if(breakfound)
    {
        // Find start of loop block.
        do
        {
            get_token();
        }
        while(*token != '{' && tok != END);

        putback();
        breakfound = false;
        find_eob(); // now, find end of loop
        return;
    }
}

// Execute a do loop.
void exec_do()
{
    int cond;
    char *temp;


    // Save location of top of do loop.
    putback(); // put back do
    temp = prog;

    get_token(); // get start of loop block

    // Confirm start of block.
    get_token();
    if(*token != '{')
        throw InterpExc(BRACE_EXPECTED);
    putback();

    interp(); // interpret loop

    // Check for break in loop.
    if(breakfound)
    {
        prog = temp;
        // Find start of loop block.
        do
        {
            get_token();
        }
        while(*token != '{' && tok != END);

        // Find end of while block.
        putback();
        find_eob();

        // Now, find end of while expression.
        do
        {
            get_token();
        }
        while(*token != ';' && tok != END);
        if(tok == END) throw InterpExc(SYNTAX);

        breakfound = false;
        return;
    }

    get_token();
    if(tok != WHILE) throw InterpExc(WHILE_EXPECTED);

    eval_exp(cond); // check the loop condition

    // If true loop; otherwise, continue on.
    if(cond) prog = temp;
}

// Execute a for loop.
void exec_for()
{
    int cond;
    char *temp, *temp2;
    int paren ;

    get_token(); // skip opening (
    eval_exp(cond); // initialization expression

    if(*token != ';') throw InterpExc(SEMI_EXPECTED);
    prog++; // get past the ;
    temp = prog;

    for(;;)
    {
        // Get the value of the conditional expression.
        eval_exp(cond);

        if(*token != ';') throw InterpExc(SEMI_EXPECTED);
        prog++; // get past the ;
        temp2 = prog;

        // Find start of for block.
        paren = 1;
        while(paren)
        {
            get_token();
            if(*token == '(') paren++;
            if(*token == ')') paren--;
        }

        // Confirm start of block.
        get_token();
        if(*token != '{')
            throw InterpExc(BRACE_EXPECTED);
        putback();

        // If condition is true, interpret
        if(cond)
            interp();
        else   // otherwise, skip to end of loop
        {
            find_eob();
            return;
        }

        prog = temp2; // go to increment expression

        // Check for break in loop.
        if(breakfound)
        {
            // Find start of loop block.
            do
            {
                get_token();
            }
            while(*token != '{' && tok != END);

            putback();
            breakfound = false;
            find_eob(); // now, find end of loop
            return;
        }

        // Evaluate the increment expression.
        eval_exp(cond);

        prog = temp; // loop back to top
    }
}

// Execute a cout statement.
void exec_cout()
{
    int val;

    get_token();
    if(*token != LS) throw InterpExc(SYNTAX);

    do
    {
        get_token();

        if(token_type==STRING)
        {
            // Output a string.
            cout << token;
        }
        else if(token_type == NUMBER ||
                token_type == IDENTIFIER)
        {
            // Output a number.
            putback();
            eval_exp(val);
            cout << val;
        }
        else if(*token == '\'')
        {
            // Output a character constant.
            putback();
            eval_exp(val);
            cout << (char) val;
        }

        get_token();
    }
    while(*token == LS);

    if(*token != ';') throw InterpExc(SEMI_EXPECTED);
}

// Execute a cin statement.
void exec_cin()
{
    int val;
    char chval;
    token_ireps vtype;

    get_token();
    if(*token != RS) throw InterpExc(SYNTAX);

    do
    {
        get_token();
        if(token_type != IDENTIFIER)
            throw InterpExc(NOT_VAR);

        vtype = find_var_type(token);

        if(vtype == CHAR)
        {
            cin >> chval;
            assign_var(token, chval);
        }
        else if(vtype == INT)
        {
            cin >> val;
            assign_var(token, val);
        }

        get_token();
    }
    while(*token == RS);

    if(*token != ';') throw InterpExc(SEMI_EXPECTED);
}

// Find the end of a block.
void find_eob()
{
    int brace;

    get_token();
    if(*token != '{')
        throw InterpExc(BRACE_EXPECTED);

    brace = 1;

    do
    {
        get_token();
        if(*token == '{') brace++;
        else if(*token == '}') brace--;
    }
    while(brace && tok != END);

    if(tok==END) throw InterpExc(UNBAL_BRACES);
}

// Determine if an identifier is a variable. Return
// true if variable is found; false otherwise.
bool is_var(char *vname)
{
    // See if vname a local variable.
    if(!local_var_stack.empty())
        for(int i=local_var_stack.size()-1;
                i >= func_call_stack.top(); i--)
        {
            if(!strcmp(local_var_stack[i].var_name, vname))
                return true;
        }

    // See if vname is a global variable.
    for(unsigned i=0; i < global_vars.size(); i++)
        if(!strcmp(global_vars[i].var_name, vname))
            return true;

    return false;
}

// Return the type of variable.
token_ireps find_var_type(char *vname)
{
    // First, see if it's a local variable.
    if(!local_var_stack.empty())
        for(int i=local_var_stack.size()-1;
                i >= func_call_stack.top(); i--)
        {
            if(!strcmp(local_var_stack[i].var_name, vname))
                return local_var_stack[i].v_type;
        }

    // Otherwise, try global vars.
    for(unsigned i=0; i < global_vars.size(); i++)
        if(!strcmp(global_vars[i].var_name, vname))
            return local_var_stack[i].v_type;

    return UNDEFTOK;
}

listing 3
// ***** Internal Library Functions *****

// Add more of your own, here.

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include "mccommon.h"

using namespace std;

// Read a character from the console.
// If your compiler supplies an unbuffered
// character intput function, feel free to
// substitute it for the call to cin.get().
int call_getchar()
{
    char ch;

    ch = getchar();

    // Advance past ()
    get_token();
    if(*token != '(')
        throw InterpExc(PAREN_EXPECTED);

    get_token();
    if(*token != ')')
        throw InterpExc(PAREN_EXPECTED);

    return ch;
}

// Write a character to the display.
int call_putchar()
{
    int value;

    eval_exp(value);

    putchar(value);

    return value;
}

// Return absolute value.
int call_abs()
{
    int val;

    eval_exp(val);

    val = abs(val);

    return val;
}

// Return a randome integer.
int call_rand()
{

    // Advance past ()
    get_token();
    if(*token != '(')
        throw InterpExc(PAREN_EXPECTED);

    get_token();
    if(*token != ')')
        throw InterpExc(PAREN_EXPECTED);

    return rand();
}

listing 4
// Common declarations used by parser.cpp, minicpp.cpp,
// or libcpp.cpp, or by other files that you might add.
//
const int MAX_T_LEN  = 128;   // max token length
const int MAX_ID_LEN = 31;    // max identifier length
const int PROG_SIZE  = 10000; // max program size
const int NUM_PARAMS = 31;    // max number of parameters

// Enumeration of token types.
enum tok_types { UNDEFTT, DELIMITER, IDENTIFIER,
                 NUMBER, KEYWORD, TEMP, STRING, BLOCK
               };

// Enumeration of internal representation of tokens.
enum token_ireps { UNDEFTOK, ARG, CHAR, INT, SWITCH,
                   CASE, IF, ELSE, FOR, DO, WHILE, BREAK,
                   RETURN, COUT, CIN, END
                 };

// Enumeration of two-character operators, such as <=.
enum double_ops { LT=1, LE, GT, GE, EQ, NE, LS, RS, INC, DEC };

// These are the constants used when throwing a
// syntax error exception.
//
// NOTE: SYNTAX is a generic error message used when
// nothing else seems appropriate.
enum error_msg
{
    SYNTAX, NO_EXP, NOT_VAR, DUP_VAR, DUP_FUNC,
    SEMI_EXPECTED, UNBAL_BRACES, FUNC_UNDEF,
    TYPE_EXPECTED, RET_NOCALL, PAREN_EXPECTED,
    WHILE_EXPECTED, QUOTE_EXPECTED, DIV_BY_ZERO,
    BRACE_EXPECTED, COLON_EXPECTED
};

extern char *prog;  // current location in source code
extern char *p_buf; // points to start of program buffer

extern char token[MAX_T_LEN+1]; // string version of token
extern tok_types token_type; // contains type of token
extern token_ireps tok; // internal representation of token

extern int ret_value; // function return value

extern bool breakfound; // true if break encountered

// Exception class for Mini C++.
class InterpExc
{
    error_msg err;
public:
    InterpExc(error_msg e)
    {
        err = e;
    }
    error_msg get_err()
    {
        return err;
    }
};

// Interpreter prototypes.
void prescan();
void decl_global();
void call();
void putback();
void decl_local();
void exec_if();
void find_eob();
void exec_for();
void exec_switch();
void get_params();
void get_args();
void exec_while();
void exec_do();
void exec_cout();
void exec_cin();
void assign_var(char *var_name, int value);
bool load_program(char *p, char *fname);
int find_var(char *s);
void interp();
void func_ret();
char *find_func(char *name);
bool is_var(char *s);
token_ireps find_var_type(char *s);

// Parser prototypes.
void eval_exp(int &value);
void eval_exp0(int &value);
void eval_exp1(int &value);
void eval_exp2(int &value);
void eval_exp3(int &value);
void eval_exp4(int &value);
void eval_exp5(int &value);
void atom(int &value);
void sntx_err(error_msg error);
void putback();
bool isdelim(char c);
token_ireps look_up(char *s);
int find_var(char *s);
tok_types get_token();
int internal_func(char *s);
bool is_var(char *s);

// "Standard library" prototypes.
int call_getchar();
int call_putchar();
int call_abs();
int call_rand();

listing 5
/* Mini C++ Demonstration Program #1.

   This program demonstrates all features
   of C++ that are recognized by Mini C++.
*/

int i, j; // global vars
char ch;

int main()
{
    int i, j; // local vars

    // Call a "standard library' function.
    cout << "Mini C++ Demo Program.\n\n";

    // Call a programmer-defined function.
    print_alpha();

    cout << "\n";

    // Demonstrate do and for loops.
    cout << "Use loops.\n";
    do
    {
        cout << "Enter a number (0 to quit): ";
        cin >> i;

        // Demonstrate the if
        if(i < 0 )
        {
            cout << "Numbers must be positive, try again.\n";
        }
        else
        {
            for(j = 0; j <= i; ++j)
            {
                cout << j << " summed is ";
                cout << sum(j) << "\n";
            }
        }
    }
    while(i != 0);

    cout << "\n";

    // Demonstrate the break in a loop.
    cout << "Break from a loop.\n";
    for(i=0; i < 100; i++)
    {
        cout << i << "\n";
        if(i == 5)
        {
            cout << "Breaking out of loop.\n";
            break;
        }
    }

    cout << "\n";

    // Demonstrate the switch
    cout << "Use a switch.\n";
    for(i=0; i < 6; i++)
    {
        switch(i)
        {
        case 1: // can stack cases
        case 0:
            cout << "1 or 0\n";
            break;
        case 2:
            cout << "two\n";
            break;
        case 3:
            cout << "three\n";
            break;
        case 4:
            cout << "four\n";
            cout << "4 * 4 is "<< 4*4 << "\n";
//        break; // this break is optional
            // no case for 5
        }
    }
    cout << "\n";

    cout << "Use a library function to generate "
         << "10 random integers.\n";
    for(i=0; i < 10; i++)
    {
        cout << rand() << " ";
    }

    cout << "\n";
    cout << "Done!\n";

    return 0;
}

// Sum the values between 0 and num.
// This function takes a parameter.
int sum(int num)
{
    int running_sum;

    running_sum = 0;

    while(num)
    {
        running_sum = running_sum + num;
        num--;
    }
    return running_sum;
}

// Print the alphabet.
int print_alpha()
{
    cout << "This is the alphabet:\n";

    for(ch = 'A'; ch<='Z'; ch++)
    {
        putchar(ch);
    }
    cout << "\n";

    return 0;
}

listing 6
// Nested loop example.
int main()
{
    int i, j, k;

    for(i = 0; i < 5; i = i + 1)
    {
        for(j = 0; j < 3; j = j + 1)
        {
            for(k = 3; k ; k = k - 1)
            {
                cout << i <<", ";
                cout << j << ", ";
                cout << k << "\n";
            }
        }
    }

    cout << "done";

    return 0;
}

listing 7
// Assigments as operations.
int main()
{
    int a, b;

    a = b = 5;

    cout << a << " " << b << "\n";

    while(a=a-1)
    {
        cout << a << " ";
        do
        {
            cout << b << " ";
        }
        while((b=b-1) > -5);
        cout << "\n";
    }

    return 0;
}

listing 8
// This program demonstrates a recursive function.

// A recursive function that returns the
// factorial of i.
int factr(int i)
{
    if(i<2)
    {
        return 1;
    }
    else
    {
        return i * factr(i-1);
    }
}

int main()
{
    cout << "Factorial of 4 is: ";
    cout << factr(4) << "\n";

    cout << "Factorial of 6 is: ";
    cout << factr(6) << "\n";

    return 0;
}

listing 9
// A more rigorous example of function arguments.

int f1(int a, int b)
{
    int count;

    cout << "Args for f1 are ";
    cout << a << " " << b << "\n";

    count = a;
    do
    {
        cout << count << " ";
    }
    while(count=count-1);

    cout << a << " " << b
         << " " << a*b << "\n";

    return a*b;
}

int f2(int a, int x, int y)
{
    cout << "Args for f2 are ";
    cout << a << " " << x << " "
         << y << "\n";
    cout << x / a << " ";
    cout << y*x << "\n";

    return 0;
}

int main()
{
    f2(10, f1(10, 20), 99);

    return 0;
}

listing 10
// Exercise the loop statements.
int main()
{
    int a;
    char ch;

    // The while.
    cout << "Enter a number: ";
    cin >> a;
    while(a)
    {
        cout << a*a << " ";
        --a;
    }
    cout << "\n";

    // The do-while.
    cout << "\nEnter characters, 'q' to quit.\n";
    do
    {
        // Use two "standard library" functions.
        ch = getchar();
        putchar(ch);
    }
    while(ch != 'q');
    cout << "\n\n";

    // the for.
    for(a=0; a<10; ++a)
    {
        cout << a << " ";
    }

    cout << "\n\nDone!\n";

    return 0;
}

listing 11
// Demonstrate nested scopes.

int x; // global x

int main()
{
    int i;

    i = 4;

    x = 99; // global x is 99

    if(i == 4)
    {
        int x; // local x
        int num; // local to if statement

        x = i * 2;
        cout << "Outer local x before loop: "
             << x << "\n";

        while(x--)
        {
            int x; // another local x

            x = 18;
            cout << "Inner local x: " << x << "\n";
        }

        cout << "Outer local x after loop: "
             << x << "\n";
    }

    // Can't refer to num here because it is local
    // to the preceding if block.
//  num = 10;

    cout << "Global x: " << x << "\n";
}

~

posted on 2012-09-01 23:27  Jackiesteed  阅读(1817)  评论(0编辑  收藏  举报