python语法分析器(parser)源码阅读(三)

本文探讨了C语言中parsetok.c和tokenizer.c文件中出现的tok_state和parser_state结构体。tok_state主要在PyTokenizer_Get函数中用于保存输入字符串信息,而parser_state在PyParser_AddToken函数中用于进行token分析。二者通过char*str建立联系,但tok_state在使用时需加struct前缀避免报错。解析过程中,tok_state的token信息传递给parser_state构建语法分析树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于结构体tok_state以及结构体parser_state

这两个结构体主要出现在parsetok.c以及tokenizer.c文件中,对于这两个结构体,它们之间的关系应该是承上启下的关系即先生成tok_state然后再生成相应的parser_state.

下图是通过sizeof取得的两个结构体的大小。

可以看出,这两个结构体都比较大,再加上结构体内指针指向的地址空间,结构体包含的实际信息量要更大。其中结构体内部的具体信息在源码里都有详细描述,这里不再写。

先看一下tok_state,通过我的观察,这个主要用在PyTokenizer_Get(struct tok_state *tok, char **p_start, char **p_end)这个函数上,位于tokenizer.c文件中第1838行,通过这个函数,主要用于获取输入的字符串。然后信息通过tok_state保存下来。

这里出现了一个我一直想不通的问题,为啥tok_state使用时必须在前面加一个struct,不加就会报错。

再看一下parser_state,它集中出现在parser.c文件中,这个文件中主要的一个函数是PyParser_AddToken,通过这个函数来进行token的分析。

注意这个for循环,我思考了很久才发现,tok_stateparser_state似乎并没有特别的交集,但是,它们之间其实是通过一个char* str来产生联系的。

        type = PyTokenizer_Get(tok, &a, &b);
        //。。。。。。。省略一部分代码
        len = (a != NULL && b != NULL) ? b - a : 0;
        str = (char *) PyObject_MALLOC(len + 1);
        if (str == NULL) {
            err_ret->error = E_NOMEM;
            break;
        }
        if (len > 0)
            strncpy(str, a, len);
        str[len] = '\0';

 变量a代表的正是tok_state的前面的那个token,比如输入1+1的话,那么在一个循环中,会是这样的。

a的变化
第一次循环1
第二次循环+
第三次循环1

然后a的值赋值给str后 ,str又作为变量传给了PyParser_AddToken,然后parser_state又构建了起来。

但是看一下parsetok的返回值,它返回的是一个node指针,也就是说,parser_state作为一个整体并没有全部被返回出来,而是仅返回了它其中的一个成员变量node*,源码上的注释说它是语法分析树的顶指针,实际上就应该是parser.c中要做的事情了

LR语法分析器是一种自底向上的解析方法,广泛应用于编译器设计中。以下是关于LR语法分析器实现的一些详细说明: ### LR Parser 的基本原理 LR 解析器的核心在于通过状态转换图来识别输入字符串是否符合给定的上下文无关文法。它主要依赖于项目集族(Item Set Family)、动作表(Action Table)以及跳转表(Goto Table)。这些表格可以通过SLR(1),LR(0),LR(1) 或者 LALR(1) 方法构建[^5]。 #### 动作表和跳转表的作用 - **动作表**:定义了解析过程中遇到不同符号时应该采取的动作,可能的操作包括移入(shift)、规约(reduce)、接受(accept)或者报错(error)。 - **跳转表**:用于指导当读取到非终结符时如何切换当前的状态。 ### 实现示例 下面是一个简单的 Python 版本的 SLR(1) 解析器框架,展示了如何基于预计算好的 Action 和 Goto 表格执行解析过程: ```python class LRParser: def __init__(self, action_table, goto_table, grammar_rules): self.action_table = action_table self.goto_table = goto_table self.grammar_rules = grammar_rules def parse(self, input_tokens): stack = [0] symbols = ['S'] # Start symbol index = 0 while True: state = stack[-1] token = input_tokens[index] if index < len(input_tokens) else '$' action = self.action_table.get((state, token), None) if not action: raise Exception(f"Parsing error at {token}") act_type, value = action if act_type == 's': # Shift operation stack.append(value) symbols.append(token) index += 1 elif act_type == 'r': # Reduce operation lhs, rhs = self.grammar_rules[value] for _ in range(len(rhs)): stack.pop() symbols.pop() new_state = stack[-1] next_state = self.goto_table[(new_state, lhs)] stack.append(next_state) symbols.append(lhs) elif act_type == 'acc': print("Parsing successful!") break return "Success" # Example usage with predefined tables and rules. action_table_example = { (0,'id'): ('s',2), (0,'$'): ('acc',None), (1,'+'): ('s',3), (1,'$'): ('r',( 'E',['T','+','E'] )), ... } goto_table_example = { (0,'E'): 1, (0,'T'): 4, ... } rules_example = [ ('E', ['T']), ('E', ['T', '+', 'E']), ('T', ['F']), ('T', ['F', '*', 'T']), ('F', ['(', 'E', ')']), ('F', ['id']) ] parser = LRParser(action_table=action_table_example, goto_table=goto_table_example, grammar_rules=rules_example) input_string = ["id", "+", "id", "$"] result = parser.parse(input_string) print(result) ``` 此代码片段仅作为概念验证,并未包含完整的 Action/Goto 表格填充逻辑。实际应用需根据具体文法规则生成相应表格[^6]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值