CNSH → C 通用转译器 v2.0
CNSH 是一门中文母语的系统级语言:编译为 C99,再经 gcc/clang 编译为原生可执行文件,内置 DNA 追溯与三色审计。零三方依赖(仅 Python 标准库)。
快速开始
cd packaging/cnsh-v2
# 端到端:CNSH 源码 → C99 → 原生可执行文件 → 运行
python3 -m cnsh_v2 build examples/演示_主程序.cnsh
./examples/演示_主程序
# 只转译
python3 -m cnsh_v2 translate examples/演示_主程序.cnsh
# 只审计 / 查 DNA / 格式化 / 版本
python3 -m cnsh_v2 audit examples/演示_主程序.cnsh
python3 -m cnsh_v2 dna examples/演示_主程序.cnsh
python3 -m cnsh_v2 fmt examples/演示_主程序.cnsh
python3 -m cnsh_v2 version
交互模式(REPL)
python3 -m cnsh_v2 repl # 启动
python3 -m cnsh_v2 repl --debug # 显示生成的 CNSH 源与编译报告
python3 -m cnsh_v2 repl --no-color # 老终端
REPL 里直接输入 CNSH,回车即执行:
龍魂❯ 整数 x = 21
龍魂❯ 打印 x * 2
42
龍魂❯ 函数 加倍(整数 n) 返回类型 整数 {
... 返回 n * 2
... }
✅ 已登记声明
龍魂❯ 打印 加倍(50)
100
内置指令:.帮助 .退出 .清空 .历史 .声明 .载入 <文件> .版本 .重置
执行方式说明(重要):cnsh_v2 是转译器,没有 eval/运行时。
REPL 每段都是「生成 CNSH 源 → Compiler.build() → 跑本机可执行 → 回收 stdout」。
变量与顶层声明跨段保留,旧段输出不会重放(用标记截断)。
性能(Apple M4 Max / clang 21,复算:python3 tools/bench_交互解释器.py):
| 指标 | 实测 |
|---|---|
| 单段全链路中位 | ≈65 ms(编译占 98%) |
| 会话层(生成 20 段源) | 0.004 ms(可忽略) |
| 语句 1 → 800 段 | 74 → 93 ms(1.26×) |
结论:瓶颈是每段一次 clang,不是「重放旧语句」——千段以内不需要为它 .重置。
风格检查(lint)
python3 -m cnsh_v2 lint examples/ # 检查
python3 -m cnsh_v2 lint . --fix # 自动修 L001(行尾空格)
python3 -m cnsh_v2 lint . --json # 机器可读
python3 -m cnsh_v2 lint . --strict # CI:W/L 也算失败
规则只管风格与工程卫生,语法/语义由编译器负责(E1xxx/E2xxx),不重复造。
| 代码 | 含义 | 可自动修 |
|---|---|---|
| L001 | 行尾空格 | ✅ --fix |
| L002 | 缩进不是本文件缩进单位的倍数 | — |
| L003 | 视觉宽度 > 100(中日韩按 2 计) | — |
| L004 | 文件头 15 行无 DNA 签章 | — |
| L005 | 有 DNA 但无许可证/归属声明 | — |
| W001 | 同文件内函数返回类型写法不一致 | — |
| W003 | 用了英文关键字(if/for/return…) | — |
| E001/E002 | 控制字符 / 非 UTF-8 | — |
为什么规则要跟着文件走:仓内 .cnsh 有 v0.1(2 空格缩进)、v1(定义 …:)、
v2(函数 … 返回类型)三种方言。用 v2 的 4 空格去量 v0.1 文件会 100% 误报
(实测 155 个文件里 L002 命中 208 次,全是假的)。所以缩进单位由文件自身推断,
装饰分隔线(# ═════)不算超宽,字符串字面量里的 if 不算英文关键字。
存量基线:155 个文件里 51% 有提示,其中 45% 是行尾空格、38% 缺许可证 —— 都是历史欠账, 不是新代码门槛。E 级为 0,说明没有真错误。
虚拟环境(venv)
python3 -m cnsh_v2 venv create # 建 .cnsh-env/
eval "$(python3 -m cnsh_v2 venv activate)" # 激活(导出三个环境变量)
python3 -m cnsh_v2 venv install ./我的库/ # 装本地包(目录里需 cnsh.toml)
python3 -m cnsh_v2 venv list # 已装包
python3 -m cnsh_v2 venv info # 环境信息
cnsh_venv_deactivate # 停用
CNSH 编译到 C,没有解释器隔离需求。venv 隔离的是三样:stdlib 覆盖、 包搜索路径、构建产物目录。不复制工具链。
三个变量,以及谁真的消费它们(设了没人读就是装饰品,所以逐条列出):
| 变量 | 消费点 | 作用 |
|---|---|---|
CNSH_ENV |
modules.ModuleResolver._搜索根 · cli.build |
环境根进搜索路径;build 无 -o 时产物落 $CNSH_ENV/build/ |
CNSH_STDLIB |
ModuleResolver._find |
仅对 stdlib/... 前缀生效,优先于系统 stdlib |
CNSH_PKG_PATH |
ModuleResolver._搜索根 |
逐条追加包搜索根 |
优先级:$CNSH_STDLIB(仅 stdlib/ 前缀)→ $CNSH_ENV → $CNSH_PKG_PATH…
→ 源文件目录 → 包根。未设变量时行为与旧版完全一致。
不实现:cnsh init 与 cnsh.toml 项目约定 —— 本仓没有这两样,venv 不假装能初始化项目;
包目录里读 cnsh.toml(没有就用目录名/unknown)。也不做远程索引与依赖求解。
顺带修掉一条静默路径:宽容模式下缺失导入会降级成空模块,而 resolver.missing
原先没有任何人读 ⇒ 缺模块完全静默,用户只看到「编译通过 + 运行时报未定义符号」。
现在 Compiler 会输出 W3001: 导入模块缺失,已降级为空模块。
新建项目(init)
python3 -m cnsh_v2 init 我的项目 --venv
cd 我的项目
eval "$(python3 -m cnsh_v2 venv activate)"
python3 -m cnsh_v2 venv install # 按 cnsh.toml 的 [dependencies]
python3 -m cnsh_v2 build # 省略源文件 = 用 cnsh.toml 的 entry
./build/主程序
生成的结构:
我的项目/
├── cnsh.toml 项目配置
├── 主程序.cnsh 入口(能编译、能跑)
├── tests/test_主程序.cnsh 自带主函数的测试骨架(能编译、能跑、失败返回非零)
├── stdlib/ 项目私有 stdlib 覆盖
├── build/ 产物(已 gitignore)
├── .gitignore
└── README.md
cnsh.toml 的每个字段都有人消费:
| 字段 | 消费点 |
|---|---|
project.entry |
build/translate/audit/dna/fmt 省略源文件时用它 |
project.target |
--target,仅 c / c99 / c11 |
project.name / version |
项目标识;venv install 读 name |
dependencies |
venv install 不带参数时按表安装 |
build.output_dir |
build 默认输出目录(保留源文件相对路径) |
故意不提供 flags 与 dev-dependencies:没有对应实现,写了也不会生效。
不支持 --target python/js/rust:编译器只产出 C,写进配置也编译不出来,所以直接报错。
⚠️ 中文包名必须加引号:"工具库" = "../lib"。
TOML 裸键只允许 ASCII 字母数字与 _/-;写成 工具库 = "..." 会让整份配置解析失败
(不是那一行失效),venv install 会明确报「解析失败」而不是假装「表为空」。
一段 CNSH
导入 "stdlib/字符串.cnsh"
函数 主函数() 返回类型 整数 {
整数 和 = 0
对于 (整数 i = 0; i < 10; i++) {
和 += i
}
如果 〖和 > 40〗 {
打印 字符串_拼接("和是", "四十五")
} 否则 {
打印「不大」
}
返回 0
}
目录
docs/ 语言规范(SPEC/TYPE_SYSTEM/SCOPE_RULES/UNDEFINED_BEHAVIOR/ABI/VERSIONING/CHANGELOG/ROADMAP)
cnsh_v2/ 转译器本体(lexer/parser/sema/dna/audit/modules/codegen_c/compiler/cli/...)
stdlib/ 标准库 CNSH 侧声明(字符串/数学/文件/时间/输入/内存)
tests/ 一致性(JSON 用例)+ 黄金输出 + 回归
tools/ LSP 骨架(当前仅语法诊断)
examples/ 端到端示例
测试
python3 tests/run_tests.py # 一致性 20 例(纯标准库 runner)
python3 -m pytest tests/test_golden.py tests/regression/ # 黄金输出 + 回归(或各自直接 python3 跑)
治理口径
- DNA 前缀
#龍芯⚡️,用户 UID9622;旧前缀自动迁移(cnsh_v2/dna.py)。 - 三色审计 🟢/🟡/🔴,strict 模式 🔴 阻断编译(
cnsh_v2/audit.py)。 - 生成的 C 文件头部自动注入 DNA 注释 + 干支时间戳(
08_BIN/lh_输出戳.py,失败降级 ISO,不影响编译)。
断言库(stdlib/断言.cnsh)
导入 "stdlib/断言.cnsh"
函数 主函数() 返回类型 整数 {
断言相等(2 + 2, 4, "四则运算应成立")
断言为真(5 > 3, "")
断言相等文本("龍魂", "龍魂", "")
打印「✅ 全部通过」
返回 0
}
失败时立即打印原因到 stderr 并以退出码 1 终止——不是返回一个 0/1 让调用方自己记得检查。
| 函数 | 说明 |
|---|---|
断言(条件, 消息) |
条件为假 ⇒ 失败 |
断言为真 / 断言为假 |
同上 / 取反 |
断言相等(期望, 实际, 消息) |
整数比较 |
断言不等(值一, 值二, 消息) |
整数比较 |
断言相等文本(期望, 实际, 消息) |
文本比较(C 无重载,故单列) |
格式化(fmt)
cnsh fmt 主程序.cnsh # 打印格式化结果
cnsh fmt 主程序.cnsh -w # 原地写回
cnsh fmt . --check # CI:需要格式化的文件存在则退出码 1
cnsh fmt 主程序.cnsh --dry-run # 打印 diff,不写回
规则:行尾空白、整行注释 #x→# x(装饰行豁免)、连续空行压到 2、末尾恰好 1 个换行。
不做缩进规范化:仓内 v0.1 用 2 空格、v2 用 4 空格,强行统一不该由 fmt 决定。
解析不过的文件一个字节都不动(退出码 2)。原因:现有 formatter.format_source()
实测会删光注释(Token 流重建,注释不进流),用它写回等于数据丢失。
所以 fmt 只做行级处理,并带两条护栏:写回前再解析一次、注释行数不许减少。
调试(debug)
cnsh build --debug 主程序.cnsh # 只产出带调试符号的产物
cnsh debug 主程序.cnsh # 编译 + 启动 lldb/gdb
cnsh debug 主程序.cnsh --print-only # 只打印命令
调试模式下 codegen 在每条语句前发 #line N "源.cnsh",lldb 里变量声明直接显示
decl = 主程序.cnsh:12——不需要 .cnsh.map(没有调试器会读那种文件)。
⚠️ macOS 把 DWARF 放进同名 .dSYM,可执行文件本身不含。
所以「用 file 验证二进制含 DWARF」这条判据在 macOS 上永远假阴性,别用它。
发版(维护者)
python3 tools/发布.py --check # 本地校验:版本双写 / 构建 / twine check / 关键模块
PYPI_TOKEN=pypi-... python3 tools/发布.py --upload
python3 tools/发布.py --verify --wait 30 # 从 PyPI 拉回来实装实跑
脚本挡四件事:
- 版本双写(
version.py与pyproject.toml各写一次,漏改就发错版本) - 代理(构建的隔离环境会继承 socks5,报
Missing dependencies for SOCKS support) - 残包(构建成功 ≠ 包里对:逐个检查关键模块与
cli子命令是否真在 wheel 内) - token 泄漏(只经环境变量传给子进程,不进 argv、不回显)
⚠️ PyPI 同一版本号只能发一次,发错只能换号 —— 所以 --upload 前会把能查的都查一遍。
Metadata
Release files for cnsh-v2 2.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| cnsh_v2-2.2.0.tar.gz | 133.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cnsh_v2-2.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 284.4 kB
Release files / cnsh_v2-2.2.0.tar.gz
| Download URL | cnsh_v2-2.2.0.tar.gz |
|---|---|
| Size | 133.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
434b3634c5058b9cf0e87aa63fa3c087223fade51ae564c15d1b3b5f0986752b
|
|
BLAKE2b-256 checksum How to use checksums |
989944cb1f7daf51974474b0a410989f613b685ff903b1af750026d29b013e53
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / cnsh_v2-2.2.0-py3-none-any.whl
| Download URL | cnsh_v2-2.2.0-py3-none-any.whl |
|---|---|
| Size | 151.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6d36dbf03e96db844077fc693df1a8529bf092b263b58eb8b2aacacc8295c361
|
|
BLAKE2b-256 checksum How to use checksums |
6fef43d30cff22f346f31757564879d504c932eee1695cee9ce0a83c83e5a8f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Security for cnsh-v2 2.2.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.