菜鸟科技网

lex命令如何使用?

lex 是一个经典的词法分析器生成器,它的主要作用是根据用户定义的词法规则(也就是描述“单词”或“记号”的模式),自动生成一个名为 lex.yy.c 的 C 语言源代码文件,这个 C 文件包含了能够从输入流中识别出这些“单词”并执行相应动作的代码。

lex命令如何使用?-图1
(图片来源网络,侵删)

生成的 lex.yy.c 文件通常需要与一个语法分析器(最常见的是由 yaccbison 生成的)配合使用,来完成整个编译或解释过程。


lex 的工作流程

lex 的工作流程可以分为以下四个步骤:

  1. 创建 .l 文件: 用户编写一个以 .l.L 为后缀的源文件,这个文件被称为 lex 源文件,它包含了词法规则和对应的动作代码。

  2. 运行 lex 命令: 在命令行中,对 .l 文件运行 lex 命令。

    lex命令如何使用?-图2
    (图片来源网络,侵删)
    lex your_file.l

    lex 编译器会读取 .l 文件,并生成一个名为 lex.yy.c 的 C 语言文件,这个文件包含了一个名为 yylex() 的函数,这个函数就是词法分析器的核心。

  3. 编译并链接: 将 lex.yy.c 文件与 C 编译器(如 gcc)一起编译,你还需要链接 lexflex 的运行时库(-ll)。

    gcc lex.yy.c -o my_program -ll
    • -o my_program:指定输出的可执行文件名为 my_program
    • -ll:链接 lex 的库文件。注意: 这个 -ll 必须放在源文件之后,否则可能会导致链接错误。
  4. 运行程序: 执行编译好的程序,程序会从标准输入(通常是你的键盘)读取文本,yylex() 函数会开始工作,不断地匹配规则并执行动作。


.l 文件的结构

一个 lex 源文件由三个主要部分组成,它们之间由 分隔。

lex命令如何使用?-图3
(图片来源网络,侵删)
/* 第一部分:定义和声明 */
%{
    /* C 代码块 */
    #include <stdio.h>
    int my_variable = 0;
%}
/* 正则表达式定义 */
DIGIT    [0-9]
ID       [a-zA-Z][a-zA-Z0-9]*
%%
/* 第二部分:规则 */
/* 格式: <正则表达式> { <C 代码动作> } */
{DIGIT}+   {
             printf("Found an integer: %s\n", yytext);
             /* yytext 是一个全局变量,指向当前匹配的文本 */
           }
{ID}       {
             printf("Found an identifier: %s\n", yytext);
           }
"if"       {
             printf("Found a keyword: 'if'\n");
           }
[ \t\n]    {
             /* 忽略空白字符(空格、制表符、换行) */
           }
.          {
             /* 匹配任何其他单个字符,并打印警告 */
             printf("Unknown character: %s\n", yytext);
           }
%%
/* 第三部分:C 语言代码 */
/* 这里的代码会被原样复制到 lex.yy.c 文件的末尾 */
int main() {
    printf("Starting lexical analyzer...\n");
    yylex(); /* 调用词法分析器 */
    printf("Analysis finished.\n");
    return 0;
}

各部分详解:

第一部分:定义和声明

  • 双百分号包裹的区域,这里的任何代码都会被原封不动地复制到生成的 lex.yy.c 文件中,通常用于包含头文件(如 stdio.h)、定义全局变量或函数原型。
  • 正则表达式定义:在这里可以定义简单的正则表达式,以便在规则部分重复使用,使代码更清晰。DIGIT 定义了 [0-9]ID 定义了标识符的模式。

第二部分:规则

这是 lex 文件的核心,每一行规则由两部分组成:

  1. 正则表达式:描述要匹配的模式。
  2. 动作:用花括号 包裹的 C 语言代码,当输入文本与正则表达式匹配时,就会执行这部分代码。
  • yytext:这是一个非常重要的全局字符数组,它包含了当前匹配到的字符串。
  • yyleng:一个全局整型变量,表示当前匹配到的字符串的长度。
  • yylex()lex 生成的词法分析器函数,它会持续从输入读取字符,尝试与规则进行匹配,一旦找到最长的匹配项,就会执行对应的动作,然后返回。

匹配优先级

  • lex 总是尝试匹配最长的可能字符串。
  • 如果有多个规则匹配了相同长度的字符串,那么在 .l 文件中写在最前面的规则优先。

第三部分:C 语言代码 同样会被原样复制到 lex.yy.c 文件的末尾,通常用于放置 main() 函数或其他辅助函数。


一个完整的示例

让我们创建一个简单的计算器词法分析器,它能识别整数、加号、减号和换行符。

文件名:calculator.l

%{
#include <stdio.h>
%}
/* 定义数字和操作符 */
DIGIT    [0-9]
PLUS     \+
MINUS    \-
NEWLINE  \n
%%
{DIGIT}+ {
    printf("Integer: %s\n", yytext);
}
{PLUS} {
    printf("Operator: PLUS\n");
}
{MINUS} {
    printf("Operator: MINUS\n");
}
{NEWLINE} {
    /* 忽略换行符,或者用它来分隔表达式 */
    /* printf("End of line.\n"); */
}
[ \t] {
    /* 忽略空格和制表符 */
}
. {
    printf("Unknown character: %s\n", yytext);
}
%%
int main() {
    printf("Enter an expression (e.g., 123+456-789):\n");
    yylex(); /* 开始词法分析 */
    return 0;
}

编译和运行:

  1. 生成 C 代码

    lex calculator.l

    这会生成 lex.yy.c

  2. 编译并链接

    gcc lex.yy.c -o calculator -ll
    • 注意 -ll 的位置。
  3. 运行程序

    ./calculator

测试运行:

$ ./calculator
Enter an expression (e.g., 123+456-789):
100 + 200 - 50
Integer: 100
Operator: PLUS
Integer: 200
Operator: MINUS
Integer: 50
^D  (按下 Ctrl+D 表示文件结束)

lex 的现代替代品:flex

在现代的 Linux/Unix 系统中,你几乎不会直接使用 lex 命令,而是使用它的一个增强版替代品——flex (Fast Lexical Analyzer Generator)

  • 兼容性flexlex 的超集,绝大多数为 lex 编写的代码都可以在 flex 下直接编译和运行。
  • 功能增强flex 提供了更多功能,比如更好的错误信息、REJECT 选项、开始状态等。
  • 默认安装:在大多数现代发行版中,flex 是默认安装的,而 lex 可能只是一个指向 flex 的符号链接。

在实际使用中,你可能会这样操作:

# 使用 flex 代替 lex
flex your_file.l
# 剩下的编译步骤完全一样
gcc lex.yy.c -o my_program -ll

lexyacc/bison 的协作

lex 只负责词法分析,它识别出一个个的“记号”(Token),INTEGER, PLUS, IDENTIFIER 等,但它不理解这些记号组合起来的语法结构(IF 后面必须跟着一个表达式)。

这就需要语法分析器(如 yaccbison)来上场。

  1. lex 的任务:从输入流中提取记号,并将它们“喂”给语法分析器,当 lex 匹配到一个记号时,它会返回一个代表该记号的整数值(Token ID)。
  2. yacc/bison 的任务:接收这些记号,并根据用户定义的语法规则来判断输入的记号流是否符合语法结构,如果符合,就执行相应的语义动作(比如构建抽象语法树 AST 或直接计算结果)。

协作流程:

  1. 你用 lex.l 文件,定义所有记号的模式。
  2. 你用 yaccbison.y 文件,定义语法规则和记号的整数值。
  3. lex 生成的 yylex() 函数在匹配到规则后,不是执行打印等操作,而是返回一个记号 ID 给 yacc/bison
  4. yacc/bison 生成的 yyparse() 函数会调用 yylex() 来获取下一个记号,并根据语法规则进行解析。

这是一个编译器前端的标准工作模式。

特性 描述
全称 Lexical Analyzer Generator
作用 根据词法规则,自动生成 C 语言的词法分析器代码 (lex.yy.c)。
输入文件 .l 文件,包含定义、规则和 C 代码。
核心函数 yylex(),由 lex 生成,负责匹配输入和执行动作。
关键变量 yytext (匹配的文本), yyleng (文本长度)。
编译命令 lex file.l 生成 C 代码,gcc lex.yy.c -ll 编译链接。
现代替代品 flex (Fast Lexical Analyzer),功能更强,是事实上的标准。
协作工具 通常与 yaccbison (语法分析器生成器) 配合使用,共同构成编译器前端。
分享:
扫描分享到社交APP
上一篇
下一篇