lex与yacc程序配合编写语法分析程序

前言

凡是lex识别正则表达式的地方,yacc都可以识别完整的语法,lex将输入流分成块(标记),然后yacc去的这些块并将他们逻辑性的归组到一起

语法

yacc采用你指定的语法,并为这些语法中的表达式创建分析程序,语法是是一系列规则,用来识别输入符号

statement -> NAME = expression

expression -> NUMBER + NUMBER | NUMBER - NUMBER

->左侧缩写为LHS(left -hand side),右侧缩写为RHS(right-hand side),LHS必须为非终结符,输入中的和被词法分析程序返回的为终结符或标记

使用树状结构来表示所分析的句子,例如输入“fred = 12 + 13”


递归规则

规则可以直接或者间接的引用本身

expression -> NUMBER
			| expression + NUMBER
			| expression - NUMBER

现在这个规则可以匹配"fred = 12 + 13 - 11 +7"这样的句子了

移进/规约分析

yacc的语法程序总是在寻找哪一个规则和当前能看到的标记匹配,每次读取一个标记,如果没有匹配到规则,就压入内部堆栈,并且换到一个反映刚刚读取的标记的新的状态,这个动作叫做移进,如果匹配到了一条规则,就会把右侧符号弹出堆栈,将左侧符号压入堆栈,并且切换到一种反映新符号的新状态,这个动作叫做规约,通过不断移进规约,语法程序就可以分析输入了

yacc不能分析的语法

  • 歧义语法:同样的输入,符合多个分析树
  • 需要向前(输入流的后面)看多个标记才能确定是否匹配到规则
phrase -> cart_animal AND CART
		| work_animal AND PLOW

cart_animal -> HORSE | GOAT
work_animal -> HORSE | OX

对于上面的文法,并没有歧义,输入如果是“HORSE AND CART”,当看到HORSE的时候,并不知道是cart_animal 还是work_animal,因为要知道后面AND 和后面的CART的时候,才可以确定是cart_animal,所以这个不行,改成下面的就可以,因为只向前看了一个标记,这个yacc是支持的
phrase -> cart_animal CART
		| work_animal PLOW

cart_animal -> HORSE | GOAT
work_animal -> HORSE | OX
当输入“HORSE CART”,读取HORSE的时候并不知道是cart_animal 还是work_animal,但是下一个是CART,所以规约成cart_animal CART,再规约到phrase

加减法程序

cal.y
%{
#include <stdio.h>
%}

%token PRINT NUMBER
%%
statement: 
	PRINT expression		{
								printf("\ntest yacc PRINT expression\n");
								printf("result is %d", $2);
							}
	|expression 			{
								printf("\ntest yacc expression\n");
								printf("%d is the result", $1);
							}
	;

expression: 
	expression '+' NUMBER 	{
								printf("\ntest yacc expression + NUMBER\n");
								$$ = $1 + $3;
							}
	|NUMBER 				{
								printf("\ntest yacc NUMBER\n");
								$$ = $1;
							} 


cal.l
%{
#include "y.tab.h"
extern int yylval;
%}
%%
print {printf("\ntest lex print\n"); return PRINT;}
[0-9]+ {printf("\ntest lex NUMBER\n");yylval = atoi(yytext);  return NUMBER;}
[ \t] ;
\n return 0;
. {printf("\ntest lex %s", yytext); return yytext[0];} 
%%

mini2:cal nosources$ yacc -d cal.y && lex cal.l && cc -o cal y.tab.c lex.yy.c -ly -ll
mini2:cal nosources$ ./cal
print 2+3

test lex print

test lex NUMBER

test yacc NUMBER

test lex +
test lex NUMBER

test yacc expression + NUMBER

test yacc PRINT expression
result is 5mini2:cal nosources$ 
可见,“print 2+3” 这条语句,先接受到print,然后接受2,并且识别成NUMBER,然后接受+,然后接受3,识别成NUMBER,这时候,可以规约expression + NUMBER,之后又可以规约 PRINT expression,最终得到“result is 5”



  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验二 词法分析器 一、实验目的 掌握词法分析器的构造原理,掌握手工编程或LEX编程方法之一。 二、实验内容 编写一个LEX程序,使之生成一个词法分析器,能够输入的源程序转换为单词序列输出。 三、实验环境 Flex+VC6.0 四、实验注意 1.Id正则表达式:{letter}({letter}|{digit})* 2.Num正则表达式:{digit}+(\.{digit}+)?(E[+-]?{digit}+)? 3.注释:(\/\*(.)*\*\/) 4.关键字再加上其他字符就又能编程id,所以在词法分析时,id的判断应该放在关键字前面,这样才不会误判 5.由于本程序知识简单的打印数字,因此没有考虑数字的转换 6.">="比">"多一个字符,它应该放在前面判断,其他类似的也应该如此安排 五、实验代码 ******************************************************************************* 实验文件:lex.l、lex.yy.c 实验结果:lex.exe 运行方式:打开lex.exe,弹出input.txt,在其中输入所要测试的程序,保存并关闭,即可在output.txt中看到所得结果 ******************************************************************************* %{ void Install(char *type); %} %option noyywrap delim [ \t] newline [\n] digit [0-9] num {digit}+(\.{digit}+)?(E[+-]?{digit}+)? letter [A-Za-z] id {letter}({letter}|{digit})* key ("if"|"while"|"do"|"break"|"true") basic ("int"|"float"|"bool"|"char") op (">="|""|"<"|"="|"!="|"+"|"-"|"*"|"/") comment (\/\*(.)*\*\/) %% delim {;} newline {printf("\n");} {num} {Install("Num");} {key} {Install("Key");} {basic} {Install("Basic");} {op} {Install("Op");} ";" {Install("Comma");} {id} {Install("ID");} {comment} {Install("Comment");} "(" | "[" | "{" {Install("lbracket");} ")" | "]" | "}" {Install("rbracket");} %% void Install(char *s) { fprintf(yyout, "%s:%s ", s, yytext); } int main() { printf("please input the test program in input.txt\n"); system("input.txt"); yyin = fopen("input.txt", "r"); yyout = fopen("output.txt", "w" ); yylex(); fclose(yyout); fclose(yyin); printf("analysis result in output.txt\n"); system("output.txt"); return 0; } 六、实验小结 本次的实验由于使用了flex,所以代码较短,麻烦的事flex的正则式表达,由于该使用规则只有简单介绍,而网上找的教程难免有比重就轻之嫌,所以得到上述表达式着实费力,且有的没有成功,例如bracket的(\ ((.)*\ ))或者("("(.)*")")使用时都没有成功,所以便单独写出,有点不伦不类。至于其他的,都较为简单,完。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值