Читаем Программирование полностью

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

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

  Число

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

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

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

  Число

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

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

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

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

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();

  case '+':

    return primary();

  default:

    error("ожидается первичное выражение");

  }

}

Этот код настолько прост, что работает с первого раза.

<p id="AutBody_Root120"><strong>7.5. Остаток от деления: %</strong></p>

Обдумывая проект калькулятора, мы хотели, чтобы он вычислял остаток от деления — оператор %. Однако этот оператор не определен для чисел с плавающей точкой, поэтому мы отказались от этой идеи. Настало время вернуться к ней снова.

Это должно быть простым делом.

1. Добавляем символ % как Token.

2. Преобразовываем число типа double в тип int, чтобы впоследствии применить к нему оператор %.

Вот как изменится код функции term():

case '%':

  { double d = primary();

    int i1 = int(left);

    int i2 = int(d);

    return i1%i2;

  }

Для преобразования чисел типа double в числа типа int проще всего использовать явное выражение int(d), т.е. отбросить дробную часть числа. Несмотря на то что это избыточно (см. раздел 3.9.2), мы предпочитаем явно указать, что знаем о произошедшем преобразовании, т.е. избегаем непреднамеренного или неявного преобразования чисел типа double в числа типа int. Теперь получим правильные результаты для целочисленных операндов. Рассмотрим пример.

> 2%3;

= 0

> 3%2;

= 1

> 5%3;

= 2

Как обработать операнды, которые не являются целыми числами? Каким должен быть результат следующего выражения:

> 6.7%3.3;

Это выражение не имеет корректного результата, поэтому запрещаем применение оператора % к аргументам с десятичной точкой. Проверяем, имеет ли аргумент дробную часть, и в случае положительного ответа выводим сообщение об ошибке.

Вот как выглядит результат функции term():

double term()

{

  double left = primary();

  Token t = ts.get(); // получаем следующую лексему

                      // из потока Token_stream

  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;

    }

    case '%':

      { double d = primary();

        int i1 = int(left);

        if (i1 != left)

          error ("Левый операнд % не целое число");

        int i2 = int(d);

        if (i2 != d) error ("Правый операнд % не целое число");

        if (i2 == 0) error("%: деление на нуль");

        left = i1%i2;

        t = ts.get();

        break;

    }

    default:

      ts.putback(t); // возвращаем t обратно в поток

                     // Token_stream

      return left;

    }

  }

}

Здесь мы лишь проверяем, изменилось ли число при преобразовании типа double в тип int. Если нет, то можно применять оператор %. Проблема проверки целочисленных операндов перед использованием оператора % — это вариант проблемы сужения (см. разделы 3.9.2 и 5.6.4), поэтому ее можно решить с помощью оператора narrow_cast.

case '%':

  { int i1 = narrow_cast(left);

    int i2 = narrow_cast(term());

    if (i2 == 0) error("%: деление на нуль");

    left = i1%i2;

    t = ts.get();

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже