ARTICLE DETAIL

资讯详情

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

Jsonnet 仓库中的 Rapid YAML 集成:vendoring 单头文件方案与 std.parseYaml 底层实现解析

Jsonnet 仓库中的 Rapid YAML 集成:vendoring 单头文件方案与 std.parseYaml 底层实现解析 编程语言模板引擎CLI【免费下载链接】jsonnetJsonnet - The data templating language项目地址https://gitcode.com/gh_mirrors/js/jsonnet点击查看免费下载本文聚焦 Jsonnet 数据模板语言仓库gh_mirrors/js/jsonnet对第三方程 Rapid YAMLryml 的引入方式仓库采用 v0.10.0 单头文件single header发布版进行 vendoring并以rapidyaml.cpp作为唯一翻译单元完成实例化。文章将梳理该 vendoring 目录的文件布局、Bazel 与 CMake 两条构建链路并深入 core/vm.cpp 中std.parseYaml内建函数如何基于 ryml 的事件驱动解析器与as_json转换器把 YAML 输入转为 Jsonnet 值。读完本文你将掌握 Jsonnet 源码树中 YAML 解析能力的完整调用链能够据此自定义构建选项、排查解析错误并理解其best-effortYAML 子集支持的真实边界。一、关联文档与 vendoring 目录全景1.1 关联文档的核心内容third_party/rapidyaml/README.md 全文内容如下此为 Jsonnet 仓库对该第三方库的引入说明官方项目名称为 Rapid YAML简写 ryml# Vendoring This uses the single header release of Rapid YAML. Download from: https://github.com/biojppm/rapidyaml/releases/tag/v0.10.0 rapidyaml-0.10.0.hpp rapidyaml.cpp instantiates the library as a single translation unit.文档虽短但明确了三个关键事实是理解整个集成的钥匙引入方式不是常规的子模块或系统依赖而是把 Rapid YAML 的单头文件发布版直接 vendor 进仓库源码树版本锁定固定使用 v0.10.0 这个 release 对应的头文件rapidyaml-0.10.0.hpp实例化策略rapidyaml.cpp作为单个翻译单元single translation unit实例化整个库避免每个包含该头文件的编译单元都重复生成符号定义。1.2 目录文件清单vendoring 目录共包含 4 个文件其各自职责如下表文件作用关键说明third_party/rapidyaml/README.md引入说明声明 vendoring 方式、版本来源与单翻译单元策略third_party/rapidyaml/rapidyaml-0.10.0.hppryml 全量实现官方生成的 amalgamated聚合单头文件约 4.4 万行包含库的全部声明与定义third_party/rapidyaml/rapidyaml.cpp实例化翻译单元仅 2 行定义RYML_SINGLE_HDR_DEFINE_NOW后包含头文件third_party/rapidyaml/BUILDBazel 构建目标将上述两文件打包为cc_library目标ryml1.3 单头文件的使用约定聚合头文件本身带有一段官方使用说明位于 rapidyaml-0.10.0.hpp 开头可随意在任意头文件中包含聚合头文件很大为加快编译速度建议用其内置的 include guard_RYML_SINGLE_HEADER_AMALGAMATED_HPP_包裹 include避免同一翻译单元重复展开必须且只能在恰好一个源文件中定义RYML_SINGLE_HDR_DEFINE_NOW后再包含这会让头文件中的函数与类定义而非仅声明在该翻译单元中生成这正是rapidyaml.cpp的角色如需编译为共享库可预先定义RYML_SHARED符号以控制导出/导入。Jsonnet 仓库严格遵循了这一约定rapidyaml.cpp 的全部内容即为#define RYML_SINGLE_HDR_DEFINE_NOW #include rapidyaml-0.10.0.hpp二、构建集成Bazel 目标与 CMake 可选系统依赖2.1 Bazel私有可见的ryml库目标third_party/rapidyaml/BUILD 定义了cc_library目标rymlload(rules_cc//cc:cc_library.bzl, cc_library) licenses([permissive]) package(default_visibility [//visibility:private]) cc_library( name ryml, srcs [ rapidyaml.cpp, ], hdrs [ rapidyaml-0.10.0.hpp, ], includes [.], visibility [//core:__pkg__], )要点解读srcs只有rapidyaml.cpp一个源文件头文件作为hdrs暴露includes [.]使core目录下代码可以按#include ryml.hpp/#include ryml_std.hpp这种 ryml 官方习惯写法引入实际由聚合头文件内部映射到rapidyaml-0.10.0.hppvisibility [//core:__pkg__]限定只有core包可以使用它仓库默认可见性为private这是典型的第三方依赖仅限核心层消费的分层约束。在 core/BUILD 中可以看到该依赖被实际挂接deps [ //third_party/rapidyaml:ryml, ... ]2.2 CMake可选系统 Rapid YAML与 Bazel 强制使用仓库内 vendor 版本不同CMake 构建提供了使用系统安装的 Rapid YAML 的开关。在 CMakeLists.txt 中option(USE_SYSTEM_RAPIDYAML Use the system-provided rapidyaml library OFF)默认值为OFF即默认仍然使用仓库内third_party/rapidyaml/下的聚合头文件与实例化单元CMakeLists.txt 中列出third_party/rapidyaml/rapidyaml-0.10.0.hpp与third_party/rapidyaml/rapidyaml.cpp作为源文件并附加对应 include 路径。当显式开启-DUSE_SYSTEM_RAPIDYAMLON时构建系统会执行find_package(ryml 0.10.0 REQUIRED)并要求系统提供版本号为0.10.0的 ryml 包随后以ryml::ryml作为链接目标CMakeLists.txt 中target_link_libraries(... PRIVATE ryml::ryml)。因此版本一致性v0.10.0是两种构建方式都必须满足的前提vendoring 路径靠锁定头文件保证系统路径靠find_package的版本约束保证。2.3 发布包清单在 Python 打包相关文件中vendored 头文件也被显式纳入发布清单例如 MANIFEST.in 中保留了对third_party/rapidyaml/目录内容的收录setup.py 同样引用该路径确保源码发行包sdist完整携带 YAML 解析依赖。三、消费方std.parseYaml内建函数的底层实现Jsonnet 标准库暴露给用户的 YAML 解析入口是std.parseYaml(str)。它在语义层由 core/desugarer.cpp 注册case 36: return {UparseYaml, {Ustr}};在虚拟机层注册为内建函数core/vm.cpp 中builtins[std:parseYaml] Interpreter::builtinParseYaml;最终实现位于 core/vm.cpp 的Interpreter::builtinParseYaml。3.1 调用流程builtinParseYaml的实现要点core/vm.cppconst AST *builtinParseYaml(const LocationRange loc, const std::vectorValue args) { validateBuiltinArgs(loc, parseYaml, args, {Value::STRING}); std::string value encode_utf8(static_castHeapString *(args[0].v.h)-value); json j; try { // Use a custom EventHandler so we can attach error handling. ryml::EventHandlerTree et{ryml::Callbacks{ nullptr, nullptr, nullptr, Interpreter::handleRapidYamlError }}; ryml::Parser pe(et); ryml::Tree tree ryml::parse_in_arena(pe, ryml::to_csubstr(value)); if (tree.type(tree.root_id()).is_notype()) { // Nothing to do; j is already null! } else if (tree.is_stream(tree.root_id())) { for (ryml::ConstNodeRef node : tree.crootref().children()) { std::ostringstream jsonText; jsonText ryml::as_json(node); j.push_back(json::parse(jsonText.str())); } } else { std::ostringstream jsonText; jsonText ryml::as_json(tree); j json::parse(jsonText.str()); } } catch (const RapidYamlError exc) { throw makeError(loc, exc.msg_); } bool filled_unused; otherJsonToHeap(j, filled_unused, scratch); return nullptr; }整个链路可以拆解为四个阶段参数校验与编码先经validateBuiltinArgs确认参数是字符串再通过encode_utf8将 Jsonnet 内部 UTF-16UString编码为 UTF-8 字节串供 ryml 解析事件驱动解析构造ryml::EventHandlerTree挂载自定义ryml::Callbacks把错误回调指向Interpreter::handleRapidYamlError然后用ryml::Parser配合ryml::parse_in_arena将输入解析为 ryml 的Treearena 内存池中的树转换为 JSON 中间表示若根节点是无类型空输入直接得到null若根节点是stream即包含多个用---分隔的 YAML 文档则遍历每个子文档用ryml::as_json(node)生成 JSON 文本后json::parse进数组j返回 Jsonnet 数组否则是单文档直接用ryml::as_json(tree)生成 JSON 文本再解析为单个 JSON 值回填堆对象最后经otherJsonToHeap把中间 JSON 值转成 Jsonnet 运行时值字符串会做 UTF-8 解码回到内部表示这一步也解释了空输入返回null的行为。3.2 自定义错误回调与行号定位ryml 默认的错误处理走全局回调Jsonnet 则传入自定义Callbacks第四个字段为错误回调对应静态成员函数handleRapidYamlErrorcore/vm.cppstatic void handleRapidYamlError(const char* inner_msg, size_t length, ryml::Location loc, void * /* unused: userdata */) { std::ostringstream msg; msg YAML error: loc.line :; if (loc.col) { msg loc.col :; } else if (loc.offset) { msg loc.offset :; } msg std::string_view(inner_msg, length); throw RapidYamlError(msg.str()); }其作用是把 ryml 的解析错误含loc.line/loc.col/loc.offset定位信息包装成自定义异常RapidYamlError声明于 core/vm.cpp并带上前缀YAML error:。该异常随后在builtinParseYaml中被捕获并转换为带 Jsonnet 源码位置的运行时错误。配套的负向测试用例 test_suite/error.std_parseYaml1.jsonnet内容为std.parseYaml(a: b:)验证了这一点语法不合法的 YAML 会被拒绝并产生带定位信息的错误输出对应 golden 文件 error.std_parseYaml1.jsonnet.golden。3.3 标准库文档中的能力边界doc/_stdlib_gen/stdlib-content.jsonnet 中记录了std.parseYaml的官方描述availableSince: 0.18.0值得原样摘录以保证事实边界Parses a YAML string. This is provided as a best-effort mechanism and should not be relied on to provide a fully standards compliant YAML parser. YAML is a superset of JSON, consequently downcasting or manifestation of YAML into JSON or Jsonnet values will only succeed when using the subset of YAML that is compatible with JSON. The parser does not support YAML documents with scalar values at the root. The root node of a YAML document must start with either a YAML sequence or map to be successfully parsed.即官方明确声明的限制best-effort不承诺完整 YAML 规范兼容JSON 超集约束只有能与 JSON/Jsonnet 值兼容的 YAML 子集才能成功向下转换根节点不得是标量文档根必须是序列sequence或映射map才能解析成功。3.4 标准库测试对行为语义的锁定test_suite/stdlib.jsonnet 中的测试用例把std.parseYaml的行为边界钉死为可断言的事实std.assertEqual(std.parseYaml( \n), null) std.assertEqual(std.parseYaml(1\n---), [1, null]) std.assertEqual(std.parseYaml({}), {}) std.assertEqual(std.parseYaml([]), []) std.assertEqual( std.parseYaml(||| foo: bar: - true - 42 - 1.0 |||), { foo: { bar: [true, 42, 1] } } ) std.assertEqual( std.parseYaml(||| --- foo: bar: - true - 42 - 1.0 --- wibble: wobble: - true - 42 - 1.0 |||), [{ foo: { bar: [true, 42, 1] } }, { wibble: { wobble: [true, 42, 1] } }] ) std.assertEqual(std.parseYaml(version: 1.2.3), { version: 1.2.3 }) std.assertEqual(std.type(std.parseYaml(id: 12345).id), string)这些断言精确对应了 core/vm.cpp 中的三条实现路径std.parseYaml( \n)与std.parseYaml(1\n---)验证空输入/stream 边界空白输入得null1\n---首个文档是根标量、后续为空文档得到[1, null]{}、[]及多级映射/序列验证单文档常规解析含布尔、整数、浮点数类型推断1.0被归一化为整数1---分隔的多文档流验证stream 处理返回数组每元素对应一个文档core/vm.cpp 中遍历tree.crootref().children()的逻辑version: 1.2.3与id: 12345验证了标量值语义未加引号的1.2.3保持为字符串而非数字加引号的12345也保持字符串类型对应 ryml 对标量 tag 的判定。3.5 命令行层面的 YAML 输出印证除了解析方向Jsonnet 命令行工具还支持 YAML 方向的输出构成闭环。在 test_cmd/run_cmd_tests.sh 中do_test nonewline_yaml1 1 --no-trailing-newline --yaml-stream -e [{a:1},{b:2},{c:3}] do_test yaml1 0 -y -e [1,2,3] do_test yaml2 1 -y -e null if do_test yaml3 0 -y -o ${OUT_DIR}/yaml3/stream -e [1,2,3]; then check_file yaml3 ${OUT_DIR}/yaml3/stream yaml3.golden.stream fi可以看到-yYAML 输出与--yaml-streamYAML 多文档流输出选项及对应 golden 文件如 yaml1.golden.stdout、yaml3.golden.stream。这与std.parseYaml的输入方向相互印证说明 ryml 在 Jsonnet 仓库中支撑的是完整的YAML 进出能力。注意std.parseYaml的方向是解析 YAML 为 Jsonnet 值输入侧而-y选项是把 Jsonnet 值表现成 YAML输出侧两者在代码中分别位于 core/vm.cpp 的解析内建函数与 manifestation 输出逻辑中。四、错误处理与排查实践4.1 常见的两类失败模式从源码结构与测试可以归纳出std.parseYaml的两个典型失败点YAML 语法错误如std.parseYaml(a: b:)由handleRapidYamlError抛出带YAML error:前缀与行列信息的异常。运行时报错格式为YAML error: line:col: messagecol 缺失时退化为 offset最终被包装成 Jsonnet 运行时错误见 core/vm.cpp与 JSON 不兼容的 YAML 结构由于 Jsonnet 值模型是 JSON 超集受限的对象键、无标签类型等根节点为标量的文档、依赖 YAML 自定义 tag 或非 JSON 兼容标量语义的内容可能解析失败或产生与预期不符的类型这与官方文档声明的best-effort一致。4.2 如何在实践中规避边界根节点保证是 map 或 sequence在 YAML 顶层用key:或- item组织内容不要直接写123这类根标量需要多文档时明确用---分隔此时std.parseYaml返回数组空文档对应null元素对版本号、ID 等字符串化数值保持预期version: 1.2.3解析为字符串1.2.3如需数值应使用1.23或显式类型标注方式注意 Jsonnet 侧接收的是 JSON 兼容子集不确定时先用std.type断言参考 test_suite/stdlib.jsonnet 的做法对解析结果做类型检查。五、扩展阅读仓库内相关路径速查为便于继续深入阅读源码整理本主题涉及的关键文件用途路径vendoring 说明third_party/rapidyaml/README.md聚合头文件与实例化单元third_party/rapidyaml/rapidyaml-0.10.0.hpp、third_party/rapidyaml/rapidyaml.cppBazel 目标定义third_party/rapidyaml/BUILD、core/BUILDCMake 系统依赖开关CMakeLists.txtUSE_SYSTEM_RAPIDYAMLstd.parseYaml实现core/vm.cpp、错误回调 core/vm.cpp内建函数注册与脱糖core/vm.cpp、core/desugarer.cpp标准库文档描述doc/_stdlib_gen/stdlib-content.jsonnet、doc/ref/stdlib.html行为测试与负向用例test_suite/stdlib.jsonnet、test_suite/error.std_parseYaml1.jsonnetYAML 输出方向的命令行测试test_cmd/run_cmd_tests.sh六、小结Jsonnet 仓库通过vendoring 单头文件 单翻译单元实例化的轻量方案引入 Rapid YAML v0.10.0Bazel 侧以私有ryml目标供core消费CMake 侧提供USE_SYSTEM_RAPIDYAML开关以便替换为系统安装的同版本库。消费端std.parseYaml走事件解析树 →as_json转 JSON → 回填 Jsonnet 值的调用链并通过自定义Callbacks把 ryml 的行列定位错误翻译成 Jsonnet 风格的运行时错误。官方对std.parseYaml的定位是 best-effort 的 YAML 子集支持0.18.0起可用根节点须为 map 或 sequence多文档流返回数组这些边界均由 test_suite/stdlib.jsonnet 中的断言精确锁定。理解这一集成有助于你在实际工程中判断何时可用std.parseYaml、如何为仓库配置构建选项以及如何解读 YAML 解析失败时的错误信息。赞分享编程语言模板引擎CLI【免费下载链接】jsonnetJsonnet - The data templating language项目地址https://gitcode.com/gh_mirrors/js/jsonnet点击查看免费下载相关推荐Rapid YAML高效解析与生成YAML的C库Rapid YAML高效解析与生成YAML的C库 项目介绍 Rapid YAML 简称 ryml是一个用C编写的快速YAML解析和生成库。它不仅支开发工具Nhost 仓库中的 YAML 解析层:oasdiff/yaml 库的 JSON 桥接机制与位置溯源(Origin)实现解析Nhost 仓库中的 YAML 解析层:oasdiff/yaml 库的 JSON 桥接机制与位置溯源 Origin 实现解析 本文以 Nhost 仓库 vend后端认证鉴权数据库无服务开发工具云原生Go 中基于 JSON 中间层的 YAML 编解码解析 sigs.k8s.io/yaml 在 Moby 仓库中的实现与应用Go 中基于 JSON 中间层的 YAML 编解码解析 sigs.k8s.io/yaml 在 Moby 仓库中的实现与应用 sigs.k8s.io/yaml云原生容器运行时虚拟化容器编排上一篇如何用ROFL-Player解决英雄联盟回放文件兼容性问题终极免费方案下一篇终极怪物猎人世界插件三步快速配置新手也能轻松掌握游戏数据创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表