Программирование >>  Формирование пользовательского контейнера 

1 ... 138 139 140 [ 141 ] 142 143 144 ... 156


vt.v type = tok; запоминает тип

vt.value =0; инициализирует переменную с нулевым значением

Обрабатывает список, разделенный запятыми, do {

get token{); получает имя var

Проверяет, нет ли уже переменной с тем же именем, что и у локальной переменной в этой области видимости, if(!local var stack.empty()) for(int i=local var stack.size()-1; i >= nest scope stack. topO ; i-)

i f(!s trcmp(local var s tack[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);

Каждый раз, когда встречается локальная переменная, ее имя, тип и значение (первоначально 0) помещаются в стек. Процесс протекает следующим образом. Сначала функция decl locai () читает тип объявленной переменной или переменных и устанавливает их начальное значение, равное нулю. Далее она входит в цикл, в котором читаются имена из разделенного запятыми списка идентификаторов. Каждый проход цикла помещает информацию об одной переменной в стек локальных переменных. Во время обработки проверяется, нет ли уже переменной с таким же именем в текущей области видимости (переменные, объявленные в текущей области видимости, находятся между текущей верщиной стека iосаi var stack и значением индекса, сохраненным в вершине стека nest scope stack). В конце выполняется проверка - для того, чтобы убедиться, что последняя лексема содержит точку с запятой.



Вызов функций, определенных пользователем

Возможно, самая трудная часть реализации интерпретатора для языка С++ - это управление выполнением функций, определенных пользователем. И не только, потому что интерпретатору нужно начинать чтение исходного кода с новой позиции, а затем возвращаться в вызывающую процедуру после завершения функции, ему приходится иметь дело со следующими тремя задачами: передачей параметров, выделением памяти для них и возвратом значения из функции.

Все вызовы функций (за исключением начального вызова функции maino) выполняются через анализатор выражений из функции atom о с помощью вызова функции call (). Именно эта функция обрабатывает тонкости вызова функций. Далее приведен ее код. Давайте внимательно исследуем эту функцию.

Вызывает функцию.

void call О

char *1ос, *teiip; int Ivartenp;

Сначала находит точку входа функции, loc = find func(token);

if(loc == NULL)

throw InterpExc(FUNC UNDEF); функция не определена else {

Сохраняет индекс стека локальных переменных. Ivartenp = local var stack.size();

get args(); получает аргументы функции

tenp = prog; сохраняет местоположение return

func call stack.push(1vartenp); заносит в стек индекс локальной переменной

prog = loc; переустанавливает prog на начало функции get params(); загружает параметры функции со значениями аргументов

interpO; интерпретирует функцию

prog = tenp; переустанавливает программный указатель



if{func call stack.empty()) throw InterpExc(RET NOCALL);

Восстанавливает прежнее состояние 1ocal var stack. local var stack.resize(func call stack.top()); func call stack.pop0;

Первое, что делает функция call (), - находит местоположение в исходном коде точки входа заданной функции с помощью вызова функции find func(). Далее она сохраняет текущий размер стека локальных переменных в переменной ivartemp. Затем вызывается функция get args () для обработки любых аргументов функции. Функция get args() читает разделенный запятыми список выражений и помещает их в стек локальных переменных в обратном порядке (выражения заносятся в стек в обратном порядке, потому что так их легче сопоставить с соответствующими им параметрами, когда функция интерпретируется). У помещаемых в стек значений нет имен. Имена даются параметрам функцией get params (), к которой мы скоро вернемся.

Когда аргументы функции обработаны, текущее значение переменной prog сохраняется в переменной temp. Этот адрес - точка возврата функции. Далее значение ivartemp заносится в стек вызовов функций func caii stack. Его задача - запоминать значение индекса вершины стека локальных переменных при каждом вызове функций. Это значение представляет собой начальную точку в стеке локальных переменных для переменных (и параметров), относящихся к функции, которая вызывается. Значение в вершине стека вызовов функций используется для того, чтобы помешать функции обращаться к любой локальной переменной, не объявленной в ней. Следующие две строки кода устанавливают программный указатель на начало функции и с помощью вызова функции get params() связывают имена ее формальных параметров со значениями аргументов, уже находящихся в стеке локальных переменных. Для этого функция get params () читает каждый параметр и копирует его имя в соответствующий аргумент, уже помещенный в стек локальных переменных local var stack. Непосредственное выполнение функции осуществляется функцией interpO. Когда функция interpO возвращается, указатель программы (prog) устанавливается в точку возврата, а индексу стека локальных переменных присваивается значение, бывшее у него до вызова функции. Этот финальный шаг обеспечивает эффективное удаление из стека всех локальных переменных и параметров функции.



1 ... 138 139 140 [ 141 ] 142 143 144 ... 156

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика