Программный код, реализующий это правило, немного сложен, поэтому он открывает больше возможностей для синтаксических ошибок.
double primary()
{
Token t = get_token();
switch (t.kind) {
case '(': // обработка варианта '('выражение')'
{ double d = expression();
t = get_token();
if (t.kind != ')') error("')' expected");
return d;
}
case '8': // используем '8' для представления числа
return t.value; // возвращаем значение числа
default:
error("ожидается первичное выражение");
}
}
По сравнению с функциями expression()
и term()
в этом программном коде нет ничего нового. В нем используются те же самые языковые конструкции и методы, и объекты класса Token
обрабатываются точно так же.
6.6. Испытание первой версии
Для того чтобы выполнить эти функции калькулятора, необходимо реализовать функции get_token()
и main()
. Функция main()
тривиальна: мы просто вызываем функцию expression()
и выводим результат на печать.
int main()
try {
while (cin)
cout << expression() << '\n';
keep_window_open();
}
catch (exception& e) {
cerr << e.what() << endl;
keep_window_open ();
return 1;
}
catch (...) {
cerr << "exception \n";
keep_window_open ();
return 2;
}
Обработка ошибок представляет собой обычный шаблон (см. раздел 5.6.3). Отложим реализацию функции get_token()
до раздела 6.8 и протестируем эту первую версию калькулятора.
ПОПРОБУЙТЕ
Первая версия программы, имитирующей работу калькулятора (включая функцию get_token()
), содержится в файле calculator00.cpp
. Запустите его и испытайте.
Нет ничего удивительного в том, что эта первая версия калькулятора работает не совсем так, как мы ожидали. Мы пожимаем плечами и спрашиваем себя: “Почему?”, или “Почему программа работает так, а не иначе?”, или “Что же она делает?” Введите число 2
и символ перехода на новую строку. Ответа вы не получите! Введите символ перехода на новую строку еще раз, чтобы убедиться, что компьютер не завис. Ответа по-прежнему нет. Введите число 3
и символ перехода на новую строку. Ответа нет! Введите число 4
и символ перехода на новую строку. Ответ равен 2
! Теперь экран выглядит так:
2
3
4
2
Введем выражение 5+6
. Ответ равен 5
, а экран выглядит так:
2
3
4
2
5+6
5
Несмотря на свой опыт, скорее всего, вы будете сильно озадачены. Даже опытный программист будет озадачен таким поведением программы. Что происходит? В этот момент попробуйте выйти из программы. Как это сделать? Мы “забыли” указать в программе команду выхода, но прекращение работы может спровоцировать ошибка, поэтому введите символ х
, и программа в ответ выведет на экран фразу Неправильная лексема и закончит работу. Наконец-то хоть что-то работает, как запланировано!
Однако мы забыли провести различие между вводом и выводом на экран. Прежде чем перейти к решению основной задачи, давайте исправим вывод, чтобы экран лучше отражал то, что мы делаем. Добавим символ =, чтобы отметить результат.
while (cin) cout << "= " << expression() << '\n'; // версия 1
Теперь введем ту же самую последовательность символов, что и раньше. На экране появится следующее:
2
3
4
= 2
5+6
= 5
x
Неправильная лексема
Странно! Попробуйте понять, почему программа делает это. Мы попробовали еще несколько примеров. Только посмотрите на эту головоломку!
• Почему программа реагирует после ввода символов 2
и 3
и ввода символа перехода на новую строку?
• Почему после ввода числа 4
программа выводит на экран число 2
, а не 4
?
• Почему при вычислении выражения 5+6
программа выводит число 5
, а не 11
?
Существует множество способов получить такие загадочные результаты. Некоторые из них мы проверим в следующей главе, а пока просто подумаем. Может ли программа руководствоваться неверной арифметикой? Это крайне маловероятно: значение 4
не может быть равным 2
, а 5+6
равно 11
, а не 5
. Попробуем разобраться, что происходит, когда мы вводим символы 1 2 3 4+5 6+7 8+9 10 11 12
и символ перехода на новую строку.
1 2 3 4+5 6+7 8+9 10 11 12
= 1
= 4
= 6
= 8
= 10
Что? Ни 2
, ни 3
. Почему число 4
в выводе есть, а числа 9
нет (т.е. 4+5
)? Почему среди результатов есть число 6
и нет числа 13
(т.е. 6+7
)?