Мы реализуем класс
Token_stream
в разделе 6.8, как только увидим, как его следует использовать. Имея поток
Token_stream
, можем переписать функцию
expression()
так, чтобы она записывала неиспользованную лексему обратно в поток
Token_stream
.
double expression()
{
double left = term(); // считываем и вычисляем Терм
Token t = ts.get(); // получаем следующую лексему
// из потока лексем
while(true) {
switch(t.kind) {
case '+':
left += term(); // вычисляем и добавляем Терм
t = ts.get();
break;
case '–':
left –= term(); // вычисляем и вычитаем Терм
t = ts.get();
break;
default:
ts.putback(t); // помещаем объект t обратно
// в поток лексем
return left; // финал: символов + и – нет;
// возвращаем ответ
}
}
}
Кроме того, такие же изменения следует внести в функцию
term()
.
double term()
{
double left = primary();
Token t = ts.get(); // получаем следующую лексему
// из потока лексем
while(true) {
switch (t.kind) {
case '*':
left *= primary();
t = ts.get();
break;
case '/':
{
double d = primary();
if (d == 0) error("деление на нуль");
left /= d;
t = ts.get();
break;
}
default:
ts.putback(t); // помещаем объект t обратно в поток лексем
return left;
}
}
}
Для последней функции программы грамматического анализа
primary()
достаточно заменить функцию
get_token()
функцией
ts.get()
; функция
primary()
использует каждую лексему, которую она считывает.
6.7. Испытание второй версии
Итак, мы готовы к испытанию второй версии. Введем число
2
и символ перехода на новую строку. Нет ответа. Попробуйте ввести еще один символ перехода на новую строку, чтобы убедиться, что компьютер не завис. По-прежнему нет ответа. Введите число
3
и символ перехода на новую строку. Ответ равен
2
. Попробуйте ввести выражение
2+2
и символ перехода на новую строку. Ответ равен 3. Экран выглядит следующим образом:
2
3
=2
2+2
=3
Хм... Может быть, наша функция
putback()
и ее использование в функции
expression()
и
term()
не решает проблему. Попробуем другой тест.
2 3 4 2+3 2*3
= 2
= 3
= 4
= 5
Да! Это правильные ответы! Но последний ответ (
6
) пропущен. Проблема следующей лексемы не решена. Однако на этот раз она заключается не в том, что наш программный код “съедает” символы, а в том, что он вообще не получает информации, пока не будет введено следующее выражение. Результат вычисления выражения не выводится на экран немедленно; он откладывается до тех пор, пока программа не увидит первую лексему следующего выражения. К сожалению, программа не видит эту лексему, пока мы не нажмем клавишу <Enter> после следующего выражения. Эта программа на самом деле не настолько плоха, она просто немного медленно реагирует.
Как исправить этот недостаток? Очевидное решение — потребовать немедленно выполнить вывод. Договоримся считать, что каждое выражение завершается точкой с запятой, которая одновременно служит триггером вывода. Кроме того, добавим в программу команду выхода. Для этого подходит символ
q
(первая буква слова
quit
(выход)). Функция
main()
содержит инструкцию
while (cin) cout << "=" << expression() << '\n'; // version 1
Заменим ее более запутанной, но более полезной инструкцией.
double val = 0;
while (cin) {
Token t = ts.get();
if (t.kind == 'q') break; // 'q' для выхода
if (t.kind == ';') // ';' для команды "печатать немедленно"
cout << "=" << val << '\n';