@nova-lang/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,322 @@
1
+ 我设计了一种全新的标记语言,命名为 **Nova**(全称:**N**ested **O**rdered **V**ersatile **A**rchitecture)。它并非将 HTML、YAML、TeX 等语法简单堆砌,而是从它们各自的优势中抽象出**统一节点模型**——一切皆是可带属性、可含子节点的**函数式块(Block)**。Nova 的语法规则如下。
2
+
3
+ ---
4
+
5
+ ## 安装
6
+
7
+ ### npm(推荐)
8
+ ```bash
9
+ npm install -g @nova-lang/cli
10
+ # 需要先安装 Zig 编译器:https://ziglang.org/download/
11
+ nova examples/sample.nv output.html
12
+ ```
13
+
14
+ ### pip
15
+ ```bash
16
+ pip install -e .
17
+ nova examples/sample.nv output.html
18
+ ```
19
+
20
+ ### Zig 编译
21
+ ```bash
22
+ zig build
23
+ ./zig-out/bin/nova examples/sample.nv output.html
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Nova 语法规范 v1.0
29
+
30
+ ### 核心设计理念
31
+ - **一切皆块**:文档元素、元数据、样式、数据类型、宏都是块,语法形式统一。
32
+ - **缩进定界**:块内容由缩进界定,省略闭合标记,类似 YAML 和 Python。
33
+ - **标签即函数**:块类型通过 `@命令名` 调用,属性写在圆括号 `()` 里,子块写在缩进大括号 `{}` 里——这是一种**函数式标记**。
34
+ - **双模数据**:内联采用自然书写,数据表可采用紧凑的类 CSV 表示或外部引用。
35
+ - **强类型可编译**:可内嵌类型定义和接口,直接生成代码或验证数据。
36
+ - **数学与宏不变质**:保留 TeX 强大排版,但将其融入统一语法。
37
+
38
+ ---
39
+
40
+ ### 1. 基础词法
41
+
42
+ ```nova
43
+ // 单行注释
44
+ /* 多行
45
+ 注释 */
46
+
47
+ // 文档元数据块
48
+ @meta {
49
+ title: "Nova 示例"
50
+ author: Li Hua
51
+ date: 2026-06-20
52
+ }
53
+
54
+ // 引入外部包(如预定义类型、宏集)
55
+ @use "nova/std/typography"
56
+ @use "nova/schema/gencode"
57
+ ```
58
+
59
+ - 字符串:`"双引号"` 或 `'单引号'`,支持 `#{...}` 插值。
60
+ - 数字:`42`、`3.14`、`6.02e23`、`0xAB`。
61
+ - 布尔与空:`true`、`false`、`null`。
62
+ - 标识符:`[a-zA-Z_][a-zA-Z0-9_-]*`。
63
+
64
+ ---
65
+
66
+ ### 2. 块语法(融合 HTML/XML 结构 + YAML 简洁)
67
+
68
+ 一个块的基本形式为:
69
+
70
+ ```nova
71
+ @块名(属性1: 值, 属性2: 值) 可选内联正文 {
72
+ 子块1
73
+ 子块2
74
+ }
75
+ ```
76
+
77
+ - **无子块**可省略大括号和缩进:
78
+ `@image(src: "photo.png", alt: "头像")`
79
+ - **块名省略**时为无名容器,等价于 `<div>` 或 YAML 映射。
80
+ - **属性列表**写在紧跟的 `()` 中,逗号分隔,键值对用 `:` 分隔(也可用 `=`),布尔属性可省略值。
81
+ - **子块**必须比父块缩进一层(2 个空格)。
82
+
83
+ #### 示例
84
+ ```nova
85
+ @page {
86
+ @header {
87
+ @title "Nova 语言手册"
88
+ @author "Li Hua"
89
+ }
90
+ @body {
91
+ @section(id: "intro") {
92
+ @p "Nova 是一种全新的可编程标记语言。"
93
+ }
94
+ }
95
+ }
96
+ ```
97
+ 相当于 HTML 的 `<page><header><title>...</title>`…,但无需闭合标签,且结构由缩进清晰表达。
98
+
99
+ ---
100
+
101
+ ### 3. 内联元素与格式(融合 HTML 行内标签 + TeX 命令)
102
+
103
+ 内联内容使用 `@标记{ 文本 }` 或 `@标记(属性){ 文本 }`:
104
+ ```nova
105
+ 这是一段包含 @em{强调} 和 @strong{粗体} 的文本,
106
+ 还有 @a(href: "https://nova-lang.org"){链接} 和 @code{print(x)}。
107
+ ```
108
+ 与 TeX 的 `\emph{...}` 相似,但统一了属性传递方式。
109
+
110
+ 数学公式直接内嵌,支持 TeX 语法:
111
+ - 行内:`$E = mc^2$`
112
+ - 展示:`$$ \sum_{i=1}^n i = \frac{n(n+1)}{2} $$`
113
+ - 也可使用块形式:`@equation { x = \frac{-b \pm \sqrt{b^2-4ac}}{2a} }`
114
+
115
+ ---
116
+
117
+ ### 4. 列表与映射(YAML 优势)
118
+
119
+ - **无序列表**用 `-` 或 `*` 引导:
120
+ ```nova
121
+ @ul {
122
+ - 苹果
123
+ - 香蕉
124
+ - 橘子
125
+ }
126
+ ```
127
+ - **有序列表**用 `+` 后跟数字或 `#`:
128
+ ```nova
129
+ @ol {
130
+ + 1. 第一步
131
+ + 2. 第二步
132
+ }
133
+ ```
134
+ - **映射**用键值对,可内联或展开:
135
+ ```nova
136
+ @config {
137
+ host: "localhost"
138
+ port: 8080
139
+ timeout: 30s
140
+ }
141
+ ```
142
+
143
+ 列表项本身可以是块,允许包含多段落或其他块:
144
+ ```nova
145
+ - @p "第一点详细说明"
146
+ @note "补充信息"
147
+ - @p "第二点"
148
+ ```
149
+
150
+ ---
151
+
152
+ ### 5. 表格系统(融合 CSV 紧凑 + 管道表可读性)
153
+
154
+ Nova 提供三种表格写法,适应不同场景。
155
+
156
+ #### 5.1 内联二维数组(CSV 风格)
157
+ ```nova
158
+ @table {
159
+ [["姓名", "年龄", "城市"],
160
+ ["张三", 30, "北京"],
161
+ ["李四", 25, "上海"]]
162
+ }
163
+ ```
164
+
165
+ #### 5.2 带表头的块表(可读性)
166
+ ```nova
167
+ @table {
168
+ @header { 姓名, 年龄, 城市 }
169
+ @row { 张三, 30, 北京 }
170
+ @row { 李四, 25, 上海 }
171
+ }
172
+ ```
173
+
174
+ #### 5.3 外部数据引用(CSV 文件绑定)
175
+ ```nova
176
+ @table(src: @csv("data/measurements.csv"), caption: "测量数据")
177
+ ```
178
+ 这样保留了 CSV 的简单数据交换能力,同时能被解析器直接处理。
179
+
180
+ ---
181
+
182
+ ### 6. 宏与变量(TeX 宏 + 现代模板)
183
+
184
+ 宏定义使用 `@def` 或 `@macro`,支持位置参数和块参数。
185
+ ```nova
186
+ @def greet(name) {
187
+ @p "您好,#{name}!"
188
+ }
189
+
190
+ @def framed(title: String = "提示", @content) {
191
+ @div(style: "border") {
192
+ @strong "#{title}"
193
+ @content
194
+ }
195
+ }
196
+
197
+ @greet("世界")
198
+ @framed("警告") {
199
+ 这是一条重要信息。
200
+ }
201
+ ```
202
+ 调用方式与普通块完全一致,实现了 **标记即代码**。
203
+
204
+ 还支持**命名参数**、**默认值**和**块参数**(如 `@content`),这吸收了 LaTeX3 的新特性,但语法更清晰。
205
+
206
+ ---
207
+
208
+ ### 7. 类型定义与代码生成(gencode / 接口描述语言)
209
+
210
+ Nova 可直接定义强类型消息,用于生成序列化代码、验证数据,或作为 API 合同。
211
+ ```nova
212
+ @schema(Person) {
213
+ id: Int32 @1
214
+ name: String @2 = ""
215
+ email: String? @3 // ? 表示可选
216
+ tags: List<String> @4
217
+ }
218
+
219
+ @service(UserAPI) {
220
+ getUser(id: Int32) -> Person
221
+ listUsers() -> List<Person>
222
+ updateUser(id: Int32, data: UpdateMask) -> Person
223
+ }
224
+ ```
225
+ 编译器可根据这些定义输出 Go struct、Protobuf、JSON Schema 或 Python dataclass。这继承了 gencode 的核心优势,并且语法与文档其他部分无缝融合。
226
+
227
+ ---
228
+
229
+ ### 8. 交叉引用与参考文献(LaTeX 优势)
230
+
231
+ **标签**使用 `@label(标识符)`,**引用**使用 `@ref(标识符)`,**文献**使用 `@cite(key)`。
232
+ ```nova
233
+ @chapter(id: "intro") {
234
+ @title "简介"
235
+ @label(sec:intro)
236
+ }
237
+
238
+ 参见 @ref(sec:intro) 的讨论。该方法源自 @cite(lamport94)。
239
+
240
+ @bibliography {
241
+ @entry(key: lamport94, type: book,
242
+ author: "Leslie Lamport",
243
+ title: "LATEX: A Document Preparation System",
244
+ year: 1994)
245
+ }
246
+ ```
247
+ 文档的元数据中可指定参考文献风格,生成 PDF 或 HTML 时自动格式化。
248
+
249
+ ---
250
+
251
+ ### 9. 环境与条件(TeX 环境 + 现代流程控制)
252
+
253
+ - **环境块**:`@if(条件) { ... }`,`@for(item in list) { ... }`。
254
+ - **条件显示**:
255
+ ```nova
256
+ @if(@defined(debug)) {
257
+ @warn "调试模式开启,当前值:#{value}"
258
+ }
259
+ ```
260
+ - **循环生成列表项**:
261
+ ```nova
262
+ @ul {
263
+ @for(user in users) {
264
+ - @strong(user.name) (#{user.email})
265
+ }
266
+ }
267
+ ```
268
+
269
+ 这使得文档不仅仅是静态文本,而是带有轻量逻辑的**活文档**。
270
+
271
+ ---
272
+
273
+ ### 10. 完整综合示例
274
+
275
+ ```nova
276
+ @use "nova/std"
277
+ @use "nova/plot"
278
+
279
+ @meta {
280
+ title: "Nova 语言白皮书"
281
+ authors: ["Li Hua", "Zhang Wei"]
282
+ version: 1.0
283
+ }
284
+
285
+ @page {
286
+ @title "Nova 语言白皮书"
287
+
288
+ @abstract {
289
+ Nova 统一了文档与数据,提供前所未有的编写体验。
290
+ 详见 @ref(sec:design)。
291
+ }
292
+
293
+ @chapter(id: "intro") @label(sec:intro) {
294
+ @p "本文介绍 Nova 的语法及设计理念。"
295
+ @p "数学示例:能量守恒 $E = mc^2$。"
296
+ }
297
+
298
+ @chapter(id: "design") @label(sec:design) {
299
+ @p "核心架构基于统一函数式块。"
300
+ @table(caption: "主流语言对比") {
301
+ @header { 特性, HTML, YAML, TeX, Nova }
302
+ @row { 标签, ✅, ❌, ✅, ✅ 函数式 }
303
+ @row { 缩进块, ❌, ✅, 部分, ✅ }
304
+ @row { 数学, ❌, ❌, ✅, ✅ }
305
+ @row { 类型定义, ❌, ❌, ❌, ✅ }
306
+ }
307
+ }
308
+
309
+ @chapter(id: "api") {
310
+ @p "数据模型定义:"
311
+ @schema(Measurement) {
312
+ sensor_id: Int32 @1
313
+ value: Float64 @2 = 0.0
314
+ unit: String @3 = "mV"
315
+ }
316
+ }
317
+
318
+ @bibliography {
319
+ @entry(key: nova2026, title: "Nova Language Spec", year: 2026)
320
+ }
321
+ }
322
+ ```
package/bin/nova.js ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { spawnSync } = require("child_process");
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+
8
+ function findBinary() {
9
+ const binName = process.platform === "win32" ? "nova.exe" : "nova";
10
+
11
+ const candidates = [
12
+ path.join(__dirname, "..", "zig-out", "bin", binName),
13
+ path.join(__dirname, "..", "bin", binName),
14
+ ];
15
+
16
+ if (process.env.NOVA_BINARY) {
17
+ candidates.unshift(process.env.NOVA_BINARY);
18
+ }
19
+
20
+ for (const p of candidates) {
21
+ try {
22
+ if (fs.statSync(p).isFile()) {
23
+ return p;
24
+ }
25
+ } catch {}
26
+ }
27
+
28
+ return null;
29
+ }
30
+
31
+ function main() {
32
+ const binary = findBinary();
33
+ if (!binary) {
34
+ console.error(
35
+ "Nova Zig binary not found.\n" +
36
+ " Build with: npm run build (requires Zig compiler)\n" +
37
+ " Or set: NOVA_BINARY=/path/to/nova\n"
38
+ );
39
+ process.exit(1);
40
+ }
41
+
42
+ const result = spawnSync(binary, process.argv.slice(2), {
43
+ stdio: "inherit",
44
+ });
45
+
46
+ process.exit(result.status ?? 1);
47
+ }
48
+
49
+ main();
package/build.zig ADDED
@@ -0,0 +1,25 @@
1
+ const std = @import("std");
2
+
3
+ pub fn build(b: *std.Build) void {
4
+ const target = b.standardTargetOptions(.{});
5
+ const optimize = b.standardOptimizeOption(.{});
6
+
7
+ const exe_mod = b.createModule(.{
8
+ .root_source_file = b.path("src/main.zig"),
9
+ .target = target,
10
+ .optimize = optimize,
11
+ });
12
+
13
+ const exe = b.addExecutable(.{
14
+ .name = "nova",
15
+ .root_module = exe_mod,
16
+ });
17
+
18
+ b.installArtifact(exe);
19
+
20
+ const run_cmd = b.addRunArtifact(exe);
21
+ run_cmd.step.dependOn(b.getInstallStep());
22
+
23
+ const run_step = b.step("run", "Run Nova");
24
+ run_step.dependOn(&run_cmd.step);
25
+ }
package/install.js ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { execSync, spawnSync } = require("child_process");
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+
8
+ const ROOT = path.resolve(__dirname);
9
+ const BIN_DIR = path.join(ROOT, "zig-out", "bin");
10
+ const BIN_NAME = process.platform === "win32" ? "nova.exe" : "nova";
11
+ const BIN_PATH = path.join(BIN_DIR, BIN_NAME);
12
+
13
+ function alreadyBuilt() {
14
+ try {
15
+ return fs.statSync(BIN_PATH).isFile();
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+
21
+ function findZig() {
22
+ try {
23
+ const r = spawnSync("which", ["zig"], { stdio: "pipe" });
24
+ if (r.status === 0) {
25
+ return r.stdout.toString().trim();
26
+ }
27
+ } catch {}
28
+ try {
29
+ const r = spawnSync("where", ["zig"], { stdio: "pipe", shell: true });
30
+ if (r.status === 0) {
31
+ return r.stdout.toString().trim().split("\n")[0];
32
+ }
33
+ } catch {}
34
+ return null;
35
+ }
36
+
37
+ function build() {
38
+ if (alreadyBuilt()) {
39
+ console.log("nova: binary already built at " + BIN_PATH);
40
+ return;
41
+ }
42
+
43
+ const zig = findZig();
44
+ if (!zig) {
45
+ console.log(
46
+ "nova: Zig compiler not found. Skipping build.\n" +
47
+ " Install Zig: https://ziglang.org/download/\n" +
48
+ " Then run: npm run build"
49
+ );
50
+ return;
51
+ }
52
+
53
+ console.log("nova: building with " + zig + " ...");
54
+ const result = spawnSync(zig, ["build"], {
55
+ cwd: ROOT,
56
+ stdio: "inherit",
57
+ });
58
+
59
+ if (result.status === 0) {
60
+ console.log("nova: built successfully -> " + BIN_PATH);
61
+ } else {
62
+ console.error("nova: build failed (exit code " + result.status + ")");
63
+ process.exit(result.status ?? 1);
64
+ }
65
+ }
66
+
67
+ build();
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@nova-lang/cli",
3
+ "version": "0.1.0",
4
+ "description": "Nova: Nested Ordered Versatile Architecture — a programmable markup language CLI",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "markup",
8
+ "markdown",
9
+ "typesetting",
10
+ "document",
11
+ "schema",
12
+ "code-generation"
13
+ ],
14
+ "bin": {
15
+ "nova": "bin/nova.js"
16
+ },
17
+ "scripts": {
18
+ "postinstall": "node install.js",
19
+ "build": "zig build",
20
+ "build-release": "zig build -Doptimize=ReleaseSafe",
21
+ "clean": "rm -rf zig-out zig-cache .zig-cache",
22
+ "test": "nova lex examples/basics.nv && nova ast examples/basics.nv"
23
+ },
24
+ "files": [
25
+ "bin/",
26
+ "install.js",
27
+ "build.zig",
28
+ "src/"
29
+ ],
30
+ "engines": {
31
+ "node": ">=16"
32
+ },
33
+ "os": ["darwin", "linux", "win32"],
34
+ "cpu": ["x64", "arm64"]
35
+ }