如何递归解析扁平列表为嵌套函数结构

小静酱_5157

小静酱_5157

2026-10-30

483人浏览

原创

如何递归解析扁平列表为嵌套函数结构

本文介绍一种基于迭代器与前瞻缓冲区的稳健方法,将带层级语义的扁平 token 列表(含函数标识、参数位置、转义机制)准确还原为嵌套字典结构,完美支持零值转义、多层递归调用与语法错误定位。

本文介绍一种基于迭代器与前瞻缓冲区的稳健方法,将带层级语义的扁平 token 列表(含函数标识、参数位置、转义机制)准确还原为嵌套字典结构,完美支持零值转义、多层递归调用与语法错误定位。

在领域特定语言(DSL)或自定义指令序列解析中,常需将线性排列的 token 流(如 [(3, (1,1)), (2, (1,3)), (1, (1,5)), (1, (1,7)), ...])按语义规则重组为树形嵌套结构。核心挑战在于:函数由三元组标识符 + 位置码构成(如 3.2.1.1 表示函数 3.2.1 的第 1 个参数开始),-1 表示闭合,而 0 作为转义符可屏蔽后续可能被误识别为函数开头的数字序列(例如 33.5.3.1 中的 33 后跟 0,则 5.3.1 不再触发嵌套解析)。原始实现因状态耦合过重、转义逻辑分散而难以维护和修正。

我们采用更清晰的分层设计:

  • 使用 LookaheadQueue 封装输入迭代器,支持安全地向前查看最多 5 个元素(覆盖函数四元组 + 转义位),避免索引越界与手动偏移管理;
  • 定义 Func 类统一抽象函数实体,封装三元键(keys)、参数位置(arg_pos)及源码位置(location),便于语义比较与错误提示;
  • 主解析函数 parse() 通过递归闭包 recur(f) 实现上下文感知:f=None 表示顶层,f 为当前正在解析的函数对象;
  • 转义处理集中且明确:仅当 items[4][0] == 0(即函数四元组后紧跟零)时,调用 items.take(1, 4) 消费该转义零,确保它不进入任何子递归作用域——这正是原代码的核心缺陷所在。

以下是完整可运行的参考实现:

from itertools import islice, chain, repeat

class LookaheadQueue(list):
    def __init__(self, iterator, maxsize, default):
        super().__init__()
        self.maxsize = maxsize
        self.iterator = chain(iterator, repeat(default))
        self.default = default
        self.take(0)  # 初始化填充

    def take(self, size=1, at=0):
        value = self[at:at+size]
        del self[at:at+size]
        self.extend(islice(self.iterator, self.maxsize - len(self)))
        return value

class Func:
    def __init__(self, items):
        self.keys = tuple(items[:3])
        self.arg_pos = items[3][0]
        self.location = f"line {items[0][1][0]}, position {items[0][1][1]}"
        self.name = ".".join(map(str, (key[0] for key in self.keys)))

    def __eq__(self, other):
        return isinstance(other, Func) and other.name == self.name

    def __repr__(self):
        return f"function {self.name}"

def parse(code):
    empty = (None, (None, None))
    items = LookaheadQueue(iter(code), 5, empty)

    def recur(f):
        argument = []
        arguments = {1: argument}
        while items[0] != empty:
            try:
                f2 = Func(items)
            except IndexError:
                break
            # 检查是否被转义 或 非起始调用(非 .1)
            if items[4][0] == 0 or (f2 != f and f2.arg_pos != 1):
                if not f:  # 顶层必须是函数调用
                    raise SyntaxError(f"Expected function call, but got non-function content at {items[0]}")
                if items[4][0] == 0:
                    items.take(1, 4)  # 消费转义零
                argument.extend(items.take(1))  # 当前 token 作为参数数据
                continue
            items.take(4)  # 消费函数四元组
            if f2.arg_pos == 1:  # 新函数调用 → 递归进入
                argument.append(recur(f2))
            elif f2.arg_pos == -1:  # 当前函数结束
                return [f.keys, arguments]
            else:  # 同一函数的新参数(如 .2, .3...)
                if f2.arg_pos in arguments:
                    raise ValueError(f"Duplicated argument {f2}.{f2.arg_pos} at {f2.location}")
                argument = []
                arguments[f2.arg_pos] = argument
        if f:
            raise SyntaxError(f"Function {f} not terminated with {f}.-1")
        return argument

    return recur(None)

关键注意事项:

  • ✅ 转义零严格单次生效:items[4][0] == 0 仅在顶层前瞻时判断,消费后即从流中移除,子递归永远看不到它;
  • ✅ 错误定位精准:Func.location 记录原始行列号,SyntaxError 消息直接指向问题 token;
  • ✅ 健壮性设计:LookaheadQueue 使用 repeat(default) 防止迭代器耗尽异常,try/except 包裹 Func(items) 提升容错;
  • ⚠️ 输入约束:要求函数标识始终为连续四元组(key1,key2,key3,pos),且转义零必须紧随其后(位置偏移为 +4);
  • ? 递归边界清晰:recur(None) 启动顶层解析,recur(f2) 进入子作用域,f2.arg_pos == -1 触发返回,无隐式状态泄漏。

此方案将复杂的状态机逻辑解耦为声明式规则,大幅提升可读性、可测试性与可扩展性——当需支持双零转义(0 0 → 单个数据 0)时,仅需增强 LookaheadQueue.take() 的模式匹配逻辑,无需重构整个解析流程。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

2023.07.20

1831

4

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

2023.07.25

4904

7

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.31

1789

3

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

2023.08.03

29357

23

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

3507

5

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

3507

5

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1223

5

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

616

4

python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

2723

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.1万人学习