|
|
|
| |
Главная Программирование С++ Предварительный просмотр в интерпретаторе
Предварительный просмотр в интерпретаторе |
Прежде чем интерпретатор начнет выполнение программы, следует решить две важные организационные задачи. ? Должны быть найдены и инициализированы все глобальные переменные. ? Должно быть определено местоположение каждой функции в программе. Эти задачи выполняются в интерпретаторе процедурой предварительного просмотра. В Mini С++ весь выполняемый код сосредоточен внутри функций, поэтому интерпретатору незачем выходить за пределы функций. Но объявления глобальных переменных находятся вне функций. Следовательно, необходимо обработать эти объявления с помощью предварительного просмотра программы. У интерпретатора нет другого (эффективного) способа узнать о них. Для увеличения скорости выполнения важно (хотя в этом нет технической необходимости) знать местоположение каждой функции, определенной в программе, чтобы обеспечить их существенно более быстрый вызов. Если этот шаг не выполняется, для каждого вызова функции потребуется длительный последовательный поиск в исходном коде программы. Обнаружение точек входа для всех функций служит и другой цели. Как вы знаете, выполнение программы на С++ начинается не с первой, строки кода, а с запуска функции maino. Более того, не требуется, чтобы описание этой функции было первым в программе. Следовательно, необходимо найти местоположение функции main () в исходном коде для того, чтобы выполнение программы могло начаться с этой точки (помните также о том, что объявления глобальных переменных могут предшествовать функции maino, поэтому даже если она описана в исходном коде первой, это вовсе необязательно означает, что с первой строки кода). Поскольку процедура предварительного просмотра находит точки входа для всех функций, она определяет И ТОЧКУ ВХОДа ДЛЯ фуНКЦИИ main (). Функция, выполняющая предварительный просмотр программы, называется prescan (). Далее приведен ее код. // Находит местоположение всех функций в программе //и запоминает глобальные переменные, void prescan() { 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) { strcpy(temp, token); get_token(); if(*token != '(') { // должна быть глобальная переменная prog = tp; // возвращается к началу объявления decl_global(); } ' else if(*token == '(') { // должна быть функция // Проверяет, не определена ли уже функция, 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; s trcpy(ft.func_name, temp); f unc_table. push_back (f t) ; do { get_token(); } while(*token != *)'); // Теперь следующей лексемой должна быть открывающая // фигурная скобка тела функции. } else putback(); } } else { if(*token == '{') brace++; if(*token == '}') brace—; } } while(tok ! = END); if(brace) throw InterpExc(UNBAL_BRACES); prog = p; } Функция prescan о работает следующим образом. Каждый раз, когда обнаруживается открывающая фигурная скобка, переменная brace увеличивается на единицу. Когда же найдена закрывающая фигурная скобка, переменная brace уменьшается на единицу. Следовательно, если переменная brace больше нуля, текущая лексема читается из тела функции. Если же при обнаружении переменной brace равна нулю, пресканер знает, что найдена глобальная переменная. Точно так же, если при brace, равной нулю, обнаружено имя функции, то это должно быть определение функции (напоминаю, что интерпретатор Mini С++ не поддерживает прототипы функций). Глобальные переменные сохраняются в векторе, названном giobai_vars и содержащем структуры типа var_type, описание которых приведено далее. // Эта структура инкапсулирует информацию, // связанную с переменными, struct var_type { char var_name[MAX_ID_LEN+l]; // имя token_ireps v_type; // тип данных int value; // значение }; Приведенная структура содержит имя, значение и тип переменной. Глобальные переменные помещаются в вектор giobai_vars с помощью функции deci_giobai (), приведенной далее. // Объявление глобальной переменной 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++) if(!strcmp(global_vars[i].varjname, token)) throw InterpExc(DUP_VAR); s tr cpy (vt. var_name, token) ; global_vars. push_back (vt) ; get_token(); } while(*token == '.'); if(*token != ¦;') throw InterpExc(SEMI_EXPECTED); } По существу функция decl_giobal () получает тип и имя переменной, инициализирует ее с нулевым значением и помещает в конец вектора giobai_vars. Но сначала проверяется, нет ли уже объявленной переменной с таким же именем. Местоположение каждой функции, определенной пользователем, помещается в вектор, названный func_table и содержащий структуры типа func_type, описание которых приведено далее. // Эта структура инкапсулирует данные о функции, struct func_type { char func_name[MAX_ID_LEN+1]; // имя token_ireps ret_type; // тип возвращаемого значения char *loc; // положение точки входа в программе }; Каждый элемент вектора содержит тип возвращаемого значения, имя и местоположение точки входа функции в исходном коде. Прежде чем внести эти сведения в вектор, функция prescan о проверяет, не существует ли уже функции с тем же именем. Обратите внимание на то, что никакой информации о параметрах не сохраняется. Она будет получена во время выполнения, когда будет сделан действительный вызов функции.
|
|
|
|
|