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

вызывает ошибку Ожидается первичное выражение. Где следует искать эту ошибку? Конечно, в функции

main()
, где обрабатываются символы ; и q. Мы добавили инструкции “печать” и “выход” просто для того, чтобы поскорее получить работающий вариант калькулятора (см. раздел 6.6), а теперь расплачиваемся за эту поспешность. Рассмотрим еще раз следующий фрагмент:

double val = 0;

while (cin) {

  cout << "> ";

  Token t = ts.get();

  if (t.kind == 'q') break;

  if (t.kind == ';')

    cout << "= " << val << '\n';

  else

    ts.putback(t);

 val = expression();

}

Если обнаруживаем точку с запятой, то вызываем функцию

expression()
, не проверяя символ
q
. Эта функция в первую очередь ищет вызов функции
term()
, которая вызывает функцию
primary()
, обнаруживающую символ q. Буква q не является первичным выражением, поэтому получаем сообщение об ошибке. Итак, после тестирования точки с запятой мы должны обработать символ q. В этот момент мы почувствовали необходимость несколько упростить логику, поэтому окончательный вариант функции
main()
выглядит так:

int main()

try

{

  while (cin) {

    cout << "> ";

    Token t = ts.get();

    while (t.kind == ';') t=ts.get(); // считываем ';'

    if (t.kind == 'q') {

      keep_window_open();

      return 0;

    }

    ts.putback(t);

    cout << "= " << expression() << endl;

  }

  keep_window_open();

  return 0;

}

catch (exception& e) {

  cerr << e.what() << endl;

  keep_window_open("~~");

  return 1;

}

catch (...) {

  cerr << "exception \n";

  keep_window_open("~~");

  return 2;

}

Это повышает надежность обработки ошибок. Таким образом, теперь можно искать новые пути улучшения калькулятора.

7.4. Отрицательные числа

 Проверив калькулятор, легко убедиться, что он не слишком элегантно обрабатывает отрицательные числа. Например, выражение

–1/2

является ошибочным.

Для того чтобы калькулятор работал корректно, мы должны были бы написать

(0–1)/2

Однако это неприемлемо.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Обычно такие проблемы выявляются на поздних этапах отладки и тестирования. Только тогда можно увидеть, что на самом деле делает программа, и получить информацию, позволяющую уточнить исходные идеи. Планируя проект, целесообразно сэкономить время и извлечь выгоду из наших уроков. Очень часто первая версия поставляется пользователям без необходимых уточнений из-за напряженного расписания и жесткой стратегии управления, которая не позволяет вносить исправления в спецификацию на поздних этапах разработки. Поздние добавления — это кошмар менеджера. На самом деле, когда программа уже достаточно работоспособна, но еще не готова к поставке, еще не поздно внести дополнения; это самый первый момент, когда можно учесть опыт ее использования. Реалистичное расписание должно учитывать это обстоятельство.

В данном случае необходимо внести исправления в грамматику, чтобы предусмотреть унарный минус. На первый взгляд легче всего внести исправления в пункт Первичное выражение. Сейчас он выглядит так:

Первичное выражение:

  Число

  "("Выражение")"

Нам требуется, чтобы этот пункт выглядел примерно таким образом:

Первичное выражение:

  Число

  "("Выражение")"

  "–" Первичное выражение

  "+" Первичное выражение

Мы добавили унарный плюс, поскольку он есть в языке С++. Если есть унарный минус, то легче реализовать унарный плюс, чем объяснить его бесполезность. Код, реализующий Первичное выражение, принимает следующий вид:

double primary()

{

  Token t = ts.get();

  switch (t.kind) {

  case '(': // обработка пункта '(' выражение ')'

  {

    double d = expression();

    t = ts.get();

    if (t.kind != ')') error("')' expected");

    return d;

  }

  case '8':         // символ '8' используется для представления числа

    return t.value; // возвращаем число

  case '–':

    return – primary();

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