Lex和Yacc工具使用方法
Lex和Yacc从入门到精通(PDF)
Lex和Yacc从入门到精通熊春雷Abstract在开发程序的过程中经常会遇到文本解析的问题,例如:解析C语言源程序,编写 脚本引擎等等,解决这种文本解析的方法有很多,一种方法就是自己手动用C或者 C++直接编写解析程序,这对于简单格式的文本信息来说,不会是什么问题,但是 对于稍微复杂一点的文本信息的解析来说,手工编写解析器将会是一件漫长痛苦 而容易出错的事情。
本系列文档就是专门用来由浅入深的介绍两个有名的Unix工 具Lex和Yacc,并会一步一步的详细解释如何用这两个工具来实现我们想要的任何 功能的解析程序,为了方便理解和应用,我会在该系列的文章中尽可能的采用具 体可行的实例来加以阐释,而且这种实例都是尽可能的和具体的系统平台无关的 ,因此我采用命令行程序作为我们的解析程序的最终结果。
1、环境配置篇开发Lex和Yacc程序最需要的程序就是lex和yacc了,如果你是Unix或者Linux系统,则 系统自带了这两个工具,无需安装,不过值得说明的是GNU/Linux下面的Lex是flex, 而Yacc则是bison。
另外需要的就是一个C/C++语言编译器,由于我们采用的是GNU的 lex和yacc,所以,理所当然的我们就使用GNU的编译器了,如果是Unix或者Linux系统 ,那么编译器应该已经安装了。
在这里我重点讨论的是Windows系统环境下的Lex和 Yacc程序的开发,至于为什么选择Windows系统作为开发平台,则是为了尽可能的让初 学者容易入门。
1.1.必备工具言归正传,首先列举Windows平台下面Lex和Yacc开发环境所需要安装的程序:1.Lex(flex.exe)和Yacc(bison.exe)环境2.C/C++编译器1.2.flex和bison值得说明的是,flex.exe和bison.exe是UnxUtils包中的文件,已经将许多 Unix/Linux平台的程序都移植到了Windows平台,可以直接到UnxUtils网站下载,下载解压缩之后在系统的PATH环境变量中增加UnxUtils所有的exe文件所在的目录,使 得DOS命令行可以直接搜索到flex.exe和bison.exe,除此之外还需要从网络上下载 bison需要的bison.simple和bison.hairy两个文件,并且还要分别设置环境变量 BISON_HAIRY指向bison.hairy,BISON_SIMPLE指向bison.simple。
编译原理lexyacc的联合使用实验报告
编译原理lexyacc的联合使用实验报告《编译原理》课程实验报告题目lex yacc 的联合使用专业班级学号姓名一. 实验题目lex yacc 的联合使用二. 实验日期三. 实验环境(操作系统,开发语言)操作系统是Windows开发语言是C语言四. 实验内容(实验要求)目的:熟悉lex和yacc的接口要求:(1)能对整数、实数进行带变量的四则运算(2)词法分析用lex实现(3)语法分析用yacc实现(4)交源代码和实验报告五. 实验步骤1.在实验二的基础上修改代码,并添加上以下的代码:double { return DOUBLE;}EOF { return 0;}{ID} { return ID;}2.在DOS环境下按照之前的步骤利用flex运行myleb.l,修改其中的错误之后,生成lex.yy.c文件。
3.参照书中第14章第7节和myparser-fifth.y文件中的代码,和老师说的思路修改myparser-sixth.y文件,按照实验要求修改代码。
4.在DOS环境下利用yacc运行myparser-sixth.y文件,会有出错提示,按照出错的地方指示,修改代码,使其最终能通过运行,生成y.tab.c文件。
(此步骤还未完成)5.打开c-free5.0,新建工程,在Source files文件夹中添加之前生成的lex.yy.c文件和y.tab.c文件,然后找到已给的calc.h文件,将其添加到新建工程中的Header files文件夹中,这时就能将三个文件组成一个类似于.exe文件类型的文件,最后运行。
6.运行并调试成功。
六. 实验体会(包括收获、心得体会、存在的问题及解决问题的方法、建议等)这是最后一次实验了,但这次的难度是有史以来最难的一次实验,因为要结合之前我们学到的所有东西。
首先要修改.l文件的代码,再把此文件用lex运行,即词法分析部分用lex 完成;然后是修改myparser-sixth.y文件,尽管有参照的代码,但是要自己实现能对整数和实数进行带变量的四则运算,对于还没有完全掌握.y文件编写的我还是太有难度了,自己先是完成了只对实数的运算,也能运行,但只要添加上实数就会出错,研究了很久,还是没有找出来哪里还有错误;有时候也会出现用yacc运行没错误,但用c-free运行就会出错,提示说.h文件有错误,找了半天,也修改了很多。
lex和Yacc详解
Lex和Y acc工具介绍――编译原理的实用工具1.词法分词器生成工具lexLex的主要功能是生成一个词法分析器(scanner)的C源码。
描述词法分析器的文件,经过lex编译后,生成一个lex.yy.c的文件,然后由C编译器编译生成一个词法分析器。
词法分析器,简单来说,其任务就是将输入的各种符号,转化成相应的标识符(token),转化后的标识符很容被后续阶段处理。
过程如错误!未找到引用源。
图1现在这个lex 文件可以用来生成一个统计行数、字符个数和单词个数的工具。
规则段是由正则表达式和相应的动作组成的。
p 1 {action 1}p 2 {action 2}……p n {action n }值得注意的是,lex 依次尝试每一个规则,尽可能地匹配最长的输入流。
如果有一些内容根可以看出lex的确按照最长的规则匹配。
程序段部分放一些扫描器的其它模块,比如一些动作执行时需要的模块。
也可以在另一个程序文件中编写,最后再链接到一起。
生成C代码后,需用C的编译器编译。
连接时需要指定链接库。
gcc的连接参数为 -ll。
2.正则表达式正则表达式可以描述有穷状态自动机(Finite Automata)接受的语言,也就是定义一个可以接受的串的集合。
转义字符(也称操作符):" \ [ ] ^ - ? . * + | ( ) $ / { } % < >这些符号有特殊含义,不能用来匹配自身。
如果需要匹配的话,可以通过引号(’’)或者转义符号(\)来指示。
比如C”++”C\+\+都可以匹配C++。
非转义字符:所有除了转义字符之外的字符都是非转义字符。
一个非转义字符可以匹配自身。
比如integer匹配文本中出现的integer。
通配符:通配符就是”.”(dot),可以匹配任何一个字符。
字符集:用一对[]指定的字符构成一个字符集。
比如[abc]表示一个字符集,可以匹配a、b、c中的任意一个字符。
使用–可以指定范围。
经典编译工具
经典编译工具Typical cpmpile tool一、正则表达式学过编译原理的朋友肯定都接触过LEX这个小型的词法扫描工具. 但是却很少有人真正把LEX用在自己的程序里. 在构造专业的编译器的时候,常常需要使用到lex和yacc. 正是因为这两个工具,使得我们编写编译器,解释器等工具的时候工作变得非常简单.不过话说回来,会使用lex和yacc的人也确实不简单. Lex和yacc里面牵涉到一系列的编译原理的理论知识,不是简单地看看书就能搞懂的. 本文只是简单地介绍一下lex和yacc的使用方法.相关编译理请查看本科教材.国内大学教材里面对于lex和yacc的介绍很少,有些根本就没有,不过在国外的编译原理教材介绍了很多. 按照学科的分类,国内大学本科里面开的<<编译原理>>教程只是讲解编译的原理,并不讲解实践. 而对于实践方面则是另外一门学科<<编译技术>>. 关于编译技术的书籍在国内是少之又少. 前不久, 听说上海交大的计科内部出版过编译技术的教材.可惜我们这些人就无法得见了. 还好,机械工业出版社引进了美国Kenneth C.Louden所著的经典著作<<编译原理及实践>>中,比较详细地介绍lex和yacc的使用.Lex属于GNU内部的工具,它通常都是gcc的附带工具. 如果你使用的Linux操作系统,那么肯定系统本身就有lex和yacc,不过yacc的名字变成了bison. 如果你使用的Windows操作系统,那么可以到cygwin或者GNUPro里面找得到. 网上也有windows版本lex和yacc,大家可以自己去找一找.本文一共有两篇,一篇是介绍lex,另一篇是介绍yacc. Lex和yacc 搭配使用, 我们构造自己的编译器或者解释器就如同儿戏. 所以我把本文的名字叫做黄金组合.本文以flex( Fase Lex)为例,两讲解如何构造扫描程序.Flex可以通过一个输入文件,然后生成扫描器的C源代码.其实扫描程序并不只用于编译器 .比如编写游戏的脚本引擎的时候,我看到很多开发者都是自己写的扫描器,其算法相当落后(完全没有DFA 的概念化), 甚至很多脚本引擎开发者的词法扫描器都没有编写,而是在运行过程中寻找token(单词). 在现代的计算机速度确实可以上小型的脚本引擎在运行中进行词法扫描, 但是作为一个合格的程序员, 或者说一个合格的计算机本科毕业生而来说, 能够运用编译原理与技术实践,应该是个基本要求.如果要说到词法分析的扫描器源代码编写, 其实也很简单, 会C语言的人都会写. 可是Kenneth Louden在<<编译原理及技术>里面,花了50多页,原因就是从理论角度,介绍标准的,可扩展的,高效的词法扫描器的编写. 里面从正则表达式介绍到DFA(有穷自动机),再到NFA(非确定性有穷自动机),最后才到代码的编写. 以自动机原理编译扫描器的方法基本上就是现在词法扫描器的标准方法, 也就是Lex使用的方法. 在Lex中,我们甚至不需要自己构造词法的DFA, 我们只需要把相应的正则表达式输入, 然后lex能够为我们自己生成DFA,然后生成源代码,可谓方便之极.本文不讲DFA, lex的输入是正则表达式, 我们直接先看看正则表达式方面知识就可以了.1.正则表达式(regular expression):对于学过编译原理的朋友来说,这一节完全可以不看.不过有些东西还是得注意一下,因为在flex中的正则表达式的使用有些具体的问题是在我们的课本上没有说明的.先看看例子:例1.1name Tangl_99这就是定义了name这个正则表达式,它就等于字符串Tangl_99.所以,如果你的源程序中出现了Tangl_99这个字符传,那么它就等于出现一次name正则表达式.例1.2digit 0|1|2|3|4|5|6|7|8|9这个表达式就是说,正则表达式digit就是0,1,2,…,9中的某一个字母.所以无论是0,2,或者是9…都是属于digit这个正则表达式的.“|”符号表示”或者”的意思.那么定义正则表达式name Tangl_99|Running,同样的,如果你的源程序中出现了Tangl_99或者Running,那么就等于出现了一次name 正则表达式.例1.3one 1*“*”符号表示”零到无限次重复”那么one所表示的字符串就可以是空串(什么字符都没有), 1, 11, 111, 11111, 11111111111, 11111111…等等.总之,one就是由0个或者N 个1所组成(N可以为任意自然数).与”*”相同的有个”+”符号.请看下面的例子1.4例1.4realone 1+“+”符号表示”1到无限次重复”那么realone和one不同的唯一一点就是,realone不包含空串,因为”+”表示至少一次重复,那么realone至少有一个1.所以realone 所表达的字符串就是1,11,111, 1111, 11111…,等等.例1.5digit [0-9]letter [a-zA-Z]这里的digit等于例1.2中的digit,也就是说,a|b|c就相当于[a-c].同理,letter也就是相当于a|b|c|d|e|f|…|y|z|A|B|C|D…|Z不过注意的一点就是,你不能把letter写成[A-z],而必须大写和小写都应该各自写出来.例1.6notA [^A]“^”表示非,也就是除了这个字符以外的所有字符所以notA表示的就是除了A以外的所有字符.下面让我们来看看一些一般高级程序语言中常用的综合例子.digit [0-9]number {digit}+letter [a-zA-Z_]digit前面多次提起过,就是0-9的阿拉伯数字.number就是所有的数字组合,也就是整数.Letter前面也提起过,唯一不同的就是多了一个下划线.因为一般我们的C语言中容许有下划线来表示定义的变量名,所以我也把下划线当成英语字母来处理了. 这里number中使用上面定义的digit正则表达式.在lex中,用{digit}就是表示正则表达式digit.newline [\n]whitespace [ \t]+newline就是提行的意思.这里我们使用的是\n这个符号,它和C语言中表示提行号一致.问题是大家可能要问到为什么要使用[]符号.因为在lex中,如果你使用[],那么里面表示的肯定就是单个字符号,而不会被理解成”\”和”n”两个字符. Whitespace就是空格符号的意思.一般的高级程序语言中有两种,一种就是简单的空格,还有一种就是\t制表符.使用了”+”符号,就表示了这些空白符号的无限组合.二、flex的使用看了第一篇的关于正则表达式的说明后,下面我们就来通过它,使用flex这个词法分析工具来构造我们的编译器的词法分析器.关于lex的教程应该是很多,这里我就简单地介绍一下,然后着重后面的lex和yacc的配合使用以及其技巧.所以,如果你不看了后还是不太明白lex或者yacc的使用,请你自己上网去查查,这方面的教程是很多的.我知道的一篇常见的就是Yacc 与 Lex 快速入门Lex 与 Yacc 介绍它的作者就是Ashish Bansal.Flex就是fast lex的意思.而lex就是Lexical Analyzar的意思.flex可以在cygwin 或者gnupro中找到.它是unix的一个工具,属于GNU 组织产品.网上也可以找到单独可以在windows下用的版本.我们一般把我们的词法扫描程序要扫描的一些单词(token)用正则表达式写好,然后作为lex的输入文件,输入命令flex xxx.l(xxx.l就是输入文件),lex经过处理后,就能得到一个名字叫lex.yy.c的C源代码.这个C源代码文件,就是我们的词法扫描程序.通常lex为我们生成的词法分析器的C源代码都是十分复杂而且庞大的,我们一般根本不会去查看里面的代码(放心好了,flex这个东西不会出错的)下面让我们看看几个我已经使用过的几个lex输入文件.这是一个前段时间我为GBA上的一个RPG游戏写的脚本引擎所使用的lex输入文件(部分)例2.1%{/* need this for the call to atof() below */#include#include#include#include "globals.h"%}digit [0-9]number ("-"|"+"?{digit}+hexnumber "0x"({digit}|[a-fA-F])+letter [a-zA-Z]identifier ({letter}|_)({number}|{letter}|_)*newline [\n]whitespace [ \t]+string \"[^"]*\"comment "#"[^#]*"#"%%{string} { return VM_STRING; }"Logo" { return VMIN_LOGO; }"FaceIn" { return VMIN_FACEIN; }"FaceOut" { return VMIN_FACEOUT; } "LoadTile" { return VMIN_LOAD_TILE; } "CreateRole" { return VMIN_CREATE_ROLE; } "ReleaseRole" { return VMIN_RELEASE_ROLE;} "CreateMap" { return VMIN_CREATE_MAP; } "ReleaseMAP" { return VMIN_RELEASE_MAP;} "ShowBitmap" { return VMIN_SHOWBITMAP; } "CreateDialog" { return VMIN_CREATE_DIALOG; } "ReleaseDialog" { return VMIN_RELEASE_DIALOG;} "Fight" { return VMIN_FIGHT; } "Delay" { return VMIN_DELAY; }"PressA" { return VMIN_PRESS_A; }"PressB" { return VMIN_PRESS_B; }"PressR" { return VMIN_PRESS_R; }"PressL" { return VMIN_PRESS_L; } "PressStart" { return VMIN_PRESS_START; } "PressSelect" { return VMIN_PRESS_SELECT;} {number} { return VM_NUMBER; } {whitespace} { /* skip whitespace */ }{identifier} { return VM_ID; }{newline} ;. ;%%int yywrap(){return 1;}这里的lex输入文件一共有三个部分,用%%分开.第一部分中的%{和}%中的内容就是直接放在lex输出C代码中的顶部.我们通过它可以来定义一些所需要的宏,函数和include一些头文件等等.我的这个lex输入文件中也没什么特别的东西,就是常规的C源文件的include头文件%{/* need this for the call to atof() below */#include#include#include#include "globals.h"%}第一部分中,除了前面的%{和}%包含的部分,下面的就是正则表达式的定义.看了第一篇的正则表达式,这样你就能够在这里派上用场了.让我们来看看我这里定义的正则表达式:digit [0-9]number ("-"|"+"?{digit}+hexnumber "0x"({digit}|[a-fA-F])+letter [a-zA-Z]identifier ({letter}|_)({number}|{letter}|_)*newline [\n]whitespace [ \t]+string \"[^"]*\"comment "#"[^#]*"#"digit就不用说了,就是0-9的阿拉伯数字定义,第一篇文章中也举了这个例子.number就是digit的1到无限次的重复,再在其前面加上”+”和”-“符号.注意:“a”:即使a是元字符,它仍是字符a\a: 当a是元字符时候,为字符aa?: 一个可选的a,也就是说可以是a,也可以没有aa|b: a或b(a): a本身[abc]: 字符a,b或c中的任一个[a-d]: a,b,d或者d中的任一个[^ab]: 除了a或b外的任何一个字符.: 除了新行之外的任一个字符{xxx}: 名字xxx表示的正则表达式这里需要特别说明的就是newline [\n]newline就是新行,这里我使用了[]把\n换行号括起来.因为如果我直接用\n表示的话,那么按照上面的规则,那就会看成\和n两个字符,所以我使用了[\n].有些时候newline也被写成[\n]|[\r\n].因为在文本文件中,一般换行一次,那么就是一个\n(0xA),可是在二进制文件中,换行有时候又是\r\n(0xD,0xA)一共两个字符号.第二部分就是定义扫描到正则表达式的动作.这些动作其实就是C代码,它们将会被镶嵌在lex输出的C文件中的yylex()函数中.上面的例子的动作其实十分平常,就是返回一个值.我们在外部使用这个lex为我们生成C代码的时候,只需要使用它的int yylex()函数.当我们使用一次yylex(),那么就会自动去扫描一个匹配的正则表达式,然后完成它相应的动作.这里的动作都是返回一值,那么yylex就会返回这个值.通常默认yylex返回0时候,表示文件扫描结束,所以你的动作中最好不要返回0,以免发生冲突.当然,动作中也可以不返回一值,那么yylex就会完成这个动作后自动扫描下一个可以被匹配的字符串,一直到扫描到文件结束.当扫描到一个可以被匹配的字符串,那么这个时候,全局变量yytext 就等于这个字符串请大家一定记住这些正则表达式的顺序.如果出现一个字符串,可以同时匹配多个正则表达式,那么它将会被定义在前面的正则表达式匹配.所以我一般把字符串string定义在最前面.如果文件中的字符没有被lex输入文件中任何一个字符匹配,那么它会自动地被标准输出.所以大家一定要记住在每个正则表达式处理完毕后,一定要加上{newline}和.这两个正则表达式的动作.好,让我们看看lex为我们输出C文件中提供一些常量Lex 变量yyinFILE* 类型。
Lex工具
Lex工具Lex工具是一种词法分析程序生成器,它可以根据词法规则说明书的要求来生成单词识别程序,由该程序识别出输入文本中的各个单词。
1、lex程序的结构-定义部分-规则部分-用户子程序部分其中规则部分是必须的,定义和用户子程序部分是任选的。
(1) 定义部分定义部分起始于"%{"符号,终止于"%}"符号,其间可以是包括include语句、声明语句在内的C语句。
%{#include "stdio.h"#include "y.tab.h"extern int lineno;%}(2) 规则部分规则部分起始于"%%"符号,终止于"%%"符号,其间则是词法规则。
词法规则由模式和动作两部分组成。
模式部分可以由任意的正则表达式组成,动作部分是由C语言语句组成,这些语句用来对所匹配的模式进行相应处理。
需要注意的是,lex将识别出来的单词存放在yytext[]字符数据中,因此该数组的内容就代表了所识别出来的单词的内容。
%%[\t] {;}[0-9]+\.?|[0-9]*\.[0-9]+{ sscanf(yytext,"%1f", &yylval.val);return NUMBER; }\n { lineno++;return '\n'; }. { return yytex+[0]; }%%(3) 用户子程序部分用户子程序部分可以包含用C语言编写的子程序,而这些子程序可以用在前面的动作中,这样就可以达到简化编程的目的。
下面是带有用户子程序的lex程序片段。
"/*" skipcmnts();. /* rest of rules */%%skipcmnts(){for ( ; ; ){while (input()!='*');if(input()!='/')unput(yytext[yylen-1]);else return;}2、lex工具的使用方法首先编写一个lex程序vi lex.l%{#include "stdio.h"%}%%[\n] ;[0-9]+ printf("Interger: %s \n",yytext);[0-9]*\.[0-9]+ printf("Float: %s\n",yytext);[a-zA-Z][a-zA-Z0-9]* printf("Word:%s\n",yytext);. printf("Other symbol:%c\n",yytext[0]);%%然后使用lex将lex.l转换成C语言程序$lex lex.l使用上述命令产生的C语言程序为lex.yy.c然后使用C编译程序将lex.yy.c编译成可执行程序regn$cc -c lex.yy.c$cc lex.yy.o -ll -o regn下面可以使用regn来识别单词$vi testfilex=355y=113p=x/y# ./regn < testfileWord:xOther symbol:=Interger: 355Word:yOther symbol:=Interger: 113Word:pOther symbol:=Word:xOther symbol:/Word:y#yacc工具--------yacc工具是一种语法分析程序生成器,它可以将有关某种语言的语法说明书转换成相应的语法分析程序,由该程序完成对相应语言中语句的语法分析工作。
LEX和YACC的使用(例子)
LEX和YACC的使⽤(例⼦)1、简单C语⾔的词法分析程序;%{#include<stdio.h>#include<stdlib.h>#include<ctype.h>#include<string.h>%}digit [0-9]letter [A-Za-z]other_char [!-@\[-~]id ({letter}|[_])({letter}|{digit}|[_])*string {({letter}|{digit}|{other_char})+}int_num {digit}+%%[ |\t|\n]+ "auto"|"double"|"int"|"struct"|"break"|"else"|"long"|"switch"|"case"|"enum"|"register"|"typedef"|"char"|"extern"|"return"|"union"|"const"|"float"|"short"|"unsigned"|"continue"|"for"|"signed"|"void"|"default"|"go \"([!-~])*\" {printf("CONST_string,%s\n",yytext);}-?{int_num}[.]{int_num}?([E][+|-]?{int_num})? {printf("CONST_real,%s\n",yytext);}"0x"?{int_num} {printf("CONST_int,%s\n",yytext);}","|";"|"("|")"|"{"|"}"|"["|"]"|"->"|"."|"!"|"~"|"++"|"--"|"*"|"&"|"sizeof"|"/"|"%"|"+"|"-"|">"|"<"|">="|"<="|"=="|"!="|"&"|"^"|"|"|"&"|"||"|"+="|"-="|"*="|"/="|"%="|">>="|"<<="|"&="|"^="|"|="|"=" {printf("%s,NULL\n",yytext);}{id} {printf("ID,%s\n",yytext);}{digit}({letter})+ {printf("error1:%s\n",yytext);}%%#include <ctype.h>Upper(char *s,int l){int i;for(i=0;i<l;i++){s[i]=toupper(s[i]);}}yywrap(){return 1;}注意:要得到输出信息,需要⾃⾏添加main函数,lex默认的main函数没有输出的。
编译器的自动生成工具LEX和YACC的使用方法
编译器的自动生成工具LEX和YACC的使用方法一、词法分析程序产生器LEX的用法1.1 Lex概述Lex是一个词法分析器(扫描器)的自动产生系统,它的示意图如图1.1。
Lex源程序是用一种面向问题的语言写成的。
这个语言的核心是正规表达式,用它描述输入串的词法结构。
在这个语言中用户还可以描述当某一个词形被识别出来时要完成的动作,例如在高级语言的词法分析器中,当识别出一个关键字时,它应该向语法分析器返回该关键字的内部编码。
Lex并不是一个完整的语言,它只是某种高级语言(称为lex的宿主语言)的扩充,因此lex 没有为描述动作设计新的语言,而是借助其宿主语言来描述动作。
我们只介绍C作为lex的宿主语言时的使用方法,在Unix系统中,FORTRAN语言的一种改进形式Ratfor也可以做lex的宿主语言。
图1.1 Lex示意图Lex自动地表示把输入串词法结构的正规式及相应的动作转换成一个宿主语言的程序,即词法分析程序,它有一个固定的名字yylex,在这里yylex是一个C语言的程序。
yylex将识别出输入串中的词形,并且在识别出某词形时完成指定的动作。
看一个简单的例子:写一个lex源程序,将输入串中的小写字母转换成相应的大写字母。
程序如下:%%[a-z] printf(“%c”,yytext[0]+'A'-'a');上述程序中的第一行%%是一个分界符,表示识别规则的开始。
第二行就是识别规则。
左边是识别小写字母的正规式。
右边就是识别出小写字母时采取的动作:将小写字母转换成相应的大写字母。
Lex的工作原理是将源程序中的正规式转换成相应的确定有限自动机,而相应的动作则插入到yylex中适当的地方,控制流由该确定有限自动机的解释器掌握,对于不同的源程序,这个解释器是相同的。
1.2 lex源程序的格式lex源程序的一般格式是:{辅助定义的部分}%%{识别规则部分}%%{用户子程序部分}其中用花括号起来的各部分都不是必须有的。
Lex与Yacc学习(七)之环境配置另一种方式
Lex与Yacc学习(七)之环境配置另⼀种⽅式必备⼯具flex与bison安装运⾏flex-2.5.4a-1.exe 和 bison-2.4.1-setup.exe ⽂件安装⾄D:\Software Files\GnuWin32下,然后按配置环境变量:将路径 D:\Software Files\GnuWin32\bin 复制于Path中。
Cygin安装配置运⾏cyg_win_setup.exe⽂件安装⾄D:\cygwin下,然后配置环境变量:将路径D:\cygwin\bin复制于Path中;注意:在D:\cygwin\bin⽂件夹下,有g++.exe ⼤⼩为1KB 与g++ 3.exe ⼤⼩为95KB ,我们需要把95KB的 g++ 3.exe命名为 g++.exe ,1 KB的g++可删除,或者命名为g++3.exe;同理,有gcc.exe ⼤⼩为1KB 与gcc 3.exe ⼤⼩为95KB ,我们需要把95KB的 gcc 3.exe命名为 gcc.exe ,1 KB的gcc可删除,或者命名为gcc 3.exe 。
必要⽂件复制我们发现D:\cygwin\bin下⾯并没有flex.exe 与bison.exe,因此,(1)将安装好的D:\Software Files\GnuWin32\bin下的flex.exe 与bison.exe复制到D:\cygwin\bin下⾯;(2)再将D:\Software Files\GnuWin32的share⽂件夹复制到D:\cygwin下⾯;(3)将D:\Software Files\GnuWin32\lib下的libfl.a 和 liby.a 复制到D:\cygwin\lib下⾯;检测配置是否成功打开D:\cygwin下的Cygwin.bat 或者系统的cmd,按照以下⽅式检验:环境搭建结束!。
Lex和Yacc入门教程(八).使用堆栈编译语法
Lex和Yacc⼊门教程(⼋).使⽤堆栈编译语法Lex和Yacc⼊门教程(⼋).使⽤堆栈编译语法分类: C++/C/C# 2007-06-05 11:08 4571⼈阅读评论(26) 收藏举报yacccommandshellunix存储linuxLex和Yacc应⽤⽅法(⼋).使⽤堆栈编译语法草⽊⽠ 20070604⼀、序前⾯⼀些系列⽂章着重介绍了递归语法树在编译理论⽅⾯的应⽤。
本⽂则会介绍另⼀种实现⽅式----堆栈。
堆栈在底层系统有⼗分⼴泛的应⽤,同样也⼗分擅长处理语法结构,这⾥通过实际⽰例探讨如何构造堆栈完成语法分析。
重要补充:下⾯是本系列⽂章全⽰例代码统⼀的调试测试环境,另对于lex,yacc⽂件需要存储为Unix格式,这⼀点和Linux,Unix下shell很类似,DOS格式的Shell是不能够被执⾏的,同样bison,lex编译DOS格式⽂件会出错误提⽰:Red Hat Linux release 9 (Shrike)Linux 2.4.20-8gcc version 3.2.2 20030222bison (GNU Bison) 1.35lex version 2.5.4flex version 2.5.4⼆、具体⽰例本⽰例主要完成功能:1 ⽀持整型,浮点型和字符串型2 ⽀持变量存储,变量名可为多个字符3 ⽀持整型,浮点型的+-*/()=运算法则4 ⽀持字符串型赋值5 ⽀持print打印整型,浮点型和字符串型6 ⽀持打印变量值7 ⽀持while if else switch四种控制结构,并⽀持控制结构的嵌套8 ⽀持> >= < <= != == 六种⽐较运算,同时也⽀持字符串的⽐较9 ⽀持 && || 复合⽐较运算10 ⽀持对空格和TAB的忽略处理11 ⽀持#的单⾏注释12 ⽀持{}多重组合13 ⽀持编译错误的具体显⽰14 ⽀持外部变量值传⼊(整型,浮点型和字符型)15 ⽀持外部变量获取(整型,浮点型和字符型)16 完整的企业应⽤模式三、⽰例全代码(略)A.stack.l----------------------------------------------B.stack.y----------------------------------------------C.stack.h----------------------------------------------D.stackparser.c----------------------------------------------E.public.h----------------------------------------------F.main.c----------------------------------------------G.mk 编译shell⽂件----------------------------------------------bison -d stack.ylex stack.lgcc -g -c lex.yy.c stack.tab.c stackparser.car -rl stack.a *.ogcc -g -o lw main.c stack.aH.mkclean shell⽂件----------------------------------------------rm stack.tab.crm stack.tab.hrm lex.yy.crm *.orm *.arm lw四、思路说明上⾯列出的代码是⽬前最长的。
LEX和YACC入门
LEX 将这些规则翻译为词法分析器。每一个规则依次包含一 个正则表达式以及该正则表达式得到匹配时要运行的一些代 码。
任何没有得到匹配的文本则简单地拷贝到标准输出。
2020/5/13
6
三、LEX程序设计
LEX正规表达式(1)
expression: value '+' value { System.out.println("Matched a '+' expression.\n"); }
2020/5/13
23
四、YACC程序设计
类似于 LEX, YACC 也有 一套变量和函数可供用户 来进行功能扩展。
YYSTYPE 定义了用来将 值从 lexer 拷贝到解析器 或者 YACC 的 yylval ( 另一个 YACC 变量)的 类型。默认的类型是 int 。 由于字符串可以从
的值。
2020/5/13
26
四、LEX与YACC结合
2020/5/13
27
public static void main(String args[]) {
int n = 1;
mylexer lexer = new mylexer();
if (lexer.yycreate(null)) {
n = lexer.yylex();
}
System.out.println("word count = "+lexer.wordCount);
两个百分号标记指出了 LEX程序中这一段的结束和三段中第二段 的开始。
10
Yacc 与 Lex 快速入门
Yacc 与 Lex 快速入门Lex 与 Yacc 介绍Lex 和 Yacc 是 UNIX 两个非常重要的、功能强大的工具。
事实上,如果你熟练掌握 Lex 和 Yacc 的话,它们的强大功能使创建 FORTRAN 和 C 的编译器如同儿戏。
Ashish Bansal 为您详细的讨论了编写自己的语言和编译器所用到的这两种工具,包括常规表达式、声明、匹配模式、变量、Yacc 语法和解析器代码。
最后,他解释了怎样把 Lex 和 Yacc 结合起来。
Lex 代表 Lexical Analyzar。
Yacc 代表 Yet Another Compiler Compiler。
让我们从 Lex 开始吧。
LexLex 是一种生成扫描器(词法分析器)的工具。
扫描器是一种识别文本中的词汇模式的程序。
这些词汇模式(或者常规表达式)在一种特殊的句子结构中定义,这个我们一会儿就要讨论。
一种匹配的常规表达式可能会包含相关的动作。
这一动作可能还包括返回一个标记。
当 Lex 接收到文件或文本形式的输入时,它试图将文本与常规表达式进行匹配。
它一次读入一个输入字符,直到找到一个匹配的模式。
如果能够找到一个匹配的模式,Lex 就执行相关的动作(可能包括返回一个标记)。
另一方面,如果没有可以匹配的常规表达式,将会停止进一步的处理,Lex 将显示一个错误消息。
Lex 和 C 是强耦合的。
一个.lex文件(Lex 文件具有.lex的扩展名)通过 lex 公用程序来传递,并生成 C 的输出文件。
这些文件被编译为词法分析器的可执行版本。
Lex 的常规表达式常规表达式是一种使用元语言的模式描述。
表达式由符号组成。
符号一般是字符和数字,但是 Lex 中还有一些具有特殊含义的其他标记。
下面两个表格定义了 Lex 中使用的一些标记并给出了几个典型的例子。
用 Lex 定义常规表达式字符含义A-Z, 0-9, a-z构成了部分模式的字符和数字。
.匹配任意字符,除了 \n。
编译原理之lex,yacc学习
编译原理之lex,yacc学习写在前⾯的⼏句废话最近在项⽬的过程中接触了lex 和 yacc,他们可以帮助我们来实现⾃⼰的领域语⾔。
最典型的应⽤就是可以帮助我们来实现⾃定义测试脚本的执⾏器。
但是,这⾥也有⼀个限制,就是测试脚本要做的基本事情必须有现成的C语⾔库来实现,否则就做不到了;如果基本的操作是⽤java来做的,那么还可以⽤Antlr,这⾥不对Antlr做详细介绍。
lex是什么?教科书上把lex的作⽤的作⽤叫做“词法分析 lexical analysis ”,这个中⽂叫法⾮常让⼈看不明⽩(叫做“符号提取”更合适),其实从它的英⽂单词lexical上来看他的意思其实是⾮常清楚的。
lexical,在webster上的解释是:of or relating to words or the vocabulary of a language as distinguished from its grammar and construction。
指的是:⼀种语⾔中关于词汇、单词的,与之相对的是这种语⾔的语法和组织这么来看的话 lexical analysis 的作⽤就应该是语⾔中的词汇和单词分析。
事实上他的作⽤就是从语⾔中提取单词。
放到编程语⾔中来说,他要做的事情其实就是提取编程语⾔占⽤的各种保留字、操作符等等语⾔的元素。
所以他的另外⼀个名字scanner其实更形象⼀些,就是扫描⼀个⽂本中的单词。
lex把每个扫⾯出来的单词叫统统叫做token,token可以有很多类。
对⽐⾃然语⾔的话,英语中的每个单词都是token,token有很多类,⽐如non(名词)就是⼀个类token,apple就是属于这个类型的⼀个具体token。
对于某个编程语⾔来说,token的个数是很有限的,不像英语这种⾃然语⾔中有⼏⼗万个单词。
lex⼯具会帮我们⽣成⼀个yylex函数,yacc通过调⽤这个函数来得知拿到的token是什么类型的,但是token的类型是在yacc中定义的。
lex与yacc快速入门
lex与yacc快速⼊门第⼀节、lex和yacc是什么? lex 代表 lexical analyzar(词法分析器),yacc 代表 yet another compiler compiler(编译器代码⽣成器)。
lex和yacc在UNIX下分别叫flex和bison. 可以搜索到很多介绍flex&bison的⽂章,但这类⽂章对初学者来说不太容易看懂。
我们举个简单的例⼦来理解lex和yacc:在linux下,有很多系统配置⽂件,⼀些linux下的软件也有配置⽂件,那么程序是如何读取配置⽂件中的信息的呢?先⽤到lex词法分析器,读取配置⽂件中的关键词(后⾯说到的token标记其实可看做关键词);然后把关键词递交给yacc,yacc对⼀些关键词进⾏匹配,看是否符合⼀定的语法逻辑,如果符合就进⾏相应动作。
上⾯举的例⼦是分析配置⽂件内容的,当然可分析其他⽂件内容,或者制作编译器等。
第⼆节、⼀个简单的lex程序。
1、程序代码。
来看⼀个简单的lex程序,代码见下⾯,这段lex程序的⽬的是:输⼊⼏⾏字符串,输出⾏数,单词数和字符的个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25/******************************************** Name : test.l* Date : Mar. 11, 2014* Blog : /lucasysfeng/* Description : ⼀个简单的lex例⼦,输⼊⼏⾏字符串,* 输出⾏数,单词数和字符的个数。
*******************************************//* 第⼀段 */%{int chars = 0;int words = 0;int lines = 0;%}/* 第⼆段 */%%[a-zA-Z]+ { words++; chars += strlen(yytext); } \n { chars++; lines++; }. { chars++; }%%/* 第三段 */main(int argc, char **argv){yylex();printf("%8d%8d%8d\n", lines, words, chars);}程序中yytext是lex变量,匹配模式的⽂本存储在这⼀变量中。
Lex和Yacc简明教程
和简明教程 Lex Yacc作者 :Thomas Niemann翻译: 傅惠忠目录序言3 导言4 Lex6 理论6练习7 YACC11 理论11练习,第一部分12练习,第二部分15 计算器18 描述18包含文件20Lex21 输入文件Yacc22 输入文件解释器26编译器27图28Lex34 进阶字符串34保留字35lex35 的调试Yacc37 进阶递归37IfElse37 歧义错误信息38继承属性39内含动作39调试Yacc39 参考书目40lex yacc lex yacc本书将教会你如何使用和构造一个编译器。
Yacc使用指南
YACC(BISON)使用指南YACC(Yet Another Compile-Compiler)是语法分析器生成工具,它生成的是LALR分析器。
Yacc于上世纪70年代产生,是美国贝尔实验室的产品,已经用于帮助实现了几百个编译器。
Yacc是linux下的工具,本实验使用的编译工具是cygwin(cygwin在windows下模拟一个linux环境)下的bison,它与Yacc的使用方法基本相同,只有很少的差别。
一.YACC的使用方法:1.用户按照Yacc规定的规则写出文法说明文件,该文件一般以.y为扩展名(有的系统以.grm为扩展名。
)2.Yacc编译器将此文法说明文件(假设该文件为filename.y)转换成用C编写的语法分析器文件filename.tab.c(如果在编译时加上某些参数还可以生成头文件filename.tab.h)。
这个文件里至少应该包含语法分析驱动程序yyparse()以及LALR分析表。
在这个文件里,语法分析驱动程序调用yylex()这个函数获取输入记号,每次调用yylex()都能获取一个输入记号。
yylex()可以由lex生成,也可以自己用c语言写一个yylex()函数,只要每次调用该函数时返回一个记号即可。
3.用C编译器(本实验所给工具为cygwin下的gcc)将filename.tab.c编译为可执行文件(cygwin下默认为a.exe)。
4. a.exe即为可执行的语法分析器。
二.文法说明文件(即Yacc源程序)的写法:文法说明文件,顾名思义,就是将一个语言的文法说清楚。
在Yacc中,这个说明文件可以分为三部分,以符号%%分开:[第一部分:定义段]%%第二部分:规则段[%%第三部分:辅助函数段]其中,第一部分及第三部分和第三部分之上的%%都可以省略(即上述方括号括起的部分可以省略)。
以%开头的符号和关键字,或者是规则段的各个规则一般顶着行首来写,前面没有空格。
1. 第一部分定义段的写法:定义段可以分为两部分:第一部分以符号%{和%}包裹,里面为以C语法写的一些定义和声明:例如,文件包含,宏定义,全局变量定义,函数声明等。
使用lex+yacc分析simple语言
使用lex yacc分析simple语言简单语句(2007-03-18 18:01:07)转载关于lex和yacc的理论知识大家可以从网上搜到一些,比较好的资料是机械工业出版社《lex与yacc》中文第二版,当然也可以从网上下到电子版看一看,这里就不赘述了。
下面介绍一个简单的算术表达式和变量说明语句的lex和yacc程序:LEX程序部分:%{//头文件,全局变量定义#include "myparser.h"#include <stdio.h>#include <string.h>#include <stdlib.h>int c;extern int yylval;%}/////////////////////////////////////////////////////////////// declarations section 说明部分可以为空// place any declarations here%%//////////////////////////////////////////////////////////// rules section 规则部分不可或缺// place your Lex rules here/***********************空格,关键字的识别*********************/// return语句返回token标记yacc进行语法识别" " ;var { printf("%10s KEY_var \n",yytext);return (VAR);}real { printf("%10s KEY_real \n",yytext);return (REAL);}integer { printf("%10s KEY_integer \n",yytext);return (INTEGER);} char { printf("%10s KEY_char \n",yytext);return (CHAR);}bool { printf("%10s KEY_bool \n",yytext);return (BOOL);}/*************************界线符的识别************************/ : { printf("%10s SYM_colon \n",yytext);return (COLON);}, { printf("%10s SYM_comma \n",yytext);return (COMMA);}/*************************标志符的识别************************/ [A-Za-z]+ {c = yytext[0];yylval = c - 'a';return(LETTER);printf("%10s \n",yytext);}/*************************整数的识别*********************/ [0-9]+ {printf("%10s \n",yytext);// c = yytext[0];//yylval = c - '0';yylval = atof(yytext);return (DIGIT);}/********************其他符号的识别***************************/c = yytext[0];return(c);}%%///////////////////////////////////////////////////////////////// programs section 辅助程序部分YACC程序部分:%{#include "mylexer.h"#include <stdio.h>#include <ctype.h>#include <stdlib.h>#include <assert.h>int regs[26];%}///////////////////////////////////////////////////////////////// // declarations section// attribute type%include {#ifndef YYSTYPE#define YYSTYPE int}// place any declarations here%start list //文法从list开始识别%token DIGIT LETTER VAR //标记%token COLON COMMA%token REAL INTEGER CHAR BOOL%left '+' '-' //左结合%left '*' '/'%left UMINUS /*supplies precedence for unary minus */%%/////////////////////////////////////////////////////////////// // rules section// place your YACC rules here (there must be at least one)list: /*empty */|list stat '\n'|list VARDECLARE '\n'|list error '\n'{yyerror;};VARDECLARE: VAR IDS COLON TYPE {printf("变量声明成功\n");} ;IDS: IDS COMMA LETTER| LETTER {$$=$1;};TYPE: REAL|INTEGER|CHAR {$$=$1;}|BOOL|{yyerror("未声明变量类型\n");yyerrok();};stat: expr{printf("算术表达式分析成功结果为:%d\n",$1);}|LETTER '=' expr//赋值语句识别{regs[$1] = $3;};expr: '(' expr ')'//算术表达式的规约识别{$$ = $2;}|'(' expr error$$=$2;yyerror("missing')'");yyerrok(); }|error expr ')'{$$=$2;yyerror("missing'('");yyerrok(); }|expr '*' expr{$$ = $1 * $3;}|expr '/' expr{ if ($3==0)yyerror("Divide by zero\n");else$$ = $1 / $3;}|expr '+' expr{$$ = $1 + $3;}|expr '-' expr{$$ = $1 - $3;|'-' expr %prec UMINUS{$$ = -$2;}|LETTER{$$ = regs[$1];}|DIGIT{$$=$1;};%%///////////////////////////////////////////////////////// programs sectionmain(){printf("*******用lex和yacc完成simple语言简单语句的分析*******\n"); printf("请输入要编译的语句:\n");return yyparse();}void yyerror(char *s){fprintf(stderr, "%s\n",s);return 0;}yywrap(){return 1;}//////////////////END///////////////////////当一起使用lex扫描程序和yacc语法分析程序时,语法分析程序(parser)是较高级别的例程。
Yacc使用指南
对于非终结符,可以这样用%type 定义其属性值的类型,例如: %type <id> sym1 sym2 %type <num> sym3 上述定义将非终结符 sym1 和 sym2 (非终结符一般用小写单词表示) 定义为 char*类型, 而将 sym3 定义为 int 类型。 2. 第二部分规则段的写法: 规则段实际上定义了文法的非终结符及产生式集合, 以及当归约整个产生式是应执行的 操作。假如产生式为 expr expr plus term | term,则在规则段应该写成: expr : expr PLUS term {语义动作} | term {语义动作} ; 其中,产生式左部的非终结符 expr 应写在冒号左边,冒号表示产生符号,产生式右部每 个用 | 分隔的部分单独写一行并用 | 分隔,整组产生式写完后,结尾处应加分号;,分号表 示同一左部的一组产生式结束。 每一行产生式后面大括号括起的语义动作表示该条产生式归约时应该执行的操作,语 义动作使用 C 语言的语法来写,他们将会被直接拷贝到 yacc 编译器生成的 c 语言源程序文 件中。在语义动作中,可以引用存放在属性值栈中的文法符号的属性值,$$符号可引用产生 式左部非终结符的属性值,而$i 则可以引用产生式右部第 i 个文法符号的属性值,当然,前 提是你必须为这些文法符号的属性定义了类型, 而且在你使用这些属性值之前, 已经将这些 属性值放入了属性值栈对应的地方。例如: expr : expr PLUS term {$$ = $1 + $3;} | term {$$ = $1;} ; 上述例子中,归约 expr PLUS term 为 expr 时,将会做如下操作:从属性值栈中取出产 生式右部 expr 的属性值($1)和 term 的属性值($3) ,将两者的和存入属性值栈中对应产 生式左部的 expr 的属性值的位置($$) 。归约 term 为 expr 时,则将 term 的属性($1)存入 产生式左部 expr 的属性值($$)中。 注意,在这个例子中,PLUS 是终结符,而 expr 和 term 都是非终结符(能出现在产生 式左部的符号都已定义为非终结符) 。 由于用到了 expr 和 term 的属性值, 因此必须先用%type 对 expr 和 term 的属性值类型进行定义(除非属性值栈只能存放默认类型 int 的元素,并且 expr 和 term 的属性值类型都为 int,这种情况下可以不必定义他们的属性值类型) 。本例中 $$=$1; 这一语义动作实际上可以省略不写, 因为将 term 归约为 expr 的过程在移进归约分析 中实际上是将栈顶的 term 退栈,而将 expr 入栈,对应的属性值栈中的操作实际上是将 term 的属性值退栈, 再将 term 的属性值作为 expr 的属性值入栈 (因为 term 的属性值要赋给 expr 的属性值) ,最终的结果是 term 的属性值仍然留在属性值栈上原来的位置,只不过现在这个 位置我们认为是 expr 的属性值。 3. 第三部分辅助函数段的写法: 辅助函数段用 C 语言语法来写,辅助函数一般指在规则段中用到或者在语法分析器的 其他部分用到的函数。这一部分一般会被直接拷贝到 yacc 编译器产生的 c 源文件中。 一般来说,除规则段用到的函数外,辅助函数段一般包括如下一些例程: yylex() , yyerror(),main()。 int yylex()是词法分析程序,它返回记号。语法分析驱动程序 yyparse()将会调用 yylex()
Lex使用指南
Lex使⽤指南Lex是由美国Bell实验室M.Lesk等⼈⽤C语⾔开发的⼀种词法分析器⾃动⽣成⼯具,它提供⼀种供开发者编写词法规则(正规式等)的语⾔(Lex语⾔)以及这种语⾔的翻译器(这种翻译器将Lex语⾔编写的规则翻译成为C语⾔程序)。
Lex是linux下的⼯具,本实验使⽤的编译⼯具是cygwin(cygwin在windows下模拟⼀个linux环境)下的flex,它与lex的使⽤⽅法基本相同,只有很少的差别。
1.Lex的基本原理和使⽤⽅法Lex的基本⼯作原理为:由正规式⽣成NFA,将NFA变换成DFA,DFA经化简后,模拟⽣成词法分析器。
其中正规式由开发者使⽤Lex语⾔编写,其余部分由Lex翻译器完成.翻译器将Lex源程序翻译成⼀个名为lex.yy.c的C语⾔源⽂件,此⽂件含有两部分内容:⼀部分是根据正规式所构造的DFA状态转移表,另⼀部分是⽤来驱动该表的总控程序yylex()。
当主程序需要从输⼊字符流中识别⼀个记号时,只需要调⽤⼀次yylex()就可以了。
为了使⽤Lex所⽣成的词法分析器,我们需要将lex.yy.c程序⽤C编译器进⾏编译,并将相关⽀持库函数连⼊⽬标代码。
Lex的使⽤步骤可如下图所⽰:2.lex源程序的写法Lex源程序必须按照Lex语⾔的规范来写,其核⼼是⼀组词法规则(正规式)。
⼀般⽽⾔,⼀个Lex源程序分为三部分,三部分之间以符号%%分隔。
[第⼀部分:定义段]%%第⼆部分:词法规则段[%%第三部分:辅助函数段]其中,第⼀部分及第三部分和第三部分之上的%%都可以省略(即上述⽅括号括起的部分可以省略)。
以%开头的符号和关键字,或者是词法规则段的各个规则⼀般顶着⾏⾸来写,前⾯没有空格。
Lex源程序中可以有注释,注释由/*和*/括起,但是请注意,注释的⾏⾸需要有前导空⽩。
1)第⼀部分定义段的写法:定义段可以分为两部分:第⼀部分以符号%{和%}包裹,⾥⾯为以C语法写的⼀些定义和声明:例如,⽂件包含,宏定义,常数定义,全局变量及外部变量定义,函数声明等。
Win7下lex_与_yacc的安装配置
Win7下lex_与_yacc的安装配置Win7下lex 与yacc的安装配置一、如何在windows下安装lex。
首先,下载下载flex和bison。
安装时,设定路径最好不要是在Program Files文件夹里面,因为文件夹名字带空格会影响以后的使用。
可如此:安装在c:\gnuwin32下面。
其次,由于我们使用的flex和bison都是GNU的工具,所以为了方便,采用的C/C++编译器也采用GNU的编译器GCC,目前Windows平台的GCC主要是MinGW编译器。
安装过程中,会自动开启控制台,仅需稍等片刻,任其自动完成。
安装完毕后,将c:\gnuwin32\lib里面的libfl.a和liby.a复制到C:\MinGW\lib里面。
再者,设置环境变量。
现在该安装的都已安装完毕,那么我们该设置环境变量了。
右键点击“计算机”,“属性”、“高级系统设置”、“环境变量”,在下面系统变量里面找到PATH,修改,在后面加上c:\gnuwin32\bin和C:\MinGW\bin。
注意每一个路径是用分号分隔的,然后写第一个路径,然后分号,第二个路径。
如果你的安装的目录和我不一样,就对应修改。
开始两个简单的文件来测试安装是否成功。
1) 新建文本文件,更改名称为lex.l,敲入下面代码%{int yywrap(void);%}%%%%int yywrap(void){return 1;}2) 新建文本文件,更改名称为yacc.y,敲入下面代码%{void yyerror(const char *s);%}%%program:;%%void yyerror(const char *s){}int main(){yyparse();return 0;}打开控制台,进入到刚才所建立文件(lex.l,yacc.y)所在的文件夹。
输入flex lex.l输入bison yacc.y如果看到当前文件夹上多了两个文件(yacc.tab.c,lex.yy.c),那么说明lexyacc已经安装配置成功。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
编译器的自动生成工具LEX和YACC的使用方法Lex自动地表示把输入串词法结构的正规式及相应的动作转换成一个宿主语言的程序,即词法分析程序,它有一个固定的名字yyler,在这里yyler是一个C语言的程序。
Yylex将识别出输入串中的词形,并且在识别出某词形时完成指定的动作。
看一个简单的例子:写一个lex源程序,将输入串中的小写字母转换成相应的大定字母。
程序如下:%%[a-z]printf(“%c”.yytext[0]+'A'-'a');上述程序中的第一行%%是一个分界符,表示识别规则的开始。
第二行就是识别规则。
左边是识别小写字母的正规式。
右边就是识别出小写字母时采取的动作:将小写字母转换成相应的大写字母。
Lex的工作原理是将源程序中的正规式转换成相应的确定有限自动机,而相应的动作则插入到yylox中适当的地方,控制流由该确定有限自动机的解释器掌握,不同的源程序,这个解释器是相同的。
1.2 lex源程序的格式lex源程序的一般格式是:{辅助定义的部分}%%{识别规则部分}%%{用户子程序部分}其中用花括号起来的各部分都不是必须有的。
当没有“用户子程序部分”时,第二个%%也可以省去。
第一个%%是必须的,因为它标志着识别规则部分的开始,最短的合法的lex源程序是:%%它的作用是将输入串照原样抄到输出文件中。
识别规则部分是Lex源程序的核心。
它是一张表,左边一列是正规式,右边一列是相应的动作。
下面是一条典型的识别规则:integer printf("found keywcrd INT");这条规则的意思是在输入串中寻找词形“integer”,每当与之匹配成功时,就打印出“foundkeyword INT”这句话。
注意在识别规则中,正规式与动作之间必须用空格分隔开。
动作部分如果只是一个简单的C表达式,则可以写在正规式右边同一行中,如果动作需要占两行以上,则须用花括号括起来,否则会出错。
上倒也可以写成:integer {printf("found keyword INT");}下面先介绍识别规则部分的写法,再介绍其余部分。
1.3 Lex用的正规式一个正规式表示一个字符串的集合。
正规式由正文字符与正规式运算符组成.正文字符组成基本的正规式,表示某一个符号串;正规式运算符则将基本的正规式组合成为复杂的正规式,表示字符串的集合。
例如:ab仅表示字符串ab,而(a b)+表示字符串的集合:{ab,abab,ababab,…)。
Lex中的正规式运算符有下列十六种:” [ ]∧ -?"*+| ()/${} %<>上述运算符需要作为正文字符出现在正规式中时,必须借助于双引号”或反斜线\,具体用法是;xyz“++”或xyz\+\+表示字符串xyz++为避免死记上述十多个运算符,建议在使用非数字或字母字符时都用双引号或反斜线。
要表示双引号本身可用\”,要表示反外线用”\”或前面说过,在识别规则中空格表示正规式的结束,因此要在正规式中引进空格必须借助双引号或反斜线,但出现在方括号[]之内的空格是例外。
几个特殊符号:\n是回车换行(newline)\t是tab\b是退格(back space)下面按照运算符的功能分别介绍上述正规式运算符。
1.字符的集合用方括号对可以表示字符的集合。
正规式[a b c]与单个字符a或b或c匹配在方括号中大多数的运算符都不起作用,只有\-和∧例外。
运算符-表示字符的范围,例如[a-z 0-9 <>-]表示由所有小写字母,所有数字、尖括号及下划线组成的字符集合。
如果某字符集合中包括-在内,则必须把它写在第一个或最后一个位置上,如[-+0-9]与所有数字和正负号匹配在字符集合中,运算符∧必须写在第一个位置即紧接在左方括号之后,它的作用是求方括号中除∧之外的字符组成的字符集合相对于计算机的字符集的补集,例如[∧abc]与除去a、b 和c以外的任何符号匹配。
运算符\在方括号中同样发挥解除运算符作用的功能。
2.与任意字符匹配的正规式运算符. 形成的正规式与除回车换行符以外的任意字符匹配。
在lex的正规式中,也可以用八进制数字与\一起表示字符,如[\40-\176]与ASCII字符集中所有在八进制40(空格)到八进制176(~)之间的可打印字符匹配。
3.可有可无的表达式运算符?指出正规式中可有可无的子式,例如ab?c与ac或abc匹配,即b是可有可无的。
4.闭包运算运算符*和十是Lex正规式中的闭包运算符,它们表示正规式中某子式的重复,例如"a*"表示由0个或多个a组成的字符串的集合,而"a+"表示由1个或多个a组成的字符串的集合,下面两个正规式是常用的:[a-z]+[A-Za-z][A-Za-z 0-9]*第一个是所有由小写字母组成的字符串的集合,第二个是由字母开头的字母数字串组成的集合。
5、选择和字符组运算符|表示选择:(ab|cd)与ab或cd匹配运算符()表示一组字符,注意()与[ ]的区别。
(ab)表示字符串ab,而[ab]则表示单个字符a或b。
圆括号()用于表示复杂的正规式,例如:(ab|cd+)?(ef)*与abefef, efef, cdef, cddd匹配,但不与abc, abcd或abcdef匹配。
6、上下文相关性lex可以识别一定范围的上下文,因此可在一定程度上表示上下文相关性。
若某正规式的第一个字符是∧,则仅当该正规出现在一行的开始处时才被匹配,一行的开始处是指整个输入串的开始或者紧接在一个回车换行之后,注意∧还有另一个作用即求补,∧的这两种用法不可能发生矛盾。
若某正规式的最后一个字符是$,则仅当该表达式出现在一行的结尾处时才被匹配,一行的结尾处是指该表达式之后紧接一个回车换行。
运算符/指出某正规式是否被匹配取决于它的后文,例如: ab/cd,仅在ab之后紧接cd的情况下才与ab匹配。
$其实是/的一个特殊情形,例如下面两个正规式等价:ab$,ab/ n 某正规式是否被匹配,或者匹配后执行什么样的动作也可能取决于该表达式的前文,前文相关性的处理方法在后面专门讨论,将用到运算符"<>"7、重复和辅助定义当被{}括起来的是数字对时,{}表示重复;当它括起来的是一个名字时,则表示辅助定义的展开。
例如:a{1,5} ,表示集合{a.aa.aaa.aaaa.aaaaa}.{digit}则与预先定义的名叫dight 的串匹配,并将有定义插入到它在正规式中出现的位置上,辅助定义在后面专门讨论。
最后,符号%的作用是作为lex源程序的段间分隔符。
1.4 Lex源程序中的动作前面说过当Lex识别出一个词形时,要完成相应的动作。
这一节叙述Lex为描述动作提供的帮助。
首先应指出,输入串中那些不与任何识别规则中的正规式匹配的字符串将被原样照望抄到输出文件中去。
因此如果用户不仅仅是希望照抄输出,就必须为每一个可能的词形提供识别规则,并在其中提供相应的动作。
用lex为工具写程序语言的词法分析器时尤其要注意。
最简单的一种动作是滤掉输入中的某些字符串,这种动作用C的空语句“;”来实现。
例:滤掉输入串中所有空格、tab和回车换行符,相应的识别规则如下:[t n];如果相邻的几条规则的动作都相同,则可以用|表示动作部分,它指出该规则的动作与下一条规则的动作相同。
例如上倒也可以写成:“ ”|“ t”|“ n”;注意t和n中的双引号可以去掉。
外部字符数组yytext的内容是当前被某规则匹配的字符串,例如正规式[a-z]+与所有由小写字母组成的字符串匹配,要想知道与它匹配的具体字符串是什么,可用下述规则:[a-z]+printf(“%s”,yytext);动作printf(“%s”,yytext)就是将字符数组yytext的内容打印出来,这个动作用得很频繁,Lex提供了一个宏ECHO来表示它,因此上述识别规则可以写成:[a-z]+ECHO;请注意,上面说过缺省的动作就是将输入串原样抄到输出文件中,那么上述规则起什么作用呢?这一点将在“规则的二义性”一节中解释。
有时有必要知道被匹配的字符串中的字符个数,外部变量yyleng就表示当前yytext中字符的个数。
例如要对输入串中单词的个数和字符的个数进行计数(单词假定是由大写或小写字母组成的字符串),可用下述规则:[a-zA-Z]+ {words++;Chars+=yyleng;}注意被匹配的字符串的第一个字符和最后一个字符分别是yytext[0]和yytext[yyleng-1]下面介绍三个Lex提供的在写动作时可能用到的C函数l.yymore()当需下一次被匹配的字符串被添加在当前识别出的字符串后面,即不使下一次的输入替换yytext中已有的内容而是接在它的内容之后,必须在当前的动作中调用yymore( )例:假设一个语言规定它的字符串括在两个双引号之间,如果某字符串中含有双引号,则在它前面加上反斜线\。
用一个正规式来表达该字符串的定义很不容易,不如用下面较简明的正规式与yymore()配合来识别:" [∧"]*{if(yytext[yyleng-1]= =’ ’yymore( );else…normal user processing}当输入串为”abc\"def”时,上述规则首先与前五个字符”abc\匹配,然后调用yymore()使余下部分”def被添加在前一部分之后,注意作为字符串结尾标志的那个双引号由”normal user proessing”部分负责处理2.yyless(n)如果当前匹配的字符串的末尾部分需要重新处理,那么可以调用yyless(n)将这部分子串“退回”给输入串,下次再匹配处理。
yyless(n)中的n是不退回的字符个数,即退回的字符个数是yyleng-n。
例;在C语言中串“=-a”具有二义性,假定要把它解释为“=-a”同时给出信息,可用下面的识别规则:=-[a-zA-Z]{printf(“Operator(=-)ambiguous n”);yyless(yyleng-1);…action for=-…}上面的规则先打印出一条说明出现二义性的信息,将运算符后面的字母返回给输入串,最后将运算符按“=-”处理.另外,如果希望把“=- a”解释为”=- a”,这只需要把负号与字母一起退回给输入串等候下次处理,用下面的规则即可:=-[a-zA-Z]{printf(“Operator(=-)ambiguous n”);yyless (yyleng-1);…action for = …}3. yywrap ( )当Lex处理到输入串的文件尾时,自动地调用yywrap(),如果yywrap()返回值是1,那么Lex就认为对输入的处理完全结束,如果yywrap()返回的值是0,Lex就认为有新的输入串等待处理。