embed 站在别处内容的位置上:src= 命名另一份文档(或本文档)里的一节、一块、一段散文,渲染时那份内容出现在这里。它没有自己的正文,有的是一套关于「取什么、怎么取、什么不能取」的规则:part= 切标题的哪部分,被嵌文档必须作为独立文档解析,目标必须是 .geml,scheme 受限,循环报错。行内还有一个小兄弟 ![[#id]]。这一页把这些全部实测一遍,并画出 GEP-0010 用 embed 做翻译投影、GEP-0011 让叶值坐标做投影目标的两个草案。
规范已定 规范正文写死的。GEP 草案 提案定义,规范未收。实测 规范不规定、参考实现今天这样做。实现偏差 本页未发现。
| 规则 | 出处 | 状态 | |
|---|---|---|---|
| 1 | src= 命名内容:一份文档,可带片段。缺 src 是 embed-missing-src error;正文被忽略并 ignored-embed-body warning。src= 和任何引用一样受检:片段不存在是 unresolved-reference error。 | §3 · §5 · A.2 | 规范已定 |
| 2 | 片段命名标题 → 整节:标题本身及其后直到下一个同级或更高级标题前的所有块。命名块 → 那一块。命名散文地址(#budget-before-detail)→ 那段散文。无片段 → 整份文档。 | §3 · §4 | 规范已定 |
| 3 | part= 只对标题目标有意义: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,把它的行内内容插进句子; 用媒体语法嵌文档是 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-0010 | GEP 草案 |
| 11 | 外部数据和媒体 src 由渲染器在渲染时取,渲染器把它们当不可信输入,可限定同源、要求 opt-in。 | §9.4 | 规范已定 |
同一个标题目标,五种取法。左边 a.geml 是源,b.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"} ===
=== 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} ===
warning: embed: `part=sideways` is not `whole`, `head`, `body` or
`intro`; the whole target stands (line 13)
Lead-in paragraph of the budget section.
Detail paragraph.
+ 表 #tbl、text #para、data #vars,整节到文档尾Lead-in paragraph of the budget section.
Lead-in paragraph of the budget section.
Detail paragraph.
+ 其后所有块,没有标题行#budget 是 H1,后面没有别的 H1,所以整节到文档尾,连 #tbl、#para、#vars 都在里面。geml get --head/--body/--intro 完全一样,一个地址和一个命令行选择器不各发明一套词。unknown-attribute,因为键是定义了的,只是值不对;全体照用而不是选空,因为悄悄选空是 §8.2 要防的失败。相对路径、meta、外部数据都相对源文档解析。嵌的一方只决定内容出现在哪。
=== 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. ===
<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 | 每一层是下一层的基 |
src= 命名的文档必须作为独立文档解析,再从结果里选目标,绝不是把一段文本拼进来。所以 a.geml 里的 rows.csv 相对 a.geml 的目录解析,和单独打开 a.geml 时一样。嵌的一方只决定结果出现在哪。#budget-before-detail 是 §4 给两块之间散文派生的地址:容器 #budget、下一块 #detail、前面没有块。它能被嵌,说明用 embed 拼一份文档时散文不会丢。五个 error 两个 warning,与 A.2 逐条对上。
=== 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 . ← 用媒体语法嵌文档 %% c1.geml 嵌 c2.geml#c2,c2.geml 嵌 c1.geml#c1
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: `` 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)
--root,符号链接先解析再判是否越界,建立不了围栏就什么都不解析(unresolvable-document)。探针都带 --root .。![[…]] 把一段话或一个值放进句子里。GEP-0011 划了两条线:什么能当目标,坐标能不能穿过 embed。
Inline: ![[a.geml#para]] and a leaf value ![[a.geml#vars["version"]]]. Row through embed: [[#tbl]] but not #tbl[1]["Item"].
$ 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
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"] 这一个叶值。
#tickets[2]、#tickets[summary])——行内时一行是各格用 ", " 连成的一行,作为 embed 时是一张带表头的单行表。一整列、或仍含下级节点的值树节点不行:它有多少成员就是多少个值。行号和格子一样是按位置取的;「哪一行」其实是谓词时,从用 where= 选出它的 view 投影。#tbl[1]["Item"] 在 b.geml 里不解析。写穿过去会从这份文档改另一份,破坏 §6 的单一来源;读穿过去会让一个值依赖两个会动的东西。报错会给出真正能解析的地址,即 embed 源上的同一个坐标:a.geml#tbl[1]["Item"]。GEP-0010:翻译是沿语言轴的投影,和沿格式轴的 .md 投影同一个形状。profile 已注册,规范未收。
translate-to=:meta 上是默认,embed 上覆盖,none 保留 GEP 草案=== meta title = "translated" profile = "geml-translator/v1" translate-to = "zh" %% 文档默认 === === embed {src=a.geml#budget} %% 继承 zh === === embed {src=a.geml#tbl translate-to=none} %% 这一块不译 ===
=== embed {src=a.geml#budget translate-to=zh} %% 没有 profile 声明 ===
tr.geml ok: no diagnostics tr-noprofile.geml warning: unknown attribute `translate-to` for block type `embed` (line 4)
A.geml 投影到 A.md 的关系相同,只是换到语言轴。translate-to 在 meta 上是默认、在 embed 上覆盖、none 保留一块。不叫 lang,因为 code {lang=} 说的是正文已经是什么语言,这里说的是要做什么,两个值空间两种词性不能共用一个键。translator= 保留:只有一个引擎时选引擎的键会「解析、无事发生、看着像支持」。全部用仓库内当前构建 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 warning | part=、失败、行内 |
| b2.geml | 散文地址目标、整份文档目标 | 0 诊断 | 独立解析 |
| c1.geml · c2.geml | 两文档互嵌 | 1 error transclusion-cycle | 失败 |
| tr.geml · tr-noprofile.geml | translate-to 有无 profile | 0 诊断 / 1 warning | 翻译 |
| get b.geml '#tbl[1]["Item"]' | 坐标穿 embed | error,给出 a.geml#tbl[1]["Item"] | 行内 |