资讯详情

从 297 行模板到 3331 行头文件:miniblink49 仓库里 Pump 代码生成流水线走一遍

📅 2026/10/14 3:19:34 | 华诺云谱 👁 阅读
从 297 行模板到 3331 行头文件:miniblink49 仓库里 Pump 代码生成流水线走一遍
从 297 行模板到 3331 行头文件miniblink49 仓库里 Pump 代码生成流水线走一遍【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49miniblink49 仓库内附带了多个版本的 V8 源码树其中 v8_5_7/testing/gtest/scripts/pump.py 是一个叫Pump的小工具单文件 Python 脚本855 行把混写了 C 代码与元代码的.pump文件展开成只在参数个数上不同的一族头文件。读完这篇你能自己写出.pump文件并驱动生成流程。全文按这个顺序走先看重复劳动长什么样再跑通一次真实转换然后是语法速查、脚本内部流水线、仓库里的真实模板最后是高频坑。五十个几乎一样的 structGoogle Test 的类型化测试需要表达类型列表Types1T1、Types2T1,T2……直到 50 个元素每个都长着一模一样的Head/Tail骨架只是模板参数个数逐一递增。手写 50 份是纯体力活结构上想改一行还得同步 50 处。Pump 版权始于 2008 年变参模板当时尚未被编译器广泛支持V1_6_PumpManual.md 前两个小节交代了这个动机而手工生成脚本又有另一个毛病脚本结构与生成代码脱节产物改一小处脚本里要做的联动修改不直观。仓库里的证据很直接产物 gtest-type-util.h 有 3331 行其中 51 个TypesN、51 个TemplatesN而生成它的 gtest-type-util.h.pump 只有 297 行。从模板到产物的最短路径先看真实输入与真实产物。.pump源文件第 126–138 行的循环块$range i 2..n $for i [[ $range j 1..i $range k 2..i template $for j, [[typename T$j]] struct Types$i { typedef T1 Head; typedef Types$(i-1)$for k, [[T$k]] Tail; }; ]]文件顶部$var n 50同一位置在产物里gtest-type-util.h 第 127–137 行就是template typename T1, typename T2 struct Types2 { typedef T1 Head; typedef Types1T2 Tail; }; template typename T1, typename T2, typename T3 struct Types3 { typedef T1 Head; typedef Types2T2, T3 Tail; };十几行元代码展开出 49 个几乎同构的 structTypes$(i-1)让每一环都引用前一环。想自己重放一次main()pump.py 第 831–851 行只在文件名以.pump结尾时才写文件否则产物走标准输出。把源文件拷到 /tmp 再跑cp v8_5_7/testing/gtest/include/gtest/internal/gtest-type-util.h.pump /tmp/demo python2 v8_5_7/testing/gtest/scripts/pump.py /tmp/demo | grep -B1 -A4 struct Types2输出就是上面那几行。注意脚本通篇是 Python 2 语法裸print语句务必用 python2 跑。反过来给它以.pump结尾的文件名脚本会去掉后缀写入文件并在头部加三行 This file was GENERATED by command … DO NOT EDIT BY HAND!!! 注释第 846–849 行——仓库里所有生成头文件顶部的警告注释就来自这里。面向参数个数展开的一套元语法元关键字以$开头[[/]]是元词法块$$开启元注释到行尾先由StripMetaComments()第 226–234 行逐行剥掉再分词。核心构造一张表列全构造作用注意点$var id exp定义元变量值形似整数时存为整数$(n1)这类算术直接可用作用域到当前元词法块末尾$range id exp..exp声明迭代变量的闭区间含两端可被后续多个$for复用边界是 Python 表达式$for id sep [[ code ]]迭代展开sep为迭代间分隔符范围必须先用$range声明sep可以是任意 C 代码或空$($)输出字面$C 代码里含美元符号时必备$id/$(exp)变量值 / 表达式值表达式用 Python 语法后紧跟字母数字会粘连见后文坑$if exp [[ ... ]] else_branch条件分支else_branch可以是$elif、$else或空[[ code ]]元词法块紧邻它的换行被吞掉$$ comment元注释分词前整体剥除顺手修 Emacs 自动缩进一个把迭代、嵌套循环、条件都串起来的完整示例V1_6_PumpManual.md 第 52–69 行$var n 3 $$ Defines a meta variable n. $range i 0..n $$ Declares the range of meta iterator i (inclusive). $for i [[ // Foo$i does blah for $i-ary predicates. $range j 1..i template size_t N $for j [[, typename A$j]] class Foo$i { $if i 0 [[ blah a; ]] $elif i 2 [[ blah b; ]] $else [[ blah c; ]] }; ]]展开得到 Foo0 到 Foo3 四个类模板参数列表随$j增长函数体按i的值走不同分支。形式文法不必单列pump.py 第 46–62 行 docstring 里的 GRAMMAR 段四行说清code是atomic_code序列atomic_code覆盖上表全部构造else_branch递归定义$elif被解析成嵌套$ifexp是 Python 简单表达式。脚本里的四段流水线pump.py 按分词 → 解析 → 求值 → 美化四段跑完整个转换分词。TOKEN_TABLE第 72–84 行用一组正则按序匹配元关键字与符号其中\[\[\n?这一条正是紧邻元括号的换行被忽略规则的实现点。TokenizeLines()第 310–387 行以生成器吐出 token每个都带Cursor行列光标后任何报错都能定位到行。解析为 AST。ParseToAST()第 577–581 行把 token 流变成与文法一一对应的节点ParseElseNode()第 475–500 行递归消费$else/$elif分支ParseExpNode()第 470–472 行把表达式里每个标识符改写成self.GetValue(标识符)从而桥接到 Python 求值。这一步保证语法错误时报错能精确到哪个 token、在哪、少了什么。求值与展开。Env第 584–638 行用栈式作用域维护变量与范围进入循环体或分支时Clone()复制环境。核心是RunAtomicCode()第 656–699 行ForNode用range(lower, upper 1)迭代即闭区间只有i ! upper才追加sep尾部分隔符不会多出来。变量找不到时GetValue()打印ERROR: meta variable xxx is undefined.并直接退出——这是写.pump时最常见的报错。美化。BeautifyCode()第 814–820 行逐行调WrapLongLine()第 790–811 行超 80 列的注释按词折行保持//缩进预处理指令折行加\续行符普通代码优先在,或;后断行、实在不行在空格处断续行统一缩进 4 格头文件保护宏、#include与 IWYU pragma 按风格指南特例放行第 783–787 行。这一步保证展开出的超长模板参数列表落盘时仍是折行规范、可直接提交的文件。值得精读的真实模板gtest-type-util.h.pump 值得细看因为它把上限压成一行第 2 行$var n 50。Types0..Types50、Templates0..Templates50、TypesT1,...,T50转换模板与它们的偏特化全部由这一个变量展开——上限想从 50 提到 60改的是一个数字而不是一片代码。第二处巧思是递归引用循环体内的typedef Types$(i-1)$for k, [[T$k]] Tail;让每个TypesN拆成首类型 前 N-1 个类型手写时这 50 份要各自想清楚引用谁。其余.pump文件是同一套用法的重复gtest-tuple.h.pump 用$var n 10加双层$for生成GTEST_k_TUPLE_(T)、GTEST_n_TYPENAMES_(T)宏族并用void补位gtest-param-test.h.pump 以$var n 50、$var maxtuple 10分别控制Values与Combine的参数上限gtest-param-util-generated.h.pump 生成Values1..ValuesNgmock 一侧还有 6 个文件展开 mockers、actions 与 matchers。容易踩的坑变量名粘连Foo$jHelper会被分词器整体认成变量jHelper必须写Foo$j[[]]Helper[[]]展开为空串只起分隔作用V1_6_PumpManual.md Tips 一节第 176 行同款建议。换行被吞$for foo之后、[[/]]紧邻的换行不出现在产物里确要产出换行就额外插一个空行。迭代未声明的范围$for的 id 必须先有$range否则GetRange()报ERROR: range xxx is undefined.退出pump.py 第 632–638 行。用 Python 3 跑脚本是 Python 2 语法python3 下直接报语法错误用python2。可以带走的一行结论Pump 用一个 Python 文件 一套$语法换掉了按参数个数展开这类样板代码$var/$range/$for/$if覆盖需求分词求值流水线加 80 列折行保证产物直接可用这正是 Google Test 里类型列表、tuple 与参数生成器这些五十重载规模头文件的标配写法。你现在就可以做一件事打开 gtest-type-util.h 看顶部那行 DO NOT EDIT再翻出同目录的.pump源对照Types2那几行把展开规则在脑子里走一遍想动手验证就按前文两条命令在 /tmp 里重放一次生成。【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑