Lex/flex 计算 ID、语句、关键字、运算符等的程序

Lex/flex program to count ids, statements, keywords, operators etc

%{
#undef yywrap
#define yywrap() 1
#include<stdio.h>
  int statements = 0;
  int ids = 0;
  int assign = 0;
  int rel = 0;
  int keywords = 0;
  int integers = 0; 
%}
DIGIT [0-9]
LETTER [A-Za-z]
TYPE int|char|bool|float|void|for|do|while|if|else|return|void
%option yylineno
%option noyywrap

%%
\n {statements++;}
{TYPE} {/*printf("%s\n",yytext);*/keywords++;}
(<|>|<=|>=|==) {rel++;}
'#'/[a-zA-Z0-9]*    {;}
[a-zA-Z]+[a-zA-Z0-9]* {printf("%s\n",yytext);ids++;}
= {assign++;}
[0-9]+ {integers++;}
.      {;}

%%
void main(int argc, char **argv)
{
  FILE *fh;
  if (argc == 2 && (fh = fopen(argv[1], "r"))) {
    yyin = fh;
  }
  yylex();
  printf("statements = %d ids = %d assign = %d rel = %d keywords = %d integers = %d \n",statements,ids,assign,rel,keywords,integers);
}

//输入file.c

#include<stdio.h>
void main(){
    float a123;
    char a;
    char b123;
    char c;
    int ab[5];
    int bc[2];
    int ca[7];
    int ds[4];
    for( a = 0; a < 5 ;a++)
     printf("%d ", a);
    return 0;
}

输出:

include
stdio
h
main
a123
a
b123
c
ab
bc
ca
ds
a
a
a
printf
d
a
statements = 14 ids = 18 assign = 1 rel = 3 keywords = 11 integers = 7

我正在打印标识符。 #include<stdio.h> 被算作标识符。我该如何避免这种情况?

我试过 '#'/[a-zA-Z0-9]* {;} rule:action 对,但它仍然被算作标识符。文件是如何被标记化的?

printf 中的 %d 字符串也被计为标识符。我已经明确写过标识符只能以字母开头,那么为什么 %d 被推断为标识符?

  1. I have tried '#'/[a-zA-Z0-9]* {;} rule:action pair but it [include] is still being counted as identifier. How is the file being tokenized?

    令牌一次识别一个。每个标记都从前一个标记结束的地方开始。

    '#'/[a-zA-Z0-9]* 匹配 '#' 前提是其后跟 [a-zA-Z0-9]*。您的意思可能是 "#"/[a-zA-Z0-9]*(带双引号)匹配 #,前提是它后跟一个字母或数字。请注意,只有 # 被匹配; / 之后的模式是 "trailing context",这基本上是一个先行断言。在这种情况下,前瞻是没有意义的,因为 [a-zA-Z0-9]* 可以匹配空字符串,所以任何 # 都会被匹配。在任何情况下,在 # 作为标记匹配后,扫描将在下一个字符处继续。所以下一个标记将是 include.

    由于拼写错误,该模式不匹配。 (源代码中没有撇号。)所以实际匹配的是您的 "fallback" 规则:模式为 . 的规则。 (我们称其为回退规则,因为它匹配任何内容。实际上,它应该是 .|\n,因为 . 匹配除换行符以外的任何内容,但只要您有一些匹配换行符的规则,它就是可接受使用 .。如果您不提供后备规则,flex 将通过操作 ECHO.)

    自动插入一个

    因此,# 被忽略(就像您按预期编写规则时一样)并且扫描再次继续使用令牌 include.

    如果你想忽略整个预处理器指令,你可以这样做

    ^[[:空白:]]#.* { ; }

  2. (from a comment) I am getting stdio and h as keywords, how does that fit the definition that I have given? What happened to the . in between?

    <被回退规则忽略后,匹配stdio。由于 [a-zA-Z]+[a-zA-Z0-9]* 不匹配字母和数字以外的任何内容,因此 . 不被视为令牌的一部分。然后.被回退规则匹配忽略,然后h被匹配.

  3. Also the %d string in printf is being counted as an identifier.

    不是真的。 % 被回退规则明确忽略(" 也是如此),然后 d 作为标识符前进。如果您想忽略字符串文字中的单词,您将必须识别并忽略字符串文字。

#include 指令是预处理器指令,因此由预处理器进行预处理。预处理器包含头文件并删除#include 指令,因此在预处理之后,当程序作为输入提供给编译器时,它没有任何预处理器指令,如#include。 因此,您无需编写代码来检测 #include,因为编译器既不会看到它,也不会设计为标记化 #include 指令。

参考文献:Is #include a token of type keyword?

在规则部分添加以下行对我有用:

#.* ;

这里的规则是#.*,动作是;。 #.* 将捕获以 # 和 ; 开头的行什么都不做,所以基本上这会忽略以 #.

开头的行