|
|
|
| |
Главная Программирование С++ Интерпретатор Mini С++
Интерпретатор — это средство выполнения программы на С++. Перед тем как перейти непосредственно к коду, было бы полезно уяснить, как вообще работает интерпретатор. Понять код интерпретатора во многих отношениях легче, чем код анализатора выражений, потому что суть процесса интерпретации можно описать с помощью следующего алгоритма: while(есть_лексемы) { получи следующую лексему; выполни назначенное действие; } Этот алгоритм может показаться невероятно простым по сравнению с алгоритмом анализатора выражений, но он в точности описывает то, что делают все интерпретаторы! Правда, следует помнить, что шаг "выполни назначенное действие" может включать чтение дополнительных лексем из входного потока и выполнение нового назначенного действия. Таким образом, шаг "выполни назначенное действие" может быть рекурсивным. Для того чтобы понять, как работает алгоритм, давайте вручную проинтерпретируем приведенный далее фрагмент кода. int а; а = 10 * num; if(а < 100) { cout « а: > Следуя алгоритму, прочитаем первую лексему int. Назначенное действие для этой лексемы — извлечь следующую лексему для того, чтобы выяснить имя объявляемой переменной (в данном случае, а) и затем запомнить его. Следующая лексема — точка с запятой, завершающая строку. Назначенное действие — игнорировать ее. Теперь получим следующую лексему — а. Поскольку в начале оператора нет ключевого слова, значит, это начало выражения. Назначенное действие — вычислить выражение, используя синтаксический анализатор выражений. Этот процесс поглощает все лексемы данного оператора. Далее читаем лексему if. Она сигнализирует о начале условного оператора, поэтому назначенное действие — обработать оператор if, что означает вычислить условное выражение. Если результат выражения равен true, выполняется блок кода, входящий в состав оператора if. В противном случае он пропускается. Только что описанный процесс характерен для любой программы на языке С++. Интерпретация прекращается, когда прочитана последняя лексема или обнаружена синтаксическая ошибка. Давайте рассмотрим интерпретатор, учитывая базовый алгоритм. В листинге 9.2 приведен полный код интерпретатора из файла minicpp.cpp. В следующих разделах он будет обсуждаться подробно. Листинг 9.2. Интерпретатор Mini С++ #include <iostream> #include <fstream> #include <new> #include <stack> #include <vector> #include <cstring> #include <cstdlib> #include <cctype> #include "mccommon.h" using namespace std; char *prog; // текущая точка выполнения в исходном коде char *p_buf; // указывает на начало буфера программы // Эта структура инкапсулирует информацию, // связанную с переменными, struct var_type { char var_name [MAX_ID_LEN+1 ]; // имя token_ireps v_type; // тип данных int value; // значение >; // Этот вектор содержит информацию о глобальных переменных. vector<var_type> global_vars; // Этот вектор содержит информацию о локальных переменных //и параметрах. vector<var_type> local_var_stack; // Эта структура инкапсулирует данные о функции, struct func_type { char f unc_name [MAX_ID_LEN+1 ]; // имя token_ireps ret_type; // тип возвращаемого значения char *loc; // положение точки входа в программе }; // Этот вектор содержит информацию о функциях. vector<func_type> func_table; // Стек для управления областью видимости функции. stack<int> func_call_stack; // Стек для управления вложенными областями. stack<int> nest_scope_stack; char token[MAX_T_LEN+1]; // текущая лексема tok_types token_type; // тип лексемы token_ireps tok; // внутреннее представление int ret_value; // значение, возвращаемое функцией bool breakfound = false; // true, если обнаружен оператор break int main(int argc, char *argv[]) { if(argc != 2) cout « "Usage: minicpp <filename>\n"; return 1; } // Выделяет память для программы, try { p_buf = new char[PROG_SIZE]; } atch (bad_alloc exc) { cout « "Could Not Allocate Program Buffer\n"; return 1; } // Загружает программу для выполнения. i f(!loacLprogram(p_buf, argv[1])) return 1; // Устанавливает указатель программы на начало программного буфера, prog = p_buf; try { // Находит местоположение всех функций //и глобальных переменных в программе. prescanO ; // Затем подготавливает вызов функции main О . // Находит начальную точку программы, prog = find_func("main"); // Проверяет некорректность функции main() // или ее отсутствие, if(!prog) { cout « "main() Not FoundNn"; return 1; } // Возвращается к открывающей скобке {. prog--; // Устанавливает значение первой лексемы, равное main, strcpy(token, "main"); // Вызывает main(), чтобы начать интерпретацию. callO; } catch(InterpExc exc) { sntx_err(exc.get_err()); return 1; ) catch(bad_alloc exc) { cout « "Out Of Memory\n"; return 1; } return ret_value; } // Загружает программу. bool load_program(char *р, char * fname) { int i=0; ifstream in(fname, ios::in | ios::binary); if(!in) { cout « "Cannot Open file.\n"; return false; } do { *p = in.getO; p++; i++; } while(!in.eof() && i < PROGJSIZE); if(i == PROGLSIZE) { cout « "Program Too Big\n"; return false; } // Завершает нулем код программы. Пропускает любую метку // EOF, присутствующую в файле. if(*(p-2) == Oxla) *(р-2) = '\0'; else *(р-1) = '\0'; in.close(); return true; > // Находит местоположение всех функций в программе //и запоминает глобальные переменные, void prescanO { char *р, *tp; char temp[MAX_ID_LEN+1]; token_ireps datatype; func_type ft; // Если brace равна 0, текущая позиция, в коде // находится за пределами любой функции, int brace = 0; р = prog; do { // Обходит код тела функции while(brace) { get_token(); if(tok == END) throw InterpExc(UNBAL_BRACES); if(*token == '{') brace++; if(*token == '}') brace—; } tp = prog; // сохраняет текущую позицию get_token(); // Проверяет, не тип ли глобальной переменной или возвращаемого // значения функции. if(tok==CHAR || tok==INT) { datatype = tok; // сохраняет тип данных get_token(); if(token_type == IDENTIFIER) { s trcpy(temp, token); get_token(); if(*token != '(') I // должна быть глобальная переменная prog = tp; // возвращается к началу объявления decl^global(); . ) else if(*token == '(') { // должна быть функция // Проверяет, не определена ли уже функция, for(unsigned i=0; i < func_table.size(); i++) i f(!s trcmp (func_table[i].func_name, temp)) throw InterpExc(DUP_FUNC); ft.loc = prog; ft.ret_type = datatype; s trcpy(ft.func_name, temp); func_table. push_back (f t) ; do { get_token(); } while(*token != ')•); // Теперь следующей лексемой будет открывающая // фигурная скобка тела функции. } else putback(); } } else { if(*to"ken == '{') brace++; if(*token == *}') brace—; ) } while(tok != END); k if(brace) throw InterpExc(UNBAL_BRACES); prog = p; } // Интерпретирует отдельный оператор или блок кода. Функция // interp() возвращается из первоначального вызова, если обнаружена // конечная фигурная скобка (или return) в функции main(). void interpO { int value; int block = 0; do { // He интерпретирует, пока не обработан break, if(breakfound) return; token_type = get_token () ; // Определяет, какая лексема представлена. if(token_type == IDENTIFIER || *token == INC || *token == DEC) { //He ключевое слово, поэтому обрабатывает выражение. putbackO; // возвращает лексему во входной поток // для последующей обработки с помощью eval_exp() eval_exp(value); // обрабатывает выражение if(*token != ';')?throw InterpExc(SEMI_EXPECTED); } else if(token_type==BLOCK) { // ограничитель блока if(*token =="){// блок block =1; // интерпретирует блок, а не оператор // Записывает вложенную область видимости. nest_scope_stack.push(local_var_stack.size()); } else { // фигурная скобка }, поэтому восстанавливает область // видимости и возвращается // Восстанавливает вложенную область видимости. local_var_stack.resize(nest_scope_stack.top()); nes t_scope_stack.pop(); return; } } else // ключевое слово switch (tok) { case CHAR: case INT: // объявляет локальные переменные putback(); decl_local(); break; case RETURN: // возвращается из вызванной функции func_ret() ; return; case IF: // обрабатывает оператор if exec_if(); break; case ELSE: // обрабатывает оператор else find_eob(); // находит конец блока else //и продолжает выполнение break; case WHILE: // обрабатьшает цикл while exec_while(); break; case DO: // обрабатывает цикл do-while exec_do(); break; case FOR: // обрабатывает цикл for exec_for(); break; case BREAK: // обрабатывает break breakfound = true; // Восстанавливает вложенную область видимости. local_var_stack.resize(nest_scope_stack.top()); nest_scope_stack.pop(); return; case SWITCH: // обрабатывает оператор switch exec_switch () ; break; case COUT: // обрабатывает консольный вывод exec_cout(); break; case CIN: // обрабатывает консольный ввод exec_cin() ; break; case END: exit(O); } } while (tok != END && block); return; } // Возвращает точку входа заданной функции. // Возвращает NULL, если не найдена, char *find_func(char *name) { unsigned i; for(i=0; i < func_table.size(); i++) i f (! s t rcmp (name, f unc_tabl e [ i ]. f unc_name)) return func_table[i].loc; return NULL; } // Объявление глобальной переменной. void decl_global() { token_ireps vartype; var_type vt; get_token(); // получает тип vartype = tok; // сохраняет тип переменной // Обрабатывает список, разделенный запятыми, do { vt.v_type = vartype; vt.value =0; // инициализирует с нулевым значением get_token(); // получает имя // Проверяет, не дублируется ли переменная, for(unsigned i=0; i < global_vars.size(); i++) i f(!s trcmp(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); } // Объявление локальной переменной. void decl_local() { var_type vt; get_token(); // получает тип переменной vt.v_type = tok; // запоминает тип vt.value =0; // инициализирует переменную с нулевым значением // Обрабатывает список, разделенный запятыми, do I get_token(); // get var name // Проверяет, нет ли уже в этой области видимости переменной //с тем же именем, что и у локальной переменной, 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); } s trcpy(vt.var_name, token); local_var_s tack~. push_back (vt) ; get_token(); } while(*token == ',*); if(*token != •;•) throw InterpExc(SEMI_EXPECTED); } // Вызывает функцию. void call() { char *loc, *temp; int lvartemp; // Сначала находит точку входа функции, loc = find_func(token); if(loc == NULL) throw InterpExc(FUNC_UNDEF); // функция не определена else { // Сохраняет индекс стека локальных переменных, lvartemp = local_var_stack.size(); get_args(); // получает аргументы функции temp = prog; // сохраняет местоположение return func_.call_stack.push(lvartemp); // заносит в стек индекс локальной // переменной prog = loc; // переустанавливает prog на начало функции get_params(); // загружает параметры функции //со значениями аргументов interpO; // интерпретирует функцию prog = temp; // переустанавливает программный указатель if(func_call_stack.empty()) throw InterpExc(RETJNOCALL); // Восстанавливает прежнее состояние local_var_stack. local_var_stack.resize(func_call_stack.top()); f unc_cal l_s tack. pop () ; } } // Заносит аргументы функции в стек // локальных переменных, void get_args() { int value, count, temp[NUM_PARAMS] ; var_type vt; count = 0; get_token(); if(*token '= '(') throw InterpExc(PAREN_EXPECTED); // Обрабатывает разделенный запятыми список значений, do { eval_exp(value); temp [covint] = value; // сохраняет временно get_token(); count++; } while(*token == *, '); count—; // Теперь заносит в local_var_stack в обратном порядке. for(; count>=0; count—) { vt.value = temp [count]; vt.v_type = ARG; 1ocai_var_stack.push_back(vt); } } // Получает параметры функции. void get_params() { var_type *p; int i; i = local_var_stack.size()-1; // Обрабатывает разделенный запятыми список параметров, do { get_token(); р = &local_var_stack[i]; if(*token •=')'){ if (tok != INT ScSc tok != CHAR) throw InterpExc(TYPE_EXPECTED); p->v_type = tok; get_token(); // Связывает имя параметра с аргументом, который уже в // стеке локальных переменных. strcpy(p->var_name, token); get_token(); ( i—; } else break; } while(*token == ','); if(*token != •)') throw InterpExc(PAREN_EXPECTED); } // Обработка возврата из функции. void func_ret() { int value; value = 0; // Получает значение return, если есть. eval_exp(value); ret_value = value; } // Присваивает значение переменной, void assign_var(char *vname, int value) { // Сначала проверяет, не локальная ли это переменная, if(!local_var_stack.empty()) for4dnt i=local_var_stack.size()-1; i >= func_call_stack.top(); i—) { i f(!strcmp(1ocal_var_s tack[i].var_name. vname)) { if(local_var_stackIi].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; } } // В противном случае проверяет глобальные переменные for(unsigned i=0; i < global_vars.size(); i++) if (!scrcmp(global_vars [i] .varname, 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); // переменная не найдена } // Находит значение переменной. int find_var(char *vname) { // Сначала проверяет, не локальная ли переменная, if(!local_var_stack.empty()) for(int i=local_var_s tack.s ize()-1; i >= func_call_stack.top(); i—) { i f(!s trcmp (local_var_s tack[i].var_name, vname)) return local_var_stack[i].value; } // В противном случае проверяет глобальные переменные, for(unsigned i=0; i < global_vars.size(); i++) i f(!s trcmp (global_vars[i].var_name, vname)) return global_vars[i].value; throw InterpExc(NOT_VAR); // переменная не найдена } // Выполняет оператор if. void exec_ifО { int cond; eval_exp(cond); // получает выражение оператора if. if(cond) { // если true, обрабатывает блок в составе IF '// Подтверждает начало блока. if(*token != '{') throw InterpExc(BRACE_EXPECTED); interp(); } else { // В противном случае пропускает блок IF и // обрабатывает ELSE, если он есть. find_eob(); // находит начало следующей строки get_token(); if(tok != ELSE) ( // Возвращает лексему, если нет ELSE. putback(); return; } // Подтверждает начало блока. get_token(); if(*token != '{') throw InterpExc(BRACE_EXPECTED); putback(); interp(); ) } // Выполняет оператор switch. void exec_switch () { int sval, cval; int brace; eval_exp(sval); // Получает выражение switch. // Проверяет начало блока. if(*token != '{') throw InterpExc(BRACE_EXPECTED); // Записывает новую область видимости. nest_scope_stack.push(local_var_stack.size()); // Теперь проверяет варианты case. for(;;) { brace = 1; // Находит вариант case, do { get_token(); if(*token == '{') brace++; else if(*token == '}') brace—; } while(tok != CASE && tok != END && brace); // Если соответствующего варианта case не найдено, то пропускает, i f(!brace) break; if(tok == END) throw InterpExc(SYNTAX); // Получает значение варианта case. eval_exp (cval) ; // Читает и отбрасывает ":и. get_token(); if(*token != *:1) throw InterpExc(COLON_EXPECTED); // Если значения совпадают, то интерпретирует, if(cval == sval) { brace = 1; do { interp(); if(*token == '{') brace++; else if(*token == '}') brace—; } while(Ibreakfound && tok != END && brace); II Находит конец оператора switch, while(brace) { get_token(); if(*token == '{') brace++; else if(*token == '}') brace—; J breakfound = falser-break; } } } // Выполняет цикл while. void exec_while() { int cond; char *cerap; putbackO; // возвращает во входной поток лексему while temp = prog; // сохраняет адрес начала цикла while get_token(); eval_exp(cond); // проверяет условное выражение // Подтверждает начало блока. if(*token != '{') throw InterpExc(BRACE_EXPECTED); if(cond) interpO; // если true, интерпретирует else { // в противном случае переходит к концу цикла find_eob(); return; } prog = temp; // возвращается к началу // Ищет оператор break в цикле. if(breakfound) { // Ищет начало блока в цикле, do { get_token(); } while(*token != •{' tok != END); putback(); breakfound = false; find_eob(); // теперь ищет конец цикла return; } } // Выполняет цикл do. void exec_do() { int cond; char *temp; // Сохраняет местоположение начала цикла do. putbackO; // возвращает во входной поток лексему do temp = prog; get_token(); // получает начало блока цикла // Подтверждает начало блока. get_token(); if(*token != '{') throw InterpExc(BRACE_EXPECTED); putback(); interpO; // интерпретирует цикл // Ищет оператор break в цикле, if(breakfound) { prog = temp; // Находит начало блока в цикле, do { get_token(); } while(*token != '{' && tok != END); // Находит конец блока while, putback(); finc_eob(); // Теперь находит конец выражения while, 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); // проверяет условие цикла // Если true, повторяет цикл; в противном случае идет дальше, if(cond) prog = temp; } // Выполняет цикл for. void exec_for() { int cond; char *temp, *temp2; int paren ; get_token(); // пропускает открывающую скобку ( eval_exp(cond); // выражение инициализации if(*token != ';') throw InterpExc(SEMI_EXPECTED); prog++; // переходит за ";" в исходном коде temp = prog; for(;;) { // Получает значение условного выражения. eval_exp(cond); if(*token != ';*) throw InterpExc(SEMI_EXPECTED); prog++; // переходит за в исходном коде temp2 = prog; // Ищет начало блока for. paren = 1; while(paren) { get_token(); if(*token == '(') paren++; if(*token == ')') paren—; } // Подтверждает начало блока. get_token () ; if(*token != '{') throw InterpExc(BRACE_EXPECTED); putback(); // Если условие true, интерпретирует, if(cond) interp(); else { // в противном случае переходит к концу блока find_eob() ; return; } prog = temp2; // переходит к инкрементному выражению // Ищет оператор break в цикле, if(breakfound) { // Ищет начало блока в цикле. do { get_token () ; } while(*token != '{' && tok != END); putback(); breakfound = false; find_eob(); // теперь ищет конец цикла return; } // Вычисляет инкрементное выражение. eval_exp(cond); prog = temp; // возвращается к началу цикла } } // Выполняет оператор cout. void exec_cout() { int val; get_token(); if(*token != LS) throw InterpExc(SYNTAX); do { get_token(); if(token_type==STRING) { // Выводит строку, cout « token; ) else if(token_type == NUMBER || token_type == IDENTIFIER) { // Выводит число, putback(); eval_exp (val) ; cout « val; } else if(*token == ' \ ' ') { // выводит символьную константу, putback(); eval_exp(val); cout « (char) val; get_token(); } while(*token == LS); if(*token != ';') throw InterpExc(SEMI_EXPECTED); } // Выполняет оператор cin. 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_yar(token, chval); > else if(vtype == INT) { cin » val; assign_var(token, va1); } get_token(); } while(*token == RS); if(*token != ';') throw InterpExc(SEMI_EXPECTED); } } // Находит конец блока. 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); ) // Определяет, является ли идентификатор переменной. Возвращает // true, если найдена переменная; false — в противном случае, bool is_var(char * vname) { // Проверяет, является ли vname локальной переменной, 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; } // Проверяет, является ли vname глобальной переменной, for(unsigned i=0; i < global_vars.size(); i++) if(!strcrap(global_vars[i].var_name, vname)) return true; return false; // Возвращает тип переменной. token_ireps find_var_type(char *vname) { // Сначала проверяет, не локальная ли это переменная, if(!local_var_stack.empty()) for(int i=local_var_stack.size()-1; i >= func_call_stack.top(); i—) { i f(!strcmp(local_var_stack[i].var_name, vname)) return local_var_stack[i].v_type; } // В противном случае, не глобальная ли. 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; }
|
|
|
|
|