定义
%%
规则
%%
用户代码
int num_lines = 0, num_chars = 0;
%%
n ++num_lines; ++num_chars;
. ++num_chars;
%%
main()
{
yylex();
printf( "# of lines = %d, # of chars = %dn",
num_lines, num_chars );
}
%{
int wordCount = 0;
%}
chars [A-za-z_'."]
numbers ([0-9])+
delim [" "nt]
whitespace {delim}+
words {chars}+
%%
{words} { wordCount++; /*
increase the word count by one*/ }
{whitespace} { /* do
nothing*/ }
{numbers} { /* one may
want to add some processing here*/ }
%%
void main()
{
yylex(); /* start the
analysis*/
printf(" No of words:
%dn", wordCount);
}
int yywrap()
{
return 1;
}
yyin FILE* 类型。 它指向 lexer 正在解析的当前文件。
yyout FILE* 类型。 它指向记录 lexer 输出的位置。 缺省情况下,yyin 和 yyout 都指向标准输入和输出。
yytext 匹配模式的文本存储在这一变量中(char*)。
yyleng 给出匹配模式的长度。
yylineno 提供当前的行数信息。(lexer不一定支持。)
yylex() 这一函数开始分析。 它由 Lex 自动生成。
yywrap() 这一函数在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解析。 因此它可以用来解析多个文件。代码可以写在第三段,这就能够解析多个文件。 方法是使用 yyin 文件指针(见上表)指向不同的文件,直到所有的文件都被解析。最后,yywrap() 可以返回 1 来表示解析的结束。
yyless(int n) 这一函数可以用来送回除了前�n? 个字符外的所有读出标记。
yymore() 这一函数告诉 Lexer 将下一个标记附加到当前标记后。
%{
/* need this for the call to atof() below */
#include
%}
DIGIT [0-9]
ID [a-z][a-z0-9]*%%
{DIGIT}+ {
printf( "An integer: %s (%d)n", yytext,
atoi( yytext ) );
}{DIGIT}+"."{DIGIT}* {
printf( "A float: %s (%g)n", yytext,
atof( yytext ) );
}if|then|begin|end|procedure|function {
printf( "A keyword: %sn", yytext );
}{ID} printf( "An identifier: %sn", yytext );"+"|"-"|"*"|"/" printf( "An operator: %sn", yytext );
"{"[^}n]*"}" /* eat up one-line comments */[ tn]+ /* eat up whitespace */
. printf( "Unrecognized character: %sn", yytext );%%
main( argc, argv )
int argc;
char **argv;
{
++argv, --argc; /* skip over program name */
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;
yylex();
}
模式 动作模式部分是正则表达式,动作部分是处理方法,动作部分如果时{开头,那么,动作将会持续到},如果动作中出现了括号{},开始采用 %{ %}来表示动作去区段。动作部分如果时 |,就表示与下一条规则执行相同的动作。
[ t]+ putchar( ' ' );
[ t]+$ /* ignore this token */
int word_count = 0;%%frob special(); REJECT;
[^ tn]+ ++word_count;
%%
a |
ab |
abc |
abcd ECHO; REJECT;
.|n /* eat up any unmatched character */
%%
mega- ECHO; yymore();
kludge ECHO;
%%
foobar ECHO; yyless(3);
[a-z]+ ECHO;
{
int i;
/* Copy yytext because unput() trashes yytext */
char *yycopy = strdup( yytext );
unput( ')' );
for ( i = yyleng - 1; i >= 0; --i )
unput( yycopy[i] );
unput( '(' );
free( yycopy );
}
%%
"/*" {
register int c;
for ( ; ; )
{
while ( (c = input()) != '*' &&
c != EOF )
; /* eat up text of comment */if ( c == '*' )
{
while ( (c = input()) == '*' )
;
if ( c == '/' )
break; /* found the end */
}
if ( c == EOF )注意: 如果简析器采用用C++编译,input()被yyinput()的替代,因为input()与C++中的流名称input冲突。
{
error( "EOF in comment" );
break;
}
}
}
将会在启动条件"STRING"的情况下被激活。[^"]* { /* eat up the string body ... */ ... }
将会在 "INITIAL", "STRING", "QUOTE"三者之一的条件下被激活。. { /* handle an escape ... */ ... }
如果是包含条件,没有开始条件的规则也会被激活执行,如果时排他条件,只有满足开始条件的规则才会被执行。具有相同排他条件的规则的集合可以使解析器独立于其他的规则。 因此,排他条件可以容易地创建微型解析器处理输入源中的独立与其他部分的一部分(如,注释)。如果对于包含与排他条件还有混淆,可以看下面的例子。
%s example %%foo do_something(); bar something_else();
等同于
上面的程序中如果没有%x example %%foo do_something(); bar something_else();
缺省规则(显示任何未被匹配的字符)在开始条件下仍然生效。等同于:%x example %%foo do_something(); <*>bar something_else();
‘BEGIN(0)’在无开始条件的规则激活条件下返回原始状态,这个状态同于开始条件下的'INITIAL',所以‘BEGIN(INITIAL)'等同于’BEGIN(0)'。<*>.|\n ECHO;
BEGIN
actions can also be given as indented code at the beginning of the rules section.请翻译) 例如,下面的代码将会仅需SPECIAL开始条件,不管合适yylex()被调用并且全局变量enter_special是true。
为了说明开始条件,我们用两种方法处理"123.456".缺省将会被解析为 '123','.','456'三个标记,如果expect-floats后面将会被解析为浮点数 123.456int enter_special; %x SPECIAL %% if ( enter_special ) BEGIN(SPECIAL);blahblahblah ...more rules follow...
下面的代码能够是被C语言注释并且统计行数。%{ #include%} %s expect %% expect-floats BEGIN(expect); [0-9]+"."[0-9]+ { printf( "found a float, = %fn", atof( yytext ) ); } n { /* that's the end of the line, so * we need another "expect-number" * before we'll recognize any more * numbers */ BEGIN(INITIAL); } [0-9]+ { printf( "found an integer, = %dn", atoi( yytext ) ); } "." printf( "found a dotn" );
%x comment %% int line_num = 1; "/*" BEGIN(comment);[^*n]* /* eat anything that's not a '*' */ "*"+[^*/n]* /* eat up '*'s not followed by '/'s */ n ++line_num; "*"+"/" BEGIN(INITIAL);
实际上,编写高速解析程序的办法时在每个规则中做尽可能多的匹配。注意: 开始条件的名字实际上时一个整形值并且能够被保存,所以,上面的代码可以扩展为:
This scanner goes to a bit of trouble to match as much text as possible with each rule. In general, when attempting to write a high-speed scanner try to match as much possible in each rule, as it's a big win.
而且,可能易使用YY_START宏来访问当前的开始条件。如上面的赋值条件可以改写为%x comment foo %% int line_num = 1; int comment_caller; "/*" { comment_caller = INITIAL; BEGIN(comment); } ..."/*" { comment_caller = foo; BEGIN(comment); } [^*n]* /* eat anything that's not a '*' */ "*"+[^*/n]* /* eat up '*'s not followed by '/'s */ n ++line_num; "*"+"/" BEGIN(comment_caller);
comment_caller = YY_STARTYYSTATE是YY_START的别名(因为AT&T lex使用了YYSTATE)。
通常,如上面的例子中所看到你,会有许多相同开始条件的处理。开始条件范围可以简化重复操作。%x str %% char string_buf[MAX_STR_CONST]; char *string_buf_ptr; " string_buf_ptr = string_buf; BEGIN(str);" { /* saw closing quote - all done */ BEGIN(INITIAL); *string_buf_ptr = '0'; /* return string constant token type and * value to parser */ } n { /* error - unterminated string constant */ /* generate error message */ } \[0-7]{1,3} { /* octal escape sequence */ int result; (void) sscanf( yytext + 1, "%o", &result ); if ( result > 0xff ) /* error, constant is out-of-bounds */ *string_buf_ptr++ = result; } \[0-9]+ { /* generate error - bad escape sequence; something * like '48' or '0777777' */ } \n *string_buf_ptr++ = 'n'; \t *string_buf_ptr++ = 't'; \r *string_buf_ptr++ = 'r'; \b *string_buf_ptr++ = 'b'; \f *string_buf_ptr++ = 'f'; \(.|n) *string_buf_ptr++ = yytext[1]; [^\n"]+ { char *yptr = yytext; while ( *yptr ) *string_buf_ptr++ = *yptr++; }
{ }
SCs 是一个或开始条件的列表。在这个开始条件范围内,每个规则将会自动具有前缀 `
{ "\n" return 'n'; "\r" return 'r'; "\f" return 'f'; "\0" return '0'; }
等价于
"\n" return 'n'; "\r" return 'r'; "\f" return 'f'; "\0" return '0';
开始条件页可以嵌套,下面时三个管理开始条件堆栈的参数。
- `void yy_push_state(int new_state)'
- 将当前的开始条件压栈,切换到 new_state 与使用 `BEGIN new_state'类似。
- `void yy_pop_state()'
- 从栈顶弹出,类似于
BEGIN
.- `int yy_top_state()'
- 返回栈顶值,不改变栈内容。
开始条件栈动态增长,没有固定限制,如果内容用尽,程序竟会终止。
为了使用开始条件栈,需要使用 `%option stack' 指令。
多输入缓存区
一些允许include文件解析器的解析器要求从几个输入流中读取内容。YY_INPUT只在结束缓存时被调用,碰到 include 后需要切换输入源,而解析一个描述也许需要很长时间。为了解决此类问题,解析器提供了创建并在多个输入缓存中创建的机制。输入缓存可以通过下面的方式创建:
YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
参数为与缓存关联的输入文件指针,以及足够的可维持size字符(如果不确定,size可以使用YY_BUF_SIZE)。返回一个YY_BUFFER_STATE,可以传递到其他的处理过程。YY_BUFFER_STATE是一个不可见结构yy_buffer_state的指针,所以可以安全地使用`((YY_BUFFER_STATE) 0)'来初始化YY_BUFFER_STATE,如果你愿意,你可以在解析器之外的源程序中引用这个不透明结构来正确的申明输入缓存。可以通过下面的参数来选择一个缓存区。
void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
切换解析器的输入缓存将会导致记接下来的匹配项来自于新的缓存中。yy_switch_to_buffer可能出现在yywrap中为继续解析做准备,替换打开一个新的文件并执行yyin. 通过yy_switch_to_buffer 或 yywrap切换输入源不改变开始条件。
void yy_delete_buffer( YY_BUFFER_STATE buffer )
void yy_flush_buffer( YY_BUFFER_STATE buffer )
/* "incl" 状态用于获取include的文件名 */
%x incl
%{
#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
%}
%%
include BEGIN(incl);
[a-z]+ ECHO;
[^a-zn]*n? ECHO;
[ t]* /* eat the whitespace */
[^ tn]+ { /* got the include file name */
if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
{
fprintf( stderr, "Includes nested too deeply" );
exit( 1 );
}
include_stack[include_stack_ptr++] =
YY_CURRENT_BUFFER;
yyin = fopen( yytext, "r" );
if ( ! yyin )
error( ... );
yy_switch_to_buffer(
yy_create_buffer( yyin, YY_BUF_SIZE ) );
BEGIN(INITIAL);
}
<> {
if ( --include_stack_ptr < 0 )
{
yyterminate();
}
else
{
yy_delete_buffer( YY_CURRENT_BUFFER );
yy_switch_to_buffer(
include_stack[include_stack_ptr] );
}
}
`yy_scan_string(const char *str)' 解析0结尾字符串。
`yy_scan_bytes(const char *bytes, int len)' 解析bytes开始的len个字符(可能包含 0 字符)
`yy_scan_buffer(char *base, yy_size_t size)'将会从base开始解析,包含size个字节, 最后的两个字节必须是 YY_END_OF_BUFFER_CHAR (ASCII NUL)。他们不会被解析, 解析范围从 `base[0]' 到 `base[size-2]'(包含)。如果你没能按照这种规定使用base(如,忘记了最后的两个YY_END_OF_BUFFER_CHAR字节), `yy_scan_buffer()' 将会返回空指针而不创建YY_BUFFER_STATE。yy_size_t类型是个整型,可以转化为整数来反映buffer的长度。
文件结束规则
赋值给yyin一个新的文件 (早期版本的flex, 此操作后必须调用特殊动作 YY_NEW_FILE; 这个操作已经不需要了);
执行一个返回申明;
执行一个特殊的`yyterminate()' 动作;
或者使用`yy_switch_to_buffer()' 切换到一个新的输入缓存区.
< >
%x quote
%%
...other rules for dealing with quotes...
<> {
error( "unterminated quote" );
yyterminate();
}
<> {
if ( *++filelist )
yyin = fopen( *filelist, "r" );
else
yyterminate();
}