十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入 PRQL 的 Lezer 语法:prql-lezer 文法、高亮与测试体系全解析

深入 PRQL 的 Lezer 语法:prql-lezer 文法、高亮与测试体系全解析 后端【免费下载链接】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是一种面向数据转换的现代语言它用可读的管道式语法替代 SQL而要让 PRQL 代码在编辑器、笔记本等前端环境中获得语法高亮、缩进与折叠支持就需要一个基于 Lezer 的增量解析器。本文以仓库 grammars/prql-lezer 为核心系统讲解该语法包的结构、文法设计、构建测试流程以及它如何被 CodeMirror 等下游工具消费帮助读者掌握 PRQL 在前端生态中的语法工具链实现。prql-lezer 是什么为 PRQL 定制的 Lezer / CodeMirror 文法prql-lezer 是 PRQL 项目的 Lezer / CodeMirror 文法实现。Lezer 是 CodeMirror 团队开发的增量解析器框架以增量和可流式著称特别适合构建代码编辑器的语法层。PRQL 之所以需要它是因为 CodeMirror 文法是若干下游工具的硬性依赖例如 Jupyter 的语法高亮就需要这样的前端解析器。从仓库现状看该文法的实现大体上已完整可用但在 src/prql.grammar 中仍留有少量 TODO 标注。另外包目前还没有搭建配套的 JS 分发机制也没有发布到 npm 等包管理器这一工作按 README 的说法可以后续补充未来可能独立成仓。从源码结构看它由三块构成src/prql.grammarLezer 文法定义文件描述 PRQL 全部语法结构src/highlight.js将语法树节点映射为lezer/highlight风格标签的样式定义test/以.txt用例文件驱动的解析测试外加专门校验高亮标签的test-highlight.js。文法设计核心从表达式到管道的层级结构src/prql.grammar 是理解整个包的关键。它顶层声明了运算符优先级precedence { power right, prefix, times left, left plus left, compare left, and left, or left }即**右结合、乘除左结合、加减左结合比较、逻辑与或依次降级。最顶层规则是Query { statements }而空格、注释、文档块与续行符被统一放入skipskip { space | Comment | Docblock | wrappedLine }一条语句可包含查询定义、模块、注解、变量声明与管道语句statements { newline* QueryDefinition? Module? Annotation? VariableDeclaration* pipelineStatement? end? }其中值得注意的几个节点QueryDefinition { kwprql NamedArg newline }处理prql target:sql.duckdb这类查询级参数Module { kwmodule Identifier { statements } }模块嵌套pipelineStatement { Pipeline (~ambigNewline newline | end) }一条管道以换行或文件结尾收束Annotation { { commaSepDeclaration? } newline }注解语法。管道由一串函数调用组成|与换行都可充当管道分隔符Pipeline { CallExpression (pipe CallExpression)* | expression | Identifier } pipe { | | ~ambigNewline newline }~ambigNewline表示非换行的模糊输入正是它让换行得以参与管道语义。函数调用是CallExpression { Identifier ArgList { (NamedArg | Declaration | test) } }即函数名 参数列表参数可以是命名参数、声明或表达式test在这里指等值测试与 Python 文法的命名一致。表达式体系非常完整覆盖 PRQL 的主要语法expression { kwthis | kwthat | kwnull | BinaryExpression | UnaryExpression | ArrayExpression | TupleExpression | NestedPipeline | CaseExpression | DateTime | Parameter | ParenthesizedExpression | RangeExpression | Identifier | boolean | number | String | FString | RString | SString | TimeUnit }比较、逻辑运算通过binaryTest组合出BinaryExpression节点其中既包含、!、~正则匹配、、、、也包含关键字形式的inunaryTest则用kw!支持逻辑非。UnaryExpression同时承担一元正负号与前缀如join customers (customer_id)。元组用花括号表达TupleExpression数组用方括号ArrayExpressioncase 表达式则是CaseExpression { kwcase TupleExpression }——case 的分支是expression expression这与full_queries.txt中case {a 1}的用例一一对应。变量声明与 Lambda 也有专属规则VariableDeclaration { kwlet VariableName (NestedPipeline (newline | end) | Lambda) } Lambda { LambdaParam* - expression (newline | end) }词法层的精妙之处token 定义tokens块中定义了日期时间、数字、字符串等基础 tokenDateTime { (date | time | date T time (...)?) }1970-01-16这类字面量TimeUnit { digit (years | ... | microseconds) }带数量前缀的时间单位Integer支持十进制含下划线分隔与科学计数、十六进制0x、二进制0b、八进制0oFloat { digit (digit | _)* . digit (digit | _)* (e Integer)? }浮点数规则。文法注释明确指出它没有 PRQL 本体那么精确——PRQL 不允许尾随下划线且允许小数点前无数字Parameter { $ (digit | identPart) }$1、$param这类参数占位符Identifier { identPart (. (identPart | *))* }支持点号限定的多段标识符identifierChar允许 ASCII 字母以及_和\u{a1}-\u{10ffff}范围内的 Unicode 字符因此 test/identifiers.txt 中räksmörgås这样的标识符可以正常解析。字符串家族被精确地区分包括普通字符串、f-string插值、s-stringSQL 片段、r-string原始字符串以及三引号字符串。文法在处理转义序列时与 PRQL 官方解析器 prqlc/prqlc-parser/src/lexer/mod.rs 保持行为一致escapeKnown覆盖\\、\xHH、\u{...}以及\\/bfnrt等已知转义产生Escape节点而escapeUnknown { \\ ![\n] }让\z这类未识别转义也能解析成功只是不产生Escape节点、不高亮——这与 lexer 中other other的行为对应。两个 token 之间通过precedence { Escape, escapeUnknown }保证已知转义优先。文法中三引号字符串、插值字符串、原始字符串的规则均有详细注释解释它们如何与 PRQL lexer 中multi_quoted_string()、raw_string()等函数对齐例如rC:\是合法的原始字符串而ra\nb跨行则不再是原始字符串见 test/strings.txt 中的对应用例。值得留意的是文法文件还专门定义了字面量 token 并让每个字面量以自身命名例如[nameEquals]、( ) [ ] { } , |。这样做是为了让highlight.js中的样式选择器可以按名称命中它们——未声明的字面量是匿名 term样式标签写了也静默不生效。高亮机制从语法树到编辑器配色高亮定义位于 src/highlight.js它把语法节点映射到lezer/highlight的语义标签tags核心映射包括语法节点高亮标签含义CallExpression/Identifierfunction(variableName)函数名prql、module、let、case、in各类 keyword 标签关键字Comment/DocblocklineComment/docString注释与文档块Integer/Floatinteger/float数字String/FString/RString/SStringstring及special(string)字符串家族Escapeescape转义序列ArithOp/CompareOp/LogicOp对应运算符标签运算符ParameterprocessingInstruction参数占位符DeclarationItem/TypeName/VariableNamepropertyName/typeName/variableName声明、类型、变量( )/[ ]/{ }/\| ,paren/squareBracket/brace/separator括号与分隔符选择器集合prqlHighlightSpec被单独导出是因为 test/test-highlight.js 需要逐条校验styleTags会静默丢弃匹配不到语法节点的选择器如果不加校验某个选择器失效后只会表现为下游编辑器里某类 token 失去配色而不会报错。关键字能成为命名节点得益于文法中的kwterm宏——它通过specialize把prql、module、let、case、in等关键字各自声明为独立命名的节点从而可被高亮选择器按名称引用。构建与测试从 npm install 到解析用例开发工作流按照 grammars/prql-lezer/README.md 的说明开发流程如下npm install # 安装依赖 npm run build # 构建 npm run test # 运行测试构建脚本定义在 package.json 中build: lezer-generator src/prql.grammar -o src/parser rollup -c, build-debug: lezer-generator src/prql.grammar --names -o src/parser rollup -c, test: mocha test/test-*.js即先用lezer-generator把 src/prql.grammar 编译成src/parser.js再由 rollup.config.js 打包成dist/index.cjsCommonJS与dist/index.jsESM两个产物同时 package.json 声明了types: dist/index.d.ts说明其目标是一个可被 JS 生态直接 import 的 npm 包。build-debug变体额外加--names便于调试。依赖方面运行时只依赖lezer/highlight与lezer/lr构建期使用lezer/generator、rollup 与 mocha。测试由 mocha 驱动test/test-prql.js 会扫描test/目录下所有.txt文件借助lezer/generator/dist/test的fileTests逐一执行。每个.txt用例的格式是输入 PRQL 片段 期望的语法树结构例如 test/identifiers.txt# Unicode identifier filter räksmörgås Query(Pipeline(CallExpression(Identifier,ArgList(Identifier))))当文法规则改动时这些期望树必须同步更新因此它们既是回归测试也是文法行为的权威文档。当前测试覆盖的主题包括arithmetics.txt算术表达式与优先级arrays.txt 与 tuples.txt数组、元组字面量datetime.txt日期时间字面量identifiers.txt含 Unicode 的标识符numbers.txt十进制、十六进制、科学计数等数字operators.txt、!、~、in、、||、??及一元运算符strings.txt字符串、转义、f/s/r 字符串与三引号字符串的边界行为misc.txt 与 full_queries.txt杂项及完整查询。以 operators.txt 为例filter foo in bar解析为BinaryExpression(Identifier,in,Identifier)join customers (customer_id)解析为UnaryExpression(CompareOp,Identifier)与文法中kwin参与比较运算、可作一元前缀的规则相互印证。而 full_queries.txt 中那段覆盖from/filter/derive/group/sort/take/join/select以及 f-string、s-string、日期字面量的完整查询其期望语法树几乎遍历了文法的全部节点类型是最佳的整体性用例。Lezer Playground 交互式调试README 还推荐了交互式开发路径——Lezer Playground打开 Lezer Playground 网站粘贴一段示例查询粘贴当前文法src/prql.grammar 的内容在网页中修正文法问题并即时观察语法树与高亮把修改后的文法复制回仓库。这一流程特别适合调整precedence、token 规则或优先级冲突的场景因为 Playground 能实时展示每个输入对应的解析树。高亮回归测试test/test-highlight.js 是另一层保障。它通过highlightTree把解析树与高亮器结合断言每个 token 携带的样式类与期望标签完全一致例如prql target:sql.duckdb中只有prql携带keyword标签、module m {...}中module带moduleKeyword、{}带brace。更关键的是第三个用例它遍历parser.nodeSet中所有节点名并对照prqlHighlightSpec中每个选择器拆出的每一项断言不存在匹配不到语法节点的选择器从而防止选择器静默失效。下游应用CodeMirror 语言支持与 Jupyterprql-lezer 并非孤立存在仓库内 web/prql-codemirror-demo/src/lang-prql/prql.ts 演示了它的典型用法把 Lezer 解析器包装成 CodeMirror 的LRLanguage并追加编辑器需要的补充配置export const prqlLanguage LRLanguage.define({ name: prql, parser: parser.configure({ props: [ indentNodeProp.add({ TupleExpression: delimitedIndent({ closing: } }), ArrayExpression: delimitedIndent({ closing: ] }), }), foldNodeProp.add({ ArrayExpression TupleExpression: foldInside, }), ], }), languageData: { closeBrackets: { brackets: [(, [, {, , , , ], stringPrefixes: [f, r, s], }, commentTokens: { line: # }, }, });这段代码说明了 prql-lezer 与完整编辑器体验之间的差距文法本身只负责解析与高亮缩进元组/数组的自动缩进、代码折叠foldInside、括号自动闭合、f/r/s 字符串前缀识别以及#行注释都要由消费方通过LRLanguage的配置补充。仓库根目录的 grammars/README.md 以及 CHANGELOG.md 也记录了 prql-lezer 在语法生态中的定位与演进。至于 README 提到的 Jupyter 语法高亮pyprql 相关需求由于包尚未发布到包管理器集成方目前需要以本地构建的方式引入dist/产物。小结prql-lezer 是 PRQL 前端工具链中小而完整的一环一份 prql.grammar 定义了从管道、函数调用、元组/数组到日期时间、参数、字符串家族的完整语法一份 highlight.js 让语法树具备可消费的语义高亮两层测试解析用例 高亮标签校验保证了文法与高亮规则的持续正确。对于希望为 PRQL 接入编辑器、笔记本或 Web IDE 的开发者而言理解这个包的文法结构、token 规则与构建测试流程是复用或扩展它的起点——在包正式发布之前通过npm install npm run build npm run test本地构建再像 prql-codemirror-demo 那样包装成LRLanguage即可获得完整的 PRQL 编辑体验。赞分享后端【免费下载链接】prqlPRQL is a modern language for transforming data — a simple, powerful, pipelined SQL replacement项目地址https://gitcode.com/gh_mirrors/pr/prql点击查看免费下载相关推荐Overleaf Lezer-LaTeX 源码解析用 Lezer 语法为 LaTeX 编写 CodeMirror 6 解析器Overleaf Lezer LaTeX 源码解析用 Lezer 语法为 LaTeX 编写 CodeMirror 6 解析器 Overleaf 的源码编辑器编译器深度学习模型优化CotEditor 中配置 PRQL 语法高亮PRQL.yaml 语法样式文件完全指南CotEditor 中配置 PRQL 语法高亮PRQL.yaml 语法样式文件完全指南 PRQLPipelined Relational Query Lan后端PRQL 语言在 GtkSourceView 中的语法高亮prql.lang 定义文件安装、嵌入与实现解析PRQL 语言在 GtkSourceView 中的语法高亮prql.lang 定义文件安装、嵌入与实现解析 PRQLPipelined Relational后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表