Skip to main content

XR Syntax

xr-syntax 是 XRobot / LibXR Python 工具使用的结构化源码解析、修改与代码生成基础库。

它解决的是源码层问题:

  • 读取已有 C++ / CMake;
  • 保留原始空白、换行、注释和用户代码;
  • 按函数、类、调用、变量、命令等结构查询;
  • 对指定结构进行不可变编辑;
  • 用同一套模型生成新的 C++ / CMake;
  • 给 XRobot、LibXR_CppCodeGenerator 提供统一 backend。

它不是 C++ 编译器,不会自己做 name lookup、overload resolution、template instantiation 或 type inference。

最基本的不变量是:

document = CppDocument.parse(source)
assert document.render_bytes() == source

即使 parser 对某段源码只能保守地表示,原始源码也不能因为解析而丢失。


安装

支持 Python 3.8–3.13,与 XRobot 和 LibXR_CppCodeGenerator 的 Python 包保持一致。

pip install xr-syntax

从源码安装:

git clone https://github.com/xrobot-org/xr-syntax.git
cd xr-syntax
pip install .

开发环境:

pip install -e ".[dev]"

当前基础包没有第三方 runtime dependency;C++ 与 CMake parser 都包含在包内。


包结构

src/xr_syntax/
├── core/       # 通用不可变 syntax tree、span、grammar、rewrite
├── cpp/        # C++ lexer/parser/query/view/factory/builder
├── cmake/      # CMake parser/query/view/factory/builder
└── format/     # 新生成源码使用的 layout IR

第一轮 code review 建议先看:

docs/REVIEW_GUIDE.md

C++:读取已有源码

解析

from xr_syntax.cpp import CppDocument

source = b"""
#include "device.hpp"

static Device device;

void app_main() {
  XR_REGISTER(device, Base);
}
""".lstrip()

document = CppDocument.parse(source)

assert document.render_bytes() == source

CppDocument 是一个不可变源码 snapshot。


查询 include

includes = document.include_views()

assert includes[0].header == "device.hpp"
assert includes[0].system is False

对于:

#include <vector>

对应:

include.header == "vector"
include.system is True

查询函数

function = document.function_views("app_main")[0]

print(function.name)
print(function.parameters)
print(function.body)

如果需要底层 syntax node:

document.functions("app_main")

查询函数调用

例如:

XR_REGISTER(device, LibXR::GPIO);

可以直接查询:

calls = document.call_views("XR_REGISTER")

for call in calls:
    print(call.callee)
    print([argument.text for argument in call.arguments])

输出参数仍然保持源码层表示:

device
LibXR::GPIO

查询变量

all_variables = document.variable_views()

globals_ = document.variable_views(
    global_scope=True,
)

locals_ = document.variable_views(
    global_scope=False,
)

每个 variable view 可以读取:

variable.name
variable.base_type
variable.storage
variable.qualifiers
variable.initializer
variable.global_scope

这里提供的是源码结构信息,不是编译器语义。

它不会判断:

  • typedef 展开后的最终类型;
  • 某个 constructor call 选择哪个 overload;
  • template 实例化结果。

查询 class / constructor

clazz = document.class_views("CameraBase")[0]

constructors = clazz.constructors(
    public_only=True,
    callable_only=True,
)

for constructor in constructors:
    print(constructor.name)
    print(constructor.parameters)

例如:

CameraBase(const CameraBase&) = delete;

仍然会被 syntax tree 表示,但 callable_only=True 会把它从“可调用构造函数”列表中过滤掉。

析构函数和 operator= 不会被误识别成 constructor。


C++:修改已有源码

CppDocument 不原地修改。

所有 edit 都返回新 document:

from xr_syntax.cpp import CppDocument, CppFactory

document = CppDocument.parse(
    '#include "a.hpp"\n'
    'void app_main() {}\n'
)

factory = CppFactory()

changed = document.insert_after(
    document.includes()[0],
    factory.include("b.hpp"),
)

assert document.render() == (
    '#include "a.hpp"\n'
    'void app_main() {}\n'
)

assert changed.render() == (
    '#include "a.hpp"\n'
    '#include "b.hpp"\n'
    'void app_main() {}\n'
)

高层 edit 完成后会重新 parse,保证 field、diagnostic 和 error-recovery 结构与新源码一致。


User Code / format / lint 区域

STM32 常见区域:

/* User Code Begin 3 */
custom_code();
/* User Code End 3 */

不再需要自己写 regex:

region = document.user_regions()[0]

print(region.name)
print(region.body_text)

只替换区域内部:

changed = document.replace_region_body(
    region,
    "\ncustom_code();\nother_code();\n",
)

同样支持:

// clang-format off
...
// clang-format on

以及:

// NOLINTBEGIN
...
// NOLINTEND

对应:

document.format_regions()
document.lint_regions()

C++:生成源码

读取和生成使用同一个 syntax model。

FileBuilder

from xr_syntax.cpp import CppFileBuilder

builder = CppFileBuilder()

builder.include("device.hpp")
builder.raw("\nstatic Device device;\n")

document = builder.build()

print(document.render())

Function / block builder

builder = CppFileBuilder()

entry = builder.function(
    "void",
    "XRobotMain",
    prefix=["[[noreturn]]"],
)

entry.parameter(
    "LibXR::GPIO&",
    "led",
)

entry.body.variable(
    "BlinkLED",
    "blink",
    initializer="BlinkLED(led, 250)",
    storage=["static"],
)

entry.body.call(
    "Run",
    ["blink"],
)

document = builder.build()

builder 最终返回的仍然是 parser-backed CppDocument,不会产生第二套“生成器 AST”。


CMake

CMake frontend 与 C++ 共用同一套 core:

  • immutable document;
  • source span;
  • grammar contract;
  • rewrite;
  • builder;
  • layout。

读取

from xr_syntax.cmake import CMakeDocument

source = b"""
project(Demo)
add_library(foo STATIC foo.cpp)
""".lstrip()

document = CMakeDocument.parse(source)

assert document.render_bytes() == source

library = document.command_views(
    "add_library"
)[0]

assert library.name == "add_library"

assert [
    argument.text
    for argument in library.arguments
] == [
    "foo",
    "STATIC",
    "foo.cpp",
]

生成

from xr_syntax.cmake import CMakeFileBuilder

builder = CMakeFileBuilder()

builder.command(
    "project",
    ["Demo", "LANGUAGES", "CXX"],
)

builder.command(
    "add_library",
    ["foo", "STATIC", "foo.cpp"],
)

document = builder.build()

print(document.render())

Green / Red syntax tree

Green

Green tree 只保存结构:

  • kind;
  • text;
  • trivia;
  • children;
  • field。

它不保存:

  • parent;
  • absolute offset;
  • owning document。

这样未修改 subtree 可以在多个 immutable snapshot 之间复用。

Red

Red view 为某个具体 snapshot 增加:

  • parent;
  • child index;
  • field;
  • byte span;
  • structural path。

因此:

Green = 可共享的不可变结构
Red   = 某个 snapshot 中的位置视图

无损表示

parser 不一定会把所有字节都作为结构节点。

例如:

  • 空白;
  • 某些注释;
  • parser 保守保留的未知片段;
  • 不完整源码。

xr-syntax 会把无法安全细分的内容继续作为 source-preserving element 保留下来。

最终必须满足:

document.render_bytes() == input_bytes

diagnostic 可以存在,静默丢源码不允许存在。


排版

已有源码的 render() 与新源码的 formatting 是两件事。

render()
→ 保留已有 syntax / trivia

layout IR
→ 决定新生成源码如何缩进和折行

layout IR 包含:

Text
Concat
Group
Indent
Line
SoftLine
HardLine
IfBreak

这样 XRobot / LibXR_CppCodeGenerator 不需要再在业务逻辑里写行宽判断。


明确不做什么

xr-syntax 负责源码结构,不负责 C++ compiler semantics。

它不会自己实现:

  • name lookup;
  • overload resolution;
  • template instantiation;
  • type inference;
  • constant evaluation;
  • ABI;
  • XRobot Module 依赖绑定;
  • XR_REGISTER 的业务语义。

例如:

“某个 Module constructor 应该绑定哪个 LibXR view?”

仍然属于 XRobot 的 domain logic。


Python 支持

与 XRobot、LibXR_CppCodeGenerator 保持一致:

Python 3.8
Python 3.9
Python 3.10
Python 3.11
Python 3.12
Python 3.13

CI 在 Linux 和 Windows 上覆盖以上版本。


注释规范

项目要求:

  • 每个 Python 文件都有模块说明;
  • 每个 class 都有说明;
  • 每个函数 / 方法(包含私有函数)都有中文说明;
  • 关键 parser / rewrite 算法使用行内注释解释“为什么”,而不是逐行复述代码。

CI 中的:

tools/check_bilingual_docs.py

会自动检查源码、测试和工具脚本的文档覆盖。


验证

当前验证包括:

  • Linux / Windows;
  • Python 3.8–3.13;
  • pytest;
  • ruff;
  • mypy strict;
  • build / wheel / sdist;
  • twine check;
  • standalone wheel smoke;
  • C++ corpus round-trip;
  • CMake corpus round-trip;
  • XRobot constructor parity;
  • XR_REGISTER corpus probe。

详细数据见:

docs/VALIDATION.md

开发

pip install -e ".[dev]"

常用检查:

python -m pytest
python tools/check_bilingual_docs.py
python -m ruff check src tests tools
python -m mypy src
python -m build

Review

第一轮 review 请先看:

docs/REVIEW_GUIDE.md

它会告诉你哪些文件决定整体架构,哪些 grammar/test/tool 文件可以后看。

Metadata

Release files for xr-syntax 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xr-syntax 0.1.0
File Size Uploaded
xr_syntax-0.1.0.tar.gz 115.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for xr-syntax 0.1.0
File Interpreter ABI Platform
xr_syntax-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 220.2 kB

Release files / xr_syntax-0.1.0.tar.gz

Download URL xr_syntax-0.1.0.tar.gz
Size 115.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f865ec66910511e3712b7544bc742ecdd4df7fda1769947003f562268c53ac69
BLAKE2b-256 checksum
How to use checksums
0724d59b6f8badc8e39e92312bb6ba566984cead642004ed2ecbff7de4902f8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / xr_syntax-0.1.0-py3-none-any.whl

Download URL xr_syntax-0.1.0-py3-none-any.whl
Size 104.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c7495325f76f1051087fd5bcc301e797683142a306e7096ef7ed2fbaea6fd8f
BLAKE2b-256 checksum
How to use checksums
cdd822a95e1beff6b280eac9e8633a3084683d8ba476bcf3056f4a2fa1920915
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page