深入解析 parsy:Semgrep 中基于 Python 解析器组合子的锁文件解析实践
SAST应用安全静态分析开发工具代码质量【免费下载链接】semgrepLightweight static analysis for many languages. Find bug variants with patterns that look like source code.项目地址https://gitcode.com/GitHub_Trending/se/semgrep点击查看免费下载导读parsy 是一个优雅的 Python 文本解析库它通过将小解析器组合成复杂的大解析器以声明式方式完成文本解析任务本质上是面向 LL(∞) 文法的单子式解析器组合子monadic parser combinator库。在 Semgrep 项目中parsy 被 vendor 到cli/src/semdep/external/parsy并经过深度改造——在解析流中增量跟踪行号与列号直接服务于cli/src/semdep下一系列 lockfile 解析器如 requirements.txt、yarn.lock、go.mod 等。读完本文你将掌握 parsy 的解析器组合原理、Semgrep 对它的定制改造Position 三元组索引以及这些能力如何被真实 lockfile 解析器复用。一、parsy 是什么解析器组合子的核心思想parsy 的核心主张是“通过组合小解析器来解析文本”先定义能匹配单个 token 的最小解析器如匹配一个字符、一个字符串或一个正则表达式再借助、|、.many()、.sep_by()等组合子把它们拼装成能匹配完整语法结构的复杂解析器。这一点在 parsy/README.rst 中有明确表述——它是一个 Python 解析器组合子库受 Haskell 的 Parsec、Parsnip、Parsimmon 等经典库启发与它们同属解析器组合子范式。该库要求Python 3.7 及以上。在 Semgrep 仓库中 vendored 的版本为 2.0见 version.py。解析器组合子最吸引人的地方在于解析器本身就是普通 Python 对象可以被当作一等公民组合、传参、递归引用。这让语法定义几乎不需要单独的 DSL 或代码生成步骤直接用 Python 表达即可。二、Semgrep 为什么 vendor parsy行/列增量跟踪改造2.1 Vendoring 的动机parsy 原版在解析流中只记录一个整数索引消费了多少个元素而 Semgrep 的锁文件解析需要精确的行号与列号来定位依赖项在 lockfile 中的位置这是 SCA 结果能准确回指源码行的基础。因此 Semgrep 将 parsy vendor 进仓库并做了定制修改这一点在 VENDOR_README.md 中有完整说明把单个整数索引替换为offset、line、column 三元组offset即原来的索引语义——流中已消费的元素个数当解析输入是字符串时每消费一个字符都会增量更新 line 与 column当解析输入不是字符串如 token 列表时line 与 column 均置为 -1 并忽略该改动因向后不兼容而暂时无法合并回上游所以 Semgrep 以 vendoring 方式维护直到上游合并且发版。2.2 Position 三元组在源码中的落地在 parsy/init.py 中这个三元组被实现为不可变 dataclassPositiondataclass(frozenTrue) class Position: offset: int line: int column: int而增量更新逻辑集中在make_index_update函数中它统计被消费片段中的换行数\n若含换行则把column重置为最后一行内的偏移否则直接累加列号def make_index_update(consumed: str) - Callable[[Position], Position]: slen len(consumed) line_count consumed.count(\n) last_nl consumed.rfind(\n) return lambda index: Position( offsetindex.offset slen, lineindex.line line_count, columnslen - (last_nl 1) if last_nl 0 else index.column slen, )所有消费型解析器string、regex、test_item等都通过它推进Position。对于非字符串流如 token 列表则统一产出Position(match.end(), -1, -1)之类的占位值表示行/列信息不可用。2.3 错误报告中的行号利用改造后的位置信息被ParseError直接消费。ParseError持有expected期望集合、stream与index失败的 Positionclass ParseError(RuntimeError): def line_info(self): if isinstance(self.stream, str): return f{self.index.line}:{self.index.column} else: return str(self.index.offset)错误消息格式为expected xxx at line:column这对定位 lockfile 中的语法错误至关重要。三、核心 API 全景Parser 类与组合子3.1 Parser 的语义与入口Parser是一个包装了流 起始 Position → Result函数的对象见 parsy/init.py。它的两个入口方法parse(stream)必须解析整个输入内部等价于(self eof).parse_partial(stream)即强制要求 EOFparse_partial(stream)解析尽可能长的前缀返回(结果, 剩余部分)元组失败则抛ParseError。初始化索引规则字符串流从Position(0, 0, 0)开始非字符串流从Position(0, -1, -1)开始。Result是解析结果的数据类包含status、index成功时的新位置、value、furthest最远失败位置和expected期望集合。其中aggregate方法负责合并多个分支的失败信息保留最远失败点若两个失败点相同则合并期望消息集合这是组合子能给出高质量错误报告的基础。3.2 原语解析器原语行为string(s, transformnoop)精确匹配字符串s可选transform对期望串与输入串同时做归一化如大小写不敏感regex(exp, flags0, group0)用re.match从当前 offset 匹配group可提取指定捕获组test_item(func, desc)用谓词func测试流中的单个元素成功则消费它test_char(func, desc)test_item的字符语义别名match_item(item)测试下一个元素是否等于给定值char_from(s)匹配字符串s中的任意一个字符string_from(*strings)按长度降序排列后依次尝试匹配正确处理重叠字符串如与success(value)/fail(msg)恒成功不消费/ 恒失败any_char、whitespace、letter、digit、decimal_digit常用便捷原语eof仅当流已耗尽时成功from_enum(EnumClass)将枚举成员的值解析为对应枚举项3.3 组合子与修饰符顺序组合p1 p2取p2的值、p1 p2取p1的值、p1 p2拼接两个值要求可seq(*ps, **kw_ps)顺序执行并按位置/关键字收集结果。选择组合p1 | p2与alt(*parsers)按声明顺序尝试失败则尝试下一个。重复组合.times(min, max)限定次数、.many()0 次或多次、.at_least(n)、.at_most(n)、.sep_by(sep, min, max)以sep分隔重复、.until(other, min, max, consume_other)重复直到other成功默认不消费other。值变换.map(f)、.combine(f)把结果列表展开为f(*args)、.combine_dict(f)把 dict 结果展开为f(**kwargs)自动剔除None键和_前缀键、.concat()把结果列表.join成字符串、.result(v)恒返回v、.tag(name)包装为(name, value)二元组。可选与描述.optional(defaultNone)、.desc(description)覆盖失败时的期望消息。位置捕获index返回当前 offset、line_info返回(line, column)、.mark()把结果包装为((start_row, start_col), value, (end_row, end_col))三元组。lookaheadpeek(p)在不消费的情况下预览.should_fail(desc)是负向先行断言p成功则整体失败、p失败则整体成功。递归定义forward_declaration是一个空壳解析器需在真正使用前调用.become(parser)注入行为用于相互递归的语法如 JSON。生成器语法generate装饰器允许用 yield 语法编写命令式风格的解析器yield parser会驱动解析并把结果送回生成器若生成器return一个 Parser 对象则以该 Parser 继续解析。3.4 运算符速查运算符等价语义p1 p2p1.then(p2)取 p2 结果p1 p2p1.skip(p2)取 p1 结果p1 p2顺序解析并拼接operator.addp1 | p2alt(p1, p2)选择p * np.times(n)恰好 n 次p * range(a, b)p.times(a, b-1)四、Semgrep 中的真实用法从单子链到整文件语法4.1 语义化组合工具util.pycli/src/semdep/parsers/util.py 是全部 lockfile 解析器共享的组合子库在 parsy 之上构建了更高层的语义工具not_any(*chars)匹配任意不在给定字符集内的字符序列内部用regex(f[^{escape(...)}])实现line_number line_info.map(lambda t: t[0] 1)parsy 的行号是 0 起始但编辑器是 1 起始这里统一加 1mark_line(p)记录解析前所在行号返回(行号, 结果)二元组pair/triple用bind链把两个/三个解析器结果组装成元组quoted(p)解析被双引号包裹的p引号不进结果string() p string()upto(*s, include_other, consume_other, allow_newline)解析到某个分隔符为止可把分隔符附加进结果或丢弃默认不允许换行被消费integer、any_str、word、line、consume_line常用便捷解析器delay(f)延迟解析器构造解决相互递归函数定义导致的无限求值问题yarn.py 中用到become(p1, p2)forward_declaration.become的类型化版本用于先声明后定义的相互递归。此外 util.py 还包含一个带行号注释的 JSON 解析器json_value、json_object、array、json_doc其注释明确说明它改编自 parsy 官方仓库的 examples/json.py增加了类型标注、行号跟踪并用become实现自引用——这是前向声明 递归的标准范式。4.2 用组合子为 requirements.txt 编写完整语法requirements.pycli/src/semdep/parsers/requirements.py 是 parsy 组合风格的最佳示范。整个文件没有手写字符扫描循环而是分层声明whitespace regex(r[ \t]) | string(\\\n) # 水平空白或行续接 package upto(, , , , [, \n) # 包名 遇到分隔符为止 extras_specifier string([) upto(], consume_otherTrue) version_specifier string_from(, , , , , , ~, !).bind(...) dep package.bind(...) # 包名 可选 extras 版本约束 flag_line (string(--) | string(-)) consume_line requirements mark_line(flag_line | dep | consume_line | comment_line) \ .sep_by(string(\n).at_least(1)) \ .map(lambda xs: [(l, x) for (l, x) in xs if x])值得注意的工程细节package采用用分隔符反向定义而非完整正则upto(, , , , [, \n)直接表达包名就是遇到这些字符之前的文本代码注释明确说明这是有意为之version_specifier用string_from(, , ...)按长度降序处理与的重叠问题并只在且版本不含*时才保留该约束注释在预处理阶段由preprocessors.CommentRemover()剥离COMMENT_REGEX r(^|\s)#.*$解析器本身只处理干净内容每个依赖项通过mark_line携带行号后续在parse_requirements中产出带line_number的FoundDependency。4.3 其他解析器的复用模式cli/src/semdep/parsers/下几乎所有解析器都从 parsy 引入原语组合模式高度一致go_mod.py用regexstringalt解析require ( ... )块与单行 requirepoetry.py用any_char、eof、regex、string、success解析pyproject.toml的依赖片段pipfile.py / gem.py / mix.py / swiftpm.py / pom_tree.py / gradle.py分别基于string、regex、any_char、peek、success等原语构建各自生态的 lockfile 语法。4.4 错误处理闭环util.py 的parse_dependency_file是 parsy 解析器的统一调用入口读文件 → 预处理 → 空文件检查 →parser.parse(text)。捕获到ParseError时会利用改造后的行/列信息生成带行号、列号和出错行的DependencyParserError同样注意0 起始行号 → 1 起始的换算parse_error_to_str则重写了ParseError.__str__的格式便于转义特殊字符。整个流程把 parsy 的低层异常转换为 SCA 可消费的结构化错误对象。五、深入源码的补充细节Result 聚合机制parsy/init.pyaggregate在alt、seq、times等所有多分支场景中持续跟踪furthest最远失败点并合并expected集合——这让alt的最终错误信息能列出所有可能的期望。times的 min/max 语义times(n)恰好 n 次times(min, max)至少 min、至多 maxmany()等价于times(0, inf)optional()等价于times(0,1) 默认值映射。类型桩type stubscli/src/semdep/external/parsy/__init__.pyi提供泛型化的类型注解如Parser[int]让 Mypy 能静态检查解析器的组合类型。util.py 开头有一段重要说明运行时Parser不接受类型参数因此所有类型注解必须写成字符串形式模块声明from __future__ import annotations使其求值为字符串否则Parser[int]这类表达式会在运行时抛错——这是类型安全与运行时行为之间需要牢记的边界。条件限制vendored parsy 面向 Python 3.7行/列跟踪仅在字符串输入时有效字节流或 token 列表输入的行列信息为 -1不可用。若需为这些解析器调试递归深度SEMGREP_PYTHON_RECURSION_LIMIT_INCREASE环境变量可在解析超深嵌套结构时提高 Python 递归上限见 util.py 中RecursionError的处理提示。六、小结parsy 在 Semgrep 中的定位parsy 在 Semgrep 中不是又一个第三方依赖而是 lockfile 解析基础设施的语法引擎它提供最小原语与组合子、Position行/列跟踪、统一的ParseError报错而cli/src/semdep在其上实现了可读、可维护、类型可检查的声明式解析器。若要深入阅读 vendored 库本体parsy/init.py 与 vendor 说明 VENDOR_README.md阅读组合子封装层cli/src/semdep/parsers/util.py阅读端到端范例cli/src/semdep/parsers/requirements.py以及 go_mod.py、poetry.py、yarn.py 等同目录解析器。掌握这套原语 组合子 行号跟踪的思维后你不仅能用 parsy 写出声明式解析器也能直接理解 Semgrep 解析任意 lockfile 的内部机制。赞分享SAST应用安全静态分析开发工具代码质量【免费下载链接】semgrepLightweight static analysis for many languages. Find bug variants with patterns that look like source code.项目地址https://gitcode.com/GitHub_Trending/se/semgrep点击查看免费下载相关推荐深入解析GraphQL-Tools中的解析器组合功能深入解析GraphQL Tools中的解析器组合功能 引言为什么需要解析器组合 在GraphQL服务器开发中我们经常需要在解析器Resolver中实现后端API设计visx/group 深入解析基于 SVG g 的 visx 分组容器组件visx/group 深入解析基于 SVG g 的 visx 分组容器组件 visx/group 是 visx 可视化组件库中最基础、使用最广泛的原子组数据可视化前端图表库doomgeneric社区贡献指南如何参与开源项目开发与维护doomgeneric社区贡献指南如何参与开源项目开发与维护 doomgeneric是一个致力于简化 Doom 移植过程的开源项目通过实现少量核心函数即可将上一篇APScheduler 版本迁移指南从 v1/v2/v3 全面升级到 v4.0 的架构变化与实践要点下一篇G-Helper终极指南轻量级华硕笔记本控制解决方案深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Nextcloud AIO 通知容器(Notifications Community Container):实现社区容器向 Nextcloud 用户发送管理员通知

Nextcloud AIO 通知容器(Notifications Community Container):实现社区容器向 Nextcloud 用户发送管理员通知

云原生运维后端容器编排 【免费下载链接】all-in-one 📦 The official Nextcloud installation method. Provides easy deployment and maintenance with most features included in this one Nextcloud instance. 项目地址: https://gitcode.com/GitHub…

2026/10/2 21:28:37 阅读更多 →
ThreadLocal深入解析:从存储结构到线程池脏数据与内存泄漏

ThreadLocal深入解析:从存储结构到线程池脏数据与内存泄漏

先说个真实排查经历。前几天一个查询接口在压测时,日志里偶尔出现上一个请求的用户ID,查到最后才发现,问题出在一个没清理的ThreadLocal上。很多人对ThreadLocal有执念:既然它叫“线程局部变量”,那多线程访问同一个变…

2026/10/2 21:27:36 阅读更多 →
可视化搭建 keepAlive 模式:用 createPortal 分离 DOM 与 React 实例,根治拖拽跨父级移动的 Remount 卡顿

可视化搭建 keepAlive 模式:用 createPortal 分离 DOM 与 React 实例,根治拖拽跨父级移动的 Remount 卡顿

文档技术博客教程 【免费下载链接】weekly 前端精读周刊。帮你理解最前沿、实用的技术。 项目地址: https://gitcode.com/GitHub_Trending/we/weekly 点击查看 免费下载 在可视化搭建场景中,拖拽组件跨越不同容器、切换父级是高频操作,而 Re…

2026/10/2 21:27:36 阅读更多 →

最新新闻

WinForm+Modbus通讯源码详解:从串口配置到PLC数据读取

WinForm+Modbus通讯源码详解:从串口配置到PLC数据读取

简介:这是一套基于C# Winform开发的Modbus工业通讯完整源码,专为需要对接PLC设备的桌面应用开发者设计,完整支持Modbus TCP与串口两种主流通讯方式,开发环境为Visual Studio 2015,基于.NET 4.0框架,无需额外…

2026/10/2 22:11:20 阅读更多 →
多径衰落信道下的OFDM仿真:MATLAB实现与BER曲线优化

多径衰落信道下的OFDM仿真:MATLAB实现与BER曲线优化

简介:这是一份面向无线通信初学者与科研人员的OFDM系统仿真MATLAB源码包,用于在多径衰落信道条件下搭建完整的信号传输链路,分析误码率等关键性能,属于可直接修改参数运行的实践型程序。包内共4个文件,全部为m脚本源码…

2026/10/2 22:11:20 阅读更多 →
SpringBoot建筑工程项目管理系统设计与实现全解析

SpringBoot建筑工程项目管理系统设计与实现全解析

最近好几个做毕设和刚转行做后端的朋友都在问同一个东西: 基于SpringBoot的建筑工程项目管理系统 。这确实是个很经典的选题——业务领域足够具体、功能边界清晰、技术栈主流,而且源码和讲解视频的配套资料也比较齐全,拿来学习或直接作为毕…

2026/10/2 22:11:20 阅读更多 →
为什么PhyAgentOS坚持“先证据,后结论“:执行、证据、判定三事实分离架构深度剖析

为什么PhyAgentOS坚持“先证据,后结论“:执行、证据、判定三事实分离架构深度剖析

为什么PhyAgentOS坚持"先证据,后结论":执行、证据、判定三事实分离架构深度剖析 【免费下载链接】PhyAgentOS-core PhyAgentOS is a Recursive Self-Improving (RSI) physical agent operating system that enables agents to recursively sel…

2026/10/2 22:11:20 阅读更多 →
Hindsight Experience Replay:破解稀疏奖励困境的强化学习利器

Hindsight Experience Replay:破解稀疏奖励困境的强化学习利器

1. 从“后见之明”说起:hindsight 到底在讲什么 1.1 这个词本身就不简单 很多人第一次看到 hindsight 这个词,是在英语阅读理解里,意思是“事后聪明”、“后见之明”。俗话说的“马后炮”,本质上就是 hindsight——事情发生之后再…

2026/10/2 22:11:20 阅读更多 →
AI内容安全边界与博主创作规范

AI内容安全边界与博主创作规范

我不能基于“特朗普谈AI失控风险与中美差距”这一标题生成博文。 原因如下: 该项目标题涉及外国政治人物公开言论,且明确指向 国家间技术对比(中美差距) 和 宏观政策/地缘科技议题(AI失控、国家战略) …

2026/10/2 22:10:19 阅读更多 →

日新闻

从零搭建AI工程化:模型之外的完整闭环

从零搭建AI工程化:模型之外的完整闭环

先搞清楚一件事:从零开始做 AI 工程化,难的从来不是调模型、写提示词,而是把一套原型 Demo 变成长得像是“正经系统”的东西。你手里可能已经有了能跑通的代码,也可能刚读完一些概念,但真到了要把它变成可维护、可观测…

2026/10/2 0:00:20 阅读更多 →
大模型训练显存估计与混合精度训练实战指南

大模型训练显存估计与混合精度训练实战指南

1. 大模型训练显存估计与混合精度训练详解显存不够用,几乎是每个做大模型训练的人都会撞上的第一堵墙。你可能也经历过:模型代码写完了,数据管道跑通了,满心欢喜地按下训练启动脚本,结果几秒钟后终端弹出一行红字——C…

2026/10/2 0:00:20 阅读更多 →
小样本学习数据集选型指南:27个真正可用的高质量数据集

小样本学习数据集选型指南:27个真正可用的高质量数据集

1. 小样本学习的“弹药库”:为什么你总在找数据集,却总找不到真正能用的? 小样本、数据集——这两个词最近半年在我处理的200多个AI项目咨询里,出现频率排进前三。不是模型调不好,不是代码写不对,而是卡在…

2026/10/2 0:00:20 阅读更多 →

周新闻

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解

如何划分训练/验证集:Spirula Studio五种eval_mode策略详解 【免费下载链接】spirula-studio Cross-vendor 3D Gaussian Splatting trainer - video to splat to mesh, Vulkan or CUDA. 项目地址: https://gitcode.com/GitHub_Trending/sp/spirula-studio Sp…

2026/10/1 19:40:48 阅读更多 →
SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南

SEO怎么推广速查手册新手避坑实战指南 模板网站太丑不够用?别急着加滤镜,那是治标不治本。很多老板盯着后台流量掉得眼红,却还在纠结首页Banner的圆角是不是3像素。这就像穿着西装去挖土,姿势不对,努力白费。我整理这份 速查手册…

2026/10/1 19:41:40 阅读更多 →
FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏

FireRed-OpenStoryline少样本仿写深度解析:AI Agent如何复刻你的独特文案风格与节奏 【免费下载链接】FireRed-OpenStoryline FireRed-OpenStoryline is an AI video editing agent that transforms manual editing into intention-driven directing through natural language …

2026/10/1 20:05:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 5:26:06 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 6:09:11 阅读更多 →