CONTENTS:linux
[TOC]git
这篇文章的内容包括:正则表达式
lex语法格式shell
linux下flex的安装和使用express
flex实例ide
flex源代码的编译和使用函数
Lex是LEXical compiler的缩写,是Unix环境下很是著名的工具,主要功能是生成一个词法分析器(scanner)的C源码,描述规则采用正则表达式(regular expression)。描述词法分析器的文件*.l,通过lex编译后,生成一个lex.yy.c 的文件,而后由C编译器编译生成一个词法分析器。词法分析器,简单来讲,其任务就是将输入的各类符号,转化成相应的标识符(token),转化后的标识符 很容易被后续阶段处理。 —— [ 百度百科 ] 工具
在使用apt软件包管理器linux系统上咱们能够很是方便地安装并使用flex。在终端中输入如下代码安装flex:flex
$> sudo apt-get install flex
flex代码的源文件每每是以.l为后缀名的。
.l文件经过如下命令编译(以文件名为scanner.l为例):this
$> flex scanner.l
编译后在源代码相同目录下会生成一个lex.yy.c,这就是生成的可以执行上述scanner.l功能的c语言代码。使用gcc编译便可生成词法分析程序1:
void yywrap() { return 1; }
$> gcc lex.yy.c -o scanner
而后将须要分析的文件(以input.txt为例)做为参数传递给scanner执行分析:
$> ./scanner input.txt
flex的语法被分为三个部分:
{definitions} %% {rules} %% {user subroutines}
LABEL REGULAR_EXPRESSION
LABEL是这里类字符串的名称,REGULAR_EXPRESSION则是匹配这种字符串的正则表达式。正则表达式的语法主要包括:
符号 | 含义 |
---|---|
\ | 或 |
[] | 括号中的字符取其一 |
\- | a-z表示ascii码中介于a-z包括a.z的字符 |
\\ | 转义(flex不能识别除字母外的字符) |
* | 0或多个字符 |
? | 0或1个字符 |
+ | 1或多个字符 |
^ | 除此以外的其他字符 |
. | 除\n外的全部字符,等价于^\n |
示例:
1. INT [1-9][0-9]*|[0] /*整数类型,0或不以0开头的由0-9组成的字符串*/ 2. FLOAT [0-9]*[.][0-9]+([eE][+-]?[0-9]*|[0])?f? /*浮点数格式*/ 3. LP \( /*一个左圆括号*/
注:用%{ %}括起来的语句将被彻底写入编译后的c语言文件中。
例如
`%{
#include <stdio.h> int num_id = 0;
%}`
规则部分的语法以下:
{LABEL1} | {LABLE2} | ... { /*TODO*/ }
TODO部分是告诉编译器在匹配到字符串以后程序须要作些什么。
例如在匹配到整数后打印这个整数:
{INT} { printf("Pick up an integer, value is %d", atoi(yytext)); printf("Pick up an integer, value is %s", yytext); }
其中atoi()函数将字符串转换为整数。
此处主要是放置用户须要执行的c语言代码。他们会被原封不动地加入到lex.yy.c文件的末尾。
这里通常用来存放main函数,详细会在后面说明。
下面经过一个实例来具体展现flex的使用方式,主要功能是扫描并匹配文件中的字符串,并回显其类型和内容,代码以下:
/************************ * scanner.l * @author mist * 2015-9-21 23:08 ************************/ %{ #include "stdio.h" #include "stdlib.h" %} INT_DEX [1-9][0-9]*|[0] INT_HEX [0][Xx]([1-9][0-9]*|[0]) INT_OCT [0][0-7] FLOAT [0-9]*[.][0-9]+([eE][+-]?[0-9]*|[0])?f? SEMI [;] COMMA [,] ASSIGNOP [=] RELOP [>]|[<]|[>][=]|[<][=]|[=][=]|[!][=](^[=]) PLUS [+] MINUS [-] STAR [*] DIV [/] AND [&][&] OR [|][|] DOT [.] NOT [!] TYPE int|float LP \( RP \) LB \[ RB \] LC \{ RC \} STRUCT struct RETURN return IF if ELSE else WHILE while SPACE [ \n\t] ID [a-zA-Z_][a-zA-Z_0-9]* /*end of definition*/ %% {SEMI} { printf("get semmi : %s\n", yytext); } {COMMA} { printf("get comma : %s\n", yytext); } {ASSIGNOP} { printf("get assignop : %s\n", yytext); } {INT_DEX} | {INT_HEX} | {INT_OCT} { printf("get an integer: %s\n", yytext); } {FLOAT} { printf("get a float: %s\n", yytext); } {PLUS} | {MINUS} | {DIV} | {STAR} { printf("get an operator: %s\n", yytext); } {RELOP} { printf("get a relop: %s\n", yytext); } {AND} | {OR} | {NOT} { printf("get a logic operator: %s\n", yytext); } {DOT} { printf("get a dot: %s\n", yytext); } {STRUCT} | {RETURN} | {IF} | {ELSE} | {WHILE} { printf("get keyword: %s\n", yytext); } {TYPE} { printf("get type: %s\n", yytext); } {LP} | {RP} | {LB} | {RB} | {LC} | {RC} { printf("get brackets : %s\n", yytext); } {SPACE} | . { /*ABANDON THESE CHARACTORS*/ } {ID} { printf("get an ID: %s\n", yytext); } %% int yywrap() { return 1; } int main(int argc, char** argv) { if (argc > 1) { if (!(yyin = fopen(argv[1], "r"))) { perror(argv[1]); return 1; } } while (yylex()); return 0;
咱们须要为生成的分析程序编写main函数。首先须要经过yyin来获取指向被分析文件的文件FILE指针,通常文件的路径经过控制台的第二个参数得到。分析部分的实体在函数yylex()中。
yywrap()用于判断是否已经扫描完了全部的文件。若是它在最后一个文件的末尾被调用,则返回值为1。此时程序将中止分析,能够用来扫描多个文件。
输入文本:
`int float {}()[] 0
0x0 0x123
123.5
.3e-10f
= >= || && ! ; ,
this_is_an_id
id123
if then else
`
输出:
get type: int get type: float get brackets : { get brackets : } get brackets : ( get brackets : ) get brackets : [ get brackets : ] get an integer: 0 get an integer: 0x0 get an integer: 0x123 get a float: 123.5 get a float: .3e-10f get assignop : = get a relop: >= get a logic operator: || get a logic operator: && get a logic operator: ! get semmi : ; get comma : , get an ID: this_is_an_id get an ID: id123 get keyword: if get an ID: then get keyword: else
另外附上词法要求:
INT / A sequence of digits without spaces1 /
FLOAT /* A real number consisting of digits and one decimal point. The deci-
mal point must be surrounded by at least one digit2 */
ID /* A character string consisting of 52 upper- or lower-case alphabetic, 10
numeric and one underscore characters. Besides, an identifier must not start
with a digit3 */
SEMI ;
COMMA ,
ASSIGNOP =
RELOP > | < | >= | <= | == | !=
PLUS +
MINUS -
STAR *
DIV /
AND &&
OR ||
DOT .
NOT !
TYPE int | float
LP (
RP )
LB [
RB ]
LC {
RC }
STRUCT struct
RETURN return
IF if
ELSE else
WHILE while
1) 词法单元INT表示的是全部(无符号)整型常数。一个十进制整数由0~9十个数字组
成,数字与数字中间没有如空格之类的分隔符。除“0”以外,十进制整数的首位数字
不为0。例如,下面几个串都表示十进制整数:0、23四、10000。为方便起见,你能够
假设(或者只接受)输入的整数都在32bits位以内。
2) 整型常数还能够以八进制或十六进制的形式出现。八进制整数由0~7八个数字组成并以
数字0开头,十六进制整数由0~九、A~F(或a~f)十六个数字组成并以0x或者0X开头。
例如,0237(表示十进制的159)、0xFF32(表示十进制的65330)。
3) 词法单元FLOAT表示的是全部(无符号)浮点型常数。一个浮点数由一串数字与一个
小数点组成,小数点的先后必须有数字出现。例如,下面几个串都是浮点数:0.七、
12.4三、9.00。为方便起见,你能够假设(或者只接受)输入的浮点数都符合IEEE754单
精度标准(即均可以转换成C语言中的float类型)。
4) 浮点型常数还能够以指数形式(即科学记数法)表示。指数形式的浮点数必须包括基
数、指数符号和指数三个部分,且三部分依次出现。基数部分由一串数字(0~9)和一
个小数点组成,小数点能够出如今数字串的任何位置;指数符号为“E”或“e”;指
数部分由可带“+”或“”(也可不带)的一串数字(0~9)组成,“+”或“”(如
果有)必须出如今数字串以前。例如01.23E12(表示1.23 1012)、43.e-4(表示43.0
10-4)、.5E03(表示0.5 103)。
5) 词法单元ID表示的是除去保留字之外的全部标识符。标识符能够由大小写字母、数字
以及下划线组成,但必须以字母或者下划线开头。为方便起见,你能够假设(或者只
接受)标识符的长度小于32个字符。
6) 除了INT、FLOAT和ID这三个词法单元之外,其它产生式中箭头右边都表示具体的字
符串。例如,产生式TYPE int | float表示:输入文件中的字符串“int”和“float”都
将被识别为词法单元TYPE。
2.2
High-level Definitions