Программирование. Принципы и практика использования C++ Исправленное издание, стр. 77
char op; cin>>lval; // считываем самый левый операнд if (!cin) error("нет первого операнда"); while (cin>>op) { // считываем оператор и правый операнд в цикле cin>>rval; if (!cin) error("нет второго операнда "); switch(op) { case '+': lval += rval; // сложение: lval = lval + rval break; case '–': lval –= rval; // вычитание: lval = lval – rval break; case '*': lval *= rval; // умножение: lval = lval * rval break; case '/': lval /= rval; // деление: lval = lval / rval break; default: // нет другого оператора: выводим результат cout << "Результат: " << lval << '\n'; keep_window_open(); return 0; } } error("неверное выражение");}Это неплохо, но попытайтесь вычислить выражение
1+2*3971–2*3–3–51+2*3(1+2)*31+(2*3)1–2*3(1–2)*31–(2*3)6.3.2. Лексемы
Теперь (каким-то образом) мы должны заранее узнать, содержит ли строка символ
*/1. Выражение не обязательно занимает только одну строку. Рассмотрим пример.
1+2Это выражение до сих пор вычислялось без проблем.
2. Как обнаружить символ
*/+–()3. Как запомнить, в каком месте стоит символ
*4. Как вычислить выражение, которое не выполняется слева направо (как
1+2*345+11.5/7программа должна создать список лексем
45+11.5/7
• Литералы с плавающей точкой, определенные в языке C++, например
3.140.274e242• Операторы, например
+–*/%• Скобки
()Внешний вид литералов с плавающей точкой может создать проблемы: считать число
1212.3е–3Как представить такие лексемы в нашей программе? Можно попытаться найти начало (и конец) лексемы, но это может привести к путанице (особенно, если позволить выражениям занимать несколько строк). Кроме того, если хранить числа в виде строки символов, то позднее следует идентифицировать это число по его цифрам; например, если мы видим строку
4242424*10+2Вид идентифицирует лексему как число, оператор или скобку. Для чисел (в нашем примере — только для чисел) в качестве значения используется само число.
Итак, как же выразить идею о паре (вид, значение) в программе? Для этого определим тип Token, представляющий лексемы. Почему? Вспомните, почему мы вообще используем типы: они хранят данные, которые нам нужны, и предоставляют возможность выполнять полезные операции над этими данными. Например, тип
intstringcharintdoublestringvectorostreamToken