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

GEML 块图解 · embed

embed 站在别处内容的位置上:src= 命名另一份文档(或本文档)里的一节、一块、一段散文,渲染时那份内容出现在这里。它没有自己的正文,有的是一套关于「取什么、怎么取、什么不能取」的规则:part= 切标题的哪部分,被嵌文档必须作为独立文档解析,目标必须是 .geml,scheme 受限,循环报错。行内还有一个小兄弟 ![[#id]]。这一页把这些全部实测一遍,并画出 GEP-0010 用 embed 做翻译投影、GEP-0011 让叶值坐标做投影目标的两个草案。

看板

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

规范已定 规范正文写死的。GEP 草案 提案定义,规范未收。实测 规范不规定、参考实现今天这样做。实现偏差 本页未发现。

规范已定 9 GEP 草案 1 实测 1
规则出处状态
1src= 命名内容:一份文档,可带片段。缺 src 是 embed-missing-src error;正文被忽略并 ignored-embed-body warning。src= 和任何引用一样受检:片段不存在是 unresolved-reference error。§3 · §5 · A.2规范已定
2片段命名标题 → 整节:标题本身及其后直到下一个同级或更高级标题前的所有块。命名块 → 那一块。命名散文地址(#budget-before-detail)→ 那段散文。无片段 → 整份文档。§3 · §4规范已定
3part= 只对标题目标有意义:head 标题行本身,body 标题下全部,intro 到第一个子标题前,whole 默认。head 与 body 划分 whole。与 geml get --head/--body/--intro 是同一套词。目标不是标题时全体照用;未知值 bad-embed-part warning,全体照用,因为悄悄选空是 §8.2 要防的失败。§3 · A.3规范已定
4被嵌文档作为独立文档解析后再选目标,不是拼接文本。它的 meta、引用、相对路径的基、外部数据都相对它自己解析;链条每一层都是下一层的基。§3规范已定
5目标必须是 .geml:别的文件是 embed-target-not-geml error,字节永不按 GEML 解析。scheme 只许 http、https、mailto、tel,其它在建模时拒绝并清空属性(unsafe-embed-scheme error),检查时忽略 U+0000–U+0020。跨文档解析限于 --root,解析符号链接后再判,fail closed。§3 · §9.4 · §9.5 · A.2规范已定
6嵌套链回到正在展开的文档是 transclusion-cycle error,报出链条,不跟进。§9.3 · A.2规范已定
7行内投影 ![[#id]] / ![[doc.geml#id]]:目标必须是单段 text,把它的行内内容插进句子;![alt](doc.geml) 用媒体语法嵌文档是 media-target-is-document error。! 是投影前缀,没有 ! 的 […] 是导航。§5.1 · §5.2 · B.4规范已定
8渲染:块级展开成 section.transclusion id data-src,内容原样;行内投影是 span.transclusion-inline data-src。--to html实测
9坐标:embed 在本文档模型里是空体 raw 块,内部没有可寻址单元,#embedB[1]["col"] 不解析,读写都不行,报错会给出 embed 源上的同一个坐标;反向允许两种:叶值(![[a.geml#vars["version"]]] 得到 1.4)和一整行——行内时各格用 ", " 连成一行,作为 embed 时是一张带表头的单行表;一整列不行。§5.2 · GEP-0011规范已定
10翻译投影:translate-to= 由 geml-translator/v1 profile 放行,在 meta 上是文档默认,在 embed 上覆盖,none 保留一块不译;translator= 保留给第二个引擎出现之后。一份译文只含 embed,id、顺序、非散文字节只有一个家。无 profile 时是 unknown-attribute warning。GEP-0010GEP 草案
11外部数据和媒体 src 由渲染器在渲染时取,渲染器把它们当不可信输入,可限定同源、要求 opt-in。§9.4规范已定
part=

嵌一节,或者一节的一部分

同一个标题目标,五种取法。左边 a.geml 是源,b.geml 是嵌的一方。

whole · head · intro · body · 未知 part 规范已定
GEML · a.geml(源)
=== meta
title = "Source A"
version = "1.4"
===
# Budget {#budget}
Lead-in paragraph of the budget section.
## Detail {#detail}
Detail paragraph.
=== table {#tbl format=csv}
Item,Cost
Hosting,120
===
=== text {#para}
One paragraph of prose to project inline.
===
=== data {#vars}
{"version": "1.4", "owner": "docs"}
===
GEML · b.geml(嵌)
=== embed {#whole src=a.geml#budget}
===
=== embed {#head src=a.geml#budget part=head}
===
=== embed {#intro src=a.geml#budget part=intro}
===
=== embed {#body src=a.geml#budget part=body}
===
=== embed {#odd src=a.geml#budget part=sideways}
===
--to html --root .
warning: embed: `part=sideways` is not `whole`, `head`, `body` or
  `intro`; the whole target stands (line 13)
section.transclusion #whole data-src="a.geml#budget"

Budget

Lead-in paragraph of the budget section.

Detail

Detail paragraph.

+ 表 #tbl、text #para、data #vars,整节到文档尾
#head · part=head

Budget

#intro · part=intro

Lead-in paragraph of the budget section.

#body · part=body

Lead-in paragraph of the budget section.

Detail

Detail paragraph.

+ 其后所有块,没有标题行
#odd · part=sideways → 与 whole 相同

Budget

…
一节
§3:片段命名标题就选整节,从标题到下一个同级或更高级标题之前。#budget 是 H1,后面没有别的 H1,所以整节到文档尾,连 #tbl、#para、#vars 都在里面。
part=
用 embed 拼出来的文档不该手写自己的标题,手写就是它要消灭的漂移,所以要能只取 body 或只取 head。三个词和 geml get --head/--body/--intro 完全一样,一个地址和一个命令行选择器不各发明一套词。
未知值
不是 unknown-attribute,因为键是定义了的,只是值不对;全体照用而不是选空,因为悄悄选空是 §8.2 要防的失败。
基

被嵌文档是独立文档,散文和整篇都能嵌

相对路径、meta、外部数据都相对源文档解析。嵌的一方只决定内容出现在哪。

散文地址、整份文档、本文档内的块 规范已定
GEML · b2.geml 与 b.geml 节选
=== embed {#lead src=a.geml#budget-before-detail}   %% §4 派生的散文地址
===
=== embed {#all src=a.geml}                        %% 无片段:整份文档
===
=== embed {#local src=#b-local}                     %% 本文档的块
===
=== text {#b-local}
Local text embedded above.
===
geml check · --to htmlok: no diagnostics
<section class="transclusion" id="lead" data-src="a.geml#budget-before-detail">
  <p>Lead-in paragraph of the budget section.
<section class="transclusion" id="all" data-src="a.geml">
  <h1>Budget … <h2>Detail … 表、text、data 全部
a.geml 里的什么相对谁解析
=== table {src=rows.csv}a.geml 的目录,不是 b.geml 的
{{version}}a.geml 的 meta,得 1.4
[[#detail]]a.geml 的 id 空间
链条 a → b → c每一层是下一层的基
规则
§3:src= 命名的文档必须作为独立文档解析,再从结果里选目标,绝不是把一段文本拼进来。所以 a.geml 里的 rows.csv 相对 a.geml 的目录解析,和单独打开 a.geml 时一样。嵌的一方只决定结果出现在哪。
散文
#budget-before-detail 是 §4 给两块之间散文派生的地址:容器 #budget、下一块 #detail、前面没有块。它能被嵌,说明用 embed 拼一份文档时散文不会丢。
失败

六种失败,加一个循环

五个 error 两个 warning,与 A.2 逐条对上。

b.geml 后半 · c1.geml ↔ c2.geml 规范已定
GEML
=== embed {#nosrc}                              ← 没有 src
===
=== embed {#withbody src=a.geml#tbl}
this body is ignored
===
=== embed {#md src=notes.md}                  ← 不是 .geml
===
=== embed {#js src=javascript:alert(1)}
===
=== embed {#gone src=a.geml#nope}              ← 片段不存在
===
Inline: … and media ![pic](a.geml).               ← 用媒体语法嵌文档

%% c1.geml 嵌 c2.geml#c2,c2.geml 嵌 c1.geml#c1
geml check --root .5 error 2 warning
error: embed: missing `src=` (line 22)
warning: embed body is ignored; the target lives in `src=` (line 24)
error: embed: `notes.md` is not a GEML document; `src=` names a `.geml`
  file (optionally with a #fragment) (line 27)
error: embed: `src=javascript:alert(1)` names a disallowed URL scheme (line 29)
error: unresolved reference `a.geml#nope` (line 31)
error: `![](a.geml)` projects a GEML document, which is not media: for
  block content use `=== embed {src=a.geml}`, for a phrase use
  `![[a.geml]]` (line 33)

c1.geml
error: transclusion cycle: c1.geml → c2.geml → c1.geml → c2.geml (line 5)
error
缺 src 命名不了任何内容;非 .geml 的字节绝不按 GEML 解析;非法 scheme 在建模时就清掉,任何下游都拿不到它;片段不存在和普通引用一样是错;媒体语法嵌文档是位置错了;循环报出整条链并停止。
warning
正文写了东西只是被忽略,内容住在 src 里;未知 part 见上一节。
围栏
§9.4:解析限于 --root,符号链接先解析再判是否越界,建立不了围栏就什么都不解析(unresolvable-document)。探针都带 --root .。
行内

行内投影、叶值坐标、坐标穿不过 embed

![[…]] 把一段话或一个值放进句子里。GEP-0011 划了两条线:什么能当目标,坐标能不能穿过 embed。

单段 text、叶值、穿透 规范已定
GEML · b.geml 末尾
Inline: ![[a.geml#para]] and a leaf value ![[a.geml#vars["version"]]].
Row through embed: [[#tbl]] but not #tbl[1]["Item"].
geml get
$ geml get b.geml '#tbl[1]["Item"]'            #tbl 是 b 里的 embed
error: `#tbl[1]["Item"]`: `embed` carries no addressable units inside
  it — a coordinate needs a table, a `data` block, or `meta` (GEP 0011);
  address it on the embed's source instead: `a.geml#tbl[1]["Item"]`
$ geml get --root . a.geml '#tbl[1]["Item"]'   到源文档上读
Hosting
--to html --root .

Inline: One paragraph of prose to project inline. and a leaf value 1.4.

第一个是 span.transclusion-inline data-src="a.geml#para";第二个是 data 块 #vars 里 ["version"] 这一个叶值。
能当目标
§5.2:行内投影的目标是恰好一段的 text 块,或者命名叶值、或 table/view 一整行的坐标(#tickets[2]、#tickets[summary])——行内时一行是各格用 ", " 连成的一行,作为 embed 时是一张带表头的单行表。一整列、或仍含下级节点的值树节点不行:它有多少成员就是多少个值。行号和格子一样是按位置取的;「哪一行」其实是谓词时,从用 where= 选出它的 view 投影。
穿不过
embed 在本文档模型里是空体 raw 块,目标渲染时才解析,所以 #tbl[1]["Item"] 在 b.geml 里不解析。写穿过去会从这份文档改另一份,破坏 §6 的单一来源;读穿过去会让一个值依赖两个会动的东西。报错会给出真正能解析的地址,即 embed 源上的同一个坐标:a.geml#tbl[1]["Item"]。
翻译

一份译文只含 embed

GEP-0010:翻译是沿语言轴的投影,和沿格式轴的 .md 投影同一个形状。profile 已注册,规范未收。

translate-to=:meta 上是默认,embed 上覆盖,none 保留 GEP 草案
GEML · tr.geml
=== meta
title = "translated"
profile = "geml-translator/v1"
translate-to = "zh"                      %% 文档默认
===
=== embed {src=a.geml#budget}               %% 继承 zh
===
=== embed {src=a.geml#tbl translate-to=none}  %% 这一块不译
===
GEML · tr-noprofile.geml
=== embed {src=a.geml#budget translate-to=zh}   %% 没有 profile 声明
===
geml check --root .
tr.geml
ok: no diagnostics

tr-noprofile.geml
warning: unknown attribute `translate-to` for block type `embed` (line 4)
形状
译文文档里只有 embed,别的什么都没有。id、顺序、每一个非散文字节只有一个家,所以漂移不是被检测出来,而是不可能发生。这与 GEP-0006 让 A.geml 投影到 A.md 的关系相同,只是换到语言轴。
一个键
translate-to 在 meta 上是默认、在 embed 上覆盖、none 保留一块。不叫 lang,因为 code {lang=} 说的是正文已经是什么语言,这里说的是要做什么,两个值空间两种词性不能共用一个键。translator= 保留:只有一个引擎时选引擎的键会「解析、无事发生、看着像支持」。
放行
§8.6:profile 只放行属性键的名字,文档模型不变。声明了 profile 且处理器认识它,键就不再报 unknown-attribute;处理器怎么拿到翻译器是实现定义的,拿不到也合规。
依据

探针文件与实测输出

全部用仓库内当前构建 node geml-parser/dist/geml.js 跑出,带 --root .。探针在会话临时目录的 emb/ 下。

探针覆盖check 结果页面里对应
a.geml · b.geml · notes.md整节、head、intro、body、未知 part、块目标、本文档目标、缺 src、正文、非 geml、非法 scheme、片段不存在、媒体嵌文档、行内投影、叶值投影5 error 2 warningpart=、失败、行内
b2.geml散文地址目标、整份文档目标0 诊断独立解析
c1.geml · c2.geml两文档互嵌1 error transclusion-cycle失败
tr.geml · tr-noprofile.gemltranslate-to 有无 profile0 诊断 / 1 warning翻译
get b.geml '#tbl[1]["Item"]'坐标穿 embederror,给出 a.geml#tbl[1]["Item"]行内