Для чтения входного потока и передачи лексем (при необходимости, со значениями) программе разбора пользователь должен разработать лексический анализатор. Лексический анализатор - функция, возвращающая целое, с именем yylex. Функция возвращает целое число, называемое номером лексемы, которое обозначает тип прочитанной лексемы. Если с лексемой связано значение, оно должно присваиваться внешней переменной yylval. Для нормального взаимодействия между программой разбора и синтаксическим анализатором номера лексем должны быть согласованы. Номера выбираются либо yacc, либо пользователем. В любом случае для символического обмена именами применяется механизм директивы #define. Например, предположим, что лексема DIGIT определена в разделе объявлений файла спецификаций. Соответствующая часть лексического анализатора могла бы выглядеть так:
yylex(){
extern int yylval;
int c;
...
c = getchar();
...
switch(c){
...
case '0':
case '1':
...
case '9':
yylval = c-'0';
return (DIGIT);
...
}
...
Нам нужно вернуть лексему с номером DIGIT и числовое значение. Если фрагмент, связанный с лексическим анализатором, помещен в программный раздел файла спецификаций, идентификатор DIGIT определяется как индекс созданного узла. Этот механизм ведет к построению легко понимаемых и модифицируемых лексических анализаторов. Единственное ограничение состоит в необходимости избегать имен лексем, совпадающих с зарезервированными словами языка Си или анализатора. Например, использование лексем if или while наверняка приведет к серьезным трудностям при компиляции. Лексема error зарезервирована для обработки ошибок и должна применяться осознанно. Как уже упоминалось, номера лексем могут выбираться либо самим построителем, либо пользователем. По умолчанию они выбираются построителем. Номер по умолчанию для литерального символа - числовое значение его кода в наборе символов. Другие имена получают номера, начиная с 257. Для явного присвоения лексеме номера после первого вхождения имени в разделе определений необходимо указать неотрицательное целое число. Это число считается номером имени или литерала. Имена или литералы, не затронутые этим механизмом, сохраняют значения по умолчанию. Важно отметить, что все номера должны быть различными.
По историческим причинам, конечный маркер должен нумероваться либо нулем, либо отрицательным числом. Этот номер не должен переопределяться пользователем. Таким образом, все лексические анализаторы должны при достижении конца входного потока возвращать либо 0, либо отрицательное число в качестве номера лексемы.
Весьма полезный инструмент для построения лексических анализаторов, lex, рассматривается в предыдущей главе. Лексические анализаторы строятся так, чтобы их поведение было согласовано с yacc. При спецификации используются не грамматические правила, а регулярные выражения. Lex легко может применяться для построения довольно сложных лексических анализаторов, но существуют языки (например, ФОРТРАН), не удовлетворяющие ни одной теоретической модели, и анализаторы для них приходится разрабатывать вручную.