IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> DOG(4):解析器的部分实现细节 -> 正文阅读

[数据结构与算法]DOG(4):解析器的部分实现细节

作者:recommend-item-box type_blog clearfix

先来说说parser

LEX、YACC的部分就不用提了,只用说说C语言相关的支持部分代码

位于include\parser\Expression.h你会发现很多这个样子的东西:

struct Expression *idexp(char *var);
struct Expression *intexp(int var);
struct Expression *boolexp(unsigned char var);
struct Expression *strexp(char *var);
struct Expression *noneexp(void);
//....

这个是为了语法分析器里的调用封装一层.具体的实现,在parser/Expression.c:

struct Expression *idexp(char *var) {
	EXP_FUNC_HEADER(EXPTP_ID)
	ret->exp.varName = lexer_scpy(var);
	EXP_FUNC_RETURN
}

struct Expression *intexp(int var) {
	EXP_FUNC_HEADER(EXPTP_INT)
	ret->exp.varInt = var;
	EXP_FUNC_RETURN
}

struct Expression *boolexp(unsigned char var) {
	EXP_FUNC_HEADER(EXPTP_BOOL)
	ret->exp.varBool = var;
	EXP_FUNC_RETURN
}
//...

可以看到大同小异:无非每个函数三个部分,头部调用一个宏,尾部调用一个宏,中间设置一个值,仅此而已.两个宏的定义位于文件首

#define EXP_FUNC_HEADER(_tp) struct Expression *ret = malloc(sizeof(struct Expression)); ret->tp = (_tp); ret->ln = ln;
#define EXP_FUNC_RETURN return ret;

分配内存,设定属性,设定行号,结尾再返回.很简单
频繁使用的结构体:

struct Expression { //表达式主类
	enum ExpressionTP tp;
	int ln; //行号
	union {
		int varInt;						// 整型字面量
		unsigned char varBool;			// 布尔值字面量
		char *varString;				// 字符串字面量
		char *varName;					// ID
		struct Expression *sgexp;		// 单目运算的参数
		struct BinExpression *bin;		// 二叉树(二元运算符参数)
		struct FunctionCallExpression *fcall;	// 函数调用
		struct IndexExpression *idx;	// 索引表达式
		struct AttributeExpression *attr;		// 属性访问表达式
		struct ExpressionList *array;	// 数组字面量
	} exp;
};

emm把他叫做"类"有点过了…
注释写得很清楚,使用的时候,直接调用前文的函数.访问时,根据tp成员的枚举值确定访问exp联合体的哪个成员(其实这里就相当于做了个多态)

一些可能迷惑的地方

关于单向链表这个事,很多坑:

一个基本的链表结构定义

struct ExpressionList { //表达式单项链表
	struct Expression *self;
	struct ExpressionList *next;
};

添加元素

struct ExpressionList *creat_explst(struct Expression *exp) {
	struct ExpressionList *ret = malloc(sizeof(struct ExpressionList));
	ret->self = exp;
	ret->next = NULL;
	EXP_FUNC_RETURN
}

struct ExpressionList *link_explst(struct ExpressionList *explst, struct Expression *exp) {
	struct ExpressionList *ret = creat_explst(exp);
	struct ExpressionList *p;
	for (p=explst; p->next; p = p->next) {;}
	p->next = ret;
	return explst;
}

函数1用来创建(分配内存)一个链表项,后者用来连接上去.

遍历链表

for (struct ExpressionList *p=lst; p; p=p->next) ;

注意的问题

明显的差异:添加元素时,for的第二部分写的是p->next,而遍历的时候必须遍历到p为空.这里利用的是for的性质:求值表达式1,求值表达式2,求值语句块,求值表达式3的流程
这里可以看出,当程序执行流到语句块的时候,就已经保证表达式2为真,此处即指针非空.添加元素的时候,当next指针为空的时候,直接转跳最后.也就是说我们得保证next指针为空在改next,不然如果覆盖了有所指向的next,就会漏掉最后一个元素,还会造成未知后果.
然而遍历的时候,只需要保证p非空,这样就能访问每一个p.

结果如何返回?

众所周知yyparse()返回int,那么我们如何获取程序解析成果呢?很简单,全局变量:

_pr的声明位于parser/Parser.cL11
这里注意,L70的处理是经常用到的,如果不加这一句的话,如果脚本是空的,就会报错语法错误,如果有这一项归约条件,但是_pr未归0,调用者就无法得知到底有没有结果,从而冒失地访问_pr造成异常

pcd其实是一回事

来看看语法(直观)

<class_name>
{
mem1;
mem2;
...
memN;
}
{
method1(argc);
method2(argc);
...
methodN(argc);
}
...

空不空行无所谓.缩写都明白吧…
我们可以观察到,pcd的语法分析夹杂了一些奇怪的检查:

fl
	: ID LS _INT RS SPL
	{
		if ($3 > 10) {
			printf("warning: number of parameters of method '%s' is too large(%d)\n", $1, $3);
		}
		$$ = creat_fl($1, $3);
	}
	| fl ID LS _INT RS SPL
	{
		if ($4 > 10) {
			printf("warning: number of parameters of method '%s' is too large(%d)\n", $1, $4);
		}
		for (struct MethodList *p=$1; p; p=p->next) {
			if (!strcmp(p->name, $2)) {
				// 有重定义
				printf("error: redefinition of method '%s'.\n", $2);
				exit(1);
			}
		}
		$$ = link_fl($1, $2, $4);
	}
	;

第一个规则和第二个规则头部都有检查函数的参数个数(话说这个纯属无聊哈),考虑到参数一律压栈处理的话呢(x64另当别论),参数太拖确实不好
第二个规则检查是否重定义,遍历链表,然后比较有没有已出现的类名,如果有的话就报错,这个很简单

最后的一点说明

由于咱们特殊的结构呵,解析程序放在动态链接库里面,那么访问全局变量似乎很不合理,如何获取解析结果?函数:

struct ClassDeclareList *gr_pcd(void) {
	return pcd_dest;
}

不知道内部机制哈(不是很了解),但是至少这个样子看起来安全些吧…

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-13 12:32:05  更:2021-08-13 12:34:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 20:29:52-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码