尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
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),仅供参考
RELATED

相关推荐

PulseBlaster在NV色心实验中的纳秒级时序控制原理与实践

PulseBlaster在NV色心实验中的纳秒级时序控制原理与实践

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

📅 2026/9/24 7:39:09
basic-computer-games 之 Hurkle 游戏 MiniScript 移植版:三种运行方式与源码逐行解析

basic-computer-games 之 Hurkle 游戏 MiniScript 移植版:三种运行方式与源码逐行解析

示例工程 【免费下载链接】basic-computer-games An updated version of the classic "Basic Computer Games" book, with well-written examples in a variety of common MEMORY SAFE, SCRIPTING programming languages. See https://coding-horror.github.io/basic…

📅 2026/9/24 7:39:09
codeburn sync 团队同步指南:将本地 AI 用量遥测安全推送到共享 OTLP 后端

codeburn sync 团队同步指南:将本地 AI 用量遥测安全推送到共享 OTLP 后端

【免费下载链接】codeburn Free, local tool to track AI coding token usage and cost across 37 tools and agents (Claude Code, Cursor, Codex, Gemini and more), by model, project, and task. npx codeburn 项目地址: https://gitcode.com/gh_mirrors/co/cod…

📅 2026/9/24 7:39:09
MORE NEWS

更多资讯

📰

AI PLC落地核心:确定性推理与工业实时性

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

📰

AutoForm R13安装深度指南:版本兼容性与许可证配置全解析

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

📰

代理IP原理与合规应用:安全实现网络访问

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

📰

通用门式起重机应力测试与数据处理:从测点布置到报告生成

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

📰

Buck电路CCM与DCM本质解析:从电感电流判据到工程落地

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

📰

ESP32脑电波控制家电:从BCI信号采集到红外遥控的完整实现

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

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬