Бьерн Страуструп.
Язык программирования С++
73
операцией присваивания, тогда как == есть операция сравнения.
Функция prim, обрабатывающая первичное, во многом похожа на
функции expr и term(). Но раз мы
дошли до низа в иерархии вызовов, то в ней кое-что придется сделать. Цикл для нее не нужен:
double number_value;
char name_string[256];
double prim() // обрабатывает первичное
{
switch (curr_tok) {
case NUMBER: // константа с плавающей точкой
get_token();
return
number_value;
case
NAME:
if (get_token() == ASSIGN) {
name*
n
=
insert(name_string);
get_token();
n->value
=
expr();
return
n->value;
}
return
look(name_string)->value;
case MINUS: // унарный минус
get_token();
return
-prim();
case
LP:
get_token();
double
e
=
expr();
if (curr_tok != RP) return error("
требуется )");
get_token();
return
e;
case
END:
return
1;
default:
return error("требуется первичное");
}
}
Когда появляется NUMBER (то есть константа с плавающей точкой), возвращается ее значение.
Функция ввода get_token() помещает значение константы в глобальную переменную number_value. Если
в программе используются глобальные переменные, то часто это указывает на то, что структура не до
конца проработана, и поэтому требуется некоторая оптимизация. Именно так обстоит дело в данном
случае. В идеале лексема должна состоять из двух частей: значения, определяющего вид лексемы (в
данной программе это token_value), и (если необходимо) собственно значения лексемы. Здесь же
имеется только одна простая переменная curr_tok, поэтому для хранения последнего прочитанного
значения NUMBER требуется глобальная переменная number_value. Такое решение проходит потому,
что калькулятор во всех вычислениях вначале выбирает одно число, а затем считывает другое из
входного потока. В качестве упражнения предлагается избавиться от этой излишней глобальной
переменной ($$3.5 [15]).
Если последнее значение NUMBER хранится в
глобальной переменной number_value, то строковое
представление последнего значения NAME хранится в name_string. Перед тем, как что-либо делать с
именем, калькулятор должен заглянуть вперед, чтобы выяснить, будет ли ему присваиваться значение,
или же будет только использоваться существующее его значение. В обоих случаях надо обратиться к
таблице имен. Эта таблица рассматривается в $$3.1.3; а здесь достаточно только знать, что она
состоит из записей, имеющих вид:
struct name {
char*
string;
name*
next;
double
value;
};
Бьерн Страуструп.
Язык программирования С++
74
Член next используется только служебными функциями, работающими с таблицей:
name* look(const char*);
name* insert(const char*);
Обе функции возвращают указатель на ту запись name, которая соответствует их параметру-строке.
Функция look() "ругается", если имя не было занесено в таблицу. Это
означает, что в калькуляторе
можно использовать имя без предварительного описания, но в первый раз оно может появиться только
в левой части присваивания.
Достарыңызбен бөлісу: