Мы добавили унарный плюс, поскольку он есть в языке С++. Если есть унарный минус, то легче реализовать унарный плюс, чем объяснить его бесполезность. Код, реализующий Первичное выражение, принимает следующий вид:
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("ожидается первичное выражение");
}
}
Этот код настолько прост, что работает с первого раза.
7.5. Остаток от деления: %
Обдумывая проект калькулятора, мы хотели, чтобы он вычислял остаток от деления — оператор
%
. Однако этот оператор не определен для чисел с плавающей точкой, поэтому мы отказались от этой идеи. Настало время вернуться к ней снова.
Это должно быть простым делом.
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), поэтому ее можно решить с помощью оператора