[编译原理]lex和bison实现逻辑表达式求值以及短路计算次数统计

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

目录

内容:逻辑表达式的求值工具

具体要求

解决方案

设计文法

设计语法制导定义

编写词法分析器

编辑语法和语义分析:

实验难点

完整代码

实验结果


内容:逻辑表达式的求值工具

输入一个包含数值的逻辑表达式,输出计算其真值,和因短路操作而跳过的数值比较的次数。输出格式为Output: [true or false], [次数]。注意,逻辑表达式的计算符合短路算法。其中,!计入逻辑运算次数,而 == 或 != 只用于非布尔型的数值比较。例如: 

  • 输入:3 == 2
    输出:Output: false, 0 
  • 输入:3 > 2
    输出:Output: true, 0 
  • 输入:3 > 2 && 10 > 1
    输出:Output: true, 0 
  • 输入:3 > 2 && 10 > 12
    输出:Output: false, 0 
  • 输入:3 < 2 && 10 > 12
    输出:Output: false, 1 
  • 输入:3 < 2 && 10 < 12
    输出:Output: false, 1 
  • 输入:( 3 <= 2 || 13 <= 19 ) && 1 < 2 
    输出:Output: true, 0 
  • 输入:( 3 >= 2 || 13 <= 19 ) && 1 < 2 
    输出:Output: true, 1 
  • 输入:( 3 >= 2 || 13 <= 19 ) || 1 < 2 
    输出:Output: true, 2 
  • 输入:! ( 3 > 2 )
    输出:Output: false, 0 
  • 输入:! ( 3 > 2 || ( 13 < 19 || 1 < 2 ) )
    输出:Output: false, 2 

具体要求

(1) 为了解决上面问题,需要设计一个文法描述该问题的逻辑表达式,并设计语法制导定义求解最后的输出值。

(2) 根据(1)的文法,设计基于flex的词法分析器。具体形式为1个flex文件的源代码。

(3) 在(2)的基础上,设计基于bison的语法(同时包含语义)分析器。具体形式为1个bison文件的源代码。

(4) 在文档中,描述(1)的内容,以及(2)(3)的设计方案(不需要粘贴很多源代码到文档中)。文档中可以包括问题的难点、求解思路等,但不要包含显而易见的步骤。文档以清楚描述内容为主,不易过长,字数和篇幅不是采分点。

(5) 已知输入的表达式中不包含中文字符。文法应该能够兼容下面字符:数值比较<、<=、>、>=、==、!=,布尔比较&&、||、!,以及括号(、)。注意:== 或 != 只用于非布尔型的数值比较

(6) 词法分析和语法分析应具有基本的报错能力,例如对于不符合的语法,如输入了字母或者小数,输出”Error.”。

解决方案

  • 设计文法

(1)S -> R
(2)R -> R or R
(3)R -> R and R

(4)R -> ! R
(5)R -> ( R )
(6)R -> value < value

(7)R -> value <= value
(8)R -> value > value

(9)R -> value >= value

(10)R -> value == value

(11)R -> value !=value

  • 设计语法制导定义

该程序输出为逻辑表达式的值以及因短路机制而跳过的运算步骤,因此,为非终结符定义综合属性val、total和cut。value值为其对应的真假值,total为其包含的总运算次数,cut为其已跳过的运算次数。

产生式

语义规则

S -> R

If(R.val) print(“Output: true,”+ R.cut);

Else print(“Output:false,”+ R.cut)

R -> R1 or R2

R.val = R1.val || R2.val; R.total = R1.total + R2.total;

If(R1.val) R.cut = R1.cut + R2.total;

Else R.cut = R1.cut + R2.cut;

R -> R1 and R2

R.val = R1.val && R2.val; R.total = R1.total + R2.total;

If(R1.val) R.cut = R1.cut + R2.cut;

Else R.cut = R1.cut + R2.total;

R -> ! R1

R.val = !R1.val; R.total = R1.total + 1; R.cut = R1.cut;

R - > ( R )

R.val = R1.val; R.total = R1.total; R.cut = R1.cut;

R -> value < value

R.val = value < value; R.total = 1; R.cut = 0;

R -> value <= value

R.val = value <= value; R.total = 1; R.cut = 0;

R -> value > value

R.val = value > value; R.total = 1; R.cut = 0;

R -> value >= value

R.val = value >= value; R.total = 1; R.cut = 0;

R -> value == value

R.val = value == value; R.total = 1; R.cut = 0;

R -> value != value

R.val = value != value; R.total = 1; R.cut = 0;

  • 编写词法分析器

定义双符号非终结符:

NLT >=

NGT <=

EQL ==

NEQ !=

AND &&

OR \|\|
{NLT} {

yylval = *yytext;

return nlt;

}/* 此处以NLT为例 */

定义数字digit非终结符,需要做数值转换,将字符串转换为整型,用到atoi函数:

digit [0-9]

%%

{digit}+ {

yylval = atoi(yytext);

return VALUE; 

}

定义单符号终结符:

[()=!><\n] {return *yytext;}

定义空白:

{whitespace}	{ ; }


{newline}	{ return 0; }

.	{
	printf("Error! Wrong token '%s'.\n", yytext);
	exit(0);
}
  • 编译语法和语义分析器

设计非终结符结构,包含val、total和cut综合属性:

typedef struct

{

     int val;

     int total;

     int cut;

}myStruct;

定义token以及左右结合规则;其中除! 为右结合外,其余比较符号皆为左结合:

%token VALUE

%token nlt ngt eql neq and or 

%right   '!'

%left   '<'  '>' 

%left   nlt ngt eql neq and or

 

编辑语法和语义分析:

S: R {{if($1.val)printf("Output: true,%d",$1.cut);else printf("Output: false,%d",$1.cut); return 0;}}

;

R: 

  |R or R    {$$.val = $1.val || $3.val;

$$.total = $1.total + $3.total;

if($1.val) $$.cut = $1.cut + $3.total;

else $$.cut = $1.cut + $3.cut;}

  |R and R    {$$.val = $1.val && $3.val;

$$.total = $1.total + $3.total;

if($1.val) $$.cut = $1.cut + $3.cut;

else $$.cut = $1.cut + $3.total;}

  |'!' R         {$$.val =  ! $2.val; $$.total = $2.total + 1; $$.cut = $2.cut;}

  |'('R')'       {$$.val = $2.val; $$.total = $2.total; $$.cut = $2.cut;}

  |VALUE '<' VALUE   {$$.val = ($1.val < $3.val); $$.total = 1; $$.cut = 0;}

  |VALUE ngt VALUE   {$$.val = ($1.val <= $3.val); $$.total = 1; $$.cut = 0;}

  |VALUE '>' VALUE   {$$.val = ($1.val > $3.val); $$.total = 1; $$.cut = 0;}

  |VALUE nlt VALUE   {$$.val = ($1.val >= $3.val); $$.total = 1; $$.cut = 0;}

  |VALUE eql VALUE   {$$.val = ($1.val == $3.val); $$.total = 1; $$.cut = 0;}

  |VALUE neq VALUE   {$$.val = ($1.val != $3.val); $$.total = 1; $$.cut = 0;}

  • 实验难点

1、本实验在macOS系统上进行,与windows不同,在安装flex、bison和gcc后,可编写.l和.y文件,编译过程如下:

bison -d mytool.y

flex -omytool.yy.c mytool.l

gcc mytool.tab.c mytool.yy.c

./a.out

2、由于非终结符有多个综合属性,因此需要定义struct添加多个属性。

3、语义设计时需要注意优先级,依次为or and ! () < <= > >= == !=

4、在词法定义中,需要给双符号比较符定义别名,才可在之后设计词法;digit非终结符需要使用atoi方法将输入的字符串转换为int格式,否则在后续的比较中无法比较两位以上的数字。

  • 完整代码

mytool.l

%{
	#include <stdio.h>
	#include "mytool.tab.h"
%}

NLT >=
NGT <=
EQL ==
NEQ !=
AND &&
OR \|\|

whitespace [ \t\f\v]
newline [\r\n]

digit	[0-9]
%%

{whitespace}	{ ; }


{newline}	{ return 0; }

{digit}+ {
yylval = atoi(yytext);
return VALUE; 
}



[()=!><\n]	{return *yytext;}


{NLT}	{
yylval = *yytext;
return nlt;
}
{NGT}	{
yylval = *yytext;
return ngt;
}
{EQL}	{
yylval = *yytext;
return eql;
}
{NEQ}	{
yylval = *yytext;
return neq;
}
{AND}	{
yylval = *yytext;
return and;
}
{OR}	{
yylval = *yytext;
return or;
}

.	{
	printf("Error! Wrong token '%s'.\n", yytext);
	exit(0);
}

%%
int yywrap(void)
{
	return 1;
}

mytool.y

%{
	#include <stdio.h>

	#ifndef BTOD_H_INCLUDED
	#define BTOD_H_INCLUDED
	typedef struct
	{
   	 int val;
   	 int total;
   	 int cut;
	}myStruct;


	#endif

	#define YYSTYPE myStruct
	int yylex(void);  
	void yyerror(char const *);      
%}
%token VALUE
%token nlt ngt eql neq and or 
%right   '!'
%left   '<'  '>' 
%left   nlt ngt eql neq and or 

%%
S: R {{if($1.val)printf("Output: true,%d",$1.cut);else printf("Output: false,%d",$1.cut); return 0;}}
;
R: 
  |R or R    {$$.val = $1.val || $3.val;
		$$.total = $1.total + $3.total;
		if($1.val) $$.cut = $1.cut + $3.total;
		else $$.cut = $1.cut + $3.cut;}
  |R and R    {$$.val = $1.val && $3.val;
		$$.total = $1.total + $3.total;
		if($1.val) $$.cut = $1.cut + $3.cut;
		else $$.cut = $1.cut + $3.total;}
  |'!' R         {$$.val =  ! $2.val; $$.total = $2.total + 1; $$.cut = $2.cut;}
  |'('R')'       {$$.val = $2.val; $$.total = $2.total; $$.cut = $2.cut;}
  |VALUE '<' VALUE   {$$.val = ($1.val < $3.val); $$.total = 1; $$.cut = 0;}
  |VALUE ngt VALUE   {$$.val = ($1.val <= $3.val); $$.total = 1; $$.cut = 0;}
  |VALUE '>' VALUE   {$$.val = ($1.val > $3.val); $$.total = 1; $$.cut = 0;}
  |VALUE nlt VALUE   {$$.val = ($1.val >= $3.val); $$.total = 1; $$.cut = 0;}
  |VALUE eql VALUE   {$$.val = ($1.val == $3.val); $$.total = 1; $$.cut = 0;}
  |VALUE neq VALUE   {$$.val = ($1.val != $3.val); $$.total = 1; $$.cut = 0;}
 
;
%%
int main()
{
	yyparse();
	return 0;
}
void yyerror(char const *msg)  
{  
    fprintf(stderr, "%s\n",msg);  
}  
  • 实验结果

        (太尴尬了,把experiment拼错了) 以下是要求给的数据:

(base) joseph@MacBook-Pro exepriment % ./a.out          
3>5&&1<2
Output: false,1%                                                                (base) joseph@MacBook-Pro exepriment % ./a.out 
3 == 2      
  Output: false,0%                                                              (base) joseph@eMacBook-Pro exepriment % ./a.out
3 > 2
 Output: true,0%                                                                (base) joseph@MacBook-Pro exepriment % ./a.out
3 > 2 && 10 > 1
      Output: true,0%                                                           (base) joseph@MacBook-Pro exepriment % ./a.out
3 > 2 && 10 > 12
      Output: false,0%                                                          (base) joseph@MacBook-Pro exepriment % ./a.out
3 > 2 && 10 > 12
      Output: false,0%                                                          (base) joseph@MacBook-Pro exepriment % ./a.out
3 < 2 && 10 > 12
      Output: false,1%                                                          (base) joseph@MacBook-Pro exepriment % ./a.out
3 < 2 && 10 < 12
      Output: false,1%                                                          (base) joseph@MacBook-Pro exepriment % ./a.out
( 3 <= 2 || 13 <= 19 ) && 1 < 2 
             Output: true,0%                                                    (base) joseph@MacBook-Pro exepriment % ./a.out
( 3 >= 2 || 13 <= 19 ) && 1 < 2
            Output: true,1%                                                     (base) joseph@MacBook-Pro exepriment % ./a.out
( 3 >= 2 || 13 <= 19 ) || 1 < 2
            Output: true,2%                                                     (base) joseph@MacBook-Pro exepriment % ./a.out
! ( 3 > 2 )
     Output: false,0%                                                           (base) joseph@MacBook-Pro exepriment % ./a.out
! ( 3 > 2 || ( 13 < 19 || 1 < 2 ) )
               Output: false,2%                 

以下是扩展的数据和错误输入:

(base) joseph@MacBook-Pro exepriment % ./a.out 
a > b
Error! Wrong token 'a'.
(base) joseph@MacBook-Pro exepriment % ./a.out
3.4 > 4.4
Error! Wrong token '.'.
(base) joseph@MacBook-Pro exepriment % ./a.out
3 > 4 , 2>3
Error! Wrong token ','.
(base) joseph@MacBook-Pro exepriment % ./a.out
1 == 1 == 1
Output: true,0%                                                                 (base) joseph@MacBook-Pro exepriment % ./a.out
! ( 3 > 2 || ! ( 13 < 19 || 1 < 2 ) )
Output: false,3%                                                                (base) joseph@MacBook-Pro exepriment % ./a.out
! ( 3 > 2 && 4 < 5 ) && !( 3 > 1 || ! ( 4 < 1 && 2 < 1))
Output: true,3%                                                                 (base) joseph@MacBook-Pro exepriment % ./a.out
  (! ( 3 > 2 && 4 < 5 ) )&& !( 3 > 1 || ! ( 4 < 1 && 2 < 1))
Output: false,5%   

实验结果与预期相符。

[编译原理]用flexbison生成逻辑表达式求值工具 文章目录实验内容逻辑表达式求值工具词法分析语法分析bison使用文法优先级结合律文法设计短路次数统计语义处理错误处理编译及运行编译结果运行结果 实验内容 逻辑表达式求值工具 ​ 输入一个包含数值的逻辑表达式,输出计算其真值,短路操作而跳过的数值比较的次数。输出格式为Output: [true or false], [次数]。注意,逻辑表达式计算符合短路算法。其中,!计入逻辑运算次数,而 == 或 != 只用于非布尔型的数值比较。 词法分析 ​ Flex器是一个开源的词法分析器,编译器将开发者 阅读详情

相关推荐

flexbison计算器1

flexbison简单计算器 这个代码是flexbison的入门代码; 简单计算器 代码使用到的文法: S->E E->E+T | E-T | T T->T*F | T/F | F F->(E) | id 产生式 语义规则 S->E {print(E.val)} F->E+T F.val =E.val+T.val F->T*F E.val=T.val+F.val F->id F.val =id.val 这里使用的简单文法没

Ln的博客 708

算数表达式逻辑表达式求值

项目中要实现校验公式,一开始使用BeanShell插件求值,后来改用Dijkstra(迪杰斯特拉)算法自己实现表达式求值。该算法使用一个符号栈一个数值栈,遍历表达式字符串当遇到数值时入数值栈。当遇到符号时若比符号栈顶的符号优先级高则入符号栈,若比符号栈顶的符号优先级低,则先把两栈中能计算的统统计算掉(直到符号栈顶的符号优先级小于遇到的符号)。遍历完再计算掉两栈中剩余的项。对于括号的处理也是类似,

9r3y的专栏 2478

Lex(flex)yacc(bison)写的简单计算

Lex文件如下: %{#include "cal.tab.h"%}%option noyywrapinteger      [0-9]+dreal        ([0-9]*"."[0-9]+)ereal        ([0-9]*"."[0-9]+[EedD][+-]?[0-9]+)real         {dreal}|{ereal}nl           /nplus        

P2P 研究 思考 4337

1225-实验四-递归下降语法分析程序设计

#include<stdio.h> #include<string> char str[10]; int index=0; void E(); //E->TX; void X(); //X->+TX | e void T(); //T->FY v...

474

算法实战】每日一题:如何实现一个简单的逻辑表达式求值

如何实现一个简单的逻辑表达式求值器要求实现一个程序,该程序能够接收一个由逻辑运算符(“or”、“and”、“not”)逻辑值(“true”、“false”)组成的字符串表达式,然后计算并输出这个表达式的结果。

千思的博客 1206

编译原理】第三章语法分析

语法分析 根据词法单元的构成规则,对源代码文件的字符流进行了词法分析,由词法单元的模式 → 正则表达式 → DFA → 词法 语法分析就是根据一个个单词,让它们组成逻辑关系,语言规约是上下文无关语法 要进行语法分析,输入的是词素流,类似于这样,这是之前词法分析得到的结果 如何进行语法分析? 先引入一个概念,文法 文法 算术表达式文法 我们都知道算术表达式是什么,就是一个式子,式子式子之间也能构成新的式子。算术运算表达式文法的构成因此有: ① E →num ② E →id ③ E →F ④ E →( E

小梁说代码的博客 3243

flex bison计算

lex yacc 的计算lex源代码 %{ #include #include #include "simple.tab.h" %} %% "+" {return ADD;} "-" {return SUB;} "*" {return MUL;} "/" {return DIV;} "\n" {return CR; }

缥缈的云 专栏 664

Bison的使用

一、Bison对输入的匹配  bison是基于你所给定的语法来生成一个可以识别这个语法中有效“语句”的语法分析器。例如下面的这个例子: statement:NAME ‘=’ expression expression:NUMBER ‘+’ NUMBER | NUMBER ‘?’ NUMBER ; 其中statement是递归的开始语句,N

zacharytse的博客 1万+

FLexBison实现简易计算

计算器主要实现了整数的加、减、乘、除、括号以及绝对值运算。其中,绝对值符号用|表示。 FLex词法分析 calculator.l %{ #include "calculator.tab.h" %} %option noyywrap %% "+" { return ADD; } "-" { return SUB; } "*" { return MUL; } "/" { return DIV; } "|" { return ABS; } "(" { return OP; } ")"

rshchx007的博客 1789

手把手教程-lex与yacc/flexbison入门(一)(使用windows环境)

前言 1.文章诞生的契机 在计算机学习中,我们有时可能会想到自制一门属于自己的编程语言,此时选择lex与yacc来生成词法分析器与语法分析器是非常不错的选择。然而,这两个工具虽然用起来简单,但对于新手来说,入门还有着些许的难度。 入门的难度通常不是因为工具的复杂,而是因为教程有些难懂。在大多数教程作者眼里,想要自制一个语言的必然不会是初学者,毕竟新手们还在被CJAVA等语言搞得晕头转向。这导致大多数lex与yacc教程都是面向进阶选手,内容“点到为止”,并心照不宣地省略了一些“常识”。导致内容在我等这般新

weixin_44007632的博客 7万+

经典yacc表达式计算器(来自bison手册)

/* 中缀符号计算器 */%{        #define YYSTYPE double          /*定义语义值的C数据类型*/        #include         #include         #include         int yylex (void);        void yyerror (char const *);%}%token NUM     

小蜜蜂的专栏 4973

词法分析flex & 语法分析bison

flex&bison

errorr0 3882

LEX & YACC 写的一个表达式计算程序

前两天觉得LEXYACC挺强大的,稍微写了一个处理表达式的程序 LEX文件:%{#include #include #include "y.tab.h"extern char *yytext;%}%%[0-9]+ yylval.number = atoi(yytext); return NUMBER;/|/| yylval.stri

feitianshu的专栏 1785

编译原理实验:FLex(scanner)与Bison(parser)实现计算

参考: Flex常用规范 Bison常用规范 flex+bison开发计算器 1 Unix Lex/YACC 发展为 Linux FLex/Bison Lex是1975年由Mike Lesk当时尚在AT&T实习的Eric Schmidt共同...

chenxijian2010的博客 3133

编译原理_计算器_flexbison实现

Staged Event Driven Architecture (SEDA)介绍 作者:朱之光 http://larryzhu.bokee.com 一、前言 二、当前流行的两种并发处理编程模型 三、SEDA架构 四、小结 五、参考文献 一、前言 Staged Event Driven Architecture (SEDA)是加州大学伯克利分校研究的一套优秀的高性能互联网服务器架构模型。其设计目标是:支持大规模并发处理、简化系统开发、支持处理监测、支持系统资源管理。本文会...

jiangbb8686的博客 657

linux计算器程序实验,编译原理利用Flex+Bison实现简单计算

编译原理利用Flex+Bison实现简单计算器一实验目的1、掌握Yacc的基本用法,并能够根据语言给出语法规则的定义,最后生成语言的解析器;2、使用使用Yacc实现一个高级计算器程序;二实验内容实现一个简单的表达式计算器,要求能进行加、减、乘、除、幂运算,注意优先级。写出详细的步骤源程序。1、在linux中安装flexbison,使用如下命令:sudoapt-getinstallfl...

weixin_36046580的博客 995

表达式求值(最详细分析+代码实现+表达式之间的相互转换)

目录 一、概念 二、前缀表达式逻辑实现方式 1.定义 2.前缀表达式计算求值 3.例子 4.代码实现 三、中缀表达式逻辑实现方式 1.定义 2.中缀表达式规则 3.中缀表达式计算求值 4.代码实现 四、后缀表达式逻辑实现方式(逆波兰表达式求值) 1.定义 2.后缀表达式计算求值 3.例子 4.代码实现 五、相互转换 1.中缀表达式转化为前缀表达式算法描述 ②例子 2.前缀表达式转化为中缀表达式 3.中缀表达式转化为后缀表达式算法描述

前景 4万+

编译原理:用Flex Bison实现一个功能更为强大的计算

 用Flex Bison实现一个功能更为强大的计算器,包含以下运算: a)  加、减、乘、除运算 b)  乘方、开方运算 c)  位运算       – 与 & 、或 |、非 ~... d)  阶乘运算 !   对数运算log 1.进一步完善计算器功能,实现对以下语法结构的分析。还可以进一步扩充功能。 12+23*

nufangdongde的专栏 3463

利用FLEX & BISON 快速实现简单的C 语言编译器前端

Flex Flex是一个生成词法分析器的工具,它可以利用正则表达式来生成匹配相应字符串的C语言代码,其语法格式基本同Lex相同。 格式 LEX的源文件由三个部份组成,每个部分之间用顶行的 `%%’ 分割,其格式如下: 定义部份 %% 规则部份 %% 用户附加C语言部份 手册-&amp;amp;amp;amp;gt;Flex, version 2.5 怎么用就不赘述了…. 主要就是用这个词法生...

unicxitoiv的博客 2万+
下一篇: [机器学习]基于信用卡欺诈的预测分析
Joseph115
博客等级 码龄7年 4粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值