Miller DSL 显式 r-string(r“...“)设计解析:让正则元字符穿越变量而不丢失
CLI数据分析【免费下载链接】millerMiller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON项目地址https://gitcode.com/gh_mirrors/mi/miller点击查看免费下载导读本文围绕 Miller 开源项目中关于DSL 显式 r-stringr...的一份实现计划文档展开深入剖析正则位置字符串隐式 raw 化这一历史机制为何无法跟随变量传递以及如何通过新增一种独立字面量类型让r\*既能直接用作正则参数、又能赋值给变量后再用于gsub/sub/~等正则场景。读完本文你将掌握 Miller DSL 中正则字面量的语法设计与编译管线词法 → AST → CST → 运行期求值、r...与r...i的语义与边界以及如何在 pkg/parsing/mlr.bnf、pkg/dsl/cst/leaves.go 中落地这一能力并配套回归测试。背景Issue #297 遗留的最后一步Miller 6 已经解决了 Issue #297匹配正则元字符的大部分问题唯一的遗留诉求是显式 r-string一种类似 Python raw string 的 DSL 字面量r...它不做任何反斜杠转义处理并且关键的是——它必须能够赋值给变量、稍后在正则位置使用rstar r\*; $y gsub($x, rstar, star);这正是 issue 讨论串中 torbiak 提出、johnkerl 认可今天无法工作的场景。johnkerl 倾向的设计issue 中的 Option 1是增量式的保留今天正则位置的普通字符串字面量的隐式 r-string 行为同时在其之上新增独立可用的显式r...字面量类型。为什么变量无法携带raw 性隐式 raw 是一个解析期 AST 技巧而非运行期值属性普通字符串字面量之所以在正则位置sub/gsub/regextract/regextract_or_else的第 2 个参数以及~/!~的右侧获得 raw不转义处理靠的是一套parse-time AST 预处理而不是运行期值的某种属性regexProtectPrePassAuxpkg/dsl/cst/root.go#L234-L262在 CST 构建前遍历 AST把sub/gsub/regextract/regextract_or_else调用点以及~/!~操作符的第 2 个子节点从NodeTypeStringLiteral改标为NodeTypeRegex——但仅当该子节点本身就是一个字符串字面量 AST 节点时才会这样做。BuildLeafNodepkg/dsl/cst/leaves.go#L16-L135随后分派NodeTypeStringLiteral走BuildStringLiteralNode调用lib.UnbackslashStringLiteral见 pkg/lib/unbackslash.go#L38-L97把\t→TAB、\\→\等而NodeTypeRegex走BuildRegexLiteralNodepkg/dsl/cst/leaves.go#L270-L274不做任何 unescape直接把原始 lexeme 包进Mlrval。问题在于位于同一参数槽位的局部变量或字段不会被预处理触碰它不是字符串字面量 AST 节点所以它的运行期字符串值早已是普通 unescape 之后的结果——正则编译阶段已经没有信息可供撤销了。例如star_re \*;由于\*不是被识别的通用转义序列UnbackslashStringLiteral的未识别转义直接丢弃反斜杠行为见 pkg/lib/unbackslash.go#L89-L93会把\*变成单独的*字节——它本身就是一个无效/误导的正则片段。这也解释了为什么隐式 raw 只能作用于字面上的字符串字面量而无法跟随值穿过变量。正则编译的单一咽喉CompileMillerRegexpkg/lib/regex.go#L97-L125 的CompileMillerRegex是sub/gsub/regextract/regextract_or_else/strmatch/strmatchx/~/!~全部走到的唯一编译入口由 pkg/bifs/regex.go 中的BIF_sub、BIF_gsub等调用。其行为若字符串以开头并以结尾剥掉两端引号后交给 Goregexp.Compile若以...i结尾则转换为 Go 的(?i)前缀若字符串完全没有包围引号则原样直接传给regexp.Compile第 124 行的 bare 回退分支。这个 bare 回退分支正是整个设计的承重事实一个完全不带引号的 raw 字符串值反斜杠保持原样、无外围引号今天就能正确编译regex.go和bifs/regex.go一行都不用改。因此只要显式 r-string 字面量在解析期构建出一个干净、不带引号的Mlrval无论直接用作正则参数、还是通过变量传递都能正确工作编译路径完全无需改动。可供参照的既有先例设计文档在语法层面找到了三个现成模板bytes_literalb...语法中已存在单字母前缀带引号字面量pkg/parsing/mlr.bnf#L98-L99注释明确说明must precede non_sigil_name since b is an idchar——r前缀同样需要这个顺序约束因为r也是合法标识符字符。其 AST/CST 节点类型与构建器BuildBytesLiteralNodepkg/dsl/cst/leaves.go#L342-L357会剥掉前缀b和包围引号然后——与 r-string 期望相反——仍然调用UnbackslashStringLiteral字节语义的 unescape如\xff变成单字节 0xff。RegexCaseInsensitivepkg/parsing/mlr.bnf#L703-L705、pkg/dsl/cst/leaves.go#L58-L67可选尾部i后缀的先例。语法规则为string_literal non_sigil_nameCST 分派若发现字面量文本没有以i结尾就追加上i然后委托给正则字面量构建器后者保留 Mlrval 上的包围引号这样CompileMillerRegex的...i分支稍后能找到它们。前车之鉴旧提交不可复用Git 提交1230553ebregex r-string feature2021 年 8 月曾添加过regex_r_string :: r {...} 并直接映射到NodeTypeRegex——但那是 Miller 6 重构之前的go/src/parsing/...树该树在pkg/...重写中被删除、从未被带过来。其单元测试和文档当时就留作 TODO 且未勾选。虽然代码不可复用但它确认了r {...} 这一语法形状是站得住脚的。文档已描述的隐式 r 行为参考文档-正则表达式约第 219-227 行已经写明了隐式 r-string 现状……如果你熟悉 Python 的 r-string所有处于正则位置的字符串都是隐式 r-string。一般来说这是正确且不易引起混淆的。但注意\t.\t作为sub的第二个参数并不等同于\t\t。这里是补充显式r...文档最自然的位置。钉死的回归测试不要动test/cases/dsl-regex-matching/0016输入为 test/input/regex-metacharacters.dkvp内容inputabc[.o*o.]def精确测试了 issue 中的隐式 r 场景gsub($input, \[, LEFT)等由提交b20a5ccd3/55209bfc5Test case for #297加入。显式 r-string 的工作绝不能改变它的行为它将继续作为隐式行为的回归护栏。设计方案从文法到 CST 构建1. 文法pkg/parsing/mlr.bnf在bytes_literal旁边、non_sigil_name之前新增r_string_literal词法规则同样的顺序要求因为r是合法标识符字符# Raw/r-strings r... (must precede non_sigil_name since r is an idchar) r_string_literal :: r { _string_char | _escape } ;紧挨RegexCaseInsensitive/StringLiteralpkg/parsing/mlr.bnf#L703-L709新增RStringCaseInsensitive与RStringLiteral两个产生式大小写不敏感变体声明在前与现有配对同约定# ra.*b (raw, case-sensitive) or ra.*bi (raw, case-insensitive). Must precede RStringLiteral. RStringCaseInsensitive :: r_string_literal non_sigil_name - { parent: 0, children: [0], type: RStringCaseInsensitive } ; RStringLiteral :: r_string_literal - { parent: 0, children: [], type: r_string_literal } ;并在MlrvalOrFunctionpkg/parsing/mlr.bnf#L639-L667中、紧挨现有RegexCaseInsensitive/StringLiteral/BytesLiteral行添加两个备选项CI 变体在前| RStringCaseInsensitive | RStringLiteral2. AST 节点类型pkg/dsl/ast_types.go靠近NodeTypeBytesLiteral/NodeTypeRegex约第 11-13 行新增两个 TNodeType 常量NodeTypeRStringLiteral TNodeType raw string literal NodeTypeRStringCaseInsensitive TNodeType case-insensitive raw string literalpkg/dsl/cst/ast_types.go普通形式靠近第 85 行、与文法type字符串一致CI 形式靠近第 115 行、与NodeTypeRegexCaseInsensitive并列新增两个 CST 常量NodeTypeRStringLiteral r_string_literal NodeTypeRStringCaseInsensitive RStringCaseInsensitive3. CST 构建器pkg/dsl/cst/leaves.go新增节点类型与构建器仿照BuildBytesLiteralNode的前缀/引号剥离但不调用UnbackslashStringLiteral// RStringLiteralNode is for explicit raw string literals r... (issue #297). // Unlike StringLiteralNode, no backslash processing is applied: r\* evaluates // to the two characters backslash-asterisk. This makes the value usable directly // as a regex-engine pattern fragment regardless of where it later travels -- as a // literal regex argument, or via a variable -- unlike the implicit-r-string trick // used for plain string literals in regex position (see regexProtectPrePassAux), // which only works at parse time and cant follow a value through a variable. type RStringLiteralNode struct { literal *mlrval.Mlrval } func (root *RootNode) BuildRStringLiteralNode(literal string) IEvaluable { // The PGPG lexer produces r_string_literal token with leading r in the lexeme. if len(literal) 1 literal[0] r { literal literal[1:] } // Case-insensitive form r...i: leave the quotes and trailing i intact, // matching BuildRegexLiteralNodes representation, so CompileMillerRegexs // existing \...\i handling applies unchanged. Case-insensitivity is only // meaningful once compiled as a regex, so this form is not intended to double // as a plain string value the way the non-CI form is. if len(literal) 3 literal[0] strings.HasSuffix(literal, \i) { return RStringLiteralNode{literal: mlrval.FromString(literal)} } // Plain form r...: strip the surrounding quotes for a clean raw-string value, // usable both as a regex argument (via CompileMillerRegexs bare-string // fallback) and as an ordinary string value. if len(literal) 2 literal[0] literal[len(literal)-1] { literal literal[1 : len(literal)-1] } return RStringLiteralNode{literal: mlrval.FromString(literal)} } func (node *RStringLiteralNode) Evaluate( state *runtime.State, ) *mlrval.Mlrval { return node.literal }在BuildLeafNode的现有分派pkg/dsl/cst/leaves.go#L42-L67中新增两个 caseCI 分支镜像NodeTypeRegexCaseInsensitive的缺少i则追加模式case asts.NodeType(NodeTypeRStringLiteral): return root.BuildRStringLiteralNode(sval), nil case asts.NodeType(NodeTypeRStringCaseInsensitive): if sval astNode.Children ! nil len(astNode.Children) 0 { sval tokenLit(astNode.Children[0]) } if sval ! !strings.HasSuffix(sval, i) { sval sval i } return root.BuildRStringLiteralNode(sval), nilregexProtectPrePassAux无需任何改动——它只改标已经是NodeTypeStringLiteral的节点语法原生的r...节点到达时已自带NodeTypeRStringLiteral/NodeTypeRStringCaseInsensitive标签会原样穿过预处理。4. 其他地方无需改动pkg/lib/regex.go 与 pkg/bifs/regex.go 都不需要修改——正如上文正则编译的单一咽喉所分析的bare 回退分支天然支持无引号 raw 值。5. 重新生成解析器pkg/parsing/mlr.bnf 经由 Miller 自己的 PGPG 生成器github.com/johnkerl/pgpg非 goyacc/lex通过tools/build-dsl需几分钟重新生成 pkg/parsing/lexer/lexer.go 和 pkg/parsing/parser/parser.go。按 pkg/parsing/README.md 与 README-dev.md 的说明这些生成文件是提交到版本库的——因此编辑mlr.bnf后要运行tools/build-dsl并把重新生成的产物与文法改动一起提交。6. 测试在test/cases/dsl-regex-matching/下新增用例沿用既有0016的结构覆盖(a) 直接以r\[风格字面量作为正则参数使用(b) 头条场景变量携带 rawrstar r\*; $y gsub($x, rstar, star)(c)r...作为普通非正则值使用确认按 raw 打印/存储——例如ra\tb是 4 个字符a、\、t、b而不是一个 tab(d)r...i大小写不敏感匹配例如rabci ~ ABC。test/cases/dsl-regex-matching/0016保持不动作为既有隐式 r 行为的回归护栏。7. 文档扩展 docs/src/reference-main-regular-expressions.md.in 中现有的隐式 r-string 段落约第 219-227 行补充新的显式r...语法issue 中的变量携带 raw示例rstar r\*; gsub($x, rstar, star)关于 CI 形式保留引号的不对称性的说明有文档记载的权衡不是 bugr...i面向正则位置当作普通值使用时它会保留...i外壳与今天隐式...i正则字面量的行为一致。随后通过make -C docs/src forcebuild重建文档。验证清单make build编译通过手动检查echo a[ | mlr put $a gsub($a, r\[, left_square) echo a* | mlr put rstar r\*; $a gsub($a, rstar, STAR) mlr -n put end { print r\t } # 打印两个字符而不是一个 tabmake check单元 回归测试确认test/cases/dsl-regex-matching/0016与新增的 r-string 用例同时通过推送前执行make lint。小结一处只读的加法设计这个设计的精髓在于零侵入隐式 r-string 继续作为正则位置的默认行为受0016回归测试保护显式r...作为新的独立字面量类型叠加其上。由于CompileMillerRegex的 bare 回退分支早已支持无引号 raw 值整个实现只需要文法规则 两个 AST/CST 节点类型 一个不调用UnbackslashStringLiteral的构建器而正则编译路径、BIF 层和既有预处理全部原样保留——这正是 Issue #297 最后一步最稳妥的落地方式。赞分享CLI数据分析【免费下载链接】millerMiller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON项目地址https://gitcode.com/gh_mirrors/mi/miller点击查看免费下载相关推荐PRQL 字符串完全指南String 字面量、转义序列与 F/R/S 三种字符串格式详解PRQL 字符串完全指南String 字面量、转义序列与 F/R/S 三种字符串格式详解 PRQL 作为一门管道的 SQL 替代语言其字符串系统涵盖普通字符后端Miller 正则表达式完全指南~、strmatch/strmatchx 与 DSL 正则捕获机制Miller 正则表达式完全指南 ~ 、 strmatch / strmatchx 与 DSL 正则捕获机制 Miller一款面向 CSV、TSV、DKVCLI数据分析LocalSend元数据保留文件属性与创建时间保持原样LocalSend元数据保留文件属性与创建时间保持原样 痛点文件传输中的元数据丢失问题 在日常工作中你是否遇到过这样的困扰通过传统方式传输文件后文件的即时通讯网络/通信上一篇Upsonic 数据分析技能中的统计检验速查指南从检验选择、效应量到样本量与多重比较的完整实战参考下一篇Pencil免费开源GUI原型设计工具快速上手指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

XXE漏洞从原理到实战:外部实体注入的检测、利用与防御

XXE漏洞从原理到实战:外部实体注入的检测、利用与防御

做了几年安全测试,如果只让我选一个“看起来冷门、实际一打一个准”的漏洞,我大概率会选XXE。很多团队把精力全扑在SQL注入和XSS上,结果某一天扫出个XML外部实体注入,直接懵在原地——这玩意儿到底怎么利用?怎么修复&a…

2026/9/26 21:51:09 阅读更多 →
RAG+LLM抽取年报AI变量,构建绿色全要素生产率实证模型

RAG+LLM抽取年报AI变量,构建绿色全要素生产率实证模型

简介:面向金融科技与环境经济交叉领域的研究者,项目包演示了基于RAG与大语言模型分析A股上市公司年报的完整流程,旨在量化评估人工智能对企业绿色全要素生产率(GTFP)的影响,并引入融资约束异质性视角开展稳…

2026/9/25 15:53:43 阅读更多 →
TypeScript Book 项目动态:TypeScript 7.0 正式发布,Go 原生编译器时代的性能与迁移指南

TypeScript Book 项目动态:TypeScript 7.0 正式发布,Go 原生编译器时代的性能与迁移指南

文档教程 【免费下载链接】typescript-book The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source. 项目地址: https://gitcode.com/gh_mirrors/typ/typescript-book 点击查看 免费下载 TypeScript 7.0 于…

2026/9/25 15:53:43 阅读更多 →

最新新闻

Atlas 300V 24G推理卡YOLO部署实战:昇腾NPU环境搭建与调优

Atlas 300V 24G推理卡YOLO部署实战:昇腾NPU环境搭建与调优

我手里这块卡,就是很多人问是不是“运算加速卡”的 Atlas 300V 24G。直接说结论:它是一张纯正的 AI 推理加速卡,干的是把训练好的模型“跑起来”的活,跟 GPU 那种既能训练又能渲染的通用加速卡不是一个路数。最近不少搞视觉检测的…

2026/9/26 21:51:12 阅读更多 →
华为Atlas 300V Pro部署YOLO实战:环境配置、模型转换与推理调优全指南

华为Atlas 300V Pro部署YOLO实战:环境配置、模型转换与推理调优全指南

华为 Atlas 的硬件版本和驱动版本非常敏感,网上很多部署教程讲得含含糊糊,一堆人卡在第一步。我这次用的是Atlas 300V Pro 24G 推理卡,严格来说它确实是“运算加速卡”,但它不是用来做训练的那种,它的定位是推理&#…

2026/9/26 21:51:12 阅读更多 →
Windows原生OpenSSH服务启动失败排错全指南

Windows原生OpenSSH服务启动失败排错全指南

1. 为什么 Windows 原生 SSH 不是“装个软件就完事”——从服务启动失败报错切入真实场景你是不是也遇到过这样的时刻:在 PowerShell 里敲下Start-Service sshd,回车后弹出一行红色错误:Start-Service : 无法启动服务“OpenSSH SSH Server (s…

2026/9/26 21:51:12 阅读更多 →
MariaDB 10.5.11二进制包部署实战:从解压到systemd自启

MariaDB 10.5.11二进制包部署实战:从解压到systemd自启

简介:本资源为 MariaDB 10.5.11 在 Linux x86_64 平台上的官方二进制安装包,面向需要在生产或测试环境中部署开源关系型数据库的运维工程师、后端开发与数据库学习者。MariaDB 由 MySQL 创始人主导开发,兼容 MySQL 语法,便于迁移&…

2026/9/26 21:51:12 阅读更多 →
WorkBuddy Enterprise:从超级个体到超级团队的企业级Agent平台落地实践

WorkBuddy Enterprise:从超级个体到超级团队的企业级Agent平台落地实践

1. 从「超级个体」到「超级团队」:WorkBuddy Enterprise 到底在解决什么问题过去一年,我身边不少开发者都在经历同一个变化:一个人带着几个 AI Agent,就能把过去需要小团队才能完成的事情跑起来。写代码有 CodeBuddy 这类工具辅助…

2026/9/26 21:51:12 阅读更多 →
基于深度学习的高精度人脸表情识别系统设计:从数据管线到ONNX部署的完整源码实战

基于深度学习的高精度人脸表情识别系统设计:从数据管线到ONNX部署的完整源码实战

简介:这是一套面向深度学习入门与计算机视觉实践者的高精度人脸表情识别系统源码,采用Python为主、Shell脚本为辅实现,可应用于情感分析、人机交互体验优化等场景,适合具备一定Python与神经网络基础、希望完整跑通表情识别流程的开…

2026/9/26 21:50:11 阅读更多 →

日新闻

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、…

2026/9/26 0:00:25 阅读更多 →
学校官网模拟全流程实践:从页面布局到后端接口与部署

学校官网模拟全流程实践:从页面布局到后端接口与部署

如果你正在找一门 Web 大作业的题目,或者刚开始接触 Web 前端开发想做点能拿来展示的东西,“学校官网模拟”几乎是最稳的选择。题目看着简单,但要把导航、新闻列表、轮播 Banner、二级页面、后台数据都串起来,其实已经把前端布局、…

2026/9/26 0:00:25 阅读更多 →
超级玛丽游戏源码C++:从零搭建横版跳跃游戏工程

超级玛丽游戏源码C++:从零搭建横版跳跃游戏工程

简介:这是一份面向游戏开发初学者与C进阶学习者的超级玛丽(超级马里奥)游戏源码,基于C面向对象编程实现,适合想通过经典项目理解游戏主循环、角色类设计、地图关卡加载与物理碰撞检测的读者参考。压缩包共49个文件&…

2026/9/26 0:00:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/25 19:27:14 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/26 20:27:29 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/25 20:29:09 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/25 20:29:43 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/25 20:29:31 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/25 19:27:26 阅读更多 →