/* file: 1.01_word_counter.y */
/* 一个字数统计(word_counter)程序,程序读入一个文件然后报告这个文件的行数、单词树和字符数。*/
/* 以下是Flex的第一部分为定义部分,包含选项、文字块、起始条件和转换等。
文字块存在与%{和%}之间,它们将被原样拷贝到生成文件中。
以空白字符开头的行会被原样拷贝到C文件中。通常用于/**/之间的多行注释,在每行前面需要有空白字符。
例如:用于声明选项(%option),以及起始条件start condition(使用%x、%s声明)
*/
%option noyywrap
/* %{ 和 %}之间的代码被认为是文字块,会被原样拷贝到生成的文件的开头部分。*/
%{
int chars = 0; // 定义变量,用于计数有多少个字符
int words = 0; // 定义变量,用于计数有多少个单词
int lines = 0; // 定义变量,用于计数有多少行
%}
/* 以下%%和%%之间是Flex的第二部分为规则部分,该部分定义一些匹配模式(每个模式必须放在行首)和动作(模式匹配成功是所执行的C代码,使用{}括号括住的一行或多行语句)。
注意:
在该部分也有%{ 和 %}定义的C代码语句,它们会被原封不动地拷贝到yylex()中。
在规则部分开头出现的C代码也会出现在yylex的开头,它可以包含词法分析器使用的变量,以及每次yylex()调用时所需运行的代码。
在其他部分出现的C代码必须仅仅包含注释,因为你无法预知它出现在词法分析器的什么位置。*/
%%
[a-zA-Z]+ {
words++;
chars += strlen(yytext); // yytext变量总是指向本次匹配的输入文本字符串
} // 当满足正则表达式:[a-zA-Z]+,表示成功识别到了一个单词。words++并且chars增加单词的字符长度
\n {chars++; lines++; } // 当满足正则表达式:\n,表示识别到了换行符。chars++并且lines++
. {chars++; } // 当满足正则表达式:.,表示识别到了任意一个单词。chars++
%%
/* 以下是Flex的第三部分为用户子例程部分,该部分会被拷贝到生成的词法分析器里面的C代码。
它们通常是一些与动作相关的程序代码。
*/
int main(int argc, char **argv)
{
yylex(); // 该函数是词法分析器的主要函数,调用它将进行词法分析
printf("chars=%8d\n",chars); // 打印有多少个字符
printf("words=%8d\n",words); // 打印有多少个单词
printf("lines=%8d\n",lines); // 打印有多少行
return 0;
}
/*
注意:
Q:上面的例子可以发现,第三条规则会匹配所有的字符,
难道它不会也匹配第一个模式所能匹配的单词字母吗?
A:如果两个模式都匹配的话,flex会选择在程序里面首先出现的那个模式。
*/
如果你使用的是CLion来调试运行以上的代码,你可能会碰到这样的问题:
/home/cmp/work_dir/source_code/yacc_bison_practice/ch1/cmake-build-debug/word_counter
abc
def
^D
input in flex scanner failed
Process finished with exit code 2
正如上面,在CLion中调试运行程序,输入abc换行def换行
,然后在输入ctrl+d,程序直接会报错。这是由于CLion使用ctrl+d发送EOF始终有bug。最好在Terminal运行这个程序:
源码见:
https://github.com/ronnie88597/yacc_bison_practice/blob/master/ch1/word_counter.l