Литмир - Электронная Библиотека
A
A

Мы реализуем класс

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 &lt;&lt; &quot;=&quot; &lt;&lt; expression() &lt;&lt; '\n'; // version 1

Заменим ее более запутанной, но более полезной инструкцией.

double val = 0;

while (cin) {

  Token t = ts.get();

  if (t.kind == 'q') break; // 'q' для выхода

  if (t.kind == ';')        // ';' для команды &quot;печатать немедленно&quot;

    cout &lt;&lt; &quot;=&quot; &lt;&lt; val &lt;&lt; '\n';

87
{"b":"847443","o":1}