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