GEML 与其他标记格式的比较
English | 中文
GEML 与 Markdown(GitHub 风格)、HTML、CommonMark(严格核心)、 AsciiDoc、Org-mode、Pandoc Markdown 的对照。
关于 Pandoc:它本质是一个转换器,外加它自带的 Pandoc Markdown——功能最全的 Markdown 方言。它的属性写法 {#id .class key=val} 其实正是 GEML 那套的源头。Pandoc 自身的杀手锏——多格式互转与可编程 Lua 过滤器——属于和下面「逐元素对照」不同的维度。
关于定位的说明:这不是一场打勾竞赛。尤其是 AsciiDoc,单格式开箱即用的元素比 GEML 还多。GEML 的立论靠的是这里没有别的格式同时具备的三件事——本比较意在让它们 可见,而非比拼功能数量:
- 单一原语承载每一种结构化块 —— 学习、解析、尤其生成的语法面最小(这正是它对 AI 友好的原因)。
- 构建时引用校验 —— 断链是错误,而不是悄无声息的死链;而且校验覆盖的是内容而 不只是链接:被嵌入的块、外部 CSV、
.jsonl日志、代码的行区间,都在其中。 - 自包含版本历史(
.gemlhistory)—— 无需 git、也无需任何在线服务。
图例:✓ 原生 · ◐ 靠扩展/约定 · ✗ 无 · (H) 需借原始 HTML。
只想看逐构造对照 CommonMark 0.31.2(每个叶子块、容器块、内联,以及 GEML 对它们 各自的处理),见 GEML 与 CommonMark 对照。
能力矩阵
| 元素 / 能力 | GEML | Markdown (GFM) | HTML | CommonMark | AsciiDoc | Org-mode | Pandoc Markdown |
|---|---|---|---|---|---|---|---|
| 标题 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 粗体 / 斜体 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 行内代码 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 代码块(带语言) | ✓ | ✓ | ◐ | ✓ | ✓ | ✓ | ✓ |
| 列表 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 链接 / 图片 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 音频 / 视频嵌入 | ✓ | ✗ (H) | ✓ | ✗ | ✓ | ◐ | ✗ (H) |
| 表格 | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ |
| 数据表 / 计算列 | ✓ | ✗ | ✗ | ✗ | ◐ csv | ◐ 公式 | ✗ |
| 结构化数据作为受校验的块 | ✓ json/jsonl | ✗ | ◐ 不校验 | ✗ | ◐ 不校验 | ◐ babel | ✗ |
| 块体取自外部文件 | ✓ 受校验 | ✗ | ✗ | ✗ | ✓ 不校验 | ✓ 不校验 | ◐ 过滤器 |
| 提示框 / admonition | ✓ | ◐ alerts | ◐ | ✗ | ✓ | ◐ | ◐ 围栏 div |
| 脚注 | ✓ | ✓ | ◐ | ✗ | ✓ | ✓ | ✓ |
| 描述/定义列表 | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✓ |
| 上标/下标、行内 span | ✗ | ✗ | ✓ | ✗ | ✓ | ◐ | ✓ |
| 数学(行内 / 块级) | ✓ | ◐ | ◐ | ✗ | ✓ | ✓ | ✓ |
| 图形(托管 DSL) | ✓ | ◐ mermaid | ✗ | ✗ | ✓ | ✓ | ◐ 过滤器 |
| 绑定数据表或 data 块的图表 | ✓ | ✗ | ✗ | ✗ | ◐ | ◐ | ✗ |
| 引用文献 / 参考书目 | ✗ | ✗ | ✗ | ✗ | ◐ | ✓ | ✓ |
| 文档元数据 | ✓ 原生块 | ◐ frontmatter | ✓ | ✗ | ✓ | ✓ | ✓ |
| 块 id + 交叉引用 | ✓ | ◐ 仅标题 | ✓ | ◐ | ✓ | ✓ | ✓ |
| 内容嵌入(就地渲染另一篇文档的块) | ✓ 受校验 | ✗ | ◐ iframe | ✗ | ✓ 预处理器 | ✓ | ◐ 过滤器 |
| 构建时引用校验 | ✓ 报错 | ✗ | ✗ | ✗ | ✓ 告警 | ◐ | ✗ |
| 原始 HTML 逃逸口 | ✗ (刻意) | ✓ | — | ✓ | ✓ | ✓ | ✓ |
| 纯文本可读(免渲染) | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ |
| 可编程过滤器 / 宏 | ✗ (刻意) | ✗ | ✗ | ✗ | ◐ | ✓ | ✓ Lua |
| 单一原语统一所有块 | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| 自包含版本历史 | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
加粗的三行是 GEML 的真正差异化。「原始 HTML = ✗」是特性而非缺失:它让语义保持可移植、 不绑定任何后端。Pandoc Markdown 是这里最宽的一列——它多了引用文献、定义列表、Lua 过滤器——但依然没有「单一原语、构建时校验、自包含历史」这三样。
并排语法
代码块
GEML === code {#hello lang=python}
print("hi")
===
Markdown ```python
print("hi")
```
HTML <pre><code class="language-python">print("hi")</code></pre>
CommonMark ```python
print("hi")
```
AsciiDoc [source,python]
----
print("hi")
----
Org-mode #+begin_src python
print("hi")
#+end_src
Pandoc ```{.python}
print("hi")
```文档元数据
GEML === meta
title = "Budget plan"
===
Markdown --- (YAML frontmatter —— 仅约定,非规范)
title: Budget plan
---
HTML <meta name="title" content="Budget plan">
CommonMark (无机制)
AsciiDoc = Budget plan
:version: 0.1
Org-mode #+TITLE: Budget plan
Pandoc --- (YAML 元数据块 —— 一等公民)
title: Budget plan
---提示框 / Admonition
GEML === note {#risks}
供应商锁定是主要风险。
===
Markdown > [!NOTE] (GitHub 扩展)
> 供应商锁定是主要风险。
HTML <div class="note">供应商锁定是主要风险。</div>
CommonMark (无机制 —— 只有普通引用块)
AsciiDoc [NOTE]
====
供应商锁定是主要风险。
====
Org-mode (无标准 —— 特殊块,依赖导出后端)
Pandoc ::: {.note}
供应商锁定是主要风险。
:::交叉引用,以及是否被校验
GEML 见 [[#budget]] → #budget 不存在 ⇒ 构建“错误”
Markdown 见 [budget](#budget) → 断链静默通过
HTML 见 <a href="#budget">… → 不校验
CommonMark 见 [budget](#budget) → 不校验
AsciiDoc 见 <<budget>> → 处理器对未解析 xref 告警
Org-mode 见 [[budget]] → 导出时部分校验
Pandoc 见 [budget](#budget) → 不校验(xref 靠 pandoc-crossref 过滤器)内容嵌入 —— 内容住在另一篇文档里
GEML === embed {src=handbook.geml#refund}
=== → 就地渲染那个块。src= 受引用校验,嵌入成环
是错误;而且 handbook.geml 是被**当作独立
文档解析**的——它的元数据与相对路径都相对
它自己解析,而不是相对本文件。
行内形式:![[handbook.geml#refund]](可写在句子中间)
Markdown (无机制 —— Obsidian 的 ![[note]] 是应用层扩展)
HTML <iframe src="handbook.html#refund"> (一个嵌套浏览上下文,
并非本文档里的内容)
CommonMark (无机制)
AsciiDoc include::handbook.adoc[tag=refund] (预处理器:把目标的**文本**
拼进来,此后一切相对**本
文件**解析)
Org-mode #+INCLUDE: "handbook.org::#refund" :only-contents t
Pandoc (无原生形式 —— 靠过滤器,如 pandoc-include)带计算列的表格(GEML 独有)
GEML === table {#fy25 format=csv header=1}
Segment, Q1, Q2, Q3, Q4
Cloud, 1, 2, 3, 4
===
=== view {#fy25-report src=#fy25
compute="FY [%.1f] = Q1 + Q2 + Q3 + Q4"
summary="Segment = 'Total'; FY = sum(FY)"}
=== → 逐行 FY 列 + 一行 Total 汇总行,
FY 保留 1 位小数
Org-mode | Segment | Q1 | Q2 | Q3 | Q4 | FY |
|---------+----+----+----+----+----|
#+TBLFM: $6=$2+$3+$4+$5 (灵感来源——但它是完整电子表格:单元格
引用、remote()、Emacs Lisp。GEML 只取
受限的列公式子集)
其他格式 仅静态表格 —— 无计算受校验的结构化数据(GEML 独有)
GEML === data {#limits format=json}
{ "retries": 3, "timeout_ms": 500 }
=== → 构建时被解析并**校验**;块体格式不对就是
错误,并指出出错的那一行。#limits 和任何
别的块一样可寻址、可引用、可版本化。
=== data {#events format=jsonl src=events.jsonl}
=== → 日志仍然是一个普通 .jsonl 文件,现有工具
照样 append 和 tail;文档则成为它受校验、
可制图的视图。同时写块体和 src= 是错误
—— 数据永远只有一个来源。
其他格式 一个 ```json 围栏 —— 处理器从不读、从不校验的文本,既不能寻址、
引用,也不能拿去画图。最接近的是 HTML 的
<script type="application/json"> 和 Org 带 #+NAME 的 src 块,
但两者都不校验负载。代码块指名它的来源,而不是复制一份
GEML === code {#parse lang=ts src=src/parser.ts#L14-24}
=== → 这个块**就是**那几行。文件里若已没有这些
行,构建**失败**(bad-source-range);
与 src= 并存的块体是快照,一旦与源漂移
就会告警。
AsciiDoc [source,ts]
----
include::src/parser.ts[lines=14..24]
---- → 静默取走那几行此刻的内容 —— 区间漂移
并不是错误
Org-mode #+INCLUDE: "src/parser.ts" src ts :lines "14-25" (同上,导出时)
Pandoc ```{.ts include=src/parser.ts startLine=14 endLine=24} (过滤器)
MD/HTML 复制粘贴,然后慢慢腐烂图形(托管外部 DSL)
GEML === diagram {#flow format=mermaid}
graph LR
A --> B
===
Markdown ```mermaid (GitHub 渲染;无 id/caption/校验)
graph LR
A --> B
```
AsciiDoc [mermaid]
----
graph LR
A --> B
----
Org-mode #+begin_src plantuml :file out.png
...
#+end_src
Pandoc ```{.mermaid} (靠过滤器渲染,如 mermaid-filter)
graph LR
A --> B
```
HTML/CMark 无原生图形托管绑定数据表或 data 块的图表(GEML 独有)
GEML === diagram {#rev format=geml-chart data=#fy25 type=bar x=Segment y=FY}
=== → 把表 #fy25 渲染成图表;列引用受校验。
data= 接受与表格 src= 相同的三种目标:
本文档中的块、另一篇文档的 doc.geml#id,
或一个文件。当 data 块的值是记录数组时,
它也能直接喂给图表。
其他格式 手抄数据进图表库,或用电子表格 App —— 无链接唯有 GEML 做到的
上面每种格式都能渲染一个标题和一个代码块。差别在于:当整篇文档经历变更与自动化时, 会发生什么——
- 单一类型块承载代码、表格、图形、数学、提示框与元数据——于是只有一套语法要学, 工具(或 LLM)也只需正确产出一套语法,而不是每个特性一套语法外加 HTML 兜底。
- 引用在构建时校验。 无法解析的
#id会让构建失败,而不是像在 Markdown/HTML 里那样变成一条静默的死链。 - 内容可以住在别处,并且照样受校验。
embed块(或行内![[…]])就地渲染另一 篇文档的块;src=让code块指向真实源文件的一段行区间、让table指向它的 CSV、让data块指向它的.jsonl日志。每一个被指名的目标都在构建时解析——悬空 引用、嵌入成环、以及文件变更后已经漂移的行区间,都会让构建失败。AsciiDoc 和 Org-mode 也能 include,但它们做的是文本拼接:目标自己的相对路径此后相对「谁 include 了它」解析,而失效的行区间只会静默地给出错误的几行。在 GEML 里,被指名的 文档是作为一篇独立文档解析的,因此在引用链的每一层它都相对自己解析。 - 数据是被校验的,而不是被引号包起来的。
data块承载 JSON 的值域——标量、序列、 映射——作为构建会解析、并在格式错误时拒绝的数据;而在这里其他每种格式中,一个```json围栏都只是没人读的文本。它可以带#id,因而可被引用、按块编辑、 版本化、制图;配合src=events.jsonl,记录仍留在一个可以随手 append、随手 tail 的普通文件里,文档则成为它们受校验的视图。 - 历史自包含。 伴生的
.gemlhistory文件能还原任意历史修订、把文档回滚——离线、 无 git、无在线服务。见历史扩展。
Pandoc 玩的是另一个游戏——它是通用转换器,也是抵达 docx/latex/epub 最实用的 路径。GEML 自然的未来是接入这个生态(做一个 Pandoc reader/writer),而非与之竞争。