///|
/// compaction 序列化器(J6——spec §8.41/§8.42):expand 逆面,逐值递归、无
/// 节点字典面(F2 禁复用——§8.41 钉一)。落地面 = 精确 term / 最长前缀
/// compact IRI / base 相对化(段界对齐)/ datatype+language 吸收 / 裸值缺省
/// 塌缩 / @id-term 串塌缩 / @list 吸收 + @set 保形 + 数组塌缩 / 值去重 /
/// @reverse 正向提升(reverse term 匹配)/ @nest 重嵌 / @graph·@index 面 /
/// 顶层形三钉(0 → {} / 1 → 单对象 / n → @graph 包裹)/ @context 原文发射。
/// 未实现面(deferred 在册):scoped context 压缩面(type-scoped/property-
/// scoped term 选择——#tc001/#tc013 族)、容器逆形分组(@index/@id/@type/
/// @language/@graph 容器——#tm001/#tla01 族)、@nest 链式/别名细化。
///|
/// 惰性索引取用(役P1 批3——性能内政;每 terms-map 身份至多建一次,钉②)。
/// 构建谓词与扫描版逐字同源(钉①候选集等价);末段排序不动(名唯一 ⇒
/// 排序键全序 ⇒ 集合等价 ⇒ 序列等价)。compact_by_iri = Some 即整组已建。
fn ensure_compact_index(
active : ActiveContext,
) -> (
Map[String, Array[String]],
Array[(String, String)],
Array[(String, String)],
) {
match active.compact_by_iri {
Some(by_iri) =>
(
by_iri,
active.compact_reverse.unwrap_or([]),
active.compact_prefixes.unwrap_or([]),
)
None => {
let by_iri : Map[String, Array[String]] = Map([])
let by_reverse : Array[(String, String)] = []
let prefixes : Array[(String, String)] = []
for pair in active.terms {
let name = pair.0
let td = pair.1
match td.reverse {
Some(riri) => by_reverse.push((riri, name))
None => ()
}
match td.iri {
Some(pfx) => {
// 正向精确候选(:33 同源:iri 命中且非反转承载)
if td.reverse is None {
match by_iri.get(pfx) {
Some(names) => names.push(name)
None => by_iri[pfx] = [name]
}
}
// 前缀资格**静态半**(compact_iri_word 资格闸同源;动态半 pfx
// 长度/has_prefix 留查询侧)
if name.contains("/") is false &&
name.contains("@") is false &&
(
td.is_prefix is Some(true) ||
(
td.is_prefix is None &&
td.simple_form &&
(match pfx.get_char(pfx.length() - 1) {
Some(':')
| Some('/')
| Some('?')
| Some('#')
| Some('[')
| Some(']')
| Some('@') => true
_ => false
})
)
) {
prefixes.push((pfx, name))
}
}
None => ()
}
}
active.compact_by_iri = Some(by_iri)
active.compact_reverse = Some(by_reverse)
active.compact_prefixes = Some(prefixes)
(by_iri, by_reverse, prefixes)
}
}
}
///|
/// term 名确定性序(同 IRI 多候选——Map 迭代序不定,选取须可复现):
/// 容器缺省优先 → 名短优先 → 字典序。`want_reverse` 分选:正向位匹配 td.iri,
/// 反转位匹配 td.reverse(@reverse 正向提升——#t0033/#t0050 oracle)
fn compact_term_candidates(
pred : String,
want_reverse : Bool,
active : ActiveContext,
) -> Array[String] {
let (by_iri, by_reverse, _) = ensure_compact_index(active)
let names : Array[String] = []
if want_reverse {
// 反转位:td.reverse 匹配(@reverse term 的 iri 同载反转 IRI——
// 不以 iri 有无分流;#t0033/#t0050 oracle)
for entry in by_reverse {
if entry.0 == pred {
names.push(entry.1)
}
}
} else {
// 正向位:iri 匹配且非 reverse-bearing(反转 term 不作正向键——语义翻转)
match by_iri.get(pred) {
Some(hits) =>
for name in hits {
names.push(name)
}
None => ()
}
}
names.sort_by(fn(a, b) {
let ca = match active.terms.get(a) {
Some(td) => if td.container is None { 0 } else { 1 }
None => 2
}
let cb = match active.terms.get(b) {
Some(td) => if td.container is None { 0 } else { 1 }
None => 2
}
if ca != cb {
return ca - cb
}
let la = a.length()
let lb = b.length()
if la != lb {
return la - lb
}
a.compare(b)
})
names
}
///|
/// IRI 压缩(REC §6.3 首波子集):精确 term → 最长前缀 compact IRI(term 名
/// 不含 "/"、is_prefix 未显式抑制、@keyword 别名跳过)→ base 相对化
/// (compactToRelative 门 + 段界对齐)→ 原样
fn compact_iri_word(
iri : String,
allow_exact : Bool,
allow_vocab : Bool,
allow_base : Bool,
active : ActiveContext,
options : JsonLdOptions,
) -> String {
// 精确 term(容器缺省优先;allow_exact=false = 失配兜底位——term 不可用,
// #t0006 oracle:datatype 失配 term 不作键,落前缀词)
if allow_exact {
let exact = compact_term_candidates(iri, false, active)
if exact.length() > 0 {
return exact[0]
}
}
// @vocab 相对词形(#t0021 oracle:@vocab "http://example.com/subdir/" +
// "…/subdir/vocab/embed" → "vocab/embed"——仅谓词/类型位;@id 值位禁,
// allow_vocab=false)
if allow_vocab {
match active.vocab {
Some(vocab) =>
if vocab.length() < iri.length() && iri.has_prefix(vocab) {
// 撞词守门(#t0043 oracle——suffix 为在册 term 时重展开撞词,
// 跳过 vocab 步保全 IRI)。精化(役41/#tc027 兜底位):suffix
// term 的 IRI **同于**被压 IRI 时非撞(即 exact term——键位词形
// 照用,#tc027 "baz");异于才跳(#t0043 本案)
let suffix = iri[vocab.length():].to_owned()
let collide = match active.terms.get(suffix) {
Some(td) => td.iri != Some(iri)
None => false
}
if !collide {
return suffix
}
}
None => ()
}
}
// 最长前缀 compact IRI(役P1 批3:经倒排索引 prefixes 微清单——静态资格半
// 预计算于 ensure_compact_index(逐字段同源),查询侧 = 动态半(长度/
// has_prefix)+ 最长择优,谓词同源)
let (_, _, prefixes) = ensure_compact_index(active)
let mut best_prefix : String? = None
let mut best_name : String? = None
for entry in prefixes {
let name = entry.1
match entry.0 {
pfx =>
if pfx.length() < iri.length() && iri.has_prefix(pfx) {
match best_prefix {
Some(bp) =>
if pfx.length() > bp.length() {
best_prefix = Some(pfx)
best_name = Some(name)
}
None => {
best_prefix = Some(pfx)
best_name = Some(name)
}
}
}
}
}
match (best_name, best_prefix) {
(Some(name), Some(pfx)) => {
let suffix = iri[pfx.length():].to_owned()
// 撞词守门(役41/#t0007 oracle——前缀词为在册 term 名时重展开歧义,
// 跳前缀步保全 IRI;#t0043 vocab 步同族判据)。被拒 exact term 不得
// 经前缀复得键位
let word = name + ":" + suffix
if !active.terms.contains(word) {
return word
}
}
_ => ()
}
// base 相对化(compactToRelative 门——×1 件保绝对词面;段界对齐:base 尾
// 无 "/" 补一段再对前缀,防 "http://example.com" vs "…/com/foo" 误剥——
// #tr001 oracle:option base "http://example.org/" → "@id":"a")
if options.compact_to_relative && allow_base {
match active.base {
Some(base) => {
// 片段相对优先(#tc015 oracle——base 无尾 '/' 且 iri = base + "#…"
// ⇒ "#…" 形,先于目录位判定)
if iri.has_prefix(base) {
let rest = iri[base.length():].to_owned()
if rest.has_prefix("#") {
return rest
}
// query 相对(役42/#t0066——iri = doc URL + "?…" ⇒ "?…",与
// fragment 规则对称)
if rest.has_prefix("?") {
return rest
}
}
// 边界 = base 目录位:尾 '/' 即用;否则剥最后一段取目录
// (#t0076/#t0066 oracle——@base 为文档 URL 时 @id 相对化到同目录)
let boundary = match base.get_char(base.length() - 1) {
Some('/') => base
_ => {
let mut cut = 0
for i in 0.. iri[at + 2:].find("/") is Some(_)
None => false
}
let scheme_ok = match (colon_index(base), colon_index(iri)) {
(Some(b_at), Some(i_at)) =>
base[:b_at].to_owned() == iri[:i_at].to_owned()
_ => false
}
// 值位 id 词形限定(allow_vocab=false——@id 值渲染统一旗);
// property 兜底键(fallback_vocab=true)走 vocab/相对形,不上跳
//(#t0095 oracle——vocab "" 相对键形不被 ../ 覆写)
if iri_has_path && scheme_ok && !allow_vocab {
let bp = match base.find("//") {
Some(at) => {
let rest = base[at + 2:].to_owned()
match rest.find("/") {
Some(slash) => rest[slash:].to_owned()
None => "/"
}
}
None => base
}
let ip = match iri.find("//") {
Some(at) => {
let rest = iri[at + 2:].to_owned()
match rest.find("/") {
Some(slash) => rest[slash:].to_owned()
None => "/" + rest
}
}
None => iri
}
let authority_ok = match (base.find("//"), iri.find("//")) {
(Some(b_at), Some(i_at)) => {
let b_rest = base[b_at + 2:].to_owned()
let i_rest = iri[i_at + 2:].to_owned()
let b_auth = match b_rest.find("/") {
Some(sl) => b_rest[:sl].to_owned()
None => b_rest
}
let i_auth = match i_rest.find("/") {
Some(sl) => i_rest[:sl].to_owned()
None => i_rest
}
b_auth == i_auth
}
_ => false
}
if authority_ok {
let segs_of = fn(pp : String) -> Array[String] {
let arr : Array[String] = []
let mut cur = ""
for i in 0.. {
arr.push(cur)
cur = ""
}
Some(ch) => cur = cur + ch.to_string()
None => ()
}
}
arr.push(cur)
arr
}
let bd = segs_of(bp)
let it = segs_of(ip)
// bd pop 尾 ""(bp 尾斜杠目录标);it 尾段是真实目标段——
// 不 pop(役42 施工勘误:误 pop 吃掉 parent-node ⇒ "../")
if bd.length() > 0 {
let _ = bd.pop()
}
let mut k = 0
for i in 0.. ()
}
}
iri
}
///|
/// 值 datatype 取形(排名特异性用——Value 取其 datatype;List 取**全项
/// uniform** 的 datatype(役31 typed 值对象收集项——#t0018 term4/term5、
/// #t0024 termTL1/TL2 排名位),混合/含非值项 → None;他形 None)
fn literal_datatype_of(v : ExpandedValue) -> String? {
match v {
ExpandedValue::Value(lit) => lit.datatype
ExpandedValue::List(items, ..) => {
let mut acc : String? = None
let mut mixed = false
for item in items {
let known = match item {
ExpandedValue::Value(_) => true
_ => false
}
let item_dt : String? = match item {
ExpandedValue::Value(lit) => lit.datatype
_ => None
}
if !known {
mixed = true
} else {
match (acc, item_dt) {
(None, dt) => acc = dt
(Some(a), Some(d)) => if a != d { mixed = true }
(Some(_), None) => mixed = true
}
}
}
if mixed {
None
} else {
acc
}
}
_ => None
}
}
///|
/// 标量免注判定(#t0048——number/bool raw 保形体)
fn literal_raw_is_scalar(lit : ExpandedLiteral) -> Bool {
lit.raw is Some(_)
}
///|
/// 语言标签等值(BCP47 大小写不敏感——#tla01 oracle:"eN" ≡ "en";
/// ASCII 宽松折叠——语言子序列恒 ASCII)
fn lang_tag_eq(a : String, b : String) -> Bool {
if a == b {
return true
}
if a.length() != b.length() {
return false
}
for i in 0..
if ascii_fold_lower(x) != ascii_fold_lower(y) {
return false
}
_ => return false
}
}
true
}
///|
/// ASCII 大小写折叠(lang_tag_eq 用——语言子序列恒 ASCII,A-Z ±32)
fn ascii_fold_lower(c : Char) -> Int {
let v = c.to_int()
if v >= 65 && v <= 90 {
v + 32
} else {
v
}
}
///|
/// 值适配判定(两遍分选核心——同 IRI 多 term 按值拆键:#t0006 datatype 失配
/// term 弃用落前缀词、#t0015 五 term 按值归组、#t0002 纯 term 容语言值对象):
/// bare_fit = 该 term 可把值吸成裸形(datatype/language 吸收、@list 容器吸收、
/// @id-term 串塌缩、纯 term 语言态对齐);object_fit = 值对象形在本 term 下
/// 重展开安全(@type/@language 钉死在对象内——#t0002 term2/term3 oracle)
fn compact_bare_fit(
value : ExpandedValue,
td : TermDefinition,
active : ActiveContext,
) -> Bool {
match value {
ExpandedValue::List(items, index~) =>
compact_list_bare_fit(items, index, td, active)
ExpandedValue::Node(child) => {
let id_only = child.types.is_empty() &&
child.properties.is_empty() &&
child.reverse_props.is_empty() &&
child.graph is None &&
child.included is None &&
child.index is None
if !id_only {
return false
}
match td.datatype {
Some("@id") | Some("@vocab") => true
_ => false
}
}
ExpandedValue::Value(lit) => {
// index 闸(§8.54——@index 值对象在无 index_mapping 的 term 下重展开
// 丢 @index = 不安全,两形皆拒——#t0065 oracle)
if lit.index is Some(_) &&
td.index_mapping is None &&
td.container is Some(_) {
return false
}
// @list 容器 term 只整列吸收 List 值(标量入列即多值异质并组——
// #t0018 oracle:v0.2/v0.3/裸标量归 plain term,不落 term2/term3 列)
if td.container is Some(List) {
return false
}
// 方向态对齐总闸(§8.48——方向不合者不入 bare,与语言态同判据:
// 裸形重展开由 term/缺省注回同向才安全——#tdi01/#tdi07 oracle)
let dir_aligned = match td.direction_mapping {
Some(Set(d)) => lit.direction == Some(d)
Some(Null) => lit.direction is None
None =>
match lit.direction {
Some(d) => d == active.default_direction.unwrap_or("")
None => active.default_direction is None
}
}
if !dir_aligned {
return false
}
match td.datatype {
Some(dt) => {
if dt is ("@id" | "@vocab") {
// @id 型 term 对字面量无吸收面(裸串重展开会被强转)——不计 bare
return false
}
match lit.datatype {
Some(ldt) => ldt == dt
None => false
}
}
None =>
// 无 datatype 的 term:language 吸收臂
match td.language_mapping {
Some(Set(lang)) =>
match lit.language {
Some(l) => lang_tag_eq(l, lang)
None => false
}
Some(Null) => lit.language is None
None =>
// 纯 term:语言态对齐 = 值语言 == context 缺省语言(裸形重展开
// 带回缺省语言——失配即不安全);标量免注(#t0048——number/bool
// raw 保形体不受缺省语言注入);typed 非裸串不可裸吸(裸串重
// 展开丢 @type——#tc020 value-type;raw 标量 datatype 重展开
// 自恢复不受此限)
match lit.language {
Some(lang) =>
lang_tag_eq(lang, active.default_language.unwrap_or(""))
None =>
(
active.default_language is None ||
literal_raw_is_scalar(lit)
) &&
(lit.datatype is None || literal_raw_is_scalar(lit))
}
}
}
}
}
}
///|
/// 值适配判定·对象形(值对象形在本 term 下重展开安全):@type/@language 钉死
/// 在值对象内不随 term 强转——#t0002 term2/term3 oracle(纯 term 容
/// datatype/language 值对象)
fn compact_object_fit(
value : ExpandedValue,
td : TermDefinition,
_active : ActiveContext,
) -> Bool {
match value {
// @list 值对象恒安全(重展开保 @list 形);带 @index 者例外
// (#t0041 oracle——term 重展开丢 index)
ExpandedValue::List(_, index~) => !(index is Some(_))
// 节点值:@id 型 term(非裸引用亦安全——对象形重展开仍为节点);无
// datatype 的 term 对象形安全;datatype 版 term 冲突不可用
// (#t0006 term2 oracle:裸引用对象形落回 term 键)
ExpandedValue::Node(_) =>
match td.datatype {
// @none 同权(#ttn01——节点引用对象形安全,无 coercion)
Some(dt) => dt is ("@id" | "@vocab" | "@none")
None => true
}
ExpandedValue::Value(lit) => {
// index 闸(§8.54——同 bare_fit;#t0065 oracle)
if lit.index is Some(_) &&
td.index_mapping is None &&
td.container is Some(_) {
return false
}
// 方向态对齐总闸(§8.48——与 bare_fit 同判据;方向不合者对象形也
// 不入 term,落残余兜底键——#tdi07 oracle)
let dir_aligned = match td.direction_mapping {
Some(Set(d)) => lit.direction == Some(d)
Some(Null) => lit.direction is None
None =>
match lit.direction {
Some(d) => d == _active.default_direction.unwrap_or("")
None => _active.default_direction is None
}
}
if !dir_aligned {
return false
}
match td.datatype {
// @none 同权(#ttn01——值恒值对象形,object_fit 放行)
Some("@none") => true
Some("@id") | Some("@vocab") => false
// datatype 版 term 与值态失配 → 无形可用(#t0006 oracle)
Some(_) => false
None =>
match td.language_mapping {
// language 版 term 与值语言态失配 → 无形可用
Some(_) => false
// 纯 term:对象形可用的残域 = datatype 在场(钉 @type——
// #t0002 term2 oracle)或 language 在场(钉 @language——
// #t0002 term3 oracle)或 context 缺省语言在场(对象形保住
// 无语言态,裸串重展开会被注入缺省——#t0018 v0.3 oracle)
None =>
lit.datatype is Some(_) ||
lit.language is Some(_) ||
_active.default_language is Some(_)
}
}
}
}
}
///|
/// 值压缩(REC §6.4 首波子集):td datatype 吸收 → 裸 raw;td language 吸收
/// → 裸串;缺省臂无 datatype(或 xsd:string)、无 language/direction/index
/// 的纯 @value 值对象 → 裸值(#t0009 oracle);否则值对象形(@type 压缩
/// 词形 + @language/@direction/@index 保留)
fn compact_value_jv(
literal : ExpandedLiteral,
td_opt : TermDefinition?,
active : ActiveContext,
key_active : ActiveContext,
options : JsonLdOptions,
) -> JsonValue {
match td_opt {
Some(td) => {
// datatype 吸收(raw 保形——数值/布尔原样)
match (td.datatype, literal.datatype) {
(Some(dt), Some(ldt)) =>
if dt == ldt {
return match literal.raw {
Some(raw) => raw
None => JsonValue::String(literal.value)
}
}
_ => ()
}
// language 吸收(串值裸形;方向态须合——§8.48 同判据;Null 臂 =
// 显式压制吸收无语言值——#t0015 term5/#t0089 s oracle)
match td.language_mapping {
Some(Set(lang)) =>
match literal.language {
Some(l) =>
if lang_tag_eq(l, lang) {
let dir_ok = match td.direction_mapping {
Some(Set(d)) => literal.direction == Some(d)
Some(Null) => literal.direction is None
None => true
}
if dir_ok {
return match literal.raw {
Some(raw) => raw
None => JsonValue::String(literal.value)
}
}
}
None => ()
}
Some(Null) =>
if literal.language is None {
let dir_ok = match td.direction_mapping {
Some(Set(d)) => literal.direction == Some(d)
Some(Null) => literal.direction is None
None => true
}
if dir_ok {
return match literal.raw {
Some(raw) => raw
None => JsonValue::String(literal.value)
}
}
}
_ => ()
}
// direction 吸收(串值裸形——§8.48 #tdi03 oracle:list 项 @direction
// 合 td 方向映射即裸形,重展开注回同向)
match td.direction_mapping {
Some(Set(d)) =>
if literal.direction == Some(d) {
return match literal.raw {
Some(raw) => raw
None => JsonValue::String(literal.value)
}
}
Some(Null) =>
if literal.direction is None {
return match literal.raw {
Some(raw) => raw
None => JsonValue::String(literal.value)
}
}
None => ()
}
}
None => ()
}
// @none 显式无 coercion(#ttn01——term @type @none 下值恒值对象形,
// 裸形早退跳过;datatype None 双义的解 = keyword 形标记 Some("@none"))
let none_no_coercion = match td_opt {
Some(td) => td.datatype == Some("@none")
None => false
}
let bare_datatype = match literal.datatype {
Some(dt) => dt == "http://www.w3.org/2001/XMLSchema#string"
None => true
}
// 语言态对齐(#t0015 v1 oracle——de 值在缺省 de 下裸形重展开带回同语言,
// 安全;纯串无修饰且缺省在场则对象形也不安全,分选层已拒)
let language_aligned = match literal.language {
Some(lang) => lang_tag_eq(lang, active.default_language.unwrap_or(""))
None => active.default_language is None
}
let scalar_language_immune = literal.raw is Some(_)
if !none_no_coercion &&
bare_datatype &&
(scalar_language_immune || language_aligned) &&
literal.direction is None &&
literal.index is None {
return match literal.raw {
Some(raw) => raw
None => JsonValue::String(literal.value)
}
}
// @value 键走别名压缩(#tc020——type-scoped "value":"@value" 键生效)
let value_key = compact_iri_word(
"@value", true, false, true, key_active, options,
)
let pairs : Array[(String, JsonValue)] = [
(
value_key,
match literal.raw {
Some(raw) => raw
None => JsonValue::String(literal.value)
},
),
]
match literal.datatype {
Some(dt) =>
// xsd:string 缺省 @type 不发(REC 值压缩);其余压缩词形保留
if dt != "http://www.w3.org/2001/XMLSchema#string" {
let type_key = compact_iri_word(
"@type", true, false, true, key_active, options,
)
pairs.push(
(
type_key,
JsonValue::String(
compact_iri_word(dt, true, true, true, key_active, options),
),
),
)
}
None => ()
}
// @language/@direction 走别名键(#tdi02 oracle——"direction":"@direction"
// → 键 "direction";无别名回落关键字字面)
// 值对象键随键选择链(#tc020——type-scoped "value":"@value" 别名生效)
let lang_key = compact_iri_word(
"@language", true, false, true, key_active, options,
)
let dir_key = compact_iri_word(
"@direction", true, false, true, key_active, options,
)
match literal.language {
Some(lang) => pairs.push((lang_key, JsonValue::String(lang)))
None => ()
}
match literal.direction {
Some(dir) => pairs.push((dir_key, JsonValue::String(dir)))
None => ()
}
match literal.index {
Some(ix) => {
let index_key = compact_iri_word(
"@index", true, false, true, key_active, options,
)
pairs.push((index_key, JsonValue::String(ix)))
}
None => ()
}
JsonValue::Object(pairs)
}
///|
/// 排名估长器(无递归——断 compact_node_jv 定义环):id_only 节点取**本
/// term 塌缩词**长(役31——词形随候选项,最短词形排名的本职;塌缩旗标与
/// compact_value_item 串塌缩臂逐字同源,故 id_only 面即精确渲染长)+
/// 词形级别(exact 0 < vocab 后缀 1 < 前缀词 2 < base 相对 3 < 全 IRI 4
/// ——同长 tie-break,#t0044 DefinedTerm exact 胜 base 相对);值取词长+8;
/// list 300+项*8;非裸节点 400——排名仅需相对序,精确长由选中后真渲染承担
fn compact_rank_word_len(
v : ExpandedValue,
td_opt : TermDefinition?,
active : ActiveContext,
options : JsonLdOptions,
) -> (Int, Int) {
match v {
ExpandedValue::Node(child) => {
let id_only = child.types.is_empty() &&
child.properties.is_empty() &&
child.reverse_props.is_empty() &&
child.graph is None &&
child.included is None &&
child.index is None
let id = match child.id {
Some(id) => id
None => return (400, 4)
}
if !id_only {
return (400, 4)
}
match td_opt {
Some(td) =>
match td.datatype {
// @id 型:串塌缩恒行(base 相对——compact_value_item 同旗标)
Some("@id") => {
let word = compact_iri_word(
id, false, false, true, active, options,
)
(
word.length(),
compact_word_rank(id, false, false, active, options),
)
}
// @vocab 型:exact/vocab 命中才塌,否则前缀词步(base 禁——
// compact_value_item 同判据)
Some("@vocab") => {
let exact_hit = compact_term_candidates(id, false, active).length() >
0
let vocab_hit = match active.vocab {
Some(vocab) =>
vocab.length() < id.length() && id.has_prefix(vocab)
None => false
}
let word = if exact_hit || vocab_hit {
compact_iri_word(id, true, true, true, active, options)
} else {
compact_iri_word(id, false, false, false, active, options)
}
(
word.length(),
compact_word_rank(id, exact_hit, vocab_hit, active, options),
)
}
// 无 @type 形态:id_only 节点走对象形({"@id":…}),不作裸塌缩
_ => (400, 4)
}
None => (400, 4)
}
}
ExpandedValue::Value(lit) => (lit.value.length() + 8, 3)
ExpandedValue::List(items, ..) => (300 + items.length() * 8, 3)
}
}
///|
/// 词形级别(估长器同长 tie-break 用;与本 term 塌缩词配套):exact 命中
/// 0 < vocab 后缀 1 < 前缀词 2 < base 相对 3 < 全 IRI 4——以窄旗标逐步
/// 试塌归类(每步纯串工作,无渲染递归)
fn compact_word_rank(
id : String,
exact_hit : Bool,
vocab_hit : Bool,
active : ActiveContext,
options : JsonLdOptions,
) -> Int {
if exact_hit {
return 0
}
if vocab_hit {
return 1
}
let prefix_word = compact_iri_word(id, false, false, false, active, options)
if prefix_word != id {
return 2
}
let base_word = compact_iri_word(id, false, false, true, active, options)
if base_word != id {
return 3
}
4
}
///|
/// @reverse 值级分键(役31 从 compact_node_jv 内联抽出——@reverse 定义 term
/// 与普通 term 双路复用):逐值 bare_fit + 最短词形排名(词长→词形级别)归
/// term(#t0044 oracle——dave→knows 全词、DefinedTerm→knowsVocab exact 词;
/// 同 IRI 双 term 按值拆键);无 fit 值落 compact_property_pair 兜底键。
/// 返回 (键, 值) 对数组——组内单值塌缩随 compactArrays
fn compact_reverse_split_pairs(
pred : String,
cands : Array[String],
vals : Array[ExpandedValue],
active : ActiveContext,
options : JsonLdOptions,
) -> Result[Array[(String, JsonValue)], JsonLdError] {
let rev_split : Map[String, Array[JsonValue]] = Map([])
for v in vals {
let mut best : (String, TermDefinition, Int, Int)? = None
for name in cands {
match active.terms.get(name) {
Some(td) => {
let fits = compact_bare_fit(v, td, active)
if fits {
let (word_len, word_rank) = compact_rank_word_len(
v,
Some(td),
active,
options,
)
let better = match best {
Some((_, _, bl, br)) =>
word_len < bl || (word_len == bl && word_rank < br)
None => true
}
if better {
best = Some((name, td, word_len, word_rank))
}
}
}
None => ()
}
}
match best {
Some((name, td, _, _)) => {
let jv = match
compact_value_item(v, Some(td), active, active, options) {
Ok(out) => out
Err(e) => return Err(e)
}
match rev_split.get(name) {
Some(arr) => arr.push(jv)
None => rev_split[name] = [jv]
}
}
None =>
match compact_property_pair(pred, true, [v], active, options) {
Ok(pair_out) =>
// 同键并值(多值同落一词形——恒追加)
match rev_split.get(pair_out.0) {
Some(arr) => arr.push(pair_out.1)
None => rev_split[pair_out.0] = [pair_out.1]
}
Err(e) => return Err(e)
}
}
}
let out : Array[(String, JsonValue)] = []
for name in rev_split.keys().collect() {
match rev_split.get(name) {
Some(arr) => {
let rendered = if options.compact_arrays && arr.length() == 1 {
arr[0]
} else {
JsonValue::Array(arr)
}
out.push((name, rendered))
}
None => ()
}
}
Ok(out)
}
///|
/// 单值压缩分发(节点引用串塌缩:@id-only 子节点 + td @type @id/@vocab →
/// 压缩词串;否则子节点递归对象形)
fn compact_value_item(
value : ExpandedValue,
td_opt : TermDefinition?,
active : ActiveContext,
key_active : ActiveContext,
options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
// property-scoped context 套用(压缩面——tc001/tc013 族:td @context 在
// 节点有效链之上套用,值渲染全走此链;套用失败回退——定义期已校验)。
// key_active = 键选择链(役3 双链分立——#tc011:type-scoped 注销的别名
// 只在键链生效,{"@id"} 对象形键随此链;property-scoped 对两链同权套用)
let active : ActiveContext = match td_opt {
Some(td) =>
match td.local_context {
Some(lc) =>
match
ContextProcessor::process_context(
StandardExpander::{ },
active,
lc,
ContextFlags::property_scoped(),
options,
) {
Ok(c) => c
Err(_) => active
}
None => active
}
None => active
}
let key_active : ActiveContext = match td_opt {
Some(td) =>
match td.local_context {
Some(lc) =>
match
ContextProcessor::process_context(
StandardExpander::{ },
key_active,
lc,
ContextFlags::property_scoped(),
options,
) {
Ok(c) => c
Err(_) => key_active
}
None => key_active
}
None => key_active
}
match value {
ExpandedValue::Node(child) => {
let id_only = child.types.is_empty() &&
child.properties.is_empty() &&
child.reverse_props.is_empty() &&
child.graph is None &&
child.included is None &&
child.index is None
if id_only {
match child.id {
Some(id) =>
match td_opt {
// @id/@vocab 型 term(@type 三形态)→ 串塌缩(#t0005 term2 oracle)
Some(td) =>
match td.datatype {
Some("@id") =>
return Ok(
JsonValue::String(
compact_iri_word(
id, false, false, true, active, options,
),
),
)
// @vocab 型塌缩 = exact 命中或 vocab 前缀命中才塌
// (#t0054 "enum" / #tc013 "buzz");两者皆不命中保绝对
// ——base 步不参与(#t0062 oracle:绝对 IRI 不因文档
// base 相对化)
Some("@vocab") => {
let exact_hit = compact_term_candidates(id, false, active).length() >
0
let vocab_hit = match active.vocab {
Some(v) => v.length() < id.length() && id.has_prefix(v)
None => false
}
if exact_hit || vocab_hit {
return Ok(
JsonValue::String(
compact_iri_word(
id, true, true, true, active, options,
),
),
)
}
// 前缀词步仍可(#t0063 oracle——"prefix:suffix"),
// base 步禁(绝对 IRI 不因文档 base 相对化)
let prefix_word = compact_iri_word(
id, false, false, false, active, options,
)
return Ok(JsonValue::String(prefix_word))
}
_ => ()
}
None => ()
}
None => ()
}
// 对象形:{@id键: 压缩词};键走关键词压缩(役31——@id 别名 term
// 生效,#t0022 oracle:"id":"@id" 别名下列项节点 {id:…} 非 {@id:…};
// 与值对象 @language 键同款旗标);@-词转义(#t0111 oracle——
// "@special" 重展开会被当关键字 ⇒ "./" 前缀逃逸)
let id_key = compact_iri_word(
"@id", true, false, true, key_active, options,
)
let word = match child.id {
Some(id) => compact_iri_word(id, false, false, true, active, options)
// None ⇒ 空 id_only 节点 = **空对象**(tp046 oracle——prune 摘
// @id 后的壳;旧渲染 None ⇒ "" 泄漏哨兵——§8.108 探针钉兑付。
// compact 套件零触面:无 id 无内容的嵌套节点在展开位即自由浮
// 动弃置,此臂为 framing prune 新开面)
None => return Ok(JsonValue::Object([]))
}
let word = if word.has_prefix("@") { "./" + word } else { word }
return Ok(JsonValue::Object([(id_key, JsonValue::String(word))]))
}
compact_node_graph_value_jv(child, active, options)
}
ExpandedValue::Value(literal) =>
Ok(compact_value_jv(literal, td_opt, active, key_active, options))
ExpandedValue::List(items, index~) => {
// @list 容器吸收(td @container @list → 裸数组——#t0015 term4 oracle);
// 1.0 嵌套 list = compaction to list of lists 错(#te001 oracle);
// 无容器 → @list 值对象形(带 @index ⇒ {@list,@index} 对象形——
// #t0041 oracle)
let is_list_container = match td_opt {
Some(td) => td.container is Some(List)
None => false
}
let arr : Array[JsonValue] = []
for it in items {
if it is ExpandedValue::List(_) && options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidList("compaction to list of lists"))
}
match compact_value_item(it, td_opt, active, key_active, options) {
Ok(jv) => arr.push(jv)
Err(e) => return Err(e)
}
}
if is_list_container {
return Ok(JsonValue::Array(arr))
}
// @list/@index 键走 term 词形(关键字别名——#t0042 oracle:
// "listAlias"/"indexAlias" 作键;无别名回落关键字字面)
let list_key = compact_iri_word(
"@list", true, false, true, active, options,
)
let index_key = compact_iri_word(
"@index", true, false, true, active, options,
)
let list_obj : Array[(String, JsonValue)] = [
(list_key, JsonValue::Array(arr)),
]
match index {
Some(ix) => list_obj.push((index_key, JsonValue::String(ix)))
None => ()
}
Ok(JsonValue::Object(list_obj))
}
}
}
///|
/// 值数组压缩(容器旗 + 去重 + 塌缩):@set 保形;非容器单值塌缩(compactArrays
/// 门);同形值去重。**@list 容器吸收不在此**——单 List 值 → 裸数组与 1.0 嵌套
/// list 报错由 `compact_value_item` 的 List 容器臂(本函数逐值调用它)承担;
/// 本节原有一段同义吸收块,役C2批1 收口定谳为**死带**(反向容器白名单不含 @list)
/// 并拆除(拆前 1102-1129;行为零变更——该块与 compact_value_item 逐字同源)
fn compact_property_values(
vals : Array[ExpandedValue],
td_opt : TermDefinition?,
active : ActiveContext,
options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
let mut keep_array = match td_opt {
Some(td) => td.container is Some(Set)
None => false
}
if !options.compact_arrays {
keep_array = true
}
let vals_jv : Array[JsonValue] = []
let seen : Map[String, Bool] = Map([])
for v in vals {
match compact_value_item(v, td_opt, active, active, options) {
Ok(jv) => {
let key = render_json(jv)
if !seen.contains(key) {
seen[key] = true
vals_jv.push(jv)
}
}
Err(e) => return Err(e)
}
}
if !keep_array && vals_jv.length() == 1 {
return Ok(vals_jv[0])
}
Ok(JsonValue::Array(vals_jv))
}
///|
/// 键位出口 scheme 撞名检测(役42/#te002 oracle——绝对 IRI 键的 scheme
/// 恰为在册 term 名 ⇒ 重展开读成 curie、错 IRI,Err;term 名非绝对 IRI
/// 形的键零涉)
fn check_key_scheme_collision(
key : String,
pred : String,
active : ActiveContext,
) -> Result[Unit, JsonLdError] {
if is_valid_absolute_iri(key) {
match colon_index(key) {
Some(at) => {
// 精化(役42 施工勘误——#t0006 前缀词 ex:term1 语法似绝对 IRI 且
// "ex" 在册,但重展开同于原 IRI 非撞);撞 = scheme term 重展开
// **异于**原 IRI(#te002——tag:… 经 tag term → ns/tag/… ≠ 原 IRI)
let scheme = key[:at].to_owned()
match active.terms.get(scheme) {
Some(td) =>
match td.iri {
Some(td_iri) => {
let rest = key[at + 1:].to_owned()
if td_iri + rest != pred {
// 发射词自洽豁免(役55 t0010 裁定——spec §8.127):键词
// 本身即在册 term 键 ⇒ 发射自洽于 term 查找序(term 命中
// 先于 CURIE 检查);套件正例裁决 > REC 字面混淆错
// (jsonld.js compact/frame 双错与套件正例相悖,多实现
// 裁法 = 键在册放行)。te002 键非 term 键不受扰
if !active.terms.contains(key) {
return Err(
JsonLdError::InvalidContext(
"scheme collides with term name",
),
)
}
}
}
None => ()
}
None => ()
}
}
None => ()
}
}
Ok(())
}
///|
/// 属性对压缩(term 选择 + 值数组;键 = term 名或压缩 IRI)——正向与反转对
/// 共用(反转位 term 选择走 td.reverse 匹配)
fn compact_property_pair(
pred : String,
want_reverse : Bool,
vals : Array[ExpandedValue],
active : ActiveContext,
options : JsonLdOptions,
) -> Result[(String, JsonValue), JsonLdError] {
let cands = compact_term_candidates(pred, want_reverse, active)
let td_opt : TermDefinition? = match cands {
cs if cs.length() > 0 => active.terms.get(cs[0])
_ => None
}
let key = match cands {
cs if cs.length() > 0 => cs[0]
_ => compact_iri_word(pred, true, true, true, active, options)
}
// 键位出口 scheme 撞名检测(役42/#te002——键位词为绝对 IRI 且其
// scheme 恰为在册 term 名:重展开读成 curie ⇒ 错 IRI,Err)
match check_key_scheme_collision(key, pred, active) {
Ok(_) => ()
Err(e) => return Err(e)
}
match compact_property_values(vals, td_opt, active, options) {
Ok(jv) => Ok((key, jv))
Err(e) => Err(e)
}
}
///|
/// 节点压缩:@id(压缩词)/ @type(压缩词 + 塌缩)/ 属性(@nest 重嵌——
/// td.nest 组账)/ @index / @graph(内容恒数组——#compact-t0039 oracle 单例
/// 不解包)/ @reverse(正向提升——reverse term 匹配即普通属性;无匹配保
/// 包裹形)
fn compact_node_jv(
node : ExpandedNode,
active : ActiveContext,
options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
// type-scoped 套用(压缩面——双链分立,#tc009 vs #tc013 同形异判对 oracle:
// type-scoped 定义的 term **只作键名**、 datatype/container/language 不参与
// 值成形;property-scoped 全参与。scoped = 键选择链;active(outer)=
// 值成形链)。base 轨(§8.59 第三轨):值位词形 base 相对化随 scoped 链
// (#tc015 oracle——typed-base 下 "#typed-id" 片段相对)
let scoped = match compact_apply_type_scoped(node.types, active, options) {
Ok(c) => c
Err(e) => return Err(e)
}
let value_chain : ActiveContext = if scoped.base != active.base {
{
base: scoped.base,
vocab: active.vocab,
terms: active.terms,
previous: active.previous,
default_language: active.default_language,
default_direction: active.default_direction,
compact_by_iri: active.compact_by_iri,
compact_reverse: active.compact_reverse,
compact_prefixes: active.compact_prefixes,
}
} else {
active
}
let pairs : Array[(String, JsonValue)] = []
// @id 键走 term 词形(关键字别名 term——#t0008 oracle:"uri":"@id" →
// "uri" 作键;无别名回落 "@id" 字面)
let id_key = compact_iri_word("@id", true, false, true, scoped, options)
match node.id {
Some(id) =>
// 词形走 value_chain(base 轨——type-scoped @base 传导至本节点值位,
// §8.59/#tc015 oracle:typed-base 下 "#typed-id" 片段相对)
pairs.push(
(
id_key,
JsonValue::String(
compact_iri_word(id, true, false, true, value_chain, options),
),
),
)
None => ()
}
if node.types.length() > 0 {
// @type 键走 term 词形(关键字别名——#t0022 oracle:"type":"@type" →
// "type" 作键;无别名回落 "@type" 字面)
let type_key = compact_iri_word("@type", true, false, true, scoped, options)
let types : Array[JsonValue] = []
for t in node.types {
// @type 值渲染走 outer 链(#tc014 oracle——nullification 后 vocab
// 存续;type-scoped 只管键选择,值词形随外层——双链分立同款)
types.push(
JsonValue::String(
// @type 词形不走文档相对化(役42/#t0066——类型位只经 term/
// vocab,oracle 全绝对)
compact_iri_word(t, true, true, false, active, options),
),
)
}
// 别名 term 带 @container @set ⇒ 恒数组(#t0105 oracle;#t0106
// oracle——1.0 模式数组塌缩不受 @set 容器约束)
let type_set = match scoped.terms.get(type_key) {
Some(td) =>
td.container is Some(Set) && options.processing_mode is JsonLd11
None => false
}
if options.compact_arrays && types.length() == 1 && !type_set {
pairs.push((type_key, types[0]))
} else {
pairs.push((type_key, JsonValue::Array(types)))
}
}
// @nest 归组账(td.nest → 组内 pairs——#tn001 oracle:"p2":{"@nest":
// "@nest"} → "@nest":{"p2":"v2"};nest 键 = 关键字位原样 / term 名原样)
let nest_groups : Map[String, Array[(String, JsonValue)]] = Map([])
let nest_order : Array[String] = []
// 正向属性:值感知分选(同 IRI 多 term 按值拆键——#t0006/#t0015 oracle;
// 两遍 = bare_fit 优先、object_fit 次之、残余落前缀/相对词兜底键)+
// 容器逆形映射归组路由(§8.43——单映射容器 term 成批机制)
for pair in node.properties {
let cands = compact_term_candidates(pair.0, false, scoped)
let cand_tds : Array[(String, TermDefinition)] = []
for name in cands {
match scoped.terms.get(name) {
Some(td) => cand_tds.push((name, td))
None => ()
}
}
// 多值 @set 候选偏好(§8.54——多值时 @set term 保形胜出,候选序前置;
// #t0027 oracle——label/container 同 IRI,多值归 @set 版)
if pair.1.length() > 1 {
let sets : Array[(String, TermDefinition)] = []
let rest : Array[(String, TermDefinition)] = []
for entry in cand_tds {
if entry.1.container is Some(Set) {
sets.push(entry)
} else {
rest.push(entry)
}
}
if sets.length() > 0 {
cand_tds.clear()
for entry in sets {
cand_tds.push(entry)
}
for entry in rest {
cand_tds.push(entry)
}
}
}
// 映射容器候选(单 @index/@id/@type/@language——§8.43 共性路由位)
let map_tds : Array[(String, TermDefinition)] = []
for entry in cand_tds {
let is_map = match entry.1.container {
Some(Id) | Some(Type) | Some(Index) | Some(Language) => true
_ => false
}
if is_map && entry.1.graph_secondary is None {
map_tds.push(entry)
}
}
// @graph 容器候选(§8.45——container=Graph,次级 Id/Index/None)
let mut graph_td : (String, TermDefinition)? = None
for entry in cand_tds {
if entry.1.container is Some(Graph) {
graph_td = Some(entry)
break
}
}
// 图归组账(次级键 → 内容数组;裸次级 → 内容数组直列)
let graph_subs : Array[(String, JsonValue)] = []
let graph_bares : Array[JsonValue] = []
// 映射归组账(term 键 → (subkey, 渲染值) 列表;首现序)
let map_names : Array[String] = []
let map_subs : Map[String, Array[(String, JsonValue)]] = Map([])
for entry in map_tds {
map_names.push(entry.0)
map_subs[entry.0] = []
}
// 分组账:键 → 值数组(首现序)
let group_keys : Array[String] = []
let group_vals : Map[String, Array[JsonValue]] = Map([])
let group_array : Map[String, Bool] = Map([])
let group_tds : Map[String, TermDefinition] = Map([])
let leftovers : Array[JsonValue] = []
for v in pair.1 {
// @graph 容器路由优先(§8.45——图载体值;次级定归组键)
let mut graph_done = false
match graph_td {
Some((gt_name, gtd)) =>
match v {
ExpandedValue::Node(child) =>
match child.graph {
Some(items) => {
let content : Array[JsonValue] = []
for it in items {
match it {
ExpandedValue::Node(sub) =>
match compact_node_jv(sub, active, options) {
Ok(jv) => content.push(jv)
Err(e) => return Err(e)
}
_ => ()
}
}
let none_key = compact_iri_word(
"@none", true, false, true, active, options,
)
// shaped(§8.52——单内容节点对象 / 多内容 @included 包裹;
// graph_set 载体恒数组保形——#t0082 族 oracle)
let shaped = if gtd.graph_set &&
gtd.graph_secondary is Some(_) {
// 数组形仅映射面次级(#t0082——Index 位内容数组);
// bares 面.graph object 值走 @included 语义(#t0110
// ——[#@graph,@set] 单成员 @included 包裹;#t0109
// 同语义已绿于非 graph_set 路)
JsonValue::Array(content)
} else if content.length() == 1 {
content[0]
} else {
let included_key = compact_iri_word(
"@included", true, false, true, active, options,
)
JsonValue::Object(
[("@included", JsonValue::Array(content))].map(fn(p) {
if p.0 == "@included" {
(included_key, p.1)
} else {
p
}
}),
)
}
match gtd.graph_secondary {
Some(Id) => {
let subkey = match child.id {
Some(id) =>
compact_iri_word(
id, false, false, true, active, options,
)
None => none_key
}
graph_subs.push((subkey, shaped))
}
Some(Index) => {
let subkey = match child.index {
Some(ix) => ix
None => none_key
}
// 带 @id 载体 = 载体渲染入 **term 组**(不走映射
// 组——@index 成员自带键信息,#t0083 oracle);无 id
// = shaped 入映射组(#t0081)
if child.id is Some(_) {
match
compact_node_graph_value_jv(child, active, options) {
Ok(jv) =>
match group_vals.get(gt_name) {
Some(arr) => arr.push(jv)
None => {
group_vals[gt_name] = [jv]
group_keys.push(gt_name)
group_tds[gt_name] = gtd
group_array[gt_name] = !options.compact_arrays
}
}
Err(e) => return Err(e)
}
} else {
graph_subs.push((subkey, shaped))
}
}
Some(List)
| Some(Set)
| Some(Language)
| Some(Type)
| Some(Graph) => ()
// 裸次级:带 @id 载体 = 载体渲染(#t0080 oracle——
// "@id"+"@graph" 随形);无 id = shaped
None =>
if child.id is Some(_) {
match
compact_node_graph_value_jv(child, active, options) {
Ok(jv) => graph_bares.push(jv)
Err(e) => return Err(e)
}
} else {
graph_bares.push(shaped)
}
}
graph_done = true
}
None => ()
}
_ => ()
}
None => ()
}
if graph_done {
continue
}
// 映射归组优先(§8.43——值 fit 映射容器即归组;不适值照旧分选)
let mut map_done = false
// non-map bare-fit 前置扫描(#t0089/#t0026 双 oracle 合流判据——
// **无语言值**归 language-null term(确定性辖域),language 图
// @none 不抢;**有语言值**仍归语言图按语言键归组)
let no_lang_value = match v {
ExpandedValue::Value(lit) => lit.language is None
_ => false
}
if map_tds.length() > 0 && no_lang_value {
for entry in cand_tds {
let is_map = match entry.1.container {
Some(Id) | Some(Type) | Some(Index) | Some(Language) => true
_ => false
}
if !is_map && compact_bare_fit(v, entry.1, active) {
let jv = match
compact_value_item(v, Some(entry.1), active, scoped, options) {
Ok(out) => out
Err(e) => return Err(e)
}
match group_vals.get(entry.0) {
Some(arr) => arr.push(jv)
None => {
group_vals[entry.0] = [jv]
group_keys.push(entry.0)
group_tds[entry.0] = entry.1
group_array[entry.0] = match entry.1.container {
Some(Set) | Some(List) => true
_ => !options.compact_arrays
}
}
}
map_done = true
break
}
}
if map_done {
continue
}
}
for entry in map_tds {
match compact_map_entry(v, entry.1, active, options) {
Some((subkey, rendered)) => {
match map_subs.get(entry.0) {
Some(arr) => arr.push((subkey, rendered))
None => ()
}
map_done = true
break
}
None => ()
}
}
if map_done {
continue
}
// 两遍选 term:bare_fit 优先(吸收成裸形),object_fit 次之;
// pass-1 多候选同 fit ⇒ **最短词形**排名(#t0057/#t0061/#t0044
// oracle——exact 词 < base 相对 < 全 IRI)
let mut chosen : (String, TermDefinition, Bool)? = None
for pass_bare in [true, false] {
// 排名键 = (词长, 词形级别, 特异性位降序):同长时 exact/vocab 塌缩
// 胜 base/全 IRI(#t0044),再同则 Set/Null 精确映射 > None 缺省
// 对齐(#t0018/#t0024 oracle——en 项归 termLL1 非 termLL0)
let mut best : (String, TermDefinition, Int, Int, Int)? = None
for entry in cand_tds {
let fits = if pass_bare {
compact_bare_fit(v, entry.1, active)
} else {
compact_object_fit(v, entry.1, active)
}
if fits {
let (word_len, word_rank) = compact_rank_word_len(
v,
Some(entry.1),
active,
options,
)
// 特异性双维(役30 第二维 datatype):language-mapping term
// 不得抢 typed 值(#t0015);td @type 与值 datatype 合 ⇒ +1
// (#t0024 oracle——type1 项归 termTL1 非 termLL0);列的
// datatype 位 = 全项 uniform 才计(役31 typed 值对象收集项)
let v_dt = literal_datatype_of(v)
let spec_lang = match (entry.1.language_mapping, v_dt) {
(Some(_), None) => 1
_ => 0
}
let spec_type = match (entry.1.datatype, v_dt) {
(Some(dt), Some(vdt)) => if dt == vdt { 1 } else { 0 }
_ => 0
}
let specificity = spec_lang + spec_type
let better = match best {
Some((_, _, bl, br, bs)) =>
word_len < bl ||
(
word_len == bl &&
(word_rank < br || (word_rank == br && specificity > bs))
)
None => true
}
if better {
best = Some((entry.0, entry.1, word_len, word_rank, specificity))
}
}
}
match best {
Some((name, td, _, _, _)) => chosen = Some((name, td, pass_bare))
None => ()
}
if chosen is Some(_) {
break
}
}
match chosen {
Some((name, td_scoped, is_bare)) => {
// shaping td(#tc009/#tc013 同形异判对):outer 同名 term 全权;
// scoped-only term 剥 datatype/container/language(不作值成形)、
// 保 local_context(property-scoped 链照走——#tc013 bar)
let td : TermDefinition = match active.terms.get(name) {
Some(outer_td) =>
// 双定义取舍(#tc007 oracle——scoped @id 压过 outer 同名:
// type-scoped 定义对该节点就是全量定义;@vocab 型仍随 outer
// ——tc009 不塌裁断不变)
if td_scoped.datatype == Some("@id") {
td_scoped
} else {
outer_td
}
None =>
{
datatype: if td_scoped.datatype == Some("@id") ||
// @vocab 型同权保(#tc026——scoped-only @vocab term 词形
// 塌缩;outer 同名案仍走 outer 全权臂不受此位——#tc009)
td_scoped.datatype == Some("@vocab") ||
td_scoped.local_context is Some(_) {
td_scoped.datatype
} else {
None
},
container: td_scoped.container,
container_set: td_scoped.container_set,
reverse: td_scoped.reverse,
local_context: td_scoped.local_context,
context_base_url: td_scoped.context_base_url,
index_mapping: td_scoped.index_mapping,
graph_secondary: td_scoped.graph_secondary,
is_protected: td_scoped.is_protected,
language_mapping: None,
direction_mapping: None,
is_prefix: td_scoped.is_prefix,
nest: td_scoped.nest,
simple_form: td_scoped.simple_form,
graph_set: td_scoped.graph_set,
iri: td_scoped.iri,
}
}
// 新节点对象回退(作用域两族边界表——type-scoped 不跨新节点,
// #tc015 nestedNode 外层 base 相对);id-only 节点是**父词形位**
//(对象形/串塌缩由父渲染,随 value_chain 吃本节点 type-scoped
// base——#tc015 subjectReference "#subject-reference-id")
// 完整新节点:scoped.previous 缺位 = propagate:true 类型作用域
// 存活(#tc026——子节点继承 scoped);在 = 回退外层(#tc015
// nestedNode 外层 base 相对);id-only 节点是父词形位随 value_chain
let v_ctx = match v {
ExpandedValue::Node(child) => {
let node_ctx = if scoped.previous is None {
scoped
} else {
active
}
if child.types.is_empty() &&
child.properties.is_empty() &&
child.reverse_props.is_empty() &&
child.graph is None &&
child.included is None &&
child.index is None {
value_chain
} else {
node_ctx
}
}
_ => value_chain
}
let jv = match
compact_value_item(v, Some(td), v_ctx, scoped, options) {
Ok(out) => out
Err(e) => return Err(e)
}
match group_vals.get(name) {
Some(arr) => arr.push(jv)
None => {
group_vals[name] = [jv]
group_keys.push(name)
group_tds[name] = td
let pair_is_list_absorb = match (td.container, v) {
(Some(List), ExpandedValue::List(_)) => true
_ => false
}
group_array[name] = if pair_is_list_absorb {
false // 吸收形:jv 已是裸数组,emit 直出
} else {
match td.container {
Some(Set) | Some(List) => true
_ => !options.compact_arrays
}
}
}
}
let _ = is_bare
}
None => {
// 新节点对象回退同 chosen 位(id-only 子节点=父词形位随
// value_chain;完整新节点回退 active——#tc015 同判据)
// 完整新节点:scoped.previous 缺位 = propagate:true 类型作用域
// 存活(#tc026——子节点继承 scoped);在 = 回退外层(#tc015
// nestedNode 外层 base 相对);id-only 节点是父词形位随 value_chain
let v_ctx = match v {
ExpandedValue::Node(child) => {
let node_ctx = if scoped.previous is None {
scoped
} else {
active
}
if child.types.is_empty() &&
child.properties.is_empty() &&
child.reverse_props.is_empty() &&
child.graph is None &&
child.included is None &&
child.index is None {
value_chain
} else {
node_ctx
}
}
_ => value_chain
}
let jv = match compact_value_item(v, None, v_ctx, scoped, options) {
Ok(out) => out
Err(e) => return Err(e)
}
leftovers.push(jv)
}
}
}
// @graph 容器出键(§8.45——次级键映射归并内容数组;裸次级数组直出)
match graph_td {
Some((gname, gtd)) =>
if graph_subs.length() > 0 {
let sub_pairs : Array[(String, JsonValue)] = []
let sub_keys : Array[String] = []
let sub_vals : Map[String, Array[JsonValue]] = Map([])
for entry in graph_subs {
match sub_vals.get(entry.0) {
Some(arr) => arr.push(entry.1)
None => {
sub_vals[entry.0] = [entry.1]
sub_keys.push(entry.0)
}
}
}
for sub in sub_keys {
match sub_vals.get(sub) {
Some(arr) => {
// graph_set 组:各载体已数组——摊平恒数组(#t0082 族
// oracle——值已数组不再包);非 graph_set 单值塌缩随
// compactArrays
let out = if gtd.graph_set {
let flat : Array[JsonValue] = []
for item in arr {
match item {
JsonValue::Array(items) =>
for it in items {
flat.push(it)
}
other => flat.push(other)
}
}
JsonValue::Array(flat)
} else if options.compact_arrays && arr.length() == 1 {
arr[0]
} else {
JsonValue::Array(arr)
}
sub_pairs.push((sub, out))
}
None => ()
}
}
pairs.push((gname, JsonValue::Object(sub_pairs)))
} else if graph_bares.length() > 0 {
// 出形分叉(§8.52 shaped 化——graph_set 数组恒列(#t0078);
// 裸次级单图直出 / 多图数组(#t0077/#t0096))
let gset = gtd.graph_set
if gset {
// graph_set:各图内容摊平恒数组(#t0078——单图单内容 [x] 直列)
let content : Array[JsonValue] = []
for gv in graph_bares {
match gv {
JsonValue::Array(items) =>
for it in items {
content.push(it)
}
other => content.push(other)
}
}
pairs.push((gname, JsonValue::Array(content)))
} else {
let out = if graph_bares.length() == 1 {
graph_bares[0]
} else {
JsonValue::Array(graph_bares)
}
pairs.push((gname, out))
}
}
None => ()
}
// 容器逆形映射出键(§8.43——subkey → 值;单值塌缩随 compactArrays)
for name in map_names {
match map_subs.get(name) {
Some(entries) =>
if entries.length() > 0 {
// @set 并容保形位(#ts002——[@id|@type|@language|@index, @set]
// 的 map 值恒数组,单值不塌缩)
let td_set = {
let mut found = false
for mtd in map_tds {
if mtd.0 == name {
found = mtd.1.container_set
}
}
found
}
let sub_pairs : Array[(String, JsonValue)] = []
let sub_keys : Array[String] = []
let sub_vals : Map[String, Array[JsonValue]] = Map([])
for entry in entries {
match sub_vals.get(entry.0) {
Some(arr) => arr.push(entry.1)
None => {
sub_vals[entry.0] = [entry.1]
sub_keys.push(entry.0)
}
}
}
for sub in sub_keys {
match sub_vals.get(sub) {
Some(arr) => {
let out = if options.compact_arrays &&
arr.length() == 1 &&
!td_set {
arr[0]
} else {
JsonValue::Array(arr)
}
sub_pairs.push((sub, out))
}
None => ()
}
}
// nest 接线(役43/#tn006-009——map 容器 × @nest 复合:map
// 对象入 nest 组并带 term 名中间键级 nestedindex→index→{…};
// 纯 map 路由原样顶层出)
let map_nest = {
let mut found : String? = None
for mtd in map_tds {
if mtd.0 == name {
found = mtd.1.nest
}
}
found
}
match map_nest {
Some(nest_term) =>
match nest_groups.get(nest_term) {
Some(group) =>
group.push((name, JsonValue::Object(sub_pairs)))
None => {
nest_groups[nest_term] = [
(name, JsonValue::Object(sub_pairs)),
]
nest_order.push(nest_term)
}
}
None => pairs.push((name, JsonValue::Object(sub_pairs)))
}
}
None => ()
}
}
// 各 term 组出键(单值塌缩——容器/@set/compactArrays 门)
for name in group_keys {
match (group_vals.get(name), group_array.get(name)) {
(Some(arr), Some(keep)) => {
// @json 值直出(#tjs07 oracle——@json 原值即 JSON,容器/塌缩不包;
// datatype 位是**关键字形** "@json"(丁批 #tjs01 同形),full IRI
// 形在管线中不存在——役34 勘定:旧守卫查 full IRI 恒不命中)
let td_json = match group_tds.get(name) {
Some(td) => td.datatype is Some("@json")
None => false
}
let out = if td_json && arr.length() == 1 {
arr[0]
} else if !keep && arr.length() == 1 {
arr[0]
} else {
JsonValue::Array(arr)
}
let pair_out = (name, out)
let td_nest = match group_tds.get(name) {
Some(td) => td.nest
None => None
}
match td_nest {
Some(nest_term) =>
match nest_groups.get(nest_term) {
Some(group) => group.push(pair_out)
None => {
nest_groups[nest_term] = [pair_out]
nest_order.push(nest_term)
}
}
None => pairs.push(pair_out)
}
}
_ => ()
}
}
// 空值属性 + 容器 term ⇒ 空数组出形(#t0004 oracle——myset2 [])
if pair.1.is_empty() {
let key = match cand_tds {
cs if cs.length() > 0 => cs[0].0
_ => compact_iri_word(pair.0, false, true, true, active, options)
}
pairs.push((key, JsonValue::Array([])))
}
// 残余 → 兜底键(前缀/相对词形——#t0006 "ex:term1" oracle);本属性
// 已有 term 组时禁 vocab 步(#tdi07 oracle——vocab 相对词撞 term 键,
// 落全 IRI)
if leftovers.length() > 0 {
let fallback_vocab = group_keys.length() == 0 && map_names.length() == 0
// 兜底键随键选择链(#tc016——type-scoped vocab 下 com 词形塌缩)
let fallback_key = compact_iri_word(
pair.0,
false,
fallback_vocab,
true,
scoped,
options,
)
// 键位出口 scheme 撞名检测(役42/#te002——leftover 兜底键同受检)
match check_key_scheme_collision(fallback_key, pair.0, active) {
Ok(_) => ()
Err(e) => return Err(e)
}
let out = if options.compact_arrays && leftovers.length() == 1 {
leftovers[0]
} else {
JsonValue::Array(leftovers)
}
pairs.push((fallback_key, out))
}
}
for nest_term in nest_order {
match nest_groups.get(nest_term) {
Some(group) => pairs.push((nest_term, JsonValue::Object(group)))
None => ()
}
}
match node.index {
Some(ix) => pairs.push(("@index", JsonValue::String(ix)))
None => ()
}
match node.included {
Some(incs) => {
// @included 面(#tin01 oracle:alias term "included" 作键、内容恒数组)
let arr : Array[JsonValue] = []
for inc in incs {
match compact_node_jv(inc, active, options) {
Ok(jv) => arr.push(jv)
Err(e) => return Err(e)
}
}
let included_key = compact_iri_word(
"@included", true, false, true, active, options,
)
// @included 单值解包为对象形(#tin02/#tin04/#tin05 oracle——与
// @graph 关键词条恒数组分叉:REC 出形位不同);别名 term 带
// @container @set ⇒ 恒数组(#tin01 oracle)
let included_set = match active.terms.get(included_key) {
Some(itd) => itd.container is Some(Set)
None => false
}
if options.compact_arrays && arr.length() == 1 && !included_set {
pairs.push((included_key, arr[0]))
} else {
pairs.push((included_key, JsonValue::Array(arr)))
}
}
None => ()
}
match node.graph {
Some(items) => {
let arr : Array[JsonValue] = []
for it in items {
match it {
ExpandedValue::Node(child) =>
match compact_node_jv(child, active, options) {
Ok(jv) => arr.push(jv)
Err(e) => return Err(e)
}
_ => ()
}
}
// 节点自身 @graph 关键词条 = as-array 恒真(REC 12.8.5——active
// property = @graph 时 as array;#t0016 oracle——成员带 @id 保数组。
// 塌缩只发生在值位渲染的数组步——compact_node_graph_value_jv)
pairs.push(("@graph", JsonValue::Array(arr)))
}
None => ()
}
// @reverse 正向提升(#t0033/#t0050 oracle——reverse term 匹配 → 普通属性;
// 无匹配 → "@reverse" 包裹形保真);映射容器 reverse 走归组账
let rev_map_names : Array[String] = []
let rev_map_subs : Map[String, Array[(String, JsonValue)]] = Map([])
let rev_pairs : Array[(String, JsonValue)] = []
for pair in node.reverse_props {
let cands = compact_term_candidates(pair.0, true, active)
if cands.length() > 0 {
let rev_name = cands[0]
let rev_td : TermDefinition? = match active.terms.get(rev_name) {
Some(t) => Some(t)
None => None
}
// reverse term 带映射容器 ⇒ 反转值走映射归组(#t0036 oracle——
// @reverse + @container @index → 键化出形)
let rev_is_map = match rev_td {
Some(t) =>
match t.container {
Some(Id) | Some(Type) | Some(Index) | Some(Language) => true
_ => false
}
None => false
}
if rev_is_map {
match rev_td {
Some(t) =>
for v in pair.1 {
match compact_map_entry(v, t, active, options) {
Some((subkey, rendered)) => {
let existed = rev_map_subs.contains(rev_name)
match rev_map_subs.get(rev_name) {
Some(arr) => arr.push((subkey, rendered))
None => rev_map_subs[rev_name] = [(subkey, rendered)]
}
if !existed {
rev_map_names.push(rev_name)
}
}
None =>
match
compact_property_pair(pair.0, true, [v], active, options) {
Ok(pair_out) => pairs.push(pair_out)
Err(e) => return Err(e)
}
}
}
None => ()
}
} else {
// 值级最短词形排名分键(#t0044 oracle——dave→knows 全词、
// DefinedTerm→knowsVocab exact 词;同 IRI 双 term 按值拆键;
// 役31 抽 compact_reverse_split_pairs 双路复用)
match
compact_reverse_split_pairs(pair.0, cands, pair.1, active, options) {
Ok(split_pairs) =>
for split_pair in split_pairs {
pairs.push(split_pair)
}
Err(e) => return Err(e)
}
}
} else {
// 无 @reverse 定义 term:正向 term 逐值分键(役31 #t0044 第二路——
// @type @id/@vocab 普通 term 在 @reverse 位同 IRI 双候选按值拆键,
// 出形保 @reverse 包裹);无正向候选落整体渲染兜底
let fwd_cands = compact_term_candidates(pair.0, false, active)
if fwd_cands.length() > 0 {
match
compact_reverse_split_pairs(
pair.0,
fwd_cands,
pair.1,
active,
options,
) {
Ok(split_pairs) =>
for split_pair in split_pairs {
rev_pairs.push(split_pair)
}
Err(e) => return Err(e)
}
} else {
match compact_property_pair(pair.0, false, pair.1, active, options) {
Ok(pair_out) => rev_pairs.push(pair_out)
Err(e) => return Err(e)
}
}
}
}
if rev_pairs.length() > 0 {
pairs.push(("@reverse", JsonValue::Object(rev_pairs)))
}
// reverse 映射组出键(#t0036——子键归并,组内单值塌缩随 compactArrays)
for name in rev_map_names {
match rev_map_subs.get(name) {
Some(entries) => {
let sub_pairs : Array[(String, JsonValue)] = []
let sub_keys : Array[String] = []
let sub_vals : Map[String, Array[JsonValue]] = Map([])
for entry in entries {
match sub_vals.get(entry.0) {
Some(arr) => arr.push(entry.1)
None => {
sub_vals[entry.0] = [entry.1]
sub_keys.push(entry.0)
}
}
}
for sub in sub_keys {
match sub_vals.get(sub) {
Some(arr) => {
let out = if options.compact_arrays && arr.length() == 1 {
arr[0]
} else {
JsonValue::Array(arr)
}
sub_pairs.push((sub, out))
}
None => ()
}
}
// 役42 施工勘误复回:reverse 映射组出键的原 push(拆错位 nest
// 接线时被一并抹除——#t0036 isKnownBy 顶层提升位)
pairs.push((name, JsonValue::Object(sub_pairs)))
}
None => ()
}
}
Ok(JsonValue::Object(pairs))
}
///|
/// 容器逆形映射条目(§8.43 成批机制——fit + 子键 + 键化成员剥离一体;
/// None = 值不入该映射容器,落常规分选)。四类差异钉:
/// @id = 节点 @id 作键(@id 值位旗词形——exact/vocab 禁)、值 @id 整剥
/// (#tm001/#tm002/#tm005);@type = 首 type 作键(类型位旗词形)、值留
/// 残余 type(#tm003/#tm004);@index = @index 原文作键、值 @index 剥;
/// @language = 语言原文作键、值裸串(#tm012/#tla01);无键化成员 →
/// @none 兜底(别名词形感知——#tm012 "none" / #tm017 字面)
fn compact_map_entry(
value : ExpandedValue,
td : TermDefinition,
active : ActiveContext,
options : JsonLdOptions,
) -> (String, JsonValue)? {
let none_key = compact_iri_word("@none", true, false, true, active, options)
match td.container {
Some(Id) =>
match value {
ExpandedValue::Node(child) => {
let subkey = match child.id {
Some(id) =>
compact_iri_word(id, false, false, true, active, options)
None => none_key
}
let id_key = compact_iri_word(
"@id", true, false, true, active, options,
)
match compact_node_jv(child, active, options) {
Ok(JsonValue::Object(pairs)) => {
let stripped : Array[(String, JsonValue)] = []
for p in pairs {
if p.0 != id_key {
stripped.push(p)
}
}
Some((subkey, JsonValue::Object(stripped)))
}
Ok(other) => Some((subkey, other))
Err(_) => None
}
}
_ => None
}
Some(Type) => {
let first : String? = match value {
ExpandedValue::Node(child) => child.types.get(0)
ExpandedValue::Value(lit) => lit.datatype
_ => None
}
let rendered : Result[JsonValue, JsonLdError] = match value {
ExpandedValue::Node(child) => {
// 首 type 键化、残余留(#tm004 oracle——types[1:] 浅拷贝出形);
// 剥后 id-only ⇒ 串塌缩**无条件**(#tm020/#tm022 oracle——不要求
// td @type;vocab 旗仍随 td:tm020 base 相对 / tm022 vocab 相对)
let rest : Array[String] = []
for i in 1..
match compact_apply_type_scoped([t0], active, options) {
Ok(c) => c
// Option 契约无 Err 通道——错误经兜底分选的
// compact_node_jv(含同款 apply)重现,不丢
Err(_) => return None
}
None => active
}
if variant_bare {
match variant.id {
Some(id) =>
Ok(
JsonValue::String(
compact_iri_word(
id,
false,
td.datatype is Some("@vocab"),
true,
keyed_chain,
options,
),
),
)
None => compact_node_jv(variant, keyed_chain, options)
}
} else {
compact_node_jv(variant, keyed_chain, options)
}
}
ExpandedValue::Value(lit) => {
// 字面量 datatype 键化 → 值对象去 @type 对
let rendered_lit = compact_value_jv(
lit,
None,
active,
active,
options,
)
match rendered_lit {
JsonValue::Object(pairs) => {
let stripped : Array[(String, JsonValue)] = []
for p in pairs {
if p.0 != "@type" {
stripped.push(p)
}
}
Ok(JsonValue::Object(stripped))
}
other => Ok(other)
}
}
_ => Ok(JsonValue::Null)
}
match rendered {
Ok(jv) => {
let subkey = match first {
Some(t) => compact_iri_word(t, true, true, true, active, options)
None => none_key
}
Some((subkey, jv))
}
Err(_) => None
}
}
Some(Index) => {
// 属性化索引分支(§8.47——td.index_mapping Some:子键 = index 属性
// 首值;残余留;键化值节点对象形 / @none 组走常规值压缩——不对称出形)
match td.index_mapping {
Some(raw_index_name) => {
// index_mapping 存原始 term 名(展开侧同款——用时期 IRI 展开得
// 属性 IRI;expand_iri vocab=false/docRel=true 与展开侧 3880 行
// 同参——@base 随压缩 active 链)
let prop_iri = match
IriExpander::expand_iri(
StandardExpander::{ },
active,
raw_index_name,
false,
true,
) {
Ok(iri) => iri
Err(_) => raw_index_name
}
match value {
ExpandedValue::Node(child) => {
let idx_vals : Array[ExpandedValue] = []
let rest_props : Array[(String, Array[ExpandedValue])] = []
for p in child.properties {
if p.0 == prop_iri {
for iv in p.1 {
idx_vals.push(iv)
}
} else {
rest_props.push(p)
}
}
// 子键推导:首值字面量直取串;节点引用按 prop 自己的 term 定
// 塌缩(无 term / 不可塌缩 = 无键 → @none——#tpi06 oracle)
let subkey_opt : String? = match idx_vals.get(0) {
Some(ExpandedValue::Value(lit)) => Some(lit.value)
Some(ExpandedValue::Node(ref_node)) =>
if ref_node.id is Some(_) {
let prop_cands = compact_term_candidates(
prop_iri, false, active,
)
let prop_td : TermDefinition? = match prop_cands {
pcs if pcs.length() > 0 => active.terms.get(pcs[0])
_ => None
}
match prop_td {
Some(ptd) =>
match ptd.datatype {
Some("@id") | Some("@vocab") =>
match ref_node.id {
Some(rid) =>
Some(
compact_iri_word(
rid,
false,
ptd.datatype is Some("@vocab"),
true,
active,
options,
),
)
None => None
}
_ => None
}
None => None
}
} else {
None
}
_ => None
}
// variant:prop 属性 = 残余值(空则整剥)
let variant_props : Array[(String, Array[ExpandedValue])] = []
for p in rest_props {
variant_props.push(p)
}
let variant : ExpandedNode = {
id: child.id,
types: child.types,
properties: variant_props,
reverse_props: child.reverse_props,
graph: child.graph,
included: child.included,
index: child.index,
}
if idx_vals.length() > 0 {
// 无键(首值不可串塌缩——#tpi06 oracle)→ 全形落 @none 组
//(prop 保原形走常规渲染)
if subkey_opt is None {
match
compact_value_item(value, Some(td), active, active, options) {
Ok(jv) => return Some((none_key, jv))
Err(_) => return None
}
}
// 键化(或有残余可键化候选):子键取首值;variant 挂残余
if rest_props.length() == child.properties.length() - 1 ||
idx_vals.length() > 1 {
// 残余值重挂回 prop 属性(首值已键化)
let with_rest : Array[(String, Array[ExpandedValue])] = []
for p in variant_props {
with_rest.push(p)
}
if idx_vals.length() > 1 {
let rest_vals : Array[ExpandedValue] = []
for i in 1.. {
let subkey = match subkey_opt {
Some(sk) => sk
None => none_key
}
return Some((subkey, jv))
}
Err(_) => return None
}
} else {
match compact_node_jv(variant, active, options) {
Ok(jv) => {
let subkey = match subkey_opt {
Some(sk) => sk
None => none_key
}
return Some((subkey, jv))
}
Err(_) => return None
}
}
} else {
// 无 index 属性 → @none 组(常规值压缩链——#tpi05/#tpi06)
match
compact_value_item(value, Some(td), active, active, options) {
Ok(jv) => return Some((none_key, jv))
Err(_) => return None
}
}
}
_ => return None
}
}
None => ()
}
let subkey_opt : String? = match value {
ExpandedValue::Node(child) => child.index
ExpandedValue::Value(lit) => lit.index
_ => None
}
let rendered : Result[JsonValue, JsonLdError] = match value {
ExpandedValue::Node(child) => {
// @index 键化 → 剥(浅拷贝 index None)
let variant : ExpandedNode = {
id: child.id,
types: child.types,
properties: child.properties,
reverse_props: child.reverse_props,
graph: child.graph,
included: child.included,
index: None,
}
compact_node_jv(variant, active, options)
}
ExpandedValue::Value(lit) => {
// index 清除副本(#tm009 oracle——字面量 index 键化后须裸形;
// index 在场会挡 compact_value_jv 缺省裸臂)
let bare_lit : ExpandedLiteral = {
value: lit.value,
datatype: lit.datatype,
language: lit.language,
direction: lit.direction,
raw: lit.raw,
index: None,
included: lit.included,
}
Ok(compact_value_jv(bare_lit, None, active, active, options))
}
_ => Ok(JsonValue::Null)
}
match rendered {
Ok(jv) => {
let subkey = match subkey_opt {
Some(ix) => ix
None => none_key
}
Some((subkey, jv))
}
Err(_) => None
}
}
Some(Language) =>
match value {
ExpandedValue::Value(lit) => {
// 方向过滤(§8.48——方向不合者不入语言图,落残余兜底键——
// #tdi07 oracle:不合值成 vocab:label 值对象数组)
let dir_ok = match td.direction_mapping {
Some(Set(d)) => lit.direction == Some(d)
Some(Null) => lit.direction is None
None =>
match lit.direction {
Some(d) => d == active.default_direction.unwrap_or("")
None => active.default_direction is None
}
}
// index 闸(#t0065 oracle——带 @index 的值不入语言图,落残余
// 兜底键全 IRI 值对象形)
if !dir_ok || lit.index is Some(_) {
return None
}
// 串值裸形(语言键化即剥——#tm012 oracle);无语言 → @none
let subkey = match lit.language {
Some(lang) => lang
None => none_key
}
Some((subkey, JsonValue::String(literal_value_of(lit))))
}
_ => None
}
_ => None
}
}
///|
/// 字面量串值取形(language map 裸形用;raw 在场取 raw——数值型语言图
/// 不入册,此处词形兜底)
fn literal_value_of(lit : ExpandedLiteral) -> String {
lit.value
}
///|
/// type-scoped context 套用(压缩面——tc013/tc006/tc014 族):节点 types 依次
/// 命中带 local_context 的 term(词表候选序)即套用(type_scoped 旗 =
/// propagate false + override protected false);套用失败回退当前链
/// (定义期已校验,运行期失败罕见——回退不吞数据)
fn compact_apply_type_scoped(
types : Array[String],
active : ActiveContext,
options : JsonLdOptions,
) -> Result[ActiveContext, JsonLdError] {
let mut current = active
// 逆序套用(REC §6.1.2 步 12.8.1——@type 数组倒序逐个套 type-scoped
// context,先位类型后手胜——#tc017 oracle:[Foo,Bar] Foo 的 prop 胜出)
for i in 0..
match td.local_context {
Some(lc) =>
match
ContextProcessor::process_context(
StandardExpander::{ },
current,
lc,
ContextFlags::type_scoped(),
options,
) {
Ok(c) => current = c
// 保护重定义等 invalid 不吞(#tpr03 oracle——NEG 须达
// compact_document;吞错即 neg-ok 假绿)
Err(e) => return Err(e)
}
None => ()
}
None => ()
}
}
}
Ok(current)
}
///|
/// 图对象值位渲染(REC 12.8.8.4——节点渲染后,@graph 键(含别名)单内容
/// 塌缩为对象形;关键字词条 as-array 恒真不在此路——#t0092/#t0094/#t0080
/// /#t0083 oracle 值位塌缩,#t0016 关键词条保数组)
fn compact_node_graph_value_jv(
node : ExpandedNode,
active : ActiveContext,
options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
let jv = compact_node_jv(node, active, options)
match jv {
Ok(JsonValue::Object(pairs)) => {
let graph_key = compact_iri_word(
"@graph", true, false, true, active, options,
)
let out : Array[(String, JsonValue)] = []
for p in pairs {
if p.0 == graph_key {
match p.1 {
JsonValue::Array(items) =>
if options.compact_arrays && items.length() == 1 {
out.push((p.0, items[0]))
} else {
out.push(p)
}
_ => out.push(p)
}
} else {
out.push(p)
}
}
Ok(JsonValue::Object(out))
}
_ => jv
}
}
///|
/// list 值 bare 适配(役30 从 bare_fit 内联链抽出——顶层具名函数规避
/// 深嵌套 match 文法坑;役31 闸形与 Value 臂对齐——datatype 分支优先,
/// 语言只属无 @type 面):index 闸 / @list 容器闸 / 方向闸 / type 闸
/// (uniform 整列)/ 语言镜像(含标量免疫)
fn compact_list_bare_fit(
items : Array[ExpandedValue],
index : String?,
td : TermDefinition,
active : ActiveContext,
) -> Bool {
if index is Some(_) {
false
} else if td.container is Some(List) {
let dir_ok = match td.direction_mapping {
Some(Set(d)) =>
match items.get(0) {
Some(ExpandedValue::Value(lit)) => lit.direction == Some(d)
_ => true
}
_ => true
}
if !dir_ok {
false
} else {
match td.datatype {
// @type 版 term:整列 uniform datatype 才吸收(与 Value 臂同形——
// datatype 吸收面与语言无关);混合 typed 列不归 @type term
Some(dt) => {
let mut uniform = true
for item in items {
let item_dt : String? = match item {
ExpandedValue::Value(lit) => lit.datatype
_ => None
}
if item_dt != Some(dt) {
uniform = false
}
}
uniform
}
None => {
// 无 @type:语言镜像(首项定调)+ 免注免疫(raw 标量 + typed 值
// 对象——重展开不受缺省语言注入);type 面恒容——typed 项以值
// 对象形居裸数组(役27 None⇒typed 值对象形;#t0024 termTL0 混合列)
let first_lang = match items.get(0) {
Some(ExpandedValue::Value(lit)) => lit.language
_ => None
}
let first_immune = match items.get(0) {
Some(ExpandedValue::Value(lit)) =>
lit.raw is Some(_) || lit.datatype is Some(_)
_ => false
}
let lang_ok = match td.language_mapping {
Some(Set(lang)) =>
match first_lang {
Some(fl) => lang_tag_eq(fl, lang)
None => false
}
Some(Null) => first_lang is None
None => {
// 缺省对齐(termLL0 收缺省 de 项);免注免疫(#t0018 [1,2]
// 数值列、#t0024 termTL0 typed 列)
let aligned = match first_lang {
Some(l) => lang_tag_eq(l, active.default_language.unwrap_or(""))
None => active.default_language is None
}
first_immune || aligned
}
}
lang_ok
}
}
}
} else {
false
}
}