GEML 块类型图解 · 第 4 页 / 5 · 规范 1.0

GEML 块图解 · diagram

diagram 有两种用法。一种是装外部 DSL:正文原样交给 format= 选的渲染器,处理器只负责不解释它。另一种是内置的 geml-chart:图完全由属性描述、绑到一张表或一个记录数组上,所以处理器能逐条校验,一个打错的列名就是构建错误。这一页把两种都实测一遍,十几条校验逐条对上 A.4。

看板

15 条规则,各自的出处和状态

规范已定 规范或已接受的 GEP 写死的。实测 规范不规定、参考实现今天这样做。草案缺口 草案该说而没说的。实现偏差本页未发现。

规范已定 11 实测 3 草案缺口 1
规则出处状态
外部 DSL
1raw 体,format= 选一个可插拔渲染器(mermaid、graphviz、d2、plantuml…)。处理器必须暴露渲染器注册表,不得解释正文。未知 format 是 warning,正文保留。§7 · A.4规范已定
2#id 让图可引用:[[#flow]],链接文字取 caption。§7 · §5.2规范已定
3raw 体不做 {{key}} 插值,%% 行是正文。§4规范已定
4参考实现的注册表认识 mermaid、graphviz、d2、plantuml、geml-chart、geml-code-graph。只有 mermaid 有内置引擎:输出 pre.mermaid 并在页面引入 mermaid 模块脚本;其余三个 DSL 输出 pre.diagram-src data-format= 原文,不 warning;vega-lite 和任何未知名字 warning。--to html实测
数据绑定与 geml-chart
5data= 三种目标同 table 的 src=:csv/tsv 或 json/jsonl 文件、#id、doc.geml#id。目标是 table 时贡献其模型(含计算列);是 data 块时必须是记录数组,图引用的列在每条记录里必须是标量;违规 error。§7.1 · A.4规范已定
6geml-chart 全在属性里:type 五选一 bar line area pie scatter;通道 x(类目)、y(值,逗号列表即多系列)、series、size;x、y 必填;rows=data|all|summary。正文应为空,非空 warning。§7.1规范已定
7构建时校验:缺 data、目标非表、非记录数组、缺 type、未知 type、缺通道、未知列、未知 rows、y 列非数、rows=summary 无汇总行 → error;未用通道、rows=all 无汇总行 → warning。A.4规范已定
8外部数据渲染时取,列校验推迟到渲染器;http(s) 需渲染器 opt-in,因为取 URL 会泄露读者地址和时间。§7.1 · §9.4规范已定
9要更多(标注、参考线、热力图)用宿主 DSL:format=vega-lite data=#fy,正文 raw、不做列校验。§7.1规范已定
10渲染:geml-chart 输出内联 svg.geml-chart role="img",rect / path / text;不依赖外部脚本。--to html实测
geml-code-graph(GEP-0003 已接受,codemap profile §6)
11geml-code-graph 是一种被解释的 diagram 格式:把一份 codemap 文档树画成分层的方法流图。嵌入点只有一个属性 src=,指向 codemap 的 index.geml 或某个容器文档;正文为空,非空 warning。GEP-0003 · codemap §6规范已定
12根和深度从不在嵌入点写:来自 src 所指文档的 meta(entry、graph-depth)。视图配置随数据走,嵌入点不可能漂移;换根就是指向另一个容器文档,方法级下钻是点击交互,不是属性。GEP-0003规范已定
13缺 src=、src 解析不到都是 warning:画不出东西,文档没错。A.4规范已定
14渲染:figure.code-graph > div.cg-mount data-start data-graph,图数据内联成 JSON,布局在绘制时由页面脚本完成;解析失败时同一 figure 里是 p.render-error。--to html实测
15data= 可以指 view,就像指 table 一样;而画派生列时必须指 view:compute=/summary= 是 view 的属性,它们产出的列只存在于 view 上。画 §6.1 的 FY 就是 data=#fy25-report y=FY;把同一张图指向基表会得到 chart: column `FY` not found in table。(GEP-0012 正文未提图表时,这条曾记为草案缺口;§7 已在规范正文里定下。)§7.1 · §6.1规范已定
DSL

四种外部 DSL 和一个未知名字

处理器对它们做的事完全一样:原样保留。差别只在渲染器有没有引擎。

mermaid · graphviz · d2 · plantuml · foo 规范已定 渲染实测
GEML · d1-diagram.geml
=== diagram {#flow format=mermaid caption="Review flow"}
graph LR
  A[Draft] --> B{Review}
  B -->|ok| C[Publish]
===
=== diagram {#gv format=graphviz}
digraph { a -> b }
===
=== diagram {#d2 format=d2}
a -> b
===
=== diagram {#pu format=plantuml}
@startuml
a -> b
@enduml
===
=== diagram {#odd format=foo}
anything at all, {{title}} is not interpolated in raw
===
See [[#flow]].
geml check · --to html
warning: no registered renderer for diagram format `foo`; body kept raw (line 21)
0 error(s), 1 warning(s)
figure #flow › pre.mermaid · 页面引入 mermaid 模块脚本后渲染成
Draft→Review→ ok →Publish
figure #gv › pre.diagram-src data-format="graphviz" · 已注册,无内置引擎,原文显示
digraph { a -> b }
figure #odd › 未知 format,原文保留
anything at all, {{title}} is not interpolated in raw

See Review flow.

规则
§7:format 选渲染器,正文 raw 原样交过去,处理器必须暴露注册表、不得解释正文。未知 format 只是 warning,因为正文没有丢,只是没有人画它。
两种「不画」
实测里 graphviz、d2、plantuml 在注册表里,所以不 warning,但参考实现没有内置引擎,输出的是带 data-format 的原文;宿主可以自己接引擎。vega-lite 虽然 §7.1 拿它举例,注册表里却没有,会 warning。这是「注册表开放」的正常状态,不是偏差。
raw
{{title}} 在 raw 体里不插值(§4),原样进了输出;%% 也一样。
chart

geml-chart:图全在属性里,所以能校验

四个成功的例子:绑一个派生出计算列的 view、绑一个记录数组、绑一个 csv 文件画两条线、把汇总行当额外一点。

四种数据源,一种描述方式 规范已定
GEML · d2-chart.geml 节选
=== table {#fy format=csv}
Segment,Q1,Q2
Cloud,124.5,131.2
Hardware,88.1,84.6
===
=== view {#fy-report src=#fy compute="FY = Q1 + Q2" summary="Segment = 'Total'; FY = sum(FY)"}
===                                    %% 表装事实;view 派生出 FY 与 Total 行
=== data {#recs}
[{"seg": "Cloud", "fy": 255.7}, {"seg": "Hardware", "fy": 172.7}]
===

=== diagram {#ok format=geml-chart data=#fy-report type=bar x=Segment y=FY caption="FY revenue"}
===                                    %% FY 只存在于 view 上,所以绑 view,不是绑 #fy
=== diagram {#ok2 format=geml-chart data=#recs type=pie x=seg y=fy}
===                                    %% 记录数组:键投影成列
=== diagram {#ok3 format=geml-chart data=rows.csv type=line x=Segment y="Q1, Q2"}
===                                    %% 文件;逗号列表 = 两个系列
=== diagram {#sumrow format=geml-chart data=#fy-report type=bar x=Segment y=FY rows=all}
===                                    %% 汇总行作为额外一点
--to html · #ok无诊断
svg.geml-chart role="img" · viewBox 0 0 760 380 · rect + text.c-tick
3001500
255.7
172.7
CloudHardware
内联 SVG,不加载外部脚本。#ok2 是饼图,#ok3 两条线,#sumrow 多一根 Total 柱。四个块的正文都为空。
为什么能校验
§7.1:format 仍只选渲染器,但图的全部描述在属性里,处理器读得懂,所以列名、data 的 id、rows 都能在构建时对着表校验。正文留空;写了东西是 ignored-diagram-body warning。
数据源
三种目标与 table 的 src= 同形。table 只贡献自己的列,它上面的 view 才把派生列一并贡献出来——所以 y=FY 必须指 #fy-report 而不是 #fy;把图绑到基表上是构建错误(chart: column `FY` not found in table)。data 块必须是非空的 map 序列,被引用的列每条记录都得是标量;没引用的列随便。文件是匿名表或匿名记录源。
type
五个值只改通道怎么画,从不增加新属性。要更多形状就换宿主 DSL,比如 format=vega-lite data=#fy,正文 raw、不校验列。
校验

十五条校验,一次跑完

十一个 error、四个 warning,与 A.4 逐条对上。

d2-chart.geml · d3-chart.geml 规范已定
GEML
=== diagram {#e1 format=geml-chart type=bar x=Segment y=FY}          ← 没有 data
=== diagram {#e2 format=geml-chart data=#n …}                    ← #n 是 note
=== diagram {#e3 format=geml-chart data=#badrecs x=seg y=fy}     ← 第 2 条记录缺 fy
=== diagram {#e4 format=geml-chart data=#fy-report x=Segment y=FY}  ← 没有 type
=== diagram {#e5 … type=donut …}
=== diagram {#e6 … type=bar x=Segment}                          ← 缺 y
=== diagram {#e7 … y=Nope}
=== diagram {#w1 … type=bar x=Segment y=FY size=Q1}             %% bar 不画 size
=== diagram {#e8 … rows=everything}
=== diagram {#w2 … type=bar x=Segment y=FY}
this body is ignored
===
=== diagram {#e9 … y=Segment}                                ← y 列不是数

%% d3:#plain 是个有 compute= 但没有 summary= 的 view
=== diagram {#s1 … data=#plain rows=summary}
=== diagram {#s2 … data=#plain rows=all}
=== diagram {#cg format=geml-code-graph}                         %% 没有 src
=== diagram {#vl format=vega-lite data=#plain}
{"mark": "bar", "encoding": {"y": {"field": "Nope"}}}    %% 不校验列
===
geml check11 error 4 warning
warning: geml-chart body is ignored; the chart spec lives in attributes (line 45)
error: geml-chart: missing `data=#id` (line 27)
error: geml-chart: data target `#n` is not a table (line 29)
error: geml-chart: column `fy` is missing or non-scalar in record 2 (line 31)
error: chart: missing `type` (line 33)
error: chart: unknown type `donut` (supported: bar, line, area, pie,
  scatter; use format=vega-lite for others) (line 35)
error: chart: missing required channel `y` (line 37)
error: chart: column `Nope` not found in table (line 39)
warning: chart: `size` is ignored for type `bar` (line 41)
error: chart: unknown rows scope `everything` (data|all|summary) (line 43)
error: chart: non-numeric value in a y column (line 48)

d3-chart.geml
warning: geml-code-graph: missing `src=` (nothing to render) (line 12)
warning: no registered renderer for diagram format `vega-lite`; body kept raw (line 14)
error: chart: rows=summary but the table has no summary row (line 8)
warning: chart: rows=all but the table has no summary row; using data rows (line 10)
error
图描述本身说不通:没数据、数据不是表、记录缺列、没 type、type 不在五个里、缺必填通道、列不存在、rows 不在三个里、y 列不是数、要汇总行却没有。这些在构建时就能判定,所以是 error。
warning
能画但有多余或有缺:type 用不上的通道、rows=all 却没汇总行(就画数据行)、正文写了东西(忽略)。code-graph 缺 src 也是 warning,画不出东西但文档没错。
vega-lite
正文里的 Nope 列没有被检查,符合 §7.1「宿主 DSL 的正文 raw 且不做列校验」;那条 warning 是注册表里没有它的渲染器,不是列错。
code-graph

geml-code-graph:把一份 codemap 画成方法流图

GEP-0003 已接受。它和 geml-chart 一样是被解释的格式,但走到了另一个极端:嵌入点只有 src=,别的什么都不能写。

指向 playground 里的真实 codemap 规范已定 渲染实测探针放在 playground/codemap/ 里跑,跑完删除;那里有 geml codemap build 生成的 index.geml 和每个容器一份的文档。
GEML · _probe-cg.geml
=== diagram {#g format=geml-code-graph src=index.geml caption="Repo graph"}
===
=== diagram {#g2 format=geml-code-graph src=geml-parser--chart.ts.geml#chart}
===                                    %% 带片段:不是它认的目标
=== diagram {#g3 format=geml-code-graph src=nowhere.geml}
===
=== diagram {#g4 format=geml-code-graph src=index.geml}
body here is ignored
===
geml check --root playground/codemap · --to html
warning: geml-code-graph: cannot resolve document
  `geml-parser--chart.ts.geml#chart` (line 7)
warning: geml-code-graph: cannot resolve document `nowhere.geml` (line 9)
warning: geml-code-graph body is ignored; the embed is configured by
  `src=` alone (line 11)
0 error(s), 3 warning(s)   exit 0

<figure class="code-graph" id="g"><div class="cg-mount" data-start="…" data-graph="{…}">
<figure class="code-graph" id="g2"><p class="render-error">…
<figure class="code-graph" id="g3"><p class="render-error">…
figure.code-graph #g › div.cg-mount · 图数据内联 JSON,布局绘制时算
index.geml · entry→container→method⋯ 点击下钻
根和深度来自 index.geml 的 meta,嵌入点没写、也不能写。
只有 src
GEP-0003:视图配置随数据走。根是 codemap 文档 meta 的 entry,深度是 graph-depth,嵌入点写不了这些,所以嵌入点不可能和数据漂移。想换根就指向另一个容器文档;想看方法级细节就点节点,那是交互不是属性。
与 chart 对比
两个被解释的格式,两种哲学:geml-chart 把描述全放在属性里,让处理器能校验每个列名;geml-code-graph 把描述全放在数据那边,让嵌入点只剩一个指针。共同点是正文都为空。
轻重
缺 src、解析不到都是 warning,和 code 块的路由一样:画不出东西,但文档描述的对象存在于某处,文档本身没错。
依据

探针文件与实测输出

全部用仓库内当前构建 node geml-parser/dist/geml.js 跑出。

探针覆盖check 结果页面里对应
d1-diagram.gemlmermaid、graphviz、d2、plantuml、未知 foo、raw 不插值、caption 自动文字1 warning外部 DSL
d2-chart.geml · rows.csv四个成功例;缺 data、非表、非记录数组、缺 type、未知 type、缺通道、未知列、未用通道、未知 rows、正文非空、y 非数9 error 2 warninggeml-chart、校验
d3-chart.gemlrows=summary 无汇总行、rows=all 无汇总行、code-graph 缺 src、vega-lite 正文不校验1 error 3 warning校验
playground/codemap/_probe-cg.geml指向真实 codemap 的 index、带片段的目标、不存在的文档、非空正文3 warninggeml-code-graph