Skip to content

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 的立论靠的是这里没有别的格式同时具备的三件事——本比较意在让它们 可见,而非比拼功能数量:

  1. 单一原语承载每一种结构化块 —— 学习、解析、尤其生成的语法面最小(这正是它对 AI 友好的原因)。
  2. 构建时引用校验 —— 断链是错误,而不是悄无声息的死链;而且校验覆盖的是内容而 不只是链接:被嵌入的块、外部 CSV、.jsonl 日志、代码的行区间,都在其中。
  3. 自包含版本历史(.gemlhistory)—— 无需 git、也无需任何在线服务。

图例:✓ 原生 · ◐ 靠扩展/约定 · ✗ 无 · (H) 需借原始 HTML。

只想看逐构造对照 CommonMark 0.31.2(每个叶子块、容器块、内联,以及 GEML 对它们 各自的处理),见 GEML 与 CommonMark 对照。

能力矩阵 ​

元素 / 能力GEMLMarkdown (GFM)HTMLCommonMarkAsciiDocOrg-modePandoc 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),而非与之竞争。

完整格式见核心规范,快速上手见 README。

Code MIT · Specification CC BY 4.0