6.3.3. Реализация лексем
Как должна выглядеть лексема в нашей программе? Иначе говоря, как должен выглядеть тип Token
? Класс Token
должен предусматривать выполнение операторов, например +
и –
, а также представлять числа, такие как 42
и 3.14
. В самой простой реализации нужно придумать, как задать вид лексемы и как хранить числа.
Существует много способов реализации этой идеи в программе на языке С++. Вот ее простейший вариант:
class Token { // очень простой тип, определенный пользователем
public:
char kind;
double value;
};
Класс Token
— это тип (такой же, как int
или char
), поэтому его можно использовать для определения переменных и хранения значений. Он состоит из двух частей (членов): kind
и value
. Ключевое слово class
означает “тип, определенный пользователем”; это значит, что он содержит члены (хотя в принципе может их и не содержать). Первый член, kind
, имеет тип char
и представляет собой символ. С его помощью удобно хранить символы '+'
и '*'
, чтобы представить операции *
и +
. Рассмотрим пример использования этого типа.
Token t; // t — объект класса Token
t.kind = '+'; // t представляет операцию +
Token t2; // t2 — другой объект класса Token
t2.kind = '8'; // цифра 8 означает, что "вид" является числом
t2.value = 3.14;
Для доступа к члену класса используется обозначение имя_объекта.имя_члена. Выражение t.kind
читается как “член kind
объекта t
”, а выражение t2.value
— как “член value
объекта t2
”. Объекты класса Token
можно копировать так же, как и переменные типа int
.
Token tt = t; // копирование при инициализации
if (tt.kind != t.kind) error("невозможно!");
t = t2; // присваивание
cout << t.value; // вывод числа 3.14
Имея класс Token
, можно выразить выражение (1.5+4)*11
с помощью семи лексем.
Обратите внимание на то, что для простых лексем значение не требуется, поэтому мы не используем член value
. Нам нужен символ для обозначения чисел. Мы выбрали символ '8'
просто потому, что он явно не оператор и не знак пунктуации. Использование символа '8'
для обозначения чисел немного загадочно, но это лишь на первых порах.
Класс Token
представляет пример типа, определенного пользователем. Тип, определенный пользователем, может иметь функции-члены (операции), а также данные члены. Существует много причин для определения функций-членов. В данном примере мы описали две функции-члена для того, чтобы инициализация объекта класса Token
стала проще.
class Token {
public:
char kind; // вид лексемы
double value; // для чисел: значение
Token(char ch) // создает объект класса Token
// из переменной типа char
:kind(ch), value(0) { }
Token(char ch, double val) // создает объект класса Token
:kind(ch), value(val) { } // из переменных типа
// char и double
};
Эти две функции-члена называют конструкторами (constructors). Их имя совпадает с именем типа, и они используются для инициализации (конструирования) объектов класса Token
. Рассмотрим пример.
Token t1('+'); // инициализируем t1, так что t1.kind = '+'
Token t2('8',11.5); // инициализируем t2,
// так что t2.kind = '8' и t2.value = 11.5
В первом конструкторе фрагмент :kind(ch)
, value(0)
означает “инициализировать член kind значением переменной ch
и установить член value
равным нулю”. Во втором конструкторе фрагмент :kind(ch)
, value(val)
означает “инициализировать член kind
значением переменной ch
и установить член value
равным переменной val”. В обоих вариантах нам требуется лишь создать объект класса Token
, поэтому тело функции ничего не содержит: { }
. Специальный синтаксис инициализации (список инициализации членов класса) начинается с двоеточия и используется только в конструкторах.