Главная arrow Программирование С++ arrow Вызов функций, определенных пользователем

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

Возможно, самая трудная часть реализации интерпретатора для языка С++ — это управление выполнением функций, определенных пользователем. И не только, потому что интерпретатору нужно начинать чтение исходного кода с новой позиции, а затем возвращаться в вызывающую процедуру после завершения функции, ему приходится иметь дело со следующими тремя задачами: передачей параметров, выделением памяти для них и возвратом значения из функции.
Все вызовы функций (за исключением начального вызова функции maino) выполняются через анализатор выражений из функции atom о с помощью вызова функции call (). Именно эта функция обрабатывает тонкости вызова функций. Далее приведен ее код. Давайте внимательно исследуем эту функцию.
// Вызывает функцию.
void call()
{
char *1ос,  *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(1vartemp); // заносит в стек индекс локальной // переменной
prog = loc; // переустанавливает prog на начало функции get_params(); // загружает параметры функции //со значениями аргументов
interpO; // интерпретирует функцию
prog = temp; // переустанавливает программный указатель
if(func_call_stack.empty()) throw InterpExc(RET_NOCALL);
// Восстанавливает прежнее состояние 1ocal_var_stack. local_var_stack.resize(func_call_stack.top()); func_call_stack.pop();
}
}
Первое, что делает функция call (), — находит местоположение в исходном коде точки входа заданной функции с помощью вызова функции find_func(). Далее она сохраняет текущий размер стека локальных переменных в переменной lvartemp. Затем вызывается функция get_args () для обработки любых аргументов функции. Функция get_args() читает разделенный запятыми список выражений и помещает их в стек локальных переменных в обратном порядке (выражения заносятся в стек в обратном порядке, потому что так их легче сопоставить с соответствующими им параметрами, когда функция интерпретируется). У помещаемых в стек значений нет имен. Имена даются параметрам функцией get_params (), к которой мы скоро вернемся.
Когда аргументы функции обработаны, текущее значение переменной prog сохраняется в переменной temp. Этот адрес — точка возврата функции. Далее значение lvartemp заносится в стек вызовов функций func_caii_stack. Его задача — запоминать значение индекса вершины стека локальных переменных при каждом вызове функций. Это значение представляет собой начальную точку в стеке локальных переменных для переменных (и параметров), относящихся к функции, которая вызывается. Значение в вершине стека вызовов функций используется для того, чтобы помешать функции обращаться к любой локальной переменной, не объявленной в ней. Следующие две строки кода устанавливают программный указатель на начало функции и с помощью вызова функции get_params() связывают имена ее формальных параметров со значениями аргументов, уже находящихся в стеке локальных переменных. Для этого функция get_params () читает каждый параметр и копирует его имя в соответствующий аргумент, уже помещенный В СТек ЛОКальНЫХ Переменных local_var_stack. Непосредственное выполнение функции осуществляется функцией interpO. Когда функция interp о возвращается, указатель программы (prog) устанавливается в точку возврата, а индексу стека локальных переменных присваивается значение, бывшее у него до вызова функции. Этот финальный шаг обеспечивает эффективное удаление из стека всех локальных переменных и параметров функции.
Если выполняемая функция содержит оператор return, функция interp о вызывает функцию func_ret() прежде чем вернуться в функцию call о. Эта функция, ее код приведен далее, обрабатывает любое возвращаемое значение.
// Обработка возврата из функции.
void func_ret()
{
int value; value = 0;
// Получает значение return, если есть. eval_exp(value); ret_value = value;
}
Глобальная переменная ret_value — это целочисленная переменная, содержащая возвращаемое функцией значение. На первый взгляд может показаться странным, что возвращаемое значение из функции evai_exp () получает локальная переменная value, а не глобальная переменная ret_value. Причина в том, что функция может быть рекурсивной и eval_exp () может понадобиться вызвать ту же самую функцию для получения результата. В этой ситуации для получения значения нельзя использовать глобальную переменную, так как она будет перезаписана.