【Compile】表驱动LL(1)语法分析(C++)

从零构建HC32F448开发环境:VSCode与Makefile的现代嵌入式实践 本文详细介绍如何从零构建HC32F448开发环境,基于VSCode与Makefile打造现代化嵌入式开发工作流。涵盖工具链配置、项目结构搭建、Makefile编写、外设驱动开发及调试烧录全流程,提供实用的HC32F448例程和使用方法,助力开发者高效开发。 阅读详情

题目描述

掌握预测分析程序的分析、设计与实现的基本技术与一般方法。

编写识别由下列文法所定义的表达式的预测分析程序。
E→E+T∣E−T∣TE \rightarrow E+T | E-T | TE→E+T∣E−T∣T
T→T∗F∣T/F∣FT \rightarrow T*F | T/F | FT→T∗F∣T/F∣F
F→(E)∣iF \rightarrow (E) | iF→(E)∣i
输入:从键盘输入表达式,或每行含有一个表达式的文本文件。其中,表达式中含有任意的十进制数或十六进制数,并以#结束。
如:80-5H+(6+1)+4h/2#。

题目分析

本次实验运用到了基础数据结构——栈,并且是用链表代替线性表,体现了数据结构与编译原理概念相结合的思想。经过前两次实验,这次编写代码显得驾轻就熟,遇到一些bug,能够根据程序流程图进行复查,并顺利解决。对于LL(1)文法的理解与掌握也深刻了很多。


C++

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <conio.h>
#define digit 1 // 1数字
#define op 2 // +-*/()#
#define Hh 3 // 3Hh
#define AF 4 // 4A-F
#define letter 5 // 5其它字母
using namespace std;

typedef struct node {
	char data;
	struct node* next;
};
node* temp, * top;
char cmpchar;
string line;
// 定义分析表结构
int table[5][8] = {
	{0, 0, 0, 0, 1, 0, 1, 0},
	{1, 1, 0, 0, 0, 1, 0, 1},
	{0, 0, 0, 0, 1, 0, 1, 0},
	{1 ,1, 1, 1, 0, 1, 0, 1},
	{0, 0, 0, 0, 1, 0, 1, 0}
};

char q; // 指向输入符号串中当前的字符
char word[20]; // 存储当前识别的单词
int state; // 表示所处的状态
int i; // 单词的下标

char read(string line, int k);
void push(char c);
void pop();
int i2d(char cmpchar); // EATBF
int j2d(char current); // +-*/()i#
void dopush(int t);
bool check_terminal(char ch); // 判断是否是终结符
int isDigitOrChar(char ch);
string change_i(string words); // 将含有十进制或十六进制数的表达式转换为用i代替的表达式

int main() {
	ifstream fin("D:/Compile_exp/exp2-test.txt");
	if (!fin.is_open()) {
		cout << "open file error." << endl;
		_getch();
		return -1;
	}

	while (getline(fin, line)) {
		puts("--------------------------------------------");
		string temp = line;
		line = change_i(line);
		if (line == "-1") {
			cout << temp << " is not a valid express." << endl;
			continue;
		}
		cout << "Output string is: " << line << endl;

		int i, j, t;

		push('#');
		push('E'); // 初始化
		int cur = 0;
		char current;

		while (cur < line.size()) {
			current = read(line, cur);
			cmpchar = top->data;
			pop();
			printf("Top: %c --- Cur: %c", cmpchar, current);
			// 栈顶是终结符或#,但输入串中不是终结符或#时,判定为出错
			if (check_terminal(cmpchar) && cmpchar != current) {
				cur--; // 便于判定为非法
				break;
			}

			if (current == cmpchar) {
				if (current == '#') break;
				printf("	match success of %c\n\n", current);
				cur++;
				continue;
			}
			cout << endl;
			i = i2d(cmpchar);
			j = j2d(current);
			if (table[i][j] == 1) {
				t = 10 * i + j;
				dopush(t);
			}
			else {
				cur--; // 便于判定为非法
				break;
			}
		}

		if (cur + 1 == line.size()) {
			cout << endl;
			cout << temp << endl;
			cout << "Your input is valid!" << endl;
		}
		else {
			cout << endl;
			cout << temp << endl;
			cout << "Sorry, your input is invalid!" << endl;
		}
	}

	return 0;
}

char read(string line, int k) {
	return line[k];
}

// 压栈
void push(char c) {
	temp = (node*)malloc(sizeof node);
	temp->data = c;
	temp->next = top;
	top = temp;
}

// 弹栈
void pop() {
	cmpchar = top->data;

	if (top->next != NULL)
		top = top->next;
}

// 将i字符数字化
int i2d(char cmpchar) {
	int i = 0;

	switch (cmpchar) {
	case 'E': i = 0; break;
	case 'A': i = 1; break;
	case 'T': i = 2; break;
	case 'B': i = 3; break;
	case 'F': i = 4;
	}

	return i;
}

// 将j字符数字化
int j2d(char current) {
	int j = 0;

	switch (current) {
	case '+': j = 0; break;
	case '-': j = 1; break;
	case '*': j = 2; break;
	case '/': j = 3; break;
	case '(': j = 4; break;
	case ')': j = 5; break;
	case 'i': j = 6; break;
	case '#': j = 7;
	}

	return j;
}

void dopush(int t) {
	switch (t) {
	case 4: push('A'); push('T'); break;
	case 6: push('A'); push('T'); break;
	case 10: push('A'); push('T'); push('+'); break;
	case 11: push('A'); push('T'); push('-'); break;
	case 15: break;
	case 17: break;
	case 24: push('B'); push('F'); break;
	case 26: push('B'); push('F'); break;
	case 30: break;
	case 31: break;
	case 32: push('B'); push('F'); push('*'); break;
	case 33: push('B'); push('F'); push('/'); break;
	case 35: break;
	case 37: break;
	case 44: push(')'); push('E'); push('('); break;
	case 46: push('i'); break;
	}
}

int isDigitOrChar(char ch) {
	if (ch >= 48 && ch <= 57) // 数字
		return digit;
	else if (ch == 72 || ch == 104) // H or h
		return Hh;
	else if ((ch >= 65 && ch <= 70) || (ch >= 97 && ch <= 102)) // 字母A,B,C,D,E,F
		return AF;
	else if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) // 除A~F外的其它字母
		return letter;
	else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')' || ch == '#')
		return op;
}

// 将含有十进制或十六进制数的表达式转换为用i代替的表达式
string change_i(string words) {
	memset(word, 0, sizeof word);
	state = 0;
	i = 0;
	cout << "Input string is: " << words << endl;

	string result = "";
	int cnt = 0;
	q = words[cnt++];

	while (cnt <= words.size()) {
		// 先判断状态,再判断字符
		switch (state) {
		case 0: // 0状态
			switch (isDigitOrChar(q)) {
			case digit: // 数字
				word[i++] = q;
				state = 2; // 转移到2状态
				break;
			case Hh: // H or h
			case AF: // 字母A,B,C,D,E,F or a,b,c,d,e,f
			case letter: // 字母
				word[i++] = q;
				state = 1;
				break;
			case op: // 操作符
				result += q;
				state = 0;
				break;
			default: // 其它(非法字符 )
				word[i++] = q;
				state = 5;
			}
			break;
		case 1: // 1状态
			switch (isDigitOrChar(q)) {
			case Hh: // 当前状态遇到字母、数字往下读入
			case AF:
			case digit:
			case letter:
				word[i++] = q;
				state = 1;
				break;
			case op: // 读入完毕,识别为标识符
				word[i] = '\0';
				printf("%s is an identifier.\n", word);
				//result += "i";
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 2: // 2状态
			switch (isDigitOrChar(q)) {
			case digit: // 若为数字,不改变状态往下读入
				word[i++] = q;
				state = 2;
				break;
			case Hh: // 若为Hh,转移至状态3
				word[i++] = q;
				state = 3;
				break;
			case AF: // 若为AF,则有可能是16进制,转移至状态4
				word[i++] = q;
				state = 4;
				break;
			case op: // 成功识别为整数
				word[i] = '\0';
				printf("%s is an Integer.\n", word);
				result += "i";
				result += q;
				//cout << result << endl;
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 3: // 3状态
			switch (isDigitOrChar(q)) {
			case op: // 识别为16进制数
				word[i] = '\0';
				printf("%s is a Hex digit.\n", word);
				result += "i";
				result += q;
				//cout << result << endl;
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 4: // 4状态
			switch (isDigitOrChar(q)) {

			case digit: // 若为数字或A~F,仍为状态4,往下读入
			case AF:
				word[i++] = q;
				state = 4;
				break;
			case Hh:
				word[i++] = q;
				state = 3;
				break;
			case op: // 如果16进制没有以h或H结尾,转移至错误状态
				state = 5;
				cnt--;
				break;
			default:
				word[i++] = q;
				state = 5;
			}
			break;
		case 5: // 出错状态
			if (isDigitOrChar(q) == op) { // 若为空格,则识别为非标识符
				word[i] = '\0';
				printf("%s is not an identifier.\n", word);
				memset(word, 0, sizeof word);
				i = 0;
				state = 0;
				result = "-1";
				return result;
			}
			else { // 出错序列还未读取完毕,往下读入
				word[i++] = q;
				q = words[cnt++];
				continue;
			}
			break;
		}
		q = words[cnt++]; // 指针下移(指向输入符号串中的下一个字符)
	}

	return result;
}

// 判断是否是终结符
bool check_terminal(char ch) {
	if (isDigitOrChar(ch) == op || ch == 'i') return true;
	else return false;
}
C/C++函数调用关系分析:从原理到实践,掌握大型项目代码脉络 在软件工程领域,理解代码结构是维护和优化复杂系统的基石。函数调用关系分析作为静态程序分析的核心技术之一,通过解析源代码或中间表示,自动构建函数间的调用图谱,其原理基于词法分析、语法分析和控制流分析。这项技术的价值在于将隐式的代码逻辑显式化,帮助开发者快速定位依赖、识别架构问题,并辅助进行代码重构和性能优化。在C/C++大型项目开发中,面对动辄数十万行的代码库,传统的文本搜索和IDE跳转效率低下,而函数调用关系分析工具能显著提升代码理解效率。结合vscode配置c/c++环境等常见开发场景,以及wpf tre 阅读详情

相关推荐

从Keil到VSCode:搭建C51与STM32统一开发环境

嵌入式开发中,编译工具链是连接源代码与硬件执行的关键环节,其核心原理是将高级语言翻译为目标芯片可执行的机器码。传统IDE如Keil提供了集成化解决方案,但在代码编辑体验、多项目管理和流程自动化方面存在局限。现代开发实践追求效率与灵活性,通过将轻量级编辑器与专业工具链解耦,可实现更优的工程工作流。本文聚焦Keil C51与ARM GCC两大主流工具链,详解如何在VSCode中配置编译任务与智能感知,构建支持8051和Cortex-M双架构的统一开发环境,显著提升嵌入式项目的编辑效率与维护性。

weixin_34054931的博客 446

编译原理之LL(1)语法分析实验(附完整C/C++代码与测试)

编译原理实验之LL(1)语法分析实验,使用C语言实现,附带详细注释并测试结果

人生处处是围城,一切都是最好的安排! 2万+

C语言性能优化实战:11个硬实力拆解与代码实证

在计算机编程领域,性能优化是衡量代码质量的核心指标之一。其原理在于通过算法改进、内存管理和指令级优化等手段,减少程序执行时间和资源消耗。技术价值体现在提升系统响应速度、降低硬件成本和增强用户体验。应用场景广泛覆盖操作系统内核、嵌入式系统、游戏引擎和高频交易等对延迟和资源有极致要求的领域。本文聚焦C语言,通过指针运算、手动内存管理、数据对齐和SIMD指令等具体技术,结合代码示例,深入剖析其为何在性能竞争中保持优势,为开发者提供一套可验证的优化实践指南。

weixin_33889245的博客 412

基于C++实现的LL(1)语法分析程序

一、文法 原文法 E->E+T|E-T|T T->T*F|T/F|F F->id|(E)|num 其中: id: a-f, A-F,num:0-9 消左递归 E->TA A->+TA A->-TA A-&g...

chuiqi9947的博客 595

编译原理 - LL(1)分析法:C/C++实现

LL(1)分析法是一种常用的自顶向下的语法分析方法,用于分析和解释编程语言或其他形式的文本。LL(1)代表"Left-to-Right, Leftmost derivation, 1 symbol lookahead",这表示了分析器的工作方式和限制条件,通常用于编程语言的语法分析,编写编译器或解释器。主要步骤包括构建LL(1)文法、构建LL(1)分析表和使用递归下降分析或预测分析器等算法来分析输入文本。 通过本次实验,我实现了LL(1)分析法进行语法分析,并认识到LL(1)分析法利用预测分析表和栈来进行符号匹配和产生式的选择,从而推导出输入串的语法结构。 首先,我了解到LL(1)分析法的核心是构建预测分析表。预测分析表由非终结符和终结符构成,通过预测分析表我们可以根据当前的栈顶符号和输入串的首符号,快速确定应该选择的产生式,从而进行语法推导。在实验中,我通过定义非终结符和终结符的数组以及预测分析表的初始化,构建了一个完整的预测分析表。 其次,我认识到LL(1)分析法对文法的要求比较严格,文法必须满足LL(1)文法的条件。LL(1)文法要求每个非终结符的每个产生式的选择集与其他产生

通过设计、编制、调试一个典型的语法分析程序

通过设计、编制、调试一个典型的语法分析程序,实现对词法分析程序所提供的单词序列进行语法检查和结构分析,进一步掌握常用的语法分析方法。

Bake驱动开发指南:如何为自定义语言扩展构建系统

Bake是一个现代化的C/C++项目构建系统,它的核心特性之一就是其可扩展的驱动架构。本文将为您详细介绍如何为自定义语言开发Bake驱动,让您能够轻松扩展Bake的构建能力。🚀 ## 什么是Bake驱动? Bake驱动是Bake构建系统的核心扩展机制,它允许开发者为不同的编程语言或构建任务定义专门的构建规则。每个驱动都是一个独立的模块,负责将源代码转换为可执行文件或库。Bake内置了C和C+

gitblog_00274的博客 1017

C++实现编译原理自动机、LL1文法、及LR(0)文法的代码

C++实现编译原理自动机、LL1文法、及LR(0)文法的代码

正点原子 IMX6ULL移植OV5640驱动

使用的是 正点原子的IMX6LL+ 1024 x 600的屏幕系统是自己根据busybox移植的根文件系统Linux内核是根据NXP官方结合正点原子教程修改而来 设备树也是。

XCP_12的博客 1202

编译原理LL1语法分析器(C++版)源代码.zip

自己实现的编译原理的LL1语法分析器,是自己的实验作业,用Vs2017实现,可以直接运行

编译原理LL1语法分析器(C++版)源代码

自己实现的编译原理的LL1语法分析器,是自己的实验作业,用Vs2017实现,可以直接运行,代码注释丰富,希望与大家交流学习!欢迎大家下载!

emWin嵌入式GUI显示驱动配置全解析:从架构到实战避坑指南

在嵌入式系统开发中,图形用户界面(GUI)的实现是提升产品交互体验的关键。其核心原理在于通过显示驱动框架,将上层图形算法与底层硬件控制逻辑进行高效解耦。这种分层架构的技术价值在于提供了高度的可移植性和资源可控性,使开发者能够专注于应用逻辑而非硬件细节。典型的应用场景包括工业HMI、医疗显示终端、智能家居面板等对实时性和稳定性要求较高的嵌入式设备。本文聚焦于SEGGER emWin这一广泛应用的嵌入式GUI库,深入剖析其驱动配置机制。通过详解设备抽象层、颜色转换API及硬件回调函数等核心概念,并结合显示驱动适

weixin_33907511的博客 431

LL(1)文法分析表用C语言实现

* 实验三 LL(1)分析表演示程序模板(教学实验用) * * 采用LL(1)表分析法实现表达式文法的语法检验。 * (0)E ->TX * (1)X ->+TX (2)X ->-TX (3)X ->ε * (4)T ->FY * (5)Y ->*FY (6)Y ->/FY (7)Y ->ε * (8)F ->(E) (9)F ->i * 思路:其中i指代数字。先通过词法分析,将实际数字识别 * 用i替代,再进行分析。如 * 3.14*2 => i*i * * ************************************************************/

嵌入式DMA驱动开发实战:从寄存器配置到UART数据传输

直接内存访问(DMA)是嵌入式系统中提升数据传输效率、降低CPU负载的核心技术。其工作原理是允许硬件控制器直接读写内存,无需CPU持续介入,从而在实时系统和硬件驱动中实现高效的数据搬运。这项技术的核心价值在于解放CPU算力,使其能处理更复杂的任务,同时满足高带宽和精确时序控制的应用需求。在嵌入式开发、微控制器编程和实时操作系统等场景中,DMA被广泛用于外设通信、数据采集和音频视频流处理。本文以具体的DMA控制器(如“24DMA-09”)为例,深入探讨其寄存器映射、通道配置及中断处理机制,并通过一个完整的内存

weixin_34109408的博客 347

LL1语法分析器(c++).rar

LL1语法分析器,c++实现,first,follow,分析表算法详细注释,

编译原理实验报告LR(1)分析法.doc

编译原理实验报告LR(1)分析法

STM32嵌入式开发中CMakeLists.txt核心配置与最佳实践

CMake作为跨平台构建系统生成器,是现代嵌入式开发实现工程标准化、可移植性与CI/CD集成的关键基础设施。其核心原理在于将项目描述(CMakeLists.txt)与构建执行解耦,通过声明式语法驱动交叉编译流程。技术价值体现在文本化配置支持Git协作、IDE无关性、自动化测试与持续集成能力。典型应用场景包括STM32裸机开发、多芯片平台复用及HAL/LL库工程管理。本文深入解析CMakeLists.txt在STM32项目中的工程定位、工具链配置、浮点支持、源文件管理、链接脚本集成与条件编译机制,聚焦CMak

brandy的博客 656

CentOS7下Node.js v20+安装与常见依赖库问题解决指南

本文详细介绍了在CentOS7系统下安装Node.js v20+的完整流程及常见依赖库问题的解决方案。通过升级glibc到2.28+、配置环境变量以及处理libstdc++版本问题,帮助开发者顺利在老旧系统上部署最新版Node.js,提升开发效率。

weixin_30689307的博客 157

linux+getty+串口_嵌入式er日常系列!修改u-boot与Linux调试串口痛苦经历及感触

原标题:嵌入式er日常系列!修改u-boot与Linux调试串口痛苦经历及感触 1.背景 近期一个项目,使用TI Cortex-A8的芯片AM3517作为主控开发。前期使用北京瑞泰公司的ICETEK-AM3517-KB核心板搭建开发环境。在自己做底板布板的时候的时候涂个布线方便,改用UART1作为调试串口,而原来瑞泰给的开发包,已经TI的AM3517 PSP均以UART3作为调试串口。于是在底板焊...

weixin_30193461的博客 437

LCD工程师必看:MIPI DSI的LP/HS模式切换全流程拆解(附高通平台实测)

本文为LCD工程师深度解析MIPI DSI接口中低速(LP)与高速(HS)模式切换的全流程。文章结合高通平台实测波形,从协议状态机、时序关键参数到实战调试案例,系统拆解了模式切换的本质、协同机制及常见问题排查方法,重点阐述了LP模式在低功耗控制与HS模式在高速数据传输中的核心作用与切换时序。

weixin_29240711的博客 547

SpringBoot3 + Vben5.0实战:从零搭建企业级管理后台(附完整代码)

本文详细介绍了如何从零开始,使用SpringBoot3与Vben5.0技术栈搭建现代化企业级管理后台。通过对比分析,阐述了该组合在开发效率、性能(如虚拟线程)和权限模型(RBAC)方面的优势,并提供了从项目初始化、核心功能开发到部署运维的完整实战指南,帮助开发者快速构建高性能、易维护的后台系统。

weixin_29306317的博客 172
上一篇: 【数据结构】红黑树学习笔记
下一篇: 【Compile】算符优先文法分析(C++)
CodeSlogan
博客等级 码龄6年 4227粉丝 130原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeSlogan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值