Buddy_Zhang1 发表于 2016-08-31 10:27

Linux Kbuild 的词法分析器问题

Kbuild 的词法分析器,也就是 make config 对配置的词法分析和语法分析,最后通过 Kconfig 文件生产对应的 .config。
其源码位于 /srctree/scripts/kconfig/ 目录下。
请问有哪位朋友研究过 zonconf.l 的编译命令?

nswcfd 发表于 2016-08-31 14:25

没仔细分析过这个flex文件,楼主具体的问题是什么?

PS,可以通过info flex查询具体的语法点。

nswcfd 发表于 2016-08-31 14:32

5 Format of the Input File
**************************

The `flex' input file consists of three sections, separated by a line
containing only `%%'.


         definitions
         %%
         rules
         %%
         user code

简单的例子
               int num_lines = 0, num_chars = 0;

         %%
         \n      ++num_lines; ++num_chars;
         .       ++num_chars;

         %%
         main()
               {
               yylex();
               printf( "# of lines = %d, # of chars = %d\n",
                         num_lines, num_chars );
               }

Buddy_Zhang1 发表于 2016-08-31 15:28

回复 3# nswcfd


Kbuild 系统中,当我们使用命令 “make config” 的时候,他就会去调用 scripts/kconfig/zconf.l 的词法分析器分析 KCONFIG。
通过源码分析, Kbuild 同时使用了两个词法分析器,为了分开两个词法分析器, Kbuild 会使用 flex 带上一定的编译参数来生成这两个词法分析器。
我就是想知道 flex 生产这两个词法分析器时候使用的命令是什么?

默认的词法分析器是 yylex() 另外一个是 zconflex()

nswcfd 发表于 2016-08-31 17:04

本帖最后由 nswcfd 于 2016-08-31 17:06 编辑

第一部分声明了一些状态(start conditons)
%x COMMAND HELP STRING PARAM
以及ws(空白)和n(数字或字母)两个“宏”

第二部分

[ \t]*#.*\n   |
[ \t]*\n      {
      current_file->lineno++;
      return T_EOL;
}
//匹配行末空白或注释(#开始)

[ \t]*#.*
//忽略注释以及之前的空白

[ \t]+{
      BEGIN(COMMAND);
}
//空格进入COMMAND状态

.       {
      unput(yytext);
      BEGIN(COMMAND);
}
//任意字符进入COMMAND状态, unput放回当前字符(重新解析)

<COMMAND>{
//COMMAND状态下的pattern & action
      {n}+    {
                struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
                BEGIN(PARAM);
                current_pos.file = current_file;
                current_pos.lineno = current_file->lineno;
                if (id && id->flags & TF_COMMAND) {
                        zconflval.id = id;
                        return id->token;
                }
                alloc_string(yytext, yyleng);
                zconflval.string = text;
                return T_WORD;
      }
//匹配标识符(可能已经存在),切换到PARAM状态
      .
//忽略其它字符
      \n      {
                BEGIN(INITIAL);
                current_file->lineno++;
                return T_EOL;
      }
//换行切换为INIT状态
}

<PARAM>{
//PARAM状态下的pattern & action
      "&&"    return T_AND;
      "||"    return T_OR;
      "("   return T_OPEN_PAREN;
      ")"   return T_CLOSE_PAREN;
      "!"   return T_NOT;
      "="   return T_EQUAL;
      "!="    return T_UNEQUAL;
//字符串常量,各种操作符
      \"|\'   {
                str = yytext;
                new_string();
                BEGIN(STRING);
      }
//单引号或双引号,切换为STRING状态
      \n      BEGIN(INITIAL); current_file->lineno++; return T_EOL;
//换行切换为INIT状态
      ---   /* ignore */
//忽略---
      ({n}|[-/.])+    {
                struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
                if (id && id->flags & TF_PARAM) {
                        zconflval.id = id;
                        return id->token;
                }
                alloc_string(yytext, yyleng);
                zconflval.string = text;
                return T_WORD;
      }
//匹配标识符
      #.*   /* comment */
//注释,忽略
      \\\n    current_file->lineno++;
//line continue,忽略
      .
//忽略其它字符
      <<EOF>> {
                BEGIN(INITIAL);
      }
}

<STRING>{
//STRING状态下的pattern & action
      [^'"\\\n]+/\n   {
                append_string(yytext, yyleng);
                zconflval.string = text;
                return T_WORD_QUOTE;
      }
//行末的串,不包括引号和转义
      [^'"\\\n]+      {
                append_string(yytext, yyleng);
      }
//非行未的(部分)串,不包括引号和转义
      \\.?/\n {
                append_string(yytext + 1, yyleng - 1);
                zconflval.string = text;
                return T_WORD_QUOTE;
      }
//行末的转义,串结束
      \\.?    {
                append_string(yytext + 1, yyleng - 1);
      }
//非行末的转义,串未结束
      \'|\"   {
                if (str == yytext) {
                        BEGIN(PARAM);
                        zconflval.string = text;
                        return T_WORD_QUOTE;
//字符串结束
                } else
                        append_string(yytext, 1);
//引号本身作为字符串的一部分
      }
      \n      {
                printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
                current_file->lineno++;
                BEGIN(INITIAL);
                return T_EOL;
      }
//换行结束STRING状态
      <<EOF>> {
                BEGIN(INITIAL);
      }
}

<HELP>{
//HELP状态下的pattern & acttion
      [ \t]+{
                ts = 0;
                for (i = 0; i < yyleng; i++) {
                        if (yytext == '\t')
                              ts = (ts & ~7) + 8;
                        else
                              ts++;
                }
                last_ts = ts;
                if (first_ts) {
                        if (ts < first_ts) {
                              zconf_endhelp();
                              return T_HELPTEXT;
                        }
                        ts -= first_ts;
                        while (ts > 8) {
                              append_string("      ", 8);
                              ts -= 8;
                        }
                        append_string("      ", ts);
                }
      }
//看起来像是tab展开,基于缩进量判断help是否结束(python风格……)
      [ \t]*\n/[^ \t\n] {
                current_file->lineno++;
                zconf_endhelp();
                return T_HELPTEXT;
      }
//下一行没有缩进,help结束
      [ \t]*\n      {
                current_file->lineno++;
                append_string("\n", 1);
      }
//忽略行末空白,help可以包含换行
      [^ \t\n].* {
                while (yyleng) {
                        if ((yytext != ' ') && (yytext != '\t'))
                              break;
                        yyleng--;
                }
                append_string(yytext, yyleng);
                if (!first_ts)
                        first_ts = last_ts;
      }
//help正文本身(未结束),忽略末尾的空白,并计算第一行的缩进量
      <<EOF>> {
                zconf_endhelp();
                return T_HELPTEXT;
      }
}

第三部分是原封不动的代码,不解释。

nswcfd 发表于 2016-08-31 17:14

回复 4# Buddy_Zhang1

看同目录下的makefile

conf-objs       := conf.ozconf.tab.o

$(obj)/zconf.tab.o: $(obj)/lex.zconf.c $(obj)/zconf.hash.c

$(obj)/zconf.tab.c: $(src)/zconf.y
$(obj)/lex.zconf.c: $(src)/zconf.l


%.tab.c: %.y
      bison -l -b $* -p $(notdir $*) $<
      cp $@ $@_shipped

lex.%.c: %.l
      flex -L -P$(notdir $*) -o$@ $<
      cp $@ $@_shipped

不知道有没有理解错误楼主的问题。

nswcfd 发表于 2016-08-31 17:25

flex:
-P,--prefix=STRING   use STRING as prefix instead of "yy"
-L,--noline            suppress #line directives in scanner

bison:
-p, --name-prefix=PREFIX   prepend PREFIX to the external symbols
-l, --no-lines             don't generate `#line' directives

$*代表%匹配的部分,所以,对于匹配lex.%.c的lex.zonf.c而言,%就是zconf
所以yylex就是zconflex

nswcfd 发表于 2016-08-31 17:26

不好意思审题不仔细:-L

Buddy_Zhang1 发表于 2016-08-31 17:48

回复 8# nswcfd


牛逼,我就是要这样的答案,通过你的答复,我已经找到满意回复了

牛逼

今天论坛回复给力,幸好今天没水逆。

你在微信群里叫什么名字,大神。

Buddy_Zhang1 发表于 2016-08-31 17:50

回复 7# nswcfd


我就是想要这个东西

%.tab.c: %.y
      bison -l -b $* -p $(notdir $*) $<
      cp $@ $@_shipped

lex.%.c: %.l
      flex -L -P$(notdir $*) -o$@ $<
      cp $@ $@_shipped

今天本来这是碰碰运气看能否解决

大神果然牛逼!!!
页: [1] 2
查看完整版本: Linux Kbuild 的词法分析器问题