PRQL 的 Raku 语法实现:从 Grammars 定义到形状断言测试的完整指南
后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载PRQLPipelined Relational Query Language在仓库中维护了面向多种编辑器和语言生态的语法定义grammars/raku/ 正是其中的 Raku 实现一个用 Raku 原生grammar机制书写的 PRQL 解析器附带一套以解析树形状为断言对象的测试体系。本文将带你从快速上手、安装、测试运行一路深入到grammar的规则定义、测试辅助模块的实现细节与已知的设计边界读完即可自行解析 PRQL 查询、运行并扩展这套测试语料。背景grammars/raku/在 PRQL 仓库中的定位PRQL 项目将各生态的语法定义集中存放在 grammars/ 目录下作为语法/语法高亮定义的索引页覆盖 CotEditor、GtkSourceView、KSyntaxHighlighting、emacs、nano、Lezer、Raku 等实现。与主要用于编辑器高亮的词法定义不同grammars/raku/提供的是一套完整的 Raku Grammars 解析器——它不仅能识别 token还能把整条 PRQL 查询解析成一棵带命名的捕获树并配套了断言这棵树结构的测试基础设施。该目录结构如下lib/prql.rakumodgrammar PRQL的完整定义即解析器本体t/lib/PRQLTest.rakumod测试辅助模块提供parses-to、shape等工具t/按语法主题拆分的 10 个.rakutest测试文件META6.jsonRaku 生态zef的模块元数据。快速上手解析一段 PRQL 查询README 给出了最简用法。在当前目录下执行模块源码就在本地lib/下无需安装即可使用use lib .; use prql; say PRQL.parse(from employees); say PRQL.parsefile(employees.prql);第一行把当前目录加入模块搜索路径第二行通过use prql;载入 lib/prql.rakumod模块名prql由 META6.json 的provides字段映射到该文件随后即可用 Raku 内置的Grammar.parse/Grammar.parsefile方法解析字符串或文件。PRQL.parse返回的是 Raku 的Match对象解析成功时它携带完整的命名捕获树这正是后面形状断言测试所依赖的对象解析失败则返回Nil。从源码安装如果要把这个语法作为正式的 Raku 模块安装进环境README 说明从源码安装只需一条命令zef install .zef是 Raku 生态的模块安装工具.表示安装当前目录下的模块。安装依据是 META6.json其关键字段如下字段值说明namePRQL模块名version0.0.1版本号provides{ prql: lib/prql.rakumod }模块prql指向解析器源文件raku6.*兼容 Raku 6.xlicenseArtistic-2.0开源许可tagsdata, grammar, pipeline, sql检索标签productionfalse当前非生产发布状态从元数据看仍为早期版本运行测试单文件与全量两种方式测试采用 Raku 官方的Test框架.rakutest。README 说明可以指定文件名运行单个测试文件raku t/arithmetics.rakutest要运行整个t/目录的全部测试需要先安装prove6Raku 对 Perlprove的移植再以库模式执行zef install App::Prove6 prove6 --lib t/--lib让测试可以use prql;找到 lib/prql.rakumod而测试文件自身通过use lib $?FILE.IO.parent.add(lib).Str;把同级的t/lib/加入搜索路径从而加载 PRQLTest.rakumod。核心测试理念断言解析树的形状而非仅仅能否解析README 强调了这个实现最值得关注的设计每个测试断言的是语法构建出的树的结构shape而不只是查询能否被解析。示例parses-to filter 10 * 10, statement(pipeline-statement(pipeline(call-expression(identifier«filter»,test(test-inner(binary-test(expression(number(integer«10»)),arith-op«*»,expression(number(integer«10»)))))))));这段断言的含义是filter 10 * 10应当解析为一棵statement → pipeline-statement → pipeline → call-expression的树其中filter是叶子捕获identifier其参数是一个由binary-test连接两个number(integer)的二元测试运算符是arith-op捕获的*。parses-to的来源t/lib/PRQLTest.rakumodparses-to 定义在测试辅助模块中签名是sub parses-to(Str:D $source, Str:D $expected, Str :$desc -- Bool) is export is test-assertion其工作流程是用PRQL.parse($source)解析源文本若返回Nil则直接flunk并输出did not parse: $source用shape($match)把Match渲染成规范的形状字符串用ok $got eq $expected对比失败时通过diag输出got:与expected:两份按深度缩进、每捕获一行的树形诊断。形状是如何渲染的shape与内部递归函数children把Match渲染成rule(child,child)的形式只使用语法中命名捕获的名字一个没有命名子捕获的叶子捕获会把自身匹配的文本放进«…»中展示。语法中用于分组和量化的非命名括号组是透明的其命名子捕获直接上浮到父级位置。这样设计带来一个精妙的测试特性叶子文本参与形状断言。例如五个比较运算符、四种字符串前缀f/r/s/无前缀在形状中各自不同测试不会因为共享同一个字符串而误通过。indented子程序则负责把单行形状拆成每捕获一行的缩进树用于失败诊断——README 提到整条查询的断言形状超过 2000 字符单行对比几乎不可读而«…»内的文本会被原样保留、不参与缩进计算避免匹配文本中的(,),,干扰树形排版。写断言前先预览形状在编写新测试之前可以用 README 提供的命令查看某条查询实际生成的形状raku -I lib -I t/lib -e use PRQLTest; use prql; say shape(PRQL.parse(filter 1 1))-I lib -I t/lib把解析器和测试辅助模块都加入搜索路径输出类似statement(pipeline-statement(pipeline(call-expression(identifier«filter»,test(test-inner(binary-test(expression(number(integer«1»)),arith-op«»,expression(number(integer«1»)))))))))拿到这个输出即可直接作为parses-to的期望值形成先观察、后固化的 TDD 式工作流。断言细节is test-assertion的意义parses-to被标记为is test-assertion这会让Test框架在断言失败时把定位信息回溯到.rakutest测试文件的调用处而不是模块内部的那行ok。源码注释说明若没有这个标记全部 81 个测试的失败都会指向PRQLTest.rakumod内部无法定位真正失败的测试。解析器源码剖析grammar PRQL的规则体系理解了测试机制后再来看 lib/prql.rakumod 中解析器本体的结构。整个语法是一个标准的 Rakugrammar用token/rule组合出 PRQL 的完整文法。顶层结构语句与管线TOP规则是零个或多个statementtoken TOP { statement* }statement覆盖 PRQL 的各类顶层构件文档块、注释、prql版本声明query-definition、模块、注解、变量声明以及可带尾随注释的管线语句rule statement { | doc-block | comment | query-definition | module | annotation | variable-declaration | pipeline-statement comment? }管线pipeline是 PRQL 的语义核心这里用 Raku 的%量词表达以|分隔的一个或多个调用表达式或表达式|标识符即把函数当作管道目标rule pipeline { | call-expression % | | expression | identifier }query-definition匹配prql关键字加若干命名参数对应 PRQL 查询头部的版本/方言声明module用module 名称 { ... }包裹一组语句annotation对应{...}形式的注解语法。调用与绑定call-expression是 PRQL 函数调用的核心一个标识符后跟若干命名参数、绑定声明或测试表达式rule call-expression { identifier ( | named-arg | declaration | test ) }配套规则包括named-arg标识符: 表达式、declaration与declaration-tuple标识符 表达式、case-branch表达式 表达式与case-expressioncase加元组表达式、nested-pipeline括号包裹的管线。表达式体系expression是一个token通过选择分支覆盖 PRQL 的全部表达式种类包括关键字this/that/null、二元/一元表达式、数组、元组、嵌套管线、case 表达式、日期时间、参数$1、括号表达式、区间10..20、标识符、布尔值、时间单位、数字以及四类字符串普通、f、r、stoken expression { | this | that | null | binary-expression | unary-expression | array-expression | tuple-expression | nested-pipeline | case-expression | date-time | parameter | parenthesized-expression | range-expression | identifier | boolean | time-unit | number | string | f-string | r-string | s-string }值得注意的细节identifier用[. [ident | *]]*支持点分名称与通配如c.customer_id、table.*unary-expression支持/-前缀用于sort {-sum_income}以及前缀用于 join 的(customer_id)写法binary-expression的左操作数只接受标识符见下文边界讨论。数字与进制number分浮点与整数。integer支持十进制可带_分隔符与e科学计数、0x十六进制、0b二进制、0o八进制token integer { | .digit [.digit | _]* [e [ | -]? integer]? | 0x [.xdigit | _] | 0b [01_] | 0o [0..7_] }float要求整数部分、小数点与小数部分齐备同样支持_与可选的e指数。字符串、转义与注释字符串规则覆盖单/双引号、三引号、以及f/r/s三种前缀。三引号分支用!before 否定前瞻让正文一直运行到第一个闭合三引号——因此I said hello world!这种内含引号的写法能被正确吸收源码注释明确这是文档化的形式。转义规则escape接受\xNN、\u{...}以及任意单个字符作为兜底。源码注释解释了这一设计PRQL 的词法器对无法识别的转义会保留反斜杠后的字符而不是拒绝字符串所以语法也放宽到任意字符从而自然覆盖\\、\、\等情形。注释与文档块使用#与#!前缀正文.? $$到行尾Raku 中$$匹配行尾。日期时间与时间单位date-time统一以开头支持三种形态日期T时间加可选时区Z或±HH:MM、纯日期1970-01-01、纯时间08:30、12:00:00.500。time-unit由数字加维度组成dimension枚举了microseconds到years九种时间单位并以!ww阻止词边界之后的继续匹配避免把5yearsfoo误收。变量、lambda 与类型注解variable-declaration对应 PRQL 的let绑定右侧可以是嵌套管线或 lambdalambda是零个或多个参数后跟-与表达式lambda-param与type-name支持int32形式的类型注解以及(|)联合类型语法例如测试中的arg1int32 - arg1。运算符全集三个token汇总了全部运算符arith-op-*/%//**compare-op!~inlogic-op||??测试语料十个主题文件与典型断言t/目录按语法主题拆分测试每个文件用plan N声明用例数。综合来看覆盖范围如下测试文件用例数覆盖内容arithmetics.rakutest6加减乘除、幂运算、多运算符组合arrays.rakutest—数组表达式datetime.rakutest7日期、时间、带小数秒、日期时间、时区后缀full_queries.rakutest1官网示例级完整查询的整树形状identifiers.rakutest3基础标识符、下划线数字、Unicode 标识符misc.rakutest23布尔、null、this/that、注解、区间、注释/文档块、let 与 lambda、类型注解、参数、括号、管线、derive、嵌套管线、制表符、模块、元组内管线numbers.rakutest9整数、下划线分隔、小数、科学计数、时间单位、二/八/十六进制operators.rakutest10比较、逻辑、??合并、一元-与strings.rakutest22四类字符串、三引号、各类转义、未识别转义tuples.rakutest—元组表达式从测试中看到的语法行为与设计边界测试注释里保留了若干对当前语法行为的如实记录是理解实现取舍的第一手材料二元表达式不嵌套binary-expression左操作数只接受标识符因此filter 10 10 10不会嵌套成一颗树而是被记成filter的两个参数——10 10和一个一元 10。测试注释明确写道形状记录的是语法当前构建出的结果。换行不构成管线分隔pipeline只在|上分隔步骤而rule会把换行当普通空白跳过所以多行查询中换行分隔的步骤会全部收进第一个call-expression的参数列表而不是各自成为管线步骤。full_queries.rakutest与misc.rakutest中注释位于两条语句之间的用例都体现了这一点注释还对比了 lezer 语料期望的单Pipeline双CallExpression结构。形状断言对未命名组的内容不敏感但解析仍锚定全串时区后缀01:00/Z位于date-time内部一个未命名分组中一元运算符位于unary-expression的未命名分组中所以它们的形状与无后缀/无运算符版本相同。但测试依然对这些内容敏感——因为.parse锚定整个字符串一旦语法停止接受01:00或-name解析会整体失败测试照样能发现回归。形状断言能抓住成功但错误的解析strings.rakutest中三引号内含引号的用例说明旧语法会把I said hello world!从内部引号处截断、让分支匹配空串、filter再把剩余部分收成五个参数——这仍然是一次成功的解析只有断言形状才能发现结构错了。完整查询用例一窥真实形状full_queries.rakutest 用一个接近官网示例的查询from invoices→filter→derive→filter→group/aggregate→sort→take→join→derive f...→select→derive sversion()断言了整棵树的形状其中可以看到f{c.last_name}, {c.first_name}被捕获为f-string叶子sversion()被捕获为s-stringjoin ccustomers (customer_id)中ccustomers成为declaration捕获(customer_id)成为带unary-expression的括号表达式group customer_id ( aggregate {...} )中的括号部分成为nested-pipeline。模块文档与进一步学习路径lib/prql.rakumod 自带 Raku Pod 文档head1 NAME、SYNOPSIS、DESCRIPTION其中把 PRQL 描述为a modern language for transforming data — a simple, powerful, pipelined SQL replacement与项目整体定位一致。README 的 Documentation 一节指向 Raku 官方 Grammars 教程与语言参考docs.raku.org 的language/grammar_tutorial与language/grammars对不熟悉 Rakugrammar/token/rule/捕获机制、想深入理解本实现底层的读者是最佳入口。本仓库中还有 grammars/ 索引页可横向对比其他生态Lezer、Tree-Sitter、Monarch 等对同一 PRQL 文法的不同表达方式。结语grammars/raku/是一份小而完整的PRQL 文法 测试基建参考实现grammar PRQL用 Raku 原生机制覆盖了 PRQL 的管线、函数调用、let/lambda、四类字符串、多种进制数字、日期时间、注解与模块等全部核心语法PRQLTest的形状断言测试法把断言对象从能否解析提升到解析成什么结构既能锁定预期结构、又能捕获那些解析成功但结构错误的回归十个主题测试文件则为每一种语法特性留下了可复现的行为快照。无论是想在 Raku 中集成 PRQL还是想为其他语言移植这套测试思路这里都是可以直接对照源码研读的起点。赞分享后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载相关推荐Prometheus 的 PromQL 测试脚本语言 promqltest从 load/eval 语法到 expect 断言与迁移工具的完整实战指南Prometheus 的 PromQL 测试脚本语言 promqltest从 load/eval 语法到 expect 断言与迁移工具的完整实战指南 本文围绕可观测性指标监控时序数据库告警基于 ANTLR4 的 CLU 语言语法解析grammars-v4 中 clu 语法的完整实现解析基于 ANTLR4 的 CLU 语言语法解析grammars v4 中 clu 语法的完整实现解析 导读 本文以 grammars v4 仓库中 clu/RE编程语言编译器开发工具ONNX 算子形状推断Shape Inference实现指南从 TypeAndShapeInferenceFunction 到测试验证ONNX 算子形状推断Shape Inference实现指南从 TypeAndShapeInferenceFunction 到测试验证 本篇指南以 ONN人工智能机器学习深度学习创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

RK3588嵌入式部署大模型:NPU+Ollama实战指南

RK3588嵌入式部署大模型:NPU+Ollama实战指南

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

2026/9/25 9:28:04 阅读更多 →
GD32H759+RT-Thread工控实战:SDRAM/SDIO/触摸屏三重协同设计

GD32H759+RT-Thread工控实战:SDRAM/SDIO/触摸屏三重协同设计

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

2026/9/25 11:20:58 阅读更多 →
Linux USB设备识别排查:lsusb命令详解与驱动绑定实战

Linux USB设备识别排查:lsusb命令详解与驱动绑定实战

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

2026/9/25 11:20:45 阅读更多 →

最新新闻

PaddleSeg PanopticSeg 全景分割工具箱快速上手:预训练模型推理、训练与评估实战指南

PaddleSeg PanopticSeg 全景分割工具箱快速上手:预训练模型推理、训练与评估实战指南

人工智能计算机视觉预训练 【免费下载链接】PaddleSeg Easy-to-use image segmentation library with awesome pre-trained model zoo, supporting wide-range of practical tasks in Semantic Segmentation, Interactive Segmentation, Panoptic Segmentation, Image Matting,…

2026/9/25 13:15:42 阅读更多 →
SQL Server PolyBase HDFS Kerberos 连接故障排查:hdfs-kerberos-tester 工具完全指南

SQL Server PolyBase HDFS Kerberos 连接故障排查:hdfs-kerberos-tester 工具完全指南

示例工程数据库教程后端 【免费下载链接】sql-server-samples Azure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge 项目地址: https://gitcode.com/gh_mirrors…

2026/9/25 13:15:42 阅读更多 →
react-native-mmkv 与 Recoil 集成:用 atomEffect 实现 atom 状态持久化

react-native-mmkv 与 Recoil 集成:用 atomEffect 实现 atom 状态持久化

【免费下载链接】react-native-mmkv ⚡️ The fastest key/value storage for React Native. ~30x faster than AsyncStorage! 项目地址: https://gitcode.com/gh_mirrors/re/react-native-mmkv 点击查看 免费下载 Recoil 的 atom 状态默认只存在于内存中&#xff…

2026/9/25 13:15:42 阅读更多 →
lmms-eval 多模态模型评测框架发布:全面覆盖、低成本、零污染,配 TaoToken 统一 Key 跑通评测链路

lmms-eval 多模态模型评测框架发布:全面覆盖、低成本、零污染,配 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/9/25 13:15:42 阅读更多 →
hermes-agent 真的会自我训练吗:从 self-improving 到 OpenRouter 配置的真相

hermes-agent 真的会自我训练吗:从 self-improving 到 OpenRouter 配置的真相

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

2026/9/25 13:15:42 阅读更多 →
高并发下缓存穿透与击穿的防御实践:基于Redis的封装方案

高并发下缓存穿透与击穿的防御实践:基于Redis的封装方案

做了这么多年后端,缓存穿透和缓存击穿这个问题我几乎在每个高并发项目里都要重新讲一遍。最近我把这两类问题的防御逻辑统一封装成了一个可复用的工具包,基于Redis实现,核心围绕布隆过滤器、分布式锁、本地缓存和空值缓存这套组合拳。这篇就是…

2026/9/25 13:14:41 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

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

周新闻

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

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

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

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

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

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

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

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

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

2026/9/24 14:33:56 阅读更多 →

月新闻

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

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

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

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

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

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

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

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

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

2026/9/24 12:49:17 阅读更多 →