Читать «Полный справочник по С++» онлайн - страница 444

Герберт Шилдт

II Возвращает значение переменной, double parser::find_var(char *s)

{

if(!isalpha(*s)){ serror(1); return 0.0;

}

return vars[toupper(*token)-'A'];

}

Функция допускает длинные имена переменных, но учитывает лишь их первый символ. Читатели могут модифицировать ее по своему усмотрению.

Кроме того, нам необходимо изменить функцию atom(), чтобы она могла обрабатывать не только числа, но и переменные. Вот как выглядит ее новая версия.

I II Извлекает число или значение переменной.

■ void parser::atom(double ^result)

!l switch (tok_type) { case VARIABLE:

result = find_var (token) get_token() ;

I return;

case NUMBER:

result = atof(token); get_token(); return; default: serror(0);

С технической точки зрения этих изменений вполне достаточно, чтобы выполнить синтаксический анализ выражений, содержащих переменные. Однако у нас еще нет функции, присваивающей переменным их значения. Довольно часто эта операция выполняется за пределами синтаксического анализатора, однако мы создадим свой оператор присваивания и сделаем его частью класса parser. Это можно выполнить по-разному. Во-первых, можно добавить в класс parser функцию eval_expl(). Теперь цепочка рекурсивного нисходящего анализа будет начинаться с нее. Это значит, что в начале синтаксического анализа должна вызываться функция eval_expl (), а не функция eval_ejq?2 ().

Рассмотрим определение функции eval_expi().

!11 Присваивание.

void parser::eval_expl(double ^result)

int slot;

char ttok_type;

char temp_token[80] ,

i f(tok_type==VARIABLE) {

II Сохраняем старую лексему, strcpy(temp_token, token); ttok_type = tok_type;

II Вычисляем индекс переменной, slot = toupper(*token) - 'A';

get_token()

if(*token != '=') {

putbackO; II Возвращаем текущую лексему II Восстанавливаем старую лексему -

ill присваивание не выполняется, strcpy(token, temp_token); tok_type = ttok_type;

else {

get_token(); // Извлекаем следующую часть выражения exp. eval_exp2(result),-vars[slot] = result; return;

}

}

eval_exp2(result);

}

Очевидно, что эта функция предварительно просматривает выражение, чтобы определить, действительно ли следует выполнить присваивание. Это необходимо делать потому, что имя переменной всегда предшествует оператору присваивания, но само по себе не гарантирует, что за ним обязательно следует оператор присваивания. Иначе говоря, анализатор распознает выражение А=100 как операцию присваивания и может отличить его от выражения А/10. Для этого функция eval_expl () считывает из входного потока следующую лексему. Если она не содержит знака равенства, лексема возвращается во входной поток с помощью функции putback(), которая является частью класса parser.

II Возвращает лексему во входной поток, void parser::putback()

{

char *t; t = token;

for(; *t; t++) exp_ptr—;

}

После сделанных изменений, класс parser принимает следующий вид.