[ Содержание ] [ Предыдущая ] [ Следующая ]

Описание действий

    Когда выражение сопоставляется с образцом текста на вводе, lex выполняет соответствующие действия. Этот раздел описывает некоторые особенности lex, которые помогают при описании действий. Заметим, что существует действие по умолчанию, которое копирует входные данные на выход. Это выполняется для всех символов, которые в противном случае не сопоставляются. Таким образом, пользователь lex, который желает произвести полный ввод без какого-либо вывода, должен составить правила, подбирающие все данные. Если lex используется совместно с yacc, то это считается нормальной ситуацией. Вы можете рассматривать эти действия, как заменяющие копирование входных данных в выходные. Таким образом, правило простого копирования может быть опущено.

    Самое простое, что можно сделать, это проигнорировать ввод. Описание в качестве действия пустого оператора языка СИ приводит именно к этому результату. Часто используется правило

[ \t\n];

которое вызывает игнорирование трех символов (пробел, знак табуляции, знак новой строки).

    Чтобы не описывать действие, можно использовать символ повтора |, который указывает, что действие для данного правила задает и действие для следующего. Предыдущий пример можно было бы записать следующим образом:

" "      |
"\t"     |
"\n"     ;

    Это приведет к тому же результату, только представлено в другом виде. Кавычки вокруг \n и \t не обязательны.

    Для задания более сложных действий часто нужно знать текущий текст, сопоставляемый выражениям типа

[a-z]+

    lex хранит этот текст во внешнем символьном массиве с именем yytext. Так, чтобы напечатать найденное имя, используется правило типа

[a-z]+ printf("%s",yytext);

    которое печатает символьную строку, хранящуюся в yytext. Функция языка СИ printf(2) позволяет печатать формальные параметры и данные. В этом случае форматом является print string, где знак процента (%) указывает на преобразование данных, s обозначает тип символьной строки. Данными являются символы из yytext. Это переводит подобранную строку в выходные данные. Это действие настолько заурядно, что может быть написано через ECHO. Например:

[a-z]+ ECHO;

аналогично предыдущему примеру. Так как действие по умолчанию печатает только найденные символы, то может возникнуть вопрос: зачем нужно правило, описывающее только действие по умолчанию? Такие правила нужны, чтобы избежать сопоставления каких-то других, нежелательных правил. Например, если есть правило, которое сопоставляет read, то оно будет сопоставлять экземпляры read, заключенные в bread или readjust. Чтобы избежать этого, требуется правило

[a-z]+

    Этот вопрос будет подробнее разобран далее.

    Иногда бывает удобным знать конец найденных данных, поэтому lex подсчитывает число сопоставленных символов в переменной yyleng. Чтобы подсчитать и число слов и число символов, следует написать:

[a-zA-Z]+  {words++;chars+=yyleng;}

    Будет произведен подсчет символов в словах, а результат будет передан переменной chars. Обращение к последнему символу сопоставляемой строки может произведено следующим образом:

yytext[yyleng-1]

    Иногда lex может решить, что правило не обнаружило соответствующий набор символов. Существуют две программы, помогающие в этой ситуации. Во-первых, может быть вызвана подпрограмма yymore(), предписывающая присоединить следующее вводимое выражение к концу текущего ввода. Обычно следующая вводимая символьная строка затирает текущий элемент в yytext. Во-вторых, может быть вызвана подпрограмма yyless(n), определяющая, что не все символы, подбираемые текущим выражением, требуются прямо сейчас. Аргумент n указывает число символов, содержащихся в yytext. Последующие символы, которые уже были сопоставлены, возвращаются обратно на ввод. Это обеспечивает способ просмотра данных, аналогичный задаваемому оператором (/), но в другой форме.

    Например, рассмотрим язык, который определяет группу символов, заключенных в кавычки, как символьную строку. Если в эту строку надо включить знак кавычек, то ему должна предшествовать обратная косая черта (\). Регулярные выражения, используемые для такого сопоставления, довольно сложны, поэтому предпочтительнее ввести:

\"[^"]* {
  if(yytext[yyleng-1]==`\\`)
    yymore();
  else
    ...normal user processing
}

    При этом, если встретится

"abc\"def"

то будут сопоставляться первые пять символов

"abc\

    Затем, вызов yymore() перенесет следующую часть символьной строки

"def

в конец. Заметим, что последний знак кавычки, завершающий строку, будет представлен в коде, соответствующем обычной обработке.

    Функция yyless() в различных обстоятельствах может быть использована для повторной обработки текста. Рассмотрим проблему определения неоднозначного выражения =-a в старом варианте СИ. Допустим, что это требуется представить как =- a для последующей печати. Можно применить следующее правило:

=-[a-zA-Z] {
  printf("Operator(=-)ambiguous\n");
  yyless(yyleng-1);
  ... action for =- ...
}

    Будет печататься сообщение, после каждого оператора буква будет возвращаться во входной поток, а оператор будет трактоваться как =-.

    Наоборот, может потребоваться трактовать это как = -a; чтобы добиться этого, уберите знак минус и букву для ввода. Интерпретация будет выполнена следующими строками:

=-[a-zA-Z {
  printf("Operator(=-)ambiguous\n");
  yyless(yyleng-2);
  ... action for = ...
}

    Заметим, что выражения в этих случаях могут быть записаны как

=-/[A-Za-z]

в первом случае, и

=/-[A-Za-z]

во втором. В правилах, действия которых описаны выше, не требуется возврата. Для раскрытия неопределенности нет необходимости в распознавании всего идентификатора. Тем не менее, в случае =-3 лучше использовать правило

=-/[^\t\n]

    В дополнение к этим программам, lex также разрешает доступ своим собственным подпрограммам ввода/вывода:

  1. input(), которая возвращает следующий вводимый символ;
  2. output(с), которая выводит символ c;
  3. unput(с), которая помещает символ c обратно во входной поток, чтобы затем считать его с помощью input().

    По умолчанию эти подпрограммы рассматриваются как макроопределения, но пользователь может заменить их собственными версиями. Эти подпрограммы устанавливают взаимосвязь между внешними файлами и внутренними символами, поэтому они все должны сохраняться или видоизменяться согласованно. Они могут быть переопределены, чтобы вызвать перемещение выходных или входных данных в другие области, включая другие программы или оперативную память. Но используемые наборы символов во всех подпрограммах должны быть согласованы. Нулевое значение, возвращаемое input(), должно означать конец файла. Связь между unput() и input() должна сохраняться, иначе просмотр вперед не будет осуществляться. lex вообще не будет выполнять просмотр вперед, если ему это не предписано. Но каждое провило, содержащее косую черту (/) или оканчивающееся одним из следующих символов подразумевает просмотр:

+ * ? $

    Просмотр вперед также необходим для сопоставления выражения, которое является префиксом другого выражения. Ниже рассматривается набор символов, часто используемых lex. Стандартная библиотека lex содержит 100 резервных копий символов.

    Еще одна подпрограмма библиотеки lex, которую иногда желательно переопределить, - это yywrap(), вызываемая каждый раз, когда lex достигает конца файла. Если yywrap() обращает 1, lex продолжает обычную работу до конца ввода. Иногда, тем не менее, удобно принять дополнительные входные данные из нового источника. В этом случае пользователь должен вызвать подпрограмму yywrap(), которая принимает новые входные данные и возвращает 0. Это заставит lex продолжить обработку. По умолчанию yywrap() всегда возвращает 1.

    Эта подпрограмма также удобна для печати в конце программы таблиц, кратких пояснений и т.д. Заметим, что нельзя написать обычное правило которое распознает конец файла. Единственный сделать это - использовать yywrap(). В действительности, если не установлена собственная версия input(), то файлом, содержащим пустые указатели, нельзя управлять, поскольку значение 0, возвращаемое input(), воспринимается как конец файла.

[ Содержание ] [ Предыдущая ] [ Следующая ]

c 1998-2000 SoloTony (Antonio Solo) mailto:solotony@mail.ru