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

  }

  catch (exception& e) {

    cerr << e.what() << endl; // выводим сообщение об ошибке

    clean_up_mess();

  }

}

Мы просто поместили цикл

while
в блоке
try
, который выводит сообщения об ошибке и устраняет неисправности. После этого работу можно продолжать по-прежнему. Что означает выражение “устранить неисправность”? В принципе готовность к выполнению вычислений после исправления ошибки означает, что все данные находятся в полном порядке и вполне предсказуемы. В калькуляторе единственные данные за пределами отдельных функций находятся в потоке
Token_stream
. Следовательно, мы должны убедиться, что в потоке нет лексем, связанных с прекращенными вычислениями и способных помешать будущим вычислениям.

Рассмотрим пример.

1++2*3; 4+5;

Эти выражения вызывают ошибку, и лексемы

2*3; 4+5
останутся в буферах потоков
Token_stream
и
cin
после того, как второй символ
+
породит исключение.

У нас есть две возможности.

1. Удалить все лексемы из потока

Token_stream
.

2. Удалить из потока все лексемы

Token_stream
, связанные с текущими вычислениями.

В первом случае отбрасываем все лексемы (включая

4+5;
), а во втором — отбрасываем только лексему
2*3
, оставляя лексему
4+5
для последующего вычисления. Один выбор является разумным, а второй может удивить пользователя. Обе альтернативы одинаково просто реализуются. Мы предпочли второй вариант, поскольку его проще протестировать. Он выглядит проще. Чтение лексем выполняется функцией
get()
, поэтому можно написать функцию
clean_up_mess()
, имеющую примерно такой вид:

void clean_up_mess() // наивно

{

  while (true) { // пропускаем,

                 // пока не обнаружим инструкцию "печать"

    Token t = ts.get();

    if (t.kind == print) return;

  }

}

К сожалению, эта функция не всегда работает хорошо. Почему? Рассмотрим следующий вариант:

1@z; 1+3;

Символ

@
приводит нас к разделу
catch
в цикле
while
. Тогда для выявления следующей точки с запятой вызываем функцию
clean_up_mess()
. Функция
clean_up_mess()
вызывает функцию
get()
и считывает символ
z
. Это порождает следующую ошибку (поскольку символ
z
не является лексемой), и мы снова оказываемся в блоке
catch
внутри функции
main()
и выходим из программы. Ой! У нас теперь нет шансов вычислить лексему
1+3
. Вернитесь к меловой доске!

Можно было бы уточнить содержание блоков

try
и
catch
, но это внесет в программу еще большую путаницу. Ошибки в принципе трудно обрабатывать, а ошибки, возникающие при обработке других ошибок, обрабатывать еще труднее. Поэтому стоит попытаться найти способ удалять из потока
Token_stream
символы, которые могут породить исключение. Единственный путь для ввода данных в калькулятор пролегает через функцию
get()
, и он может, как мы только что выяснили, порождать исключения. Таким образом, необходима новая операция. Очевидно, что ее целесообразно поместить в класс
Token_stream
.

class Token_stream {

public:

  Token_stream(); // создает поток Token_stream, считывающий

                  // данные из потока cin

  Token get();    // считывает лексему

  void putback(Token t); // возвращает лексему

  void ignore(char c);   // отбрасывает символы,

                         // предшествующие символу с включительно

private:

  bool full;             // есть лексема в буфере?

  Token buffer; // здесь хранится лексема, которая возвращается

                // назад с помощью функции putback()

};

Функция

ignore()
должна быть членом класса
Token_stream
, так как она должна иметь доступ к его буферу. Мы выбрали в качестве искомого символа аргумент функции
ignore()
. Помимо всего прочего, объект класса
Token_stream
не обязан знать, что калькулятор считает хорошим символом для исправления ошибок. Мы решили, что этот аргумент должен быть символом, потому что не хотим рисковать, работая с составными лексемами (мы уже видели, что при этом происходит). Итак, мы получаем следующую функцию:

void Token_stream::ignore(char c)

  // символ c обозначает разновидность лексем

{

  // сначала проверяем буфер:

  if (full && c==buffer.kind) {

    full = false;

    return;

  }

  full = false;

  // теперь проверяем входные данные:

  char ch = 0;

  while (cin>>ch)

    if (ch==c) return;

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