编译原理——基于LR分析表编写语法分析器与基于WINDOWS下FLEX与BISON的计算器实现

一、概述

  本篇博文主要提供 基于LR分析表编写语法分析器基于WINDOWS下FLEX与BISON的计算器实现 这两个编译原理实验的问题描述、实现流程以及相关实现源码和测试用例。

  博客内所有文章均为 原创,所有代码均为 原创,若转载请附原文链接。


二、基于LR分析表编写语法分析器

2.1 需求描述

【问题描述】 已知文法G[E]:

E → E + T | T

T → (E) | id | id[E]

设计LR分析表,并用c++语言编写语法分析器。

【输入形式】 一个句子
【输出形式】 句子语法结构正确,输出“Syntax analysis is right”反之输出“Error on syntax analysis”

【样例输入】 a1+a2
【样例输出】 Syntax analysis is right

2.2 实现流程

  • 根据给定文法求其 LR(0) 项目;
  • 构造可识别活前缀的 DFA ;
  • 绘制 LR(0) 分析表;
  • 将 LR(0) 分析表优化为改进后的 SLR(1) 分析表;
  • 代码模拟实现自底向上的 SLR(1) 分析 ;

2.3 C++ 源码

#include <iostream>
#include <string>
#include <cstring>
#include <stack>


using namespace std;


const char MOVE_IN = 'S';
const char REDUCTION = 'R';

const string ANALYSIS_TABLE[12][9] = {
	{"S4", "", "S3", "", "", "", "", "1", "2"},
	{"", "S8", "", "", "", "", "ACC", "", ""},
	{"", "R2", "", "R2", "", "R2", "R2", "", ""},
	{"S4", "", "S3", "", "", "", "", "5", "2"},
	{"", "R4", "", "R4", "S6", "R4", "R4", "", ""},
	{"", "S8", "", "S9", "", "", "", "", ""},
	{"S4", "", "S3", "", "", "", "", "7", "2"},
	{"", "S8", "", "", "", "S11", "", "", ""},
	{"S4", "", "S3", "", "", "", "", "", "10"},
	{"", "R3", "", "R3", "", "R3", "R3", "", ""},
	{"", "R1", "", "R1", "", "R1", "R1", "", ""},
	{"", "R5", "", "R5", "", "R5", "R5", "", ""},
};

const string GRAMMAR[6] = {
	"S E",
	"E E+T",
	"E T",
	"T (E)",
	"T i",
	"T i[E]"
};


int get_symbol_index(char symbol) {

	int symbol_index = !isalpha(symbol);
	if (symbol_index)
	{
		switch (symbol)
		{
		case '+':
			symbol_index = 1;
			break;
		case '(':
			symbol_index = 2;
			break;
		case ')':
			symbol_index = 3;
			break;
		case '[':
			symbol_index = 4;
			break;
		case ']':
			symbol_index = 5;
			break;
		case '#':
			symbol_index = 6;
			break;
		default:
			symbol_index = -1;
			break;
		}
	}

	return symbol_index;
}


void lexical_analysis(char *input_str) {

	stack<int> state_stack;
	stack<char> symbol_stack;
	int symbol_index, state_stack_top, state;
	string action;
	char action_type;

	
	// init
	state_stack.push(0);
	symbol_stack.push('#');

	while (true) {

		symbol_index = get_symbol_index(*input_str);
		state_stack_top = state_stack.top();
		action = ANALYSIS_TABLE[state_stack_top][symbol_index];

		if (action == "ACC" || !action.length())
			break;

		action_type = action[0];
		state = atoi(action.substr(1).c_str());

		if (action_type == MOVE_IN)
		{
			// S
			state_stack.push(state);

			//input_str++;
			if (isalpha(*input_str))
			{
				symbol_stack.push('i');		// i means id
				while (isalpha(*input_str) || isdigit(*input_str) || *input_str == '_')
					input_str++;
			}
			else
			{
				symbol_stack.push(*input_str);
				input_str++;
			}

			while (isblank(*input_str))
				input_str++;
			
			if (!isalpha(*input_str) && *input_str != '_' && *input_str != '+' &&
				*input_str != '(' &&  *input_str != ')' &&  *input_str != '[' &&  *input_str != ']' &&  *input_str != '#')
				break;
				
		}
		else if (action_type == REDUCTION)
		{
			// R
			string reduction_grammar = GRAMMAR[state];
			char reduction_result = reduction_grammar[0];
			string reduction = reduction_grammar.substr(2);

			for (int i = 0; i < reduction.length(); i++) {
				state_stack.pop();
				symbol_stack.pop();
			}

			state_stack_top = state_stack.top();
			if (reduction_result == 'E')
				state = atoi(ANALYSIS_TABLE[state_stack_top][7].c_str());		// E GOTO
			else if (reduction_result == 'T')
				state = atoi(ANALYSIS_TABLE[state_stack_top][8].c_str());		// T GOTO
			else {
				// err
			}
			state_stack.push(state);
			symbol_stack.push(reduction_result);

		}
		else
		{
			//err
		}

	}
	
	if (action == "ACC")
		cout << "Syntax analysis is right";
	else
		cout << "Error on syntax analysis";
}


int main() {

	char input_str[100];
	cin.getline(input_str, 100);
	int len = strlen(input_str);
	input_str[len+1] = input_str[len];
	input_str[len] = '#';
	//char str[] = {'i','+','(','i','+','i' ,')','#'};

	lexical_analysis(input_str);
	
	system("pause");
	return 0;
}

2.4 测试样例

(1)输入:a1 + a2
输出:Syntax analysis is right

(2)输入:a1 + (a2)
输出:Syntax analysis is right

(3)输入:a1++a2
输出:Error on syntax analysis

(4)输入a1[a2 + a3] + a4 + a5
输出:Syntax analysis is right

(5)输入:a1 + a2[a3] + a4 + (a5 + a6)
输出:Syntax analysis is right


三、基于WINDOWS下FLEX与BISON的计算器实现

3.1 需求描述

【问题描述】 基于词法分析程序自动构造工具Flex与语法分析程序自动构造工具Bison,编制简单的计算器程序。用Flex和Bison实现一个功能更为强大的计算器,尽可能多的包含以下运算(支持浮点数):

a) 加、减、乘、除运算

b) 乘方power、开方sqrt运算

c) 位运算 – 与、或、非…(不做)

d) 三角函数运算 – sin、cos…

e) 求阶乘

f) 求模(不做)

g) 求log以e为底的对数(不做)

h) 求log以10为底的对数

【输入形式】 数学表达式
【输出形式】 表达式的计算结果
【样例输入】 3+5*2
【样例输出】 13

3.2 Flex 源码

// fb1-5.l

%option noyywrap
%{
#define YYSTYPE double
#include"fb1-5.tab.h"
%}
 
%%
"+"     { return ADD; }
"-"     { return SUB; }
"*"     { return MUL; }
"/"     { return DIV; }
"|"     { return ABS; }
"("     { return OP; }
")"     { return CP; }
","     { return SEG; }
"!"		{ return FAC;}
"pow"	{ return POW; }
"sin"	{ return SIN; }
"cos"	{ return COS; }
"tan"	{ return TAN; }
"log"	{ return LOG; }
"sqrt"	{ return SQRT; }
""
([0-9]*\.?[0-9]+|[0-9]+\.)  { yylval = atof(yytext); return NUMBER;}
\n      { return EOL; }
<<EOF>> { return END; }
"//".* 
[ \t]   { /* ignore white space */ }
.      { yyerror("Mystery character%c\n", *yytext); }
%%

3.3 Bison 源码

// fb1-5.y

%{

#define YYSTYPE double
#include<stdio.h>
#include<math.h>
#define PI 3.14159265

%}

/* declare tokens*/
%token NUMBER
%left ADD SUB 
%left MUL DIV ABS
%left SQRT POW COS SIN TAN LOG FAC
%token OP CP SEG
%token EOL END

%%

calclist: /*nothing */
 | calclist exp EOL { printf("%g", $2); }
 | calclist exp END { printf("%g", $2); YYACCEPT;}
 | calclist EOL { printf(""); }/* blank line or a comment */
 ;

exp: factor
 | exp ADD exp { $$ = $1 + $3; }
 | exp SUB factor { $$ = $1 - $3; }
 ;

factor: term
 | factor MUL term { $$ = $1 * $3; }
 | factor DIV term { $$ = $1 / $3; } 
 ;

term: NUMBER
 | term FAC { 
	for(int i=1;i<$1;i++)
		$$ *= i;
 }
 | ABS term { $$ = $2 >= 0? $2 : - $2; }
 | SQRT OP exp CP { $$ = sqrt($3); }
 | COS OP exp CP { $$ = cos($3*PI/180); }
 | SIN OP exp CP { $$ = sin($3*PI/180); }
 | TAN OP exp CP { $$ = tan($3*PI/180); }
 | LOG OP exp CP { $$ = log10($3); } 
 | OP exp CP { $$ = $2; }
 | POW OP exp SEG exp CP { $$ = pow($3, $5); }
 ;

%%

main()
{
  yyparse();
}

yyerror(char *s)
{
  fprintf(stderr, "error: %s\n", s);
}

3.4 测试样例

(1)输入:52+ 21*2 - 2.1
输出:91.9

(2)输入:(252+3)(1-25)
输出:-1272

(3)pow(4,2)+sqrt(144)
输出:28

(4)sin(30)*cos(60)
输出:0.25

(5)输入:log(100)+3!
输出:8


四、相关草图

4.1 可识别活前缀的 DFA 草图

在这里插入图片描述

4.2 改进后的 SLR(1) 分析表草图

在这里插入图片描述

发布了244 篇原创文章 · 获赞 32 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_40697071/article/details/103774601