///|
/// 标准 expansion 实现(手写树递归;ADR-JL-4 纯函数,ActiveContext 不可变)。
/// 落码节奏:按套件迁移顺序逐方法落码——每落一个方法配一个对拍;
/// 未落码方法一律 `Unsupported("pending …")`(红而非错),禁止猜测语义。
/// **挂账红语义**:未落码方法的 `Unsupported` = 内部标记 + 账记,**门仍绿**
/// (deny-warn 双档含 stub 全过)——"全绿"≠"全实现";落码进度 = 契约烟雾的
/// Unsupported 计数(expand_standard_wbtest.mbt,常驻活账)。
/// 当前覆盖:#t0001(@id + free-floating drop)+ #t0045(顶层值对象丢弃)+
/// ①批 coercion **形态 A**(显式 @type → datatype)+ ②批 term/IRI 分拣链 +
/// ③批 @context 简单形态(term→IRI / @vocab / @base)+ #ter48(@id 位相对拒绝)。
/// **形态边界**:本批仅形态 A;形态 B(@id coercion)/ 形态 C(@json)挂 term/IRI 批后。
///|
pub(all) struct StandardExpander {}
///|
/// 显式升格(const §5 同款纪律)——仅列被 Type::method / 引擎形式调用的方法
extend StandardExpander with Expander::{
expand_element,
expand_value,
expand_array,
expand_term,
}
///|
/// IRI 合法性(形态 A 片):含 scheme 冒号且无空白("baz z" 类拒之——#t0123)
fn is_valid_absolute_iri(value : String) -> Bool {
// scheme 冒号须在非首位(":fish" 空 scheme 非绝对——#t0118 oracle:走 vocab 拼接);
// scheme 首字符须为 ALPHA(RFC 3986 scheme 文法,规范 IRI 正则同款——"_:dt"
// 的 scheme "_" 非法 ⇒ 非绝对,#ter40 oracle:bnode 不得作 datatype)
let mut i = 0
let mut has_colon = false
let mut scheme_initial_alpha = false
for ch in value {
if i == 0 {
scheme_initial_alpha = ch is ('a'..='z' | 'A'..='Z')
}
if ch is ':' && i > 0 {
has_colon = true
}
if ch is (' ' | '\t' | '\n' | '\r') {
return false
}
i += 1
}
scheme_initial_alpha && has_colon
}
///|
/// 真关键字判定(REC 1.7 关键字表——keyword 形态 + 表成员;"@iri" 等
/// keyword 形态但非表成员者**不是**关键字)
fn is_keyword(value : String) -> Bool {
value
is ("@base"
| "@container"
| "@context"
| "@direction"
| "@graph"
| "@id"
| "@import"
| "@included"
| "@index"
| "@json"
| "@language"
| "@list"
| "@nest"
| "@none"
| "@prefix"
| "@propagate"
| "@protected"
| "@reverse"
| "@set"
| "@type"
| "@value"
| "@version"
| "@vocab")
}
///|
/// keyword 形态判定(戊批——REC 3.1:"@" 后跟一个或多个纯 ALPHA;REC 关键字
/// 均为此形态。"@" 空尾 / 含 "." 等非 ALPHA = 非 keyword 形态,可作普通 term
/// ——#t0119 oracle:@/@foo.bar 可用、@ignoreMe(纯 ALPHA)被忽略)
fn has_keyword_form(value : String) -> Bool {
match value.get_char(0) {
Some('@') => {
let mut seen_at = false
let mut alpha_count = 0
let mut all_alpha = true
for ch in value {
if !seen_at {
seen_at = true
} else if ch is ('a'..='z' | 'A'..='Z') {
alpha_count += 1
} else {
all_alpha = false
break
}
}
all_alpha && alpha_count >= 1
}
_ => false
}
}
///|
/// bnode 判定(_: 前缀)
fn is_bnode_label(value : String) -> Bool {
match value.get_char(0) {
Some('_') =>
match value.get_char(1) {
Some(':') => true
_ => false
}
_ => false
}
}
///|
/// 首个冒号的代码单元索引(compact 分段用;冒号为 BMP ⇒ 切片边界必合法)
fn colon_index(value : String) -> Int? {
let mut i = 0
for ch in value {
if ch is ':' {
return Some(i)
}
i += 1
}
None
}
///|
/// 冒号切分(平台中立:字符迭代拼装,避免 StringView 切片的抛错面差异)。
/// 语义:at 位为冒号本身——prefix = at 之前、suffix = at 之后(冒号丢弃)
fn split_first_colon(value : String, at : Int) -> (String, String) {
let pre = StringBuilder()
let suf = StringBuilder()
let mut i = 0
for ch in value {
if i < at {
pre <+ "\{ch}"
} else if i > at {
suf <+ "\{ch}"
}
i += 1
}
(pre.to_string(), suf.to_string())
}
///|
/// authority 根截取(scheme://authority——绝对路径引用替换 path 时的保留段;
/// 平台中立字符迭代,避免切片抛错面差异)。无 scheme 冒号或 scheme 后非 "//"
/// → None(链式相对 base 无 authority 形态,落回相对合并)
fn authority_root(value : String) -> String? {
let scheme_end = match colon_index(value) {
Some(at) => at
None => return None
}
if !(value.get_char(scheme_end + 1) is Some('/') &&
value.get_char(scheme_end + 2) is Some('/')) {
return None
}
// scheme:// 后逐字符累加至首个 "/"(authority 终点;无 → 至串尾)
let root = StringBuilder()
let mut i = 0
for ch in value {
if i > scheme_end + 2 && ch is '/' {
break
}
root <+ "\{ch}"
i += 1
}
Some(root.to_string())
}
///|
/// RFC 3986 5.2.3 merge:value 绝对路径引用("/" 开头)→ authority 保留、
/// path 整体替换(#t0051 oracle:/issue/1 → https://w3c.github.io/issue/1);
/// value 相对 → base 截最后 "/"(含)+ value;base 无 "/" → 补根 "/"
/// (#t0129/#t0130 oracle:/example + relative-iri → /example/relative-iri)
fn merge_base_reference(base : String, reference : String) -> String {
if base is "" {
return reference
}
// 空引用(RFC 3986 5.2:R.path 空且无 query/fragment ⇒ T = base 原样——
// toRdf #t0016 oracle:@id "" 取文档全 URL 含文件名,不得坍缩到目录)
if reference is "" {
return base
}
// base 自身 fragment 剥除(RFC 3986 5.2:fragment 不参与 base 解析——
// #t0029 oracle:base "file#with-a-fragment" + "#fragment-works" →
// "file#fragment-works")
let base = match base.find("#") {
Some(idx) => base[:idx].to_owned()
None => base
}
// scheme 相对引用("//" 开头 = 引用自带 authority——RFC 3986 5.2.3:
// 保留 base scheme、authority 整体换新,#t0029 oracle)
if reference.get_char(0) is Some('/') && reference.get_char(1) is Some('/') {
match colon_index(base) {
Some(at) => {
let scheme = base[:at + 1].to_owned()
return "\{scheme}\{reference}"
}
None => ()
}
}
// 同文档 fragment 引用(#t0029 oracle:#fragment-works 锚定 base 全 URL,
// 不动 path);base 自带 fragment 场景套件内不出现
if reference.get_char(0) is Some('#') {
return "\{base}\{reference}"
}
// 查询引用("?" 开头):剥 base 自身 query 后拼接(RFC 3986 5.2.3)
if reference.get_char(0) is Some('?') {
match base.find("?") {
Some(idx) => {
let head = base[:idx].to_owned()
return "\{head}\{reference}"
}
None => return "\{base}\{reference}"
}
}
if reference.get_char(0) is Some('/') {
match authority_root(base) {
Some(root) => return "\{root}\{reference}"
None => () // 无 authority 形态 → 落相对合并
}
}
// 相对合并(RFC 5.2.3):idx 落在 scheme 后 "//" 尾内(http://a 的 idx=6)
// ⇒ base.path 空 ⇒ 补根 "/";否则 idx = 目录段截位 ⇒ 截最后 "/"(含)+
// 引用(目录替换——#t0131/#t0132 oracle:tag:example/foo + a →
// tag:example/a)。authority 感知边界取代 idx>6 魔数(短 scheme 角:
// a://b/c、ex:/a 曾误走补根臂——套件不可达,wbtest oracle 钉)
match base.rev_find("/") {
Some(idx) => {
let authority_slash_end = match colon_index(base) {
Some(at) =>
if base.get_char(at + 1) is Some('/') &&
base.get_char(at + 2) is Some('/') {
at + 2
} else {
-1
}
None => -1
}
if idx <= authority_slash_end {
"\{base}/\{reference}"
} else {
let head = base[:idx + 1].to_owned()
"\{head}\{reference}"
}
}
None =>
// base 全串无 "/"(B组①——scheme-only 形:"tag:example"/"ex:"):
// RFC 5.2.3 base.path 无目录段 ⇒ 引用替换整段路径、scheme 保留
//(#t0130/#tli11 oracle:tag:example+a → tag:a、ex:+test → ex:test)
match colon_index(base) {
Some(at) => "\{base[:at + 1].to_owned()}\{reference}"
None => "\{base}\{reference}"
}
}
}
///|
/// authority/path 切分(RFC 3986 5.2.4 点段消除只作用于 path——平台中立
/// 字符迭代拼装)。无 scheme://authority 形态 → ("", 全串)
fn split_authority_path(value : String) -> (String, String) {
let scheme_end = match colon_index(value) {
Some(at) => at
None => return ("", value)
}
if !(value.get_char(scheme_end + 1) is Some('/') &&
value.get_char(scheme_end + 2) is Some('/')) {
return ("", value)
}
let authority = StringBuilder()
let path = StringBuilder()
let mut in_path = false
let mut i = 0
for ch in value {
if i > scheme_end + 2 && ch is '/' {
in_path = true
}
if in_path {
path.write_string("\{ch}")
} else {
authority.write_string("\{ch}")
}
i += 1
}
(authority.to_string(), path.to_string())
}
///|
fn resolve_base_value(value : String, previous : String?) -> String {
if is_valid_absolute_iri(value) {
return value
}
let prev = match previous {
Some(b) => b
None => ""
}
// A组①(#t0122 s091 oracle vs #t0127 s296 反例族):引用拆 = 路径部分
// (首个 ?/# 之前)+ 尾巴(query/fragment 原样照挂)。**路径部分为空 ⇒
// merge 原样、不进路径处理**(RFC 5.2:R.path 未定义时 T.path = Base.path
// 词面——s091 base "/./" 保留);非空 ⇒ 段算法跑 merge 后整条路径(s296
// ".?a=b" → base 目录 + "?a=b")。
let mut cut = -1
for i in 0..= 0 { value[:cut].to_owned() } else { value }
let ref_suffix = if cut >= 0 { value[cut:].to_owned() } else { "" }
if ref_path is "" {
return merge_base_reference(prev, value)
}
let merged = merge_base_reference(prev, ref_path)
let (authority, full_path) = split_authority_path(merged)
if authority is "" {
return merged
}
// A组②(RFC 3986 5.2.4——remove_dot_segments 作用于 **merge 后整条
// path**,base 贡献段一并消除:#t0091 oracle,base "/./" 不保留;路径
// 为空时才词面保留——上方 A① 早退)。段源 = full_path **去首段**(首段
// 恒为根标记 "",由 seeds=[""] 承担):斜杠开形("/x" / "//authority/x")
// merge 已换根,full_path 即引用自身路径(base 段不入);相对形 full_path
// = base 目录 + 引用(merge 只换尾段),点段规则均匀作用两方贡献段。
// "//g" 合并后 full_path="" ⇒ 段空 ⇒ 渲染裸 authority(s006 oracle)。
// 段规则:"." 丢弃(**段尾 "." ⇒ 尾斜杠**,s117 "./g/." → "g/");".."
// 弹一段(**守卫 >1 护根标记——超根截停**,#t0029 still-root oracle;
// **段尾 ".." ⇒ 尾斜杠**);**内部空段保真**(RFC 不折叠 "//"——#t0128
// base "ab//de" oracle;s303 "../xyz" 弹掉的正是空段 → 单斜杠);
// **段尾空段 ⇒ 尾斜杠**。#t0122 s100:/bb/ccc/../../.. → "/"。
let segs : Array[String] = [""]
let raw = full_path.split("/").to_array()
let tokens : Array[String] = []
for j in 1..1:segs[0] 是根标记(""),禁弹(钉①——超根截停)
if segs.length() > 1 {
let _ = segs.pop()
}
if i == n - 1 {
trailing = true
}
} else if seg is "." {
if i == n - 1 {
trailing = true
}
} else if seg is "" {
// 空段:内部 = 保真入栈(#t0128);段尾 = 尾斜杠标记,不入栈
if i == n - 1 {
trailing = true
} else {
segs.push(seg)
}
} else {
segs.push(seg)
}
}
let out = StringBuilder()
out.write_string(authority)
for i, seg in segs {
// i>0 条件 join:首段(根标记 "")不产生斜杠——authority 自带 "//"
if i > 0 {
out.write_string("/")
}
out.write_string(seg)
}
if trailing {
out.write_string("/")
}
out.write_string(ref_suffix)
out.to_string()
}
///|
/// 字面量构造(raw = 原始标量:number/bool 保形;string 为 None)
fn literal_of(value : String, raw : JsonValue?) -> ExpandedValue {
ExpandedValue::Value(ExpandedLiteral::{
value,
datatype: None,
language: None,
direction: None,
raw,
index: None,
included: None,
})
}
///|
/// IRI 展开(②批分拣链 + ③批 vocab/vocab 位门控):对齐 iri_rules 表序
/// (keyword → term[仅 vocab 位] → bnode → compact → absolute → vocab relative → relative)。
/// **#ter48 oracle**:@id 位 terms-are-not-considered(vocab=false ⇒ 不查表不拼 vocab)。
/// **边界行为**:term 未命中/前缀未声明 = 非错误落链;value 位无 vocab 无 base = 原样。
impl IriExpander for StandardExpander with fn expand_iri(
_self,
active : ActiveContext,
value : String,
document_relative : Bool,
vocab : Bool,
) -> Result[String, JsonLdError] {
// 1 keyword:keyword 形态("@" 后纯 ALPHA ≥1)原样返回——**未知 keyword
// 亦原样**(节点位由属性门/忽略语义处理,#t0119/#tpr34/#tpr36 oracle);
// "@" 空尾或含非 ALPHA("." 等)= 非 keyword 形态,继续分拣(可命中 term
// 定义——#t0119 oracle:@/@foo.bar term 可用)
if has_keyword_form(value) {
return Ok(value)
}
// 2 term 精确查表:仅 vocab 位
if vocab {
match active.terms.get(value) {
Some(td) =>
match td.iri {
Some(iri) => return Ok(iri)
None => ()
}
None => ()
}
}
// 3 bnode:_: 前缀原样
if is_bnode_label(value) {
return Ok(value)
}
// 4 compact:prefix ∈ terms 且未被 @prefix:false 抑制(#tpr29 oracle)→
// 定义 IRI + suffix;**suffix 以 "//" 开头 = 非 compact**(protocol-like
// authority——#t0067 oracle:http:// 类键不被 term 前缀误吃)
match colon_index(value) {
Some(at) => {
let (prefix, suffix) = split_first_colon(value, at)
let double_slash = suffix.get_char(0) is Some('/') &&
suffix.get_char(1) is Some('/')
if !double_slash {
match active.terms.get(prefix) {
Some(td) =>
if td.is_prefix is Some(false) {
()
} else {
match td.iri {
Some(base_iri) => return Ok("\{base_iri}\{suffix}")
None => ()
}
}
None => ()
}
}
}
None => ()
}
// 5 absolute:scheme 冒号 + 无空白 → 原样
if is_valid_absolute_iri(value) {
return Ok(value)
}
// 6 vocab relative:无冒号 + vocab 位为真 → 拼接(③批解挂;
// #ter48 oracle:@id 位 vocab=false ⇒ @vocab 不参与)
let meaningful_colon = colon_index(value) is Some(at) && at > 0
if !meaningful_colon && vocab {
match active.vocab {
Some(v) => return Ok("\{v}\{value}")
None => ()
}
}
// 7 relative(document_relative 门控——「vocab 位 base 门控批」):
// vocab 位键展开(docRel=false)→ 原样返回不落 base merge(#t0003 oracle:
// 未映射 term 须被属性门丢弃,全局注入默认 base 亦不得改变键行为);
// document-relative 位(@id/@type 值、coercion 值——#t0051/#t0050/#t0056/
// #t0057 oracle)→ 复用 resolve_base_value(raw 合并 + RFC 3986 5.2.4
// 点段消除——#t0029 oracle:./ 与 ../ 相对解析消点段);无 base → 原样
if !document_relative {
return Ok(value)
}
match active.base {
Some(base) => Ok(resolve_base_value(value, Some(base)))
None => Ok(value)
}
}
///|
impl IriExpander for StandardExpander with fn handle_keyword(
_self,
value : String,
) -> Result[String, JsonLdError] {
// 关键字原样通过(@xxx 不展开)
Ok(value)
}
///|
/// compact 展开(②批):prefix ∈ terms → 定义 IRI + suffix;
/// 未声明前缀 = 非错误拒绝(InvalidValue——落链语义由 expand_iri 负责)
impl IriExpander for StandardExpander with fn expand_compact(
_self,
active : ActiveContext,
value : String,
_document_relative : Bool,
_vocab : Bool,
) -> Result[String, JsonLdError] {
match colon_index(value) {
Some(at) => {
let (prefix, suffix) = split_first_colon(value, at)
match active.terms.get(prefix) {
Some(td) =>
match td.iri {
Some(base_iri) => Ok("\{base_iri}\{suffix}")
None => Err(JsonLdError::InvalidValue("compact prefix without IRI"))
}
None => Err(JsonLdError::InvalidValue("compact prefix not declared"))
}
}
None => Err(JsonLdError::InvalidValue("expand_compact: no colon"))
}
}
///|
/// vocab 相对拼接(③批解挂):@vocab 未声明 = Err 非静默拼接;命中 = vocab + value
impl IriExpander for StandardExpander with fn concat_vocab(
_self,
active : ActiveContext,
value : String,
) -> Result[String, JsonLdError] {
match active.vocab {
Some(vocab) => Ok("\{vocab}\{value}")
None => Err(JsonLdError::InvalidValue("@vocab not declared"))
}
}
///|
/// base 相对解析(②批):简单拼接(RFC3986 完整解析挂后续)
impl IriExpander for StandardExpander with fn resolve_base(
_self,
active : ActiveContext,
value : String,
) -> Result[String, JsonLdError] {
Ok(resolve_base_value(value, active.base))
}
///|
/// term 查表(②批):s ∈ active.terms → 定义 IRI;未命中 = 非错误拒绝
/// (落链语义由 expand_iri 负责)
impl IriExpander for StandardExpander with fn lookup_term(
_self,
active : ActiveContext,
value : String,
) -> Result[String, JsonLdError] {
match active.terms.get(value) {
Some(td) =>
match td.iri {
Some(iri) => Ok(iri)
None => Err(JsonLdError::InvalidValue("term definition without IRI"))
}
None => Err(JsonLdError::Unsupported("term not in constructed context"))
}
}
///|
/// term 名是否触发一致性检查(REC 4.2.2 @id 臂):term 名含冒号(非首位、
/// 非末位)或含斜杠(任意位置)——此形态的 term 名本身是一次 IRI 引用,
/// 它自身的展开必须与 IRI 映射一致
fn term_name_has_colon_or_slash(term_name : String) -> Bool {
let length = term_name.length()
match colon_index(term_name) {
Some(at) => if at > 0 && at < length - 1 { return true }
None => ()
}
term_name.contains("/")
}
///|
/// term 名一致性检查(REC 4.2.2 @id 臂 "must be consistent" 条):term 名的
/// IRI 展开 == 该 term 的 IRI 映射;不符 → invalid IRI mapping。**1.0 模式不做
/// 检查**(#t0026 oracle:1.0 同输入正例)。两个 oracle:
/// #ter43(负·1.1)——长名 rdf:type 别名 @type:1.1 非法 / 1.0 合法;
/// #ter44(负·1.1)——compact 形 term 名映射到别处(v:term → v:somethingElse)
fn StandardExpander::check_term_name_consistency(
self : StandardExpander,
building : ActiveContext,
term_name : String,
iri_mapping : String,
options : JsonLdOptions,
) -> Result[Unit, JsonLdError] {
if options.processing_mode is JsonLd10 {
return Ok(())
}
if !term_name_has_colon_or_slash(term_name) {
return Ok(())
}
match IriExpander::expand_iri(self, building, term_name, false, true) {
Ok(self_expanded) =>
if self_expanded == iri_mapping {
Ok(())
} else {
Err(JsonLdError::InvalidIri("invalid IRI mapping"))
}
Err(e) => Err(e)
}
}
///|
/// propagate=false 时的 previous context 置位(REC 4.1.2 步 3):result 尚无
/// previous 才置为传入的 active("已有 previous 不覆盖"——外层非传播 context 的
/// 快照优先保留);propagate=true = 不置位。快照共享 active.terms 引用(本引擎
/// 的 context 构造一律先复制 terms 再改,输入 map 视为不可变)
fn previous_after(active : ActiveContext, propagate : Bool) -> PreviousContext? {
match active.previous {
Some(previous) => Some(previous)
None =>
if propagate {
None
} else {
Some(PreviousContext::{
base: active.base,
vocab: active.vocab,
terms: active.terms,
})
}
}
}
///|
/// 回退到 previous context(REC 5.1.2 映射分支:"set active context to previous
/// context from active context")——无 previous = 原样返回(顶层/传播位)
fn StandardExpander::revert_previous(active : ActiveContext) -> ActiveContext {
match active.previous {
Some(previous) =>
ActiveContext::{
base: previous.base,
vocab: previous.vocab,
terms: previous.terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
None => active
}
}
///|
/// 取元素级 `@context` 值(REC 5.1.2 "If element contains the entry @context")。
/// 只认字面 `@context` 键:关键字别名 @context 被规范禁止(invalid keyword alias)
fn element_context_value(pairs : Array[(String, JsonValue)]) -> JsonValue? {
for pair in pairs {
if pair.0 is "@context" {
return Some(pair.1)
}
}
None
}
///|
/// 键是否展开为指定关键字·**快路**(常数因子优化:形状判定/嗅探/type-scoped 扫描
/// 原先对每个键跑一次完整 IRI 展开——热路径上按"每键一次展开"付学费)。
/// 快路 = 字面命中 或 该键的 term 定义 IRI 就是该关键字;**保守回退**:仅当
/// vocab 含 `@`(病态:vocab 拼接理论上能造出 `"@xxx"`,无 oracle)时才走完整展开。
/// compact 形不可能命中关键字(前缀 IRI 是绝对 IRI,拼接结果不以 `@` 起)⇒ 不必展开。
fn StandardExpander::key_expands_to_keyword(
self : StandardExpander,
active : ActiveContext,
key : String,
keyword : String,
) -> Bool {
if key == keyword {
return true
}
match active.terms.get(key) {
Some(term_definition) =>
match term_definition.iri {
Some(iri) => if iri == keyword { return true }
None => ()
}
None => ()
}
match active.vocab {
Some(vocab) =>
if vocab.contains("@") {
return match IriExpander::expand_iri(self, active, key, false, true) {
Ok(expanded) => expanded == keyword
Err(_) => false
}
}
None => ()
}
false
}
///|
/// 值对象/单 @id 形判定(REC 5.1.2 回退门):映射含任一键展开为 @value,或恰为
/// 单条展开为 @id。两者皆以**回退前** context 判定:
/// #tc015 oracle(单 @id 形不回退,type-scoped @base 仍作用);
/// #tc020 oracle(值对象形不回退,type-scoped 的 `value:@value` 别名生效)
fn StandardExpander::is_value_or_id_shaped(
self : StandardExpander,
active : ActiveContext,
element : JsonValue,
) -> Bool {
match element {
JsonValue::Object(pairs) => {
for pair in pairs {
if StandardExpander::key_expands_to_keyword(
self,
active,
pair.0,
"@value",
) {
return true
}
}
if pairs.length() == 1 &&
StandardExpander::key_expands_to_keyword(
self,
active,
pairs[0].0,
"@id",
) {
return true
}
false
}
_ => false
}
}
///|
/// type-scoped context 应用(REC 5.1.2 "For each key … where key IRI expands to
/// @type"):快照 = 传入 active(**元素级 @context 之后**);@type 键按字典序、
/// 值数组项按字典序逐个套用其 td 的 local context(**propagate=false**,置
/// previous context 供新节点回退);后套者叠加于前者。
/// oracle:#tc011/#tc017/#tc018(@type 数组序 ≠ 应用序,字典序定胜负)、
/// #tc008(@type 别名 term 亦触发)、#tc014(`[null]` 空化)、#tc026(@propagate
/// true 覆盖默认 false)。非串项跳过(REC "if term is a string")
fn StandardExpander::apply_type_scoped_contexts(
self : StandardExpander,
active : ActiveContext,
pairs : Array[(String, JsonValue)],
options : JsonLdOptions,
) -> Result[ActiveContext, JsonLdError] {
let type_keys : Array[String] = []
for pair in pairs {
if StandardExpander::key_expands_to_keyword(self, active, pair.0, "@type") {
type_keys.push(pair.0)
}
}
type_keys.sort_by(fn(a, b) { a.compare(b) })
let mut current = active
for key in type_keys {
let terms : Array[String] = []
for pair in pairs {
if pair.0 == key {
match pair.1 {
JsonValue::Array(items) =>
for item in items {
match item {
JsonValue::String(term) => terms.push(term)
_ => ()
}
}
JsonValue::String(term) => terms.push(term)
_ => ()
}
}
}
terms.sort_by(fn(a, b) { a.compare(b) })
for term in terms {
// td 查询用快照,应用累积到 current(REC 原文;#tc018 oracle)
match active.terms.get(term) {
Some(term_definition) =>
match term_definition.local_context {
Some(local_context) =>
match
ContextProcessor::process_context(
self,
current,
local_context,
ContextFlags::type_scoped().with_context_base_url(
term_definition.context_base_url,
),
options,
) {
Ok(next) => current = next
Err(e) => return Err(e)
}
None => ()
}
None => ()
}
}
}
Ok(current)
}
///|
/// 对象展开(①批 coercion + ③批 @context):
/// @id(节点身份,绝对/bnode 校验)/ @type(节点类型数组 + 值对象 datatype 形态 A
/// 含四族验证)/ @value / @language / @direction(值对象族第一批)/
/// @context(③批解挂:process_context 构建/继承新 context,循环内经 current 传播)/
/// 非关键字键 → expand_term 属性位累加(键经 IRI 链展开)。
fn StandardExpander::expand_object(
self : StandardExpander,
active : ActiveContext,
element : JsonValue,
_active_property : String?,
options : JsonLdOptions,
) -> Result[ExpandedValue?, JsonLdError] {
let pairs = match element {
JsonValue::Object(pairs) => pairs
_ => return Err(JsonLdError::InvalidValue("expand_object: not an object"))
}
let mut node_id : String? = None
let types : Array[String] = []
let properties : Array[(String, Array[ExpandedValue])] = []
// 属性槽位索引(役P1 批4——⑧平铺属性合并索引化):同 IRI 属性合并
// (壬子批 #t0034/#t0114 oracle)原为逐键线性扫全量 properties ⇒ O(P²)。
// 与 J2 nest 合并索引(函数尾 property_index)同纪律:整段建一次、由
// merge_property_values 维护、顺序语义不变(先见者占位、逐值追加尾部)。
let property_index : Map[String, Int] = Map([])
let reverse_props : Array[(String, Array[ExpandedValue])] = []
let mut node_graph : Array[ExpandedValue]? = None
// 节点/值对象位 @index(@index mapping 批——#t0036 oracle;值对象位在收尾挂字面量)
let mut node_index : String? = None
// 节点位 @nest 条目收集(nest 纯形批 + scoped-context 批——REC 13.4.14:
// 主键循环中只收集,循环后延后处理;文档序)。条目携带该键的**属性作用域
// context**(scoped-context 批——#tc037/#tc038 oracle:@nest 别名 term 的
// local context 在其值展开时套用)
let nest_entries : Array[(ActiveContext, JsonValue)] = []
// Included Blocks 收集(@included 纯形批——主键循环中只收集不展开,
// 循环后延后处理;值对象位同样收集——#tin05 oracle 保形)
let included_values : Array[JsonValue] = []
let included_nodes : Array[ExpandedNode] = []
let mut literal : ExpandedLiteral? = None
let mut is_value_object = false
let mut saw_value = false // @value 成员可见(丙批后缀规则:language-only 值对象丢弃)
let mut has_datatype = false
// @type 先至挂起位(#tc020——值对象 @type 在 @value 之前出现时 literal
// 尚未建,直接赋值落空;@value 建立后统一回填)
let mut pending_dt : String? = None
let mut has_language = false
let mut has_direction = false
// 元素级 @context 先于主键循环处理(REC 5.1.2 次序:previous 回退 → property-scoped
// → **元素级 @context** → type-scoped → 主键循环)——文档中排在 @context 之前的键
// 亦须看到它(#t0073 oracle:"@context not first property")
let mut current = match element_context_value(pairs) {
Some(JsonValue::Null) => initial_context(options)
Some(local_context) =>
match
ContextProcessor::process_context(
self,
active,
local_context,
ContextFlags::element(),
options,
) {
Ok(next) => next
Err(e) => return Err(e)
}
None => active
}
// type-scoped context 快照(REC 5.1.2:"Initialize type-scoped context to active
// context. This is used for expanding values that may be relevant to any previous
// type-scoped context.")——**@type 值自身**用快照展开(#tc014/#tc018 oracle:
// 空化/无 vocab 的 type-scoped context 不得影响 @type 值展开),其余键用套用后
// 的 current
let type_scoped_context = current
// 套用 type-scoped contexts(快照=上;应用累积到 current,propagate=false)
current = match
StandardExpander::apply_type_scoped_contexts(self, current, pairs, options) {
Ok(next) => next
Err(e) => return Err(e)
}
// frameExpansion 值 pattern 预扫(spec §8.97 值 pattern 批):对象含
// @value/@type/@language 任一**非串形**成分({} 通配 / 数组清单)⇒ 本对象
// 为值 pattern 模式——三键全走成分保形(String 形亦保形,免撞值对象
// datatype/language 门;t0036-45 判定面)
let mut frame_value_pattern = false
if options.frame_expansion {
for scan_pair in pairs {
match scan_pair.0 {
"@value" | "@type" | "@language" =>
if !(scan_pair.1 is JsonValue::String(_)) {
frame_value_pattern = true
}
_ => ()
}
}
}
for pair in pairs {
// null IRI 映射 term("term": null / {"@id": null}——scoped-context 批):
// IRI 展开返回 null ⇒ 键整体丢弃(REC 5.1.2:expanded property 为 null 即
// drop key)。#tin06 oracle:scoped context {"data": null} 下的 "data" 键
// 丢弃——若按"移除定义"处理则会 vocab 拼成属性,是为误判
let term_iri_is_null = match current.terms.get(pair.0) {
Some(term_definition) => term_definition.iri is None
None => false
}
if term_iri_is_null {
continue
}
// 键归一(#ter51 oracle + #t0119 扩):一律经 IRI 链展开一次——真关键字
// 原样(keyword 形态步 1)、@-term 键命中查表、别名 term 汇流关键字位
let expanded_key = match
IriExpander::expand_iri(self, current, pair.0, false, true) {
Ok(k) => k
Err(e) => return Err(e)
}
// 关键字位判定按归一键:字面关键字与别名 term 汇流
let routes_to_keyword = expanded_key.get_char(0) is Some('@')
// 值 pattern 成分保形(值 pattern 模式——三键含 String 形全保形:
// @type 串须 IRI 展开、@value/@language 串词形;{} 通配 = 空 pattern
// 标桩;数组 = 清单摊开;null = 无)
if frame_value_pattern {
match expanded_key {
"@value" | "@type" | "@language" => {
let component_values : Array[ExpandedValue] = []
match pair.1 {
JsonValue::Array(items) =>
for item in items {
match
self.frame_value_pattern_component(
current, expanded_key, item,
) {
Ok(Some(one)) => component_values.push(one)
Ok(None) => ()
Err(e) => return Err(e)
}
}
single_value =>
match
self.frame_value_pattern_component(
current, expanded_key, single_value,
) {
Ok(Some(one)) => component_values.push(one)
Ok(None) => ()
Err(e) => return Err(e)
}
}
properties.push((expanded_key, component_values))
continue
}
_ => ()
}
}
// frameExpansion reverse-term pattern 保形(役52——t0029 判定面):
// term 定义带 @reverse ⇒ 值位是**反向 pattern**(非反向 map)——{}
// 通配/@default 保形入 reverse_props(键 = td 的 reverse IRI)。字面
// "@reverse" 键(t0028 map 形)走原臂不受扰
if options.frame_expansion && !frame_value_pattern {
match current.terms.get(pair.0) {
Some(term_definition) =>
match term_definition.reverse {
Some(rev) => {
let rev_pattern : ExpandedNode = match pair.1 {
JsonValue::Object(value_pairs) => {
let mut has_default_key = false
let default_raw : Array[JsonValue] = []
let mut has_value_key = false
for vp in value_pairs {
if vp.0 is "@default" {
has_default_key = true
match vp.1 {
JsonValue::Array(items) =>
for item in items {
default_raw.push(item)
}
single => default_raw.push(single)
}
}
if vp.0 is "@value" {
has_value_key = true
}
}
if has_default_key && !has_value_key {
let default_values : Array[ExpandedValue] = []
for raw in default_raw {
match self.frame_preserve_default(current, raw, options) {
Ok(values) =>
for v in values {
default_values.push(v)
}
Err(e) => return Err(e)
}
}
{
id: None,
types: [],
properties: [("@default", default_values)],
reverse_props: [],
graph: None,
included: None,
index: None,
}
} else {
match
StandardExpander::expand_element(
self,
current,
pair.1,
_active_property,
options,
) {
Ok(ExpandedValue::Node(pattern_node)) => pattern_node
// Err 传播(役53 t0054 同款——嵌套校验错不吞)
Err(e) => return Err(e)
Ok(_) => frame_empty_pattern()
}
}
}
_ => frame_empty_pattern()
}
reverse_props.push((rev, [ExpandedValue::Node(rev_pattern)]))
continue
}
None => ()
}
None => ()
}
}
if !routes_to_keyword {
// frameExpansion 保形(帧旗批——spec §8.96):帧内性质位对象值
// ①含 @default 键 ⇒ 默认对象 pattern(其余条目忽略——REC Framing
// Grammar);②非值对象形 ⇒ pattern 保形(`{}` 通配 = 展开落空回落
// 空 pattern)。须先于丢弃预扫描(`{}` 通配会撞 drop-lang-only 门)。
// 值对象形(@value)照常规路径(值 pattern 本就展开为 Value);
// @id/@type 位通配/数组形不在本批(t0023+/t0064 批)
if options.frame_expansion {
match pair.1 {
JsonValue::Object(value_pairs) => {
// 键合法性门(役53 tg010——保形臂与常规臂同规:不可展开键
// (相对词无 base/无 term)即弃——REC "expanded property 为
// null 即 drop";"@graph" map 形帧样 subject 相对键 null 误发
// 根因)
if !(is_valid_absolute_iri(expanded_key) ||
is_bnode_label(expanded_key)) {
continue
}
let mut has_value_key = false
let mut has_default_key = false
let default_raw : Array[JsonValue] = []
for vp in value_pairs {
if vp.0 is "@value" {
has_value_key = true
}
if vp.0 is "@default" {
has_default_key = true
match vp.1 {
JsonValue::Array(items) =>
for item in items {
default_raw.push(item)
}
single => default_raw.push(single)
}
}
}
if !has_value_key {
if has_default_key {
// 默认对象 pattern = 仅 @default 性质的 Node pattern
// (值保形见 frame_preserve_default;数组默认值摊开)
let default_values : Array[ExpandedValue] = []
for raw in default_raw {
match self.frame_preserve_default(current, raw, options) {
Ok(values) =>
for v in values {
default_values.push(v)
}
Err(e) => return Err(e)
}
}
properties.push(
(
expanded_key,
[
ExpandedValue::Node({
id: None,
types: [],
properties: [("@default", default_values)],
reverse_props: [],
graph: None,
included: None,
index: None,
}),
],
),
)
continue
}
// @list 模式保形(t0057/58——REC Framing Grammar list
// pattern):帧性质值 = list 对象 ⇒ List pattern(项逐个
// pattern 展开——值对象形落 Value pattern、节点形落 Node
// pattern),不能落节点位(@list 键在节点位即弃 = 空
// pattern = 幽灵全形嵌入)
let mut list_raw : JsonValue? = None
for vp in value_pairs {
if vp.0 is "@list" {
list_raw = Some(vp.1)
}
}
match list_raw {
Some(raw_items) => {
let items = match raw_items {
JsonValue::Array(items) => items
single => [single]
}
let list_pattern_items : Array[ExpandedValue] = []
for item in items {
match
StandardExpander::expand_element(
self, current, item, _active_property, options,
) {
Ok(one) => list_pattern_items.push(one)
Err(e) => return Err(e)
}
}
properties.push(
(
expanded_key,
[ExpandedValue::List(list_pattern_items, index=None)],
),
)
continue
}
None => ()
}
// pattern 保形:正常展开,落空回落空 pattern(`{}` 通配)
let pattern_value = match
StandardExpander::expand_element(
self,
current,
pair.1,
_active_property,
options,
) {
Ok(ExpandedValue::Node(pattern_node)) => pattern_node
// Err 传播(役53 t0054——嵌套帧文法校验错不得被落空臂吞)
Err(e) => return Err(e)
Ok(_) => {
let fresh : ExpandedNode = {
id: None,
types: [],
properties: [],
reverse_props: [],
graph: None,
included: None,
index: None,
}
fresh
}
}
properties.push(
(expanded_key, [ExpandedValue::Node(pattern_value)]),
)
continue
}
}
_ => ()
}
}
// 值对象形态的**整体丢弃**预扫描(REC:被丢弃的值对象其属性不产出;
// 区别于 t0004 空数组字面值保留):
// - @value: null(#t0019 oracle)
// - 仅 @language/@direction 无 @value(#t0008 oracle:drop-lang-only)
if pair.1 is JsonValue::Object(value_pairs) {
let mut has_value_member = false
let mut has_value_null = false
let mut only_lang_dir = true
for vp in value_pairs {
match vp.0 {
"@value" => {
has_value_member = true
if vp.1 is JsonValue::Null {
has_value_null = true
}
}
"@language" | "@direction" | "@type" => ()
_ => only_lang_dir = false
}
}
// @json 值对象豁免(#tjs22 oracle:@type @json 的 null 字面量保留)
let is_json_value = StandardExpander::is_json_value_object(
self, current, value_pairs,
)
if (has_value_null || (only_lang_dir && !has_value_member)) &&
!is_json_value {
continue
}
}
// null 值属性整体丢弃(#t0003 oracle;区别于空数组字面值保留——
// #t0004 oracle:set3: [] 保留);**@json term 豁免**——null 即
// JSON null 字面量(#tjs18 oracle)
if pair.1 is JsonValue::Null {
let json_null_member = match current.terms.get(pair.0) {
Some(term_definition) => term_definition.datatype is Some("@json")
None => false
}
if !json_null_member {
continue
}
}
// term 位(_term 缺省路由):键已归一;属性值展开,键合法即累加
let values = match
StandardExpander::expand_term(
self,
current,
pair.0,
pair.1,
_active_property,
options,
) {
Ok(values) => values
Err(e) => return Err(e)
}
// reverse 项路由(reverse 批——#t0042/#t0049 oracle):值进主体
// @reverse 映射;值对象/@list 形态 → invalid reverse property value
//(#ter36 oracle)
match current.terms.get(pair.0) {
Some(term_definition) =>
match term_definition.reverse {
Some(rev) => {
for v in values {
match v {
ExpandedValue::List(_) | ExpandedValue::Value(_) =>
return Err(
JsonLdError::InvalidValue(
"invalid reverse property value",
),
)
_ => ()
}
}
reverse_props.push((rev, values))
continue
}
None => ()
}
None => ()
}
// 键合法性:绝对 IRI 或 bnode 标签(规范:expanded property 可为 blank
// node identifier——scheme-ALPHA 收紧后 bnode 在此显式放行);
// 空数组属性无条件保留(#t0004 oracle:set1~set5 无 term 定义亦保留
// "set1": [] 空数组——键合法性门不受影响);同 IRI 属性合并(壬子批——
// #t0034/#t0114 oracle:多键同展一 IRI → 单键累加,按输入文档顺序)
if is_valid_absolute_iri(expanded_key) || is_bnode_label(expanded_key) {
// 同 IRI 属性合并(壬子批 #t0034/#t0114 oracle:多键同展一 IRI →
// 单键累加,按输入文档顺序)——役P1 批4 索引化(原逐键线性扫 ⇒
// O(P²);槽位语义与 merge_property_values 全同)
merge_property_values(
properties,
property_index,
(expanded_key, values),
)
}
continue
}
// 关键字分派(按键名;@vocab/@base 等 context 层七键走手写——@context 批)
match expanded_key {
"@id" => {
// 值对象内 @id = 意外关键字(#ter37 oracle)
if is_value_object {
return Err(JsonLdError::InvalidValue("invalid value object"))
}
match pair.1 {
JsonValue::String(iri) =>
match IriExpander::expand_iri(self, current, iri, true, false) {
Ok(expanded) =>
// keyword 形非关键字(@1*ALPHA)= REC §5.2 IRI 展开**返回
// null**("reserved for future use... ignored")——节点保留、
// id 位字面 null(t0122 oracle:"@ignoreMe" → {"@id": null};
// "@"/"@foo.bar" 非 keyword 形 → 正常 base 拼接为绝对 IRI)
if has_keyword_form(expanded) && !is_keyword(expanded) {
node_id = Some(EXPLICIT_NULL_ID)
} else {
// REC @id 步无绝对性校验——@base:null 后相对值**原样保留**
//(t0060 oracle:"../document-relative"/"#document-relative"
// 保相对;此前此处 II 的 ter48 oracle 实际落在 term 名臂)
node_id = Some(expanded)
}
Err(e) => return Err(e)
}
_ =>
if options.frame_expansion {
// @id 位 pattern 保形(帧旗批——t0032/33 判定面):数组 IRI
// 清单(逐 IRI 当前 context 展开)/ `{}` 通配(空 pattern 标桩)
let id_patterns : Array[ExpandedValue] = []
match pair.1 {
JsonValue::Array(items) =>
for item in items {
match item {
JsonValue::String(iri) =>
if is_bnode_label(iri) {
// 负例 t0052(REC 帧 @id 值域——valid IRI,
// bnode 标识符不在册 ⇒ invalid frame)
return Err(
JsonLdError::InvalidValue(
"invalid frame: @id must not include a blank node identifier",
),
)
} else {
match
IriExpander::expand_iri(
self, current, iri, true, false,
) {
Ok(expanded) =>
id_patterns.push(literal_of(expanded, None))
Err(e) => return Err(e)
}
}
JsonValue::Object(_) =>
id_patterns.push(
ExpandedValue::Node(frame_empty_pattern()),
)
_ => ()
}
}
JsonValue::Object(_) =>
id_patterns.push(ExpandedValue::Node(frame_empty_pattern()))
_ => ()
}
properties.push(("@id", id_patterns))
} else {
return Err(
JsonLdError::InvalidValue("@id value must be a string"),
)
}
}
}
// @type 双侧:值对象内 = datatype(coercion 形态 A);节点侧 = 类型数组。
// 值展开一律用 **type-scoped 快照**(REC 5.1.2 @type 步:"IRI expanding
// each of its values using type-scoped context for active context";
// #tc014/#tc018 oracle)。值对象性预扫(#tc020——@type 在 @value 之前
// 出现时 is_value_object 尚未置位,文档序依赖 ⇒ 键形态先判)
"@type" => {
// frameExpansion @type 位 pattern 保形(帧旗批——t0031 match-none /
// {} 通配判定面):数组(IRI 清单,可混 {} 通配)/ 对象 {} 通配 ⇒
// properties @ 键(空数组 = match-none 保形);串形走 types 字段
// 原路(t0005/t0026 oracle 不扰)
// 可达性注记(役C1 批1 2026-10-06):本 pattern 带仅经**别名键**携带
// 非串值可达——frame_value_pattern 预扫(扫原始键)会把字面 "@type"
// 的 Array/Object/Null 值劫往值 pattern 成分带(成分带内有 bnode 拒
// 孪生门);别名 term(如 "type"→"@type")预扫看不到原始键,携非串值
// 直达此处。拆带前须先统一预扫键口径(扩到别名展开后键)——那是语义
// 变更,非清理,勿单方面动手。
if options.frame_expansion {
let pattern_form = match pair.1 {
JsonValue::Array(_) => true
JsonValue::Object(_) => true
_ => false
}
if pattern_form {
let type_patterns : Array[ExpandedValue] = []
match pair.1 {
JsonValue::Array(items) =>
for item in items {
match item {
JsonValue::String(t) =>
if is_bnode_label(t) {
// 负例 t0053(REC 帧 @type 值域同 @id——bnode
// 标识符不在册 ⇒ invalid frame)
return Err(
JsonLdError::InvalidValue(
"invalid frame: @type must not include a blank node identifier",
),
)
} else {
match
IriExpander::expand_iri(
self, type_scoped_context, t, true, true,
) {
Ok(expanded) =>
type_patterns.push(literal_of(expanded, None))
Err(e) => return Err(e)
}
}
JsonValue::Object(default_pairs) => {
// @type default 对象保形(t0064——REC "@type MAY be a
// map with a @default entry…MUST preserve";余键
// MUST be ignored)。@default 串 = type-scoped IRI
// 展开;Node pattern 携 @default IRI 字面
let expanded_default = self.expand_type_default_object(
type_scoped_context, default_pairs,
)
type_patterns.push(expanded_default)
}
_ => ()
}
}
JsonValue::Object(default_pairs) => {
let expanded_default = self.expand_type_default_object(
type_scoped_context, default_pairs,
)
type_patterns.push(expanded_default)
}
_ => ()
}
properties.push(("@type", type_patterns))
continue
}
}
if StandardExpander::has_value_key_shaped(self, current, pairs) {
is_value_object = true
}
match pair.1 {
JsonValue::String(t) =>
if is_value_object {
// 形态 A:显式 @type → datatype(合法性钉——#t0123)
match
IriExpander::expand_iri(
self, type_scoped_context, t, true, true,
) {
Ok(dt) =>
if dt is "@json" {
// @json 值对象直通(丁批——#tjs15/#tjs16 oracle):datatype
// = @json,@value 允许任意 JSON(见 @value 臂 Array/Object)
has_datatype = true
match literal {
Some(l) => l.datatype = Some("@json")
None => pending_dt = Some("@json")
}
} else if !is_valid_absolute_iri(dt) {
return Err(JsonLdError::InvalidValue("invalid typed value"))
} else if has_language {
// @type 与 @language 互斥(#ter38)
return Err(
JsonLdError::InvalidValue("invalid value object"),
)
} else if has_direction {
// @type 与 @direction 互斥(#tdi09,direction-在前序)
return Err(
JsonLdError::InvalidValue("invalid value object"),
)
} else if has_datatype {
return Err(JsonLdError::InvalidValue("invalid typed value"))
} else {
has_datatype = true
match literal {
Some(l) => l.datatype = Some(dt)
None => pending_dt = Some(dt)
}
}
Err(e) => return Err(e)
}
} else {
match
IriExpander::expand_iri(
self, type_scoped_context, t, true, true,
) {
Ok(dt) => types.push(dt)
Err(e) => return Err(e)
}
}
JsonValue::Array(many) =>
if is_value_object {
// 值对象 @type 多值 → invalid typed value(#ter54)
return Err(JsonLdError::InvalidValue("invalid typed value"))
} else {
for t in many {
match t {
JsonValue::String(iri) =>
match
IriExpander::expand_iri(
self, type_scoped_context, iri, true, true,
) {
Ok(dt) => types.push(dt)
Err(e) => return Err(e)
}
_ =>
return Err(
JsonLdError::InvalidValue("invalid type value"), // #ter28 同族
)
}
}
}
_ => return Err(JsonLdError::InvalidValue("invalid type value")) // #ter28
}
}
"@value" => {
// @value:值对象核心(string/number/bool 词形保形;null → 值对象丢弃)
is_value_object = true
saw_value = true
match pair.1 {
JsonValue::Null =>
// @json 值对象(#tjs22 oracle):null = JSON null 字面量保留
if StandardExpander::is_json_value_object(self, current, pairs) {
literal = Some(ExpandedLiteral::{
value: "",
datatype: Some("@json"),
language: None,
direction: None,
raw: Some(JsonValue::Null),
index: None,
included: None,
})
} else {
literal = None
}
JsonValue::String(sv) =>
literal = Some(
match literal {
Some(existing) => {
existing.value = sv
existing
}
None =>
ExpandedLiteral::{
value: sv,
datatype: None,
language: None,
direction: None,
raw: None,
index: None,
included: None,
}
},
)
JsonValue::Number(nv) =>
literal = Some(
match literal {
Some(existing) => {
existing.value = "\{nv}"
existing.raw = Some(JsonValue::Number(nv))
existing
}
None =>
ExpandedLiteral::{
value: "\{nv}",
datatype: None,
language: None,
direction: None,
raw: Some(JsonValue::Number(nv)),
index: None,
included: None,
}
},
)
JsonValue::Bool(bv) =>
literal = Some(
match literal {
Some(existing) => {
existing.value = "\{bv}"
existing.raw = Some(JsonValue::Bool(bv))
existing
}
None =>
ExpandedLiteral::{
value: "\{bv}",
datatype: None,
language: None,
direction: None,
raw: Some(JsonValue::Bool(bv)),
index: None,
included: None,
}
},
)
JsonValue::Array(_) | JsonValue::Object(_) => {
// @json 值对象(丁批——#tjs15/#tjs16 oracle):@type 为 "@json"
// 时,@value 允许任意 JSON(对象/数组原样入 raw)
let is_json_value = StandardExpander::is_json_value_object(
self, current, pairs,
)
if is_json_value {
literal = Some(ExpandedLiteral::{
value: "",
datatype: Some("@json"),
language: None,
direction: None,
raw: Some(pair.1),
index: None,
included: None,
})
} else {
return Err(
JsonLdError::InvalidValue(
"@value must be string, number, boolean or null",
),
)
}
}
}
}
"@language" =>
// @language:与 datatype 互斥(#ter38);键序无关(先记后合);
// language-only 对象 = 值对象形态(t0008:无 @value 成员 → 丢弃)
match pair.1 {
JsonValue::String(lang) => {
if has_datatype {
return Err(JsonLdError::InvalidValue("invalid value object"))
}
is_value_object = true
has_language = true
match literal {
Some(existing) => existing.language = Some(lang)
None =>
literal = Some(ExpandedLiteral::{
value: "",
datatype: None,
language: Some(lang),
direction: None,
raw: None,
index: None,
included: None,
})
}
}
JsonValue::Null =>
match literal {
Some(existing) => existing.language = None
None => ()
}
_ =>
return Err(
JsonLdError::InvalidValue("@language must be a string or null"),
)
}
"@direction" =>
// @direction:与 datatype 互斥(#tdi09);direction-only 对象同值对象
// 形态(t0008 同族)
match pair.1 {
JsonValue::String(dir) => {
if has_datatype {
return Err(JsonLdError::InvalidValue("invalid value object"))
}
is_value_object = true
has_direction = true
match literal {
Some(existing) => existing.direction = Some(dir)
None =>
literal = Some(ExpandedLiteral::{
value: "",
datatype: None,
language: None,
direction: Some(dir),
raw: None,
index: None,
included: None,
})
}
}
JsonValue::Null =>
match literal {
Some(existing) => existing.direction = None
None => ()
}
_ =>
return Err(
JsonLdError::InvalidValue("@direction must be a string or null"),
)
}
"@context" =>
// 已在主键循环前处理(REC 5.1.2 次序;#t0073 oracle)——此处跳过
continue
// 节点/值对象位 @index(REC 13.4.6:"If value is not a string, an invalid
// @index value error";值对象位由结果挂字面量——#t0036 oracle:节点与字面量
// 条目均可带 @index,且条目**已有** @index 保留)
"@index" =>
match pair.1 {
JsonValue::String(index_value) => node_index = Some(index_value)
_ => return Err(JsonLdError::InvalidValue("invalid @index value"))
}
"@reverse" =>
// @reverse 节点位(reverse 批——#t0037/#t0039/#t0064 oracle):
// 值须 map(映射 IRI → 节点值,键经 vocab 展开,值经 term 位展开);
// 非 map → invalid @reverse value(#ter33 oracle)
match pair.1 {
JsonValue::Object(rev_pairs) =>
for rev_pair in rev_pairs {
// 反向映射键不得为关键字(#ter25 oracle:invalid reverse
// property map)
if rev_pair.0.get_char(0) is Some('@') {
return Err(
JsonLdError::InvalidValue("invalid reverse property map"),
)
}
let rev_key = match
IriExpander::expand_iri(self, current, rev_pair.0, false, true) {
Ok(k) => k
Err(e) => return Err(e)
}
// 未映射/相对键丢弃(#t0065 oracle:"Drop unmapped keys in
// reverse map"——非绝对 IRI 的键不产出反向条目)
if !is_valid_absolute_iri(rev_key) {
continue
}
match
StandardExpander::expand_term_values_inner(
self,
current,
rev_pair.0,
rev_pair.1,
Some(rev_pair.0),
options,
) {
Ok(vs) => {
// frameExpansion:反向映射值展开落空({} 通配 pattern——
// t0029 别名 reverse 判定面)⇒ 空 pattern 保形入
// reverse_props(常规路径空值静默丢)
if options.frame_expansion && vs.is_empty() {
reverse_props.push(
(rev_key, [ExpandedValue::Node(frame_empty_pattern())]),
)
continue
}
// 反向映射值须为节点对象(#ter34 oracle:字符串值 →
// invalid reverse property value;@list 同禁)
for v in vs {
match v {
ExpandedValue::List(_) | ExpandedValue::Value(_) =>
return Err(
JsonLdError::InvalidValue(
"invalid reverse property value",
),
)
_ => ()
}
}
// 双反转互消(#t0043 oracle):@reverse 位键自身为 reverse
// term 时,@reverse 位与 td @reverse 两次反转相消——值升级为
// 当前节点的普通正属性(键 = td 的 reverse IRI,同正属性位
// reverse 项路由键取法)
let mut double_reversed : String? = None
match current.terms.get(rev_pair.0) {
Some(term_definition) =>
match term_definition.reverse {
Some(rev) => double_reversed = Some(rev)
None => ()
}
None => ()
}
match double_reversed {
Some(rev) => properties.push((rev, vs))
None => reverse_props.push((rev_key, vs))
}
}
Err(e) => return Err(e)
}
}
_ => return Err(JsonLdError::InvalidValue("invalid @reverse value"))
}
"@graph" =>
// 节点位 @graph(文档级批——#t0020 oracle:节点下 @graph 保留于节点
// 对象;#t0009/#t0021/#t0046 顶层表面化由 engine.serialize_top 门承担)
match pair.1 {
JsonValue::Array(items) => {
let collected : Array[ExpandedValue] = []
for item in items {
match
StandardExpander::append_collection_item(
self, current, item, options, collected,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
}
node_graph = Some(collected)
}
JsonValue::Object(_) => {
// 单节点对象形态归一为数组(规范允许 @graph: {...})
let collected : Array[ExpandedValue] = []
match
StandardExpander::append_collection_item(
self,
current,
pair.1,
options,
collected,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
node_graph = Some(collected)
}
JsonValue::Null => () // @graph: null = 无图内容(先验;无 oracle)
_ => return Err(JsonLdError::InvalidValue("invalid @graph value"))
}
// 节点位 @nest(nest 纯形批——REC 13.4.14:主键循环中只收集不展开,
// 主键循环后延后对...嵌套值展开与并入见循环后段(REC 步骤 14)。
// 别名 term(IRI 即 @nest)归一汇流(tn002)。值对象位 @nest 无
// oracle,循 @id 同款门(#ter37 先例)——invalid value object。
// 臂体多语句须包块(MoonBit 臂体单表达式纪律——微复现实证)
"@nest" => {
if is_value_object {
return Err(JsonLdError::InvalidValue("invalid value object"))
}
// 该键的属性作用域 context(scoped-context 批——#tc037/#tc038
// oracle:@nest 别名 term 的 @context 作用于其值)
let nest_context = match
StandardExpander::apply_property_scoped_context(
self,
current,
pair.0,
options,
) {
Ok(ctx) => ctx
Err(e) => return Err(e)
}
match pair.1 {
JsonValue::Array(items) =>
for item in items {
nest_entries.push((nest_context, item)) // 数组归一(tn007/tn008)
}
JsonValue::Null => () // 数组归一:null = 空(先验)
single => nest_entries.push((nest_context, single))
}
}
// 节点位 @included(Included Blocks 纯形批——REC:主键循环中只收集,
// 循环后延后展开。值对象位不拒——#tin05 oracle:值对象内 @included
// 保形且内层照常展开,区别于 @nest 的 invalid value object 门)
"@included" =>
match pair.1 {
JsonValue::Array(items) =>
for item in items {
// 数组元素须为对象(#tin07 oracle:串 → invalid @included value)
if item is JsonValue::Object(_) {
included_values.push(item)
} else {
return Err(JsonLdError::InvalidValue("invalid @included value"))
}
}
JsonValue::Object(_) => included_values.push(pair.1)
JsonValue::Null => () // null = 空(先验,循 @graph/@nest 同款)
_ => return Err(JsonLdError::InvalidValue("invalid @included value"))
}
_ =>
// 未知 @ 键忽略(戊批——同前一枚:@ 前缀非关键字键不产出属性)。
// frameExpansion 旗保形(帧旗批——spec §8.96):framing 旗词形入
// properties(keyword 键名),framing 算法读旗位;@embed 他值校验
// (⇒ invalid @embed value)随 embed 状态机批
if options.frame_expansion && is_framing_flag_key(expanded_key) {
match pair.1 {
JsonValue::String(word) => {
// 负例 t0054(REC @embed 值域——true/false/@always/@once/
// @never/@last;词表外 ⇒ invalid @embed value)
if expanded_key is "@embed" &&
!(word
is ("true" | "false" | "@always" | "@once" | "@never" | "@last")) {
return Err(JsonLdError::InvalidValue("invalid @embed value"))
}
properties.push((expanded_key, [literal_of(word, None)]))
}
JsonValue::Bool(flag) =>
properties.push(
(
expanded_key,
[literal_of(if flag { "true" } else { "false" }, None)],
),
)
_ => ()
}
}
}
}
// @nest 延后处理(nest 纯形批——REC 步骤 14):逐嵌套值须为节点对象且
// 任一键展开为 @value 即拒(invalid @nest value);递归展开后并入当前
// 节点——排自身属性之后(tn003),嵌套 @nest 随递归自展(tn005),数组
// 元素逐一合并(tn006/tn008)。文档序处理(ordered=false 时 nests 按
// 收集序——tn004 双 nest 官方比对无序,保序结果同为合法置换)。
// 合并索引(@nest 异键合并索引化):**整段建一次**(进入合并前按当前槽位),
// 之后由 merge_property_values 维护 ⇒ 每条 incoming 对 O(1) 查/追加
// (原实现每条都线性扫目标数组 ⇒ O(P·M) 二次方)
let property_index : Map[String, Int] = Map([])
for i in 0.. ps
_ => return Err(JsonLdError::InvalidContext("invalid @nest value"))
}
// @value 键嗅探(REC 步骤 14:"any key within nested value expands to @value"):
// 走 `key_expands_to_keyword` 快路(原实现每键跑一次完整 IRI 展开——常数因子)
for nested_pair in nested_pairs {
let raw_key = nested_pair.0
if StandardExpander::key_expands_to_keyword(
self, nest_context, raw_key, "@value",
) {
return Err(JsonLdError::InvalidContext("invalid @nest value"))
}
}
let nested_expanded = StandardExpander::expand_object(
self, nest_context, nested, _active_property, options,
)
match nested_expanded {
Ok(Some(ExpandedValue::Node(nested_node))) => {
// 并入(REC 共享 result 语义):@id 臂为赋值——嵌套覆盖先见者(先验,
// 无 oracle);类型/属性/反向逐值追加——同键并数组(tn006);
// @graph 并集(先验,无 oracle)
match nested_node.id {
Some(nested_id) => node_id = Some(nested_id)
None => ()
}
for t in nested_node.types {
types.push(t)
}
for prop_pair in nested_node.properties {
merge_property_values(properties, property_index, prop_pair)
}
for rev_pair in nested_node.reverse_props {
merge_property_values(reverse_props, reverse_index, rev_pair)
}
match (node_graph, nested_node.graph) {
(Some(existing), Some(more)) =>
for item in more {
existing.push(item)
}
(None, Some(more)) => node_graph = Some(more)
_ => ()
}
}
Ok(_) => ()
Err(e) => return Err(e)
}
}
// Included Blocks 延后处理(@included 纯形批——REC:循环后逐条目展开。
// 条目须为节点对象:@value/@list 键嗅探即拒——#tin08/#tin09 oracle
// (值对象/list 对象 → invalid @included value);串值在收集臂已拒
// (#tin07)。值对象位结果挂字面量(#tin05),节点位挂节点)
for included_entry in included_values {
let entry_pairs = match included_entry {
JsonValue::Object(ps) => ps
_ => return Err(JsonLdError::InvalidValue("invalid @included value"))
}
for entry_pair in entry_pairs {
if entry_pair.0 is ("@value" | "@list") {
return Err(JsonLdError::InvalidValue("invalid @included value"))
}
}
// @included 递归调用(REC 13.4.6.2:active property = null、from map 未传)
// ⇒ 回退门照常作用(类型作用域不跨新节点)
let included_context = if current.previous is Some(_) &&
!StandardExpander::is_value_or_id_shaped(self, current, included_entry) {
StandardExpander::revert_previous(current)
} else {
current
}
match
StandardExpander::expand_object(
self,
included_context,
included_entry,
None,
options,
) {
Ok(Some(ExpandedValue::Node(included_node))) =>
included_nodes.push(included_node)
Ok(None) => () // 空产物(先验;无 oracle——循 @graph: null 同款丢弃)
Ok(Some(_)) =>
return Err(JsonLdError::InvalidValue("invalid @included value"))
Err(e) => return Err(e)
}
}
// 形态判定:值对象(@value 键已见)优先于 node object——
// @included 结果按形态挂载:值对象位挂字面量(#tin05 保形),节点位挂节点
if is_value_object {
match literal {
Some(found) => {
// language-only 值对象(无 @value 成员)→ 丢弃(#t0008 oracle:
// "drop-lang-only" 不产出属性;placeholder literal 不升格)
if !saw_value {
return Ok(None)
}
// @type 先至挂起位回填(#tc020——文档序 @type 在 @value 前时
// datatype 挂起至此回填)
match pending_dt {
Some(dt) => found.datatype = Some(dt)
None => ()
}
// @language 只配字符串 @value(#ter39 oracle):@value 非串(raw 记录
// Number/Bool 词形)而带 @language → invalid language-tagged value;
// @value: null 在臂内已置 literal=None,先于本校验丢弃(规范次序)
if has_language && found.raw is Some(_) {
return Err(JsonLdError::InvalidValue("invalid language-tagged value"))
}
// 值对象位 Included Blocks 挂字面量(#tin05 oracle:保形输出)
if included_nodes.length() > 0 {
found.included = Some(included_nodes)
}
// 值对象位 @index(#t0036 oracle:字面量条目带 @index)
match node_index {
Some(index_value) => found.index = Some(index_value)
None => ()
}
Ok(Some(ExpandedValue::Value(found)))
}
None => Ok(None) // @value: null → 整个值对象丢弃
}
} else {
Ok(
Some(
ExpandedValue::Node(ExpandedNode::{
id: node_id,
types,
properties,
reverse_props,
graph: node_graph,
included: if included_nodes.length() > 0 {
Some(included_nodes)
} else {
None
},
index: node_index,
}),
),
)
}
}
///|
/// 同键属性值合并·索引版(@nest 异键合并索引化——原实现每条 incoming 线性扫目标
/// 数组,在"异键数 × 条目数"上 **O(P·M) 二次方**,实测 2× 输入 → 4× 耗时)。
/// 目标数组配 `Map[IRI, 槽位下标]`:命中 ⇒ 逐值追加(tn006 逐元素并值),未命中 ⇒
/// 追加新项并登记槽位。**顺序语义与原实现完全一致**(先见者占位、逐值追加到尾部);
/// 索引由调用方在进入合并前建一次,此后由本函数维护。
/// 类型数组的追加由调用方直推。
fn merge_property_values(
target : Array[(String, Array[ExpandedValue])],
index : Map[String, Int],
pair : (String, Array[ExpandedValue]),
) -> Unit {
match index.get(pair.0) {
Some(slot) =>
// 既有槽位:逐值追加(无需重建索引)
for v in pair.1 {
target[slot].1.push(v)
}
None => {
index[pair.0] = target.length()
target.push(pair)
}
}
}
///|
/// 集合位条目展开(文档级 @graph 批):图内容逐项收集。自由浮动 @set/@list
/// 纯形节点对象先分派(#t0047 oracle):@set = 透明(其值逐项并入集合);
/// @list = 整体丢弃(含带属性节点——list 内容随 list 消亡)。
/// 常规分派 = 对象 → expand_object(值对象/free-floating 丢弃——#t0046 oracle:
/// 标量/值对象/@id-only 全弃);标量 → 直接弃;嵌套数组 = 忽略(先验;无
/// oracle 不扩面)。顶层数组位的自由浮动 @set/@list 未落(同先验)。
fn StandardExpander::append_collection_item(
self : StandardExpander,
active : ActiveContext,
item : JsonValue,
options : JsonLdOptions,
out : Array[ExpandedValue],
) -> Result[Unit, JsonLdError] {
// 自由浮动 @set/@list 纯形节点对象分派(单键纯形才走;混合形态未钉,
// 走常规分派由关键字臂 Unsupported 报错——先验,防过宽)
match item {
JsonValue::Object(pairs) =>
if pairs.length() == 1 && pairs[0].0 is ("@set" | "@list") {
match pairs[0].0 {
"@set" => {
// @set 透明:其值逐项并入集合(#t0047 set 臂:set 内标量弃、
// 带属性节点保留);非数组 = 单元素递归(C 组单元素包裹对偶)
match pairs[0].1 {
JsonValue::Array(set_items) =>
for sub in set_items {
match
StandardExpander::append_collection_item(
self, active, sub, options, out,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
}
JsonValue::Null => () // @set: null 透明丢弃(先验)
single =>
match
StandardExpander::append_collection_item(
self, active, single, options, out,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
}
return Ok(())
}
"@list" => return Ok(()) // 整体丢弃:内容随 list 消亡(#t0047 list 臂)
_ => ()
}
}
_ => ()
}
// 常规分派:对象 → expand_object(graph-bearing 节点保留——is_free_floating
// 已扩);值对象/free-floating 弃
match item {
JsonValue::Object(_) => {
// @graph / 集合位的递归调用(REC 13.4.6.1:from map 未传)⇒ 回退门照常作用
let item_context = if active.previous is Some(_) &&
!StandardExpander::is_value_or_id_shaped(self, active, item) {
StandardExpander::revert_previous(active)
} else {
active
}
let expanded : ExpandedValue? = match
StandardExpander::expand_object(self, item_context, item, None, options) {
Ok(v) => v
Err(e) => return Err(e)
}
match expanded {
Some(ExpandedValue::Node(node)) =>
// 图内容 free-floating 门(#t0001 同族——@id-only/无属性节点弃;
// graph 字段节点不受此门(is_free_floating 已扩));frameExpansion
// 豁免——@id-only 帧根/子 pattern 合法(t0032 判定面)
if is_free_floating(node) && !options.frame_expansion {
()
} else {
out.push(ExpandedValue::Node(node))
}
Some(ExpandedValue::Value(_)) => () // 值对象即弃(#t0046)
Some(ExpandedValue::List(_)) => () // 自由浮动 list 产物丢弃(防御;与 @list 整弃一致)
None => () // @value: null → 丢弃
}
}
JsonValue::Null
| JsonValue::Number(_)
| JsonValue::Bool(_)
| JsonValue::String(_) => ()
JsonValue::Array(_) => () // 嵌套数组于集合位忽略(先验)
}
Ok(())
}
///|
impl Expander for StandardExpander with fn expand_element(
self,
active : ActiveContext,
element : JsonValue,
active_property : String?,
options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
match element {
JsonValue::Object(_) =>
match
StandardExpander::expand_object(
self, active, element, active_property, options,
) {
Ok(Some(other)) => Ok(other)
Ok(None) =>
Err(JsonLdError::Unsupported("empty node pending later cases"))
Err(e) => Err(e)
}
JsonValue::Array(items) =>
StandardExpander::expand_array(
self, active, items, active_property, options,
)
_ => StandardExpander::expand_value(self, active, element, active_property)
}
}
///|
impl Expander for StandardExpander with fn expand_value(
_self,
_active : ActiveContext,
value : JsonValue,
_active_property : String?,
) -> Result[ExpandedValue, JsonLdError] {
// 标量位(顶层/数组元素):字符串保留;number 保形;其余挂后续例
match value {
JsonValue::String(s) => Ok(literal_of(s, None))
JsonValue::Number(nv) =>
Ok(literal_of("\{nv}", Some(JsonValue::Number(nv))))
_ =>
Err(
JsonLdError::Unsupported("scalar expansion pending later suite cases"),
)
}
}
///|
impl Expander for StandardExpander with fn expand_array(
self,
active : ActiveContext,
items : Array[JsonValue],
active_property : String?,
options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
// 逐元素递归、滤 dropped(1.1 数组滤 null);元素循环在钩子内(步骤图无环)
let out : Array[ExpandedValue] = []
for item in items {
match
StandardExpander::expand_element(
self, active, item, active_property, options,
) {
Ok(v) => out.push(v)
Err(e) => return Err(e)
}
}
Ok(ExpandedValue::List(out, index=None))
}
///|
/// 内层 term 位展开(容器应用已由外层壳处理——递归调用方一律走此层)
/// term 位展开(①批语义):对象 → 值对象/节点;数组 → 逐元素递归累加;
/// string → 字面量;number → 字面量(raw 保形);bool/null → 丢弃(属性位)
impl Expander for StandardExpander with fn expand_term(
self,
active : ActiveContext,
term : String,
value : JsonValue,
active_property : String?,
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
StandardExpander::expand_term_values(
self, active, term, value, active_property, options,
)
}
///|
/// 属性作用域 context 应用(scoped-context 批——REC 5.1.2 "property-scoped
/// context"):active property 的 term 定义带 local context 时,其**值**展开前
/// 先做 Context Processing 并套用;无 local context / 无 term 定义 = 原样返回。
/// 两阶段语义(REC 4.2.2 注记):定义期只校验(结果丢弃,错误 → invalid
/// scoped context),展开期才真正再处理应用(#tc037/#tc038/#tin06 oracle)
fn StandardExpander::apply_property_scoped_context(
self : StandardExpander,
active : ActiveContext,
term : String,
options : JsonLdOptions,
) -> Result[ActiveContext, JsonLdError] {
match active.terms.get(term) {
Some(term_definition) =>
match term_definition.local_context {
Some(local_context) =>
ContextProcessor::process_context(
self,
active,
local_context,
ContextFlags::property_scoped().with_context_base_url(
term_definition.context_base_url,
),
options,
)
None => Ok(active)
}
None => Ok(active)
}
}
///|
/// 属性值展开的 context 组装(REC 5.1.2 映射分支次序):
/// ① property-scoped context 取自**传入 active(回退前)**——形判定与 scoped
/// 定义都以 pre-revert context 为准(#tc012/#tc020 oracle);
/// ② 回退:active.previous 存在且值既非值对象形也非单 @id 形 ⇒ 回退到 previous
/// ("term-scoped context 不跨新节点":#tc009/#tc015/#tc016/#tc017 oracle);
/// ③ 在(可能已回退的)context 上处理 property-scoped context。
/// 标量值不走本函数(标量分支不回退,只套 property-scoped——#tc007 oracle)
fn StandardExpander::property_value_context(
self : StandardExpander,
active : ActiveContext,
term : String,
value : JsonValue,
options : JsonLdOptions,
) -> Result[ActiveContext, JsonLdError] {
// ① 捕获 property-scoped context(回退**前** context 的 td——#tc012 oracle:
// type-scoped 里定义的 bar 自带 scoped context,回退会丢掉该定义,故先取)
let property_scoped : TermDefinition? = match active.terms.get(term) {
Some(term_definition) => Some(term_definition)
None => None
}
// ② 回退门
let base = match active.previous {
Some(_) =>
if StandardExpander::is_value_or_id_shaped(self, active, value) {
active
} else {
StandardExpander::revert_previous(active)
}
None => active
}
// ③ 在(可能已回退的)context 上**直接应用捕获的** scoped context
match property_scoped {
Some(term_definition) =>
match term_definition.local_context {
Some(local_context) =>
ContextProcessor::process_context(
self,
base,
local_context,
ContextFlags::property_scoped().with_context_base_url(
term_definition.context_base_url,
),
options,
)
None => Ok(base)
}
None => Ok(base)
}
}
///|
/// 容器应用壳(C 组——判据③):@container @list → 元素展开 + List 包裹后
/// 委托内层(包裹仅此一层——元素递归一律走 expand_term_values_inner,防
/// 容器/标量 coercion 互咬重入)。@container @set = 现有摊平即塌缩
/// (no-op 语义,#t0015 oracle),无需专码
fn StandardExpander::expand_term_values(
self : StandardExpander,
active : ActiveContext,
term : String,
value : JsonValue,
active_property : String?,
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
// 容器位查找用**回退前/作用域前**的 context(REC 13.4.3 在主键循环内按节点
// context 取容器映射);property-scoped 应用在值展开处(property_value_context)
let container_kind = match active.terms.get(term) {
Some(term_definition) => term_definition.container
None => None
}
// graph 容器应用(@graph 容器族——形态表见 todo §J2 开工钉):次级
// @id/@index = map 形态(键附挂条目);裸/@set = 逐值包成 graph 节点
if container_kind is Some(ContainerType::Graph) {
let graph_secondary = match active.terms.get(term) {
Some(term_definition) => term_definition.graph_secondary
None => None
}
return StandardExpander::expand_graph_container(
self, active, term, value, graph_secondary, options,
)
}
if container_kind is Some(ContainerType::List) {
// 值本身 = list/set 对象 → 拦截器直出,不二次包裹(#t0004 oracle:
// {"@list": []} under list 容器 → {"@list": []} 单层)
match value {
JsonValue::Object(obj_pairs) =>
if obj_pairs.length() > 0 &&
(obj_pairs[0].0 is "@list" || obj_pairs[0].0 is "@set") {
match
StandardExpander::expand_list_set_object(
self, active, term, obj_pairs, options,
) {
Ok(Some(vs)) => return Ok(vs)
Ok(None) => ()
Err(e) => return Err(e)
}
}
_ => ()
}
let items = match value {
JsonValue::Array(items) => items
_ => [value]
}
let out : Array[ExpandedValue] = []
for item in items {
match
StandardExpander::expand_list_item(self, active, term, item, options) {
Ok(Some(v)) => out.push(v)
Ok(None) => () // null 元素丢弃(1.1)
Err(e) => return Err(e)
}
}
return Ok([ExpandedValue::List(out, index=None)])
}
// @container 映射分支(REC 13.8.3 "container mapping includes @index, @type, or
// @id and value is a map"):本批落 @type(type map)+ @index(index map 最小
// 子集——#tc013 的 fixture 需 index map 侧);@id 容器随 @id map 批。
// 值为 map 才走映射分支;其他形态落通用分支(字符串经**隐式 @id type mapping**
// 成节点引用——#tm017 oracle)
match container_kind {
Some(ContainerType::Type) =>
match value {
JsonValue::Object(entries) =>
return StandardExpander::expand_container_map(
self,
active,
term,
entries,
ContainerType::Type,
options,
)
_ => ()
}
Some(ContainerType::Index) =>
match value {
JsonValue::Object(entries) =>
return StandardExpander::expand_container_map(
self,
active,
term,
entries,
ContainerType::Index,
options,
)
_ => ()
}
Some(ContainerType::Id) =>
match value {
JsonValue::Object(entries) =>
return StandardExpander::expand_container_map(
self,
active,
term,
entries,
ContainerType::Id,
options,
)
_ => ()
}
// @container @language(REC 13.8.2 language map):值为 map 才走语言映射分支
Some(ContainerType::Language) =>
match value {
JsonValue::Object(entries) =>
return StandardExpander::expand_language_map(
self, active, term, entries, options,
)
_ => ()
}
_ => ()
}
StandardExpander::expand_term_values_inner(
self, active, term, value, active_property, options,
)
}
///|
/// type map 展开(@container @type;REC 13.8.3):
/// ① **map context** = active 的 previous(存在即用,否则 active)——"type map 用
/// **类型索引项**的 scoped context,不用包含方的"(#tc013 oracle);
/// ② 索引项(map 键)的 term 定义在 map context 中若有 local context → 套用
/// (#tm008 oracle);
/// ③ 逐条目:expanded index = 键的 IRI 展开(vocab 位);条目值为数组则逐项;
/// 条目经**属性值展开**(含 coercion——#tm017 字符串经隐式 @id 成节点引用);
/// ④ 每个展开项:expanded index ≠ @none ⇒ **前插** @type(#tm004 已有 @type 时前插;
/// #tm012 @none 及别名不加)
fn StandardExpander::expand_container_map(
self : StandardExpander,
active : ActiveContext,
term : String,
entries : Array[(String, JsonValue)],
kind : ContainerType,
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
// ① map context:@id/@type ⇒ previous 存在即回退;@index ⇒ 用 active(REC 13.8.3)
let uses_previous = kind is (ContainerType::Type | ContainerType::Id)
let mut map_context = if uses_previous {
match active.previous {
Some(_) => StandardExpander::revert_previous(active)
None => active
}
} else {
active
}
let expanded : Array[ExpandedValue] = []
// index key(REC 13.8.3:"Initialize index key to the key's index mapping in active
// context, or @index, if it does not exist")——None 表示缺省 @index
let index_mapping = match active.terms.get(term) {
Some(term_definition) => term_definition.index_mapping
None => None
}
for entry in entries {
let index_key = entry.0
let index_value = entry.1
// ② 仅 @type 容器:索引项的 scoped context(在 map context 中查 td;用元素级
// 默认旗标——REC 此调用未传 override protected/propagate;#tm008 oracle)
if kind is ContainerType::Type {
map_context = match map_context.terms.get(index_key) {
Some(term_definition) =>
match term_definition.local_context {
Some(local_context) =>
match
ContextProcessor::process_context(
self,
map_context,
local_context,
ContextFlags::element().with_context_base_url(
term_definition.context_base_url,
),
options,
) {
Ok(next) => next
Err(e) => return Err(e)
}
None => map_context
}
None => map_context
}
}
// ③ 索引与索引值(REC:"Initialize expanded index to the result of IRI
// expanding index")
let expanded_index = match
IriExpander::expand_iri(self, map_context, index_key, false, true) {
Ok(expanded_index) => expanded_index
Err(e) => return Err(e)
}
let items = match index_value {
JsonValue::Array(items) => items
_ => [index_value]
}
// from map 语境(REC 递归调用传 true for from map):**不回退 previous**——
// 以"previous 已清空"的等价 context 表达(本批索引项展开的全部路径都按此)
let item_context = ActiveContext::{
base: map_context.base,
vocab: map_context.vocab,
terms: map_context.terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
for item in items {
let item_values = match
StandardExpander::expand_map_item_values(
self, item_context, term, item, options,
) {
Ok(values) => values
Err(e) => return Err(e)
}
for item_value in item_values {
// ④ 按容器种类写入索引语义(@none 及其别名一律跳过)
if expanded_index != "@none" {
match kind {
ContainerType::Type =>
// 前插 @type(已有 @type 时前插——#tm004 oracle)
match item_value {
ExpandedValue::Node(node) =>
node.types.insert(0, expanded_index)
ExpandedValue::Value(literal) =>
// 值对象位:无 datatype 时写为 @type(先验;无 oracle)
if literal.datatype is None {
literal.datatype = Some(expanded_index)
}
ExpandedValue::List(_) => ()
}
ContainerType::Index =>
// 索引映射(index key ≠ @index)⇒ 索引键写成**一条属性**(REC 13.8.3
// 子案例 1;#tpi06~#tpi10 oracle);否则写 @index = 原始索引键(子案例 2)
match index_mapping {
Some(index_key_name) =>
match item_value {
ExpandedValue::Node(node) => {
// expanded index key = IRI 展开 index key(REC 13.8.3)
let index_property_iri = match
IriExpander::expand_iri(
self, item_context, index_key_name, false, true,
) {
Ok(iri) => iri
Err(e) => return Err(e)
}
// re-expanded index = Value Expansion(active, index key, 索引)
let re_expanded = match
StandardExpander::expand_term_values_inner(
self,
item_context,
index_key_name,
JsonValue::String(index_key),
Some(index_key_name),
options,
) {
Ok(values) =>
match values {
[first, ..] => first
[] => continue // 无值(防御;无 oracle)
}
Err(e) => return Err(e)
}
// index property values = [re-expanded] ++ 既有同 IRI 值
let mut merged = false
for i in 0..
// "If item is a value object, it MUST NOT contain any extra
// properties; an invalid value object error"(#tpi05 oracle)
return Err(
JsonLdError::InvalidValue("invalid value object"),
)
ExpandedValue::List(_) => () // 先验:list 位无 oracle
}
None =>
// 子案例 2:条目无 @index 时写 @index = 原始索引键
match item_value {
ExpandedValue::Node(node) =>
if node.index is None {
node.index = Some(index_key)
}
ExpandedValue::Value(literal) =>
if literal.index is None {
literal.index = Some(index_key)
}
ExpandedValue::List(_) => ()
}
}
ContainerType::Id =>
// 条目无 @id 时写 @id = **document-relative、非 vocab 位**展开的索引
// (REC:"expanded index is set to the result of IRI expanding index
// using true for document relative and false for vocab";#tm005 oracle)
match item_value {
ExpandedValue::Node(node) =>
if node.id is None {
node.id = Some(
match
IriExpander::expand_iri(
self, item_context, index_key, true, false,
) {
Ok(expanded_id) => expanded_id
Err(e) => return Err(e)
},
)
}
_ => ()
}
_ => ()
}
}
expanded.push(item_value)
}
}
}
Ok(expanded)
}
///|
/// language map 展开(@container @language;REC 13.8.2):
/// 逐条目(语言键 → 值,值非数组则归一数组):条目值为 **null 跳过**、**非串 →
/// invalid language map value**;产出 `{@value: item}`,语言键**非 @none**(含
/// **展开为 @none 的别名**)时加 `@language: 原始语言键`。
/// 本引擎暂无默认 base direction / td direction mapping ⇒ direction 恒 null(不产出
/// @direction)——#tdi04~#tdi07(term direction 面)挂「@direction 批」。
/// oracle:#tm009/#tm010(@none 与别名)/ #t0030(基本)/ #tl001(null 跳过)
fn StandardExpander::expand_language_map(
self : StandardExpander,
active : ActiveContext,
term : String,
entries : Array[(String, JsonValue)],
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
let _ = options
// 方向三态解析(丙批——#tdi04~#tdi07 oracle):term direction 成员
// Set 覆盖 / Null 压制 / 未声明继承 context 默认方向;"@none" 键条目
// 不附方向(#tdi07 同族语义:none 条目保持最小形态)
let term_direction : String? = match active.terms.get(term) {
Some(term_definition) =>
match term_definition.direction_mapping {
Some(LanguageMember::Set(dir)) => Some(dir)
Some(LanguageMember::Null) => None
None => active.default_direction
}
None => active.default_direction
}
let expanded : Array[ExpandedValue] = []
for entry in entries {
let language = entry.0
let items = match entry.1 {
JsonValue::Array(items) => items
single => [single]
}
// @none 判定:键本身为 @none **或展开为 @none**(别名——#tm010 oracle)
let is_none = match
IriExpander::expand_iri(self, active, language, false, true) {
Ok(expanded_language) => expanded_language is "@none"
Err(_) => language is "@none"
}
for item in items {
match item {
JsonValue::Null => () // 条目值 null:跳过(#tl001 oracle)
JsonValue::String(text) => {
// @none 条目:语言与方向均不附(最小形态——#tdi07 对照)
let language_tag = if is_none { None } else { Some(language) }
let direction_tag : String? = if is_none {
None
} else {
term_direction
}
expanded.push(
ExpandedValue::Value(ExpandedLiteral::{
value: text,
datatype: None,
language: language_tag,
direction: direction_tag,
raw: None,
index: None,
included: None,
}),
)
}
_ => return Err(JsonLdError::InvalidValue("invalid language map value"))
}
}
}
Ok(expanded)
}
///|
/// type map 条目的值展开(from map 语境——REC 13.8.3 递归调用):属性值语义
/// (含 coercion / property-scoped / 不回退),数组逐项摊平;null 丢弃。
/// 不重入容器壳(容器已由外层键位处理)
fn StandardExpander::expand_map_item_values(
self : StandardExpander,
active : ActiveContext,
term : String,
item : JsonValue,
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
match item {
JsonValue::Array(items) => {
let out : Array[ExpandedValue] = []
for sub in items {
match
StandardExpander::expand_map_item_values(
self, active, term, sub, options,
) {
Ok(values) =>
for v in values {
out.push(v)
}
Err(e) => return Err(e)
}
}
Ok(out)
}
_ =>
StandardExpander::expand_term_values_inner(
self,
active,
term,
item,
Some(term),
options,
)
}
}
///|
/// 值对象性预扫(#tc020——任一键展开为 @value 即值对象形态,与键文档序
/// 无关;配套 @type 先至的 coercion 分路)
fn StandardExpander::has_value_key_shaped(
self : StandardExpander,
current : ActiveContext,
pairs : Array[(String, JsonValue)],
) -> Bool {
for p in pairs {
let sibling = match
IriExpander::expand_iri(self, current, p.0, false, true) {
Ok(k) => k
Err(_) => p.0
}
if sibling is "@value" {
return true
}
}
false
}
///|
/// @json 值对象识别(丁批——#tjs15/#tjs16/#tjs22 oracle):同对象存在 vocab
/// 展开为 @type 的键、其值(可别名 term,"json" → @json)展开为 @json
fn StandardExpander::is_json_value_object(
self : StandardExpander,
current : ActiveContext,
pairs : Array[(String, JsonValue)],
) -> Bool {
let mut is_json_value = false
for p in pairs {
let sibling = match
IriExpander::expand_iri(self, current, p.0, false, true) {
Ok(k) => k
Err(_) => p.0
}
match (sibling, p.1) {
("@type", JsonValue::String(t)) => {
let expanded_type = match
IriExpander::expand_iri(self, current, t, false, true) {
Ok(k) => k
Err(_) => t
}
if expanded_type is "@json" {
is_json_value = true
}
}
_ => ()
}
}
is_json_value
}
///|
fn StandardExpander::expand_term_values_inner(
self : StandardExpander,
active : ActiveContext,
term : String,
value : JsonValue,
active_property : String?,
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
// @json 字面量拦截(丁批——REC 13.8.1 @json 值展开):值原样(JSON 解析
// 结果)进 @value,datatype = @json 关键字形态(oracle tjs01:输出
// `"@type": "@json"`);对象/数组**不作节点展开**(#tjs06/#tjs08 oracle)。
// canonical 选项只影响序列化词形(JCS/RFC 8785,挂 J3),展开按解析值对拍
let scoped_for_json = match
StandardExpander::apply_property_scoped_context(self, active, term, options) {
Ok(context) => context
Err(e) => return Err(e)
}
match scoped_for_json.terms.get(term) {
Some(term_definition) =>
match term_definition.datatype {
Some("@json") => {
let lit = ExpandedLiteral::{
value: "",
datatype: Some("@json"),
language: None,
direction: None,
raw: Some(value),
index: None,
included: None,
}
return Ok([ExpandedValue::Value(lit)])
}
_ => ()
}
None => ()
}
// property-scoped context(REC 5.1.2 不回退;标量/容器臂共用——丙批/丁批/
// 原生 coercion)
let scoped = match
StandardExpander::apply_property_scoped_context(self, active, term, options) {
Ok(context) => context
Err(e) => return Err(e)
}
match value {
JsonValue::Object(_) => {
// list/set 对象拦截(C 组——判据①;#ter41/#tli01 oracle)
match value {
JsonValue::Object(obj_pairs) =>
match
StandardExpander::expand_list_set_object(
self, active, term, obj_pairs, options,
) {
Ok(Some(vs)) => return Ok(vs)
Ok(None) => ()
Err(e) => return Err(e)
}
_ => ()
}
// @language 容器的值检查已由语言映射分支(expand_language_map,REC 13.8.2)
// 承担:条目值 null 跳过、非串 → invalid language map value(#ter35 oracle:
// {"en": true} → 非串即错);非 map 值走通用分支(REC 分支条件要求 map)
match
StandardExpander::expand_object(
self,
match
StandardExpander::property_value_context(
self, active, term, value, options,
) {
Ok(context) => context
Err(e) => return Err(e)
},
value,
Some(term),
options,
) {
Ok(Some(v)) => Ok([v])
Ok(None) => Ok([])
Err(e) => Err(e)
}
}
JsonValue::Array(items) => {
let out : Array[ExpandedValue] = []
for item in items {
// 数组项走**内层**(REC 5.1.2:容器逻辑只作用于键的值一次;数组项按
// 元素语义逐项展开——重入容器壳会把映射分支误施于项,实测 #t0040
// `{"@id": …}` 项被当成 index map 处理 ⇒ 值对象而非节点)
match
StandardExpander::expand_term_values_inner(
self, active, term, item, active_property, options,
) {
Ok(vs) =>
for v in vs {
out.push(v)
}
Err(e) => return Err(e)
}
}
Ok(out)
}
JsonValue::String(sv) => {
// 标量分支(REC 5.1.2):**不回退**,只套 property-scoped context
// (#tc007 oracle:节点自身标量属性仍见 type-scoped 的 coercion)
// 默认语言/方向解析(丙批——#t0022/#t0018/#tdi01 oracle):term 成员
// 三态优先(Set 覆盖 / Null 压制),未声明继承 context 默认(scoped
// context 链已继承——property-scoped 应用保留默认值);仅串值注入
let term_language : String? = match scoped.terms.get(term) {
Some(term_definition) =>
match term_definition.language_mapping {
Some(LanguageMember::Set(lang)) => Some(lang)
Some(LanguageMember::Null) => None
None => scoped.default_language
}
None => scoped.default_language
}
let term_direction : String? = match scoped.terms.get(term) {
Some(term_definition) =>
match term_definition.direction_mapping {
Some(LanguageMember::Set(dir)) => Some(dir)
Some(LanguageMember::Null) => None
None => scoped.default_direction
}
None => scoped.default_direction
}
// coercion 应用(⑤批 A 组——判据②):type mapping 三形态只转**串值**
// (number/bool 不转,#t0088 oracle);形态 B "@id" = document-relative、
// 形态 C "@vocab" = vocab 位、形态 D = 显式 datatype 值对象
match scoped.terms.get(term) {
Some(term_definition) =>
match term_definition.datatype {
Some(coercion) =>
if coercion is "@none" {
// 显式无 coercion(#ttn01——@none 串值原样,语言/方向仍随 term)
let lit = ExpandedLiteral::{
value: sv,
datatype: None,
language: term_language,
direction: term_direction,
raw: None,
index: None,
included: None,
}
Ok([ExpandedValue::Value(lit)])
} else {
let coerced : Result[ExpandedValue, JsonLdError] = if coercion
is "@id" {
match IriExpander::expand_iri(self, scoped, sv, true, false) {
Ok(iri) =>
Ok(
ExpandedValue::Node(ExpandedNode::{
id: Some(iri),
types: [],
properties: [],
reverse_props: [],
graph: None,
included: None,
index: None,
}),
)
Err(e) => Err(e)
}
} else if coercion is "@vocab" {
// @vocab coercion 构造点(#t0057 oracle:vocab miss 后落
// document-relative 位——base 解析)
match IriExpander::expand_iri(self, scoped, sv, true, true) {
Ok(iri) =>
Ok(
ExpandedValue::Node(ExpandedNode::{
id: Some(iri),
types: [],
properties: [],
reverse_props: [],
graph: None,
included: None,
index: None,
}),
)
Err(e) => Err(e)
}
} else {
Ok(
ExpandedValue::Value(ExpandedLiteral::{
value: sv,
datatype: Some(coercion),
language: None,
direction: None,
raw: None,
index: None,
included: None,
}),
)
}
match coerced {
Ok(v) => Ok([v])
Err(e) => Err(e)
}
}
None => {
let lit = ExpandedLiteral::{
value: sv,
datatype: None,
language: term_language,
direction: term_direction,
raw: None,
index: None,
included: None,
}
Ok([ExpandedValue::Value(lit)])
}
}
None => {
let lit = ExpandedLiteral::{
value: sv,
datatype: None,
language: term_language,
direction: term_direction,
raw: None,
index: None,
included: None,
}
Ok([ExpandedValue::Value(lit)])
}
}
}
JsonValue::Number(nv) => {
// 原生值 coercion(#t0031/#t0061 oracle):datatype 映射(非 @id/@vocab/
// @json 形态)⇒ {@value, @type: dt};@id/@vocab coercion 不转原生值
//(#t0088 同族)
let dt = match scoped.terms.get(term) {
Some(term_definition) => term_definition.datatype
None => None
}
let lit = match dt {
Some(coercion) =>
// @none 同族不转原生值(#ttn01——数/布尔 raw 保形无 @type)
if coercion is ("@id" | "@vocab" | "@json" | "@none") {
ExpandedLiteral::{
value: "\{nv}",
datatype: None,
language: None,
direction: None,
raw: Some(JsonValue::Number(nv)),
index: None,
included: None,
}
} else {
ExpandedLiteral::{
value: "\{nv}",
datatype: Some(coercion),
language: None,
direction: None,
raw: Some(JsonValue::Number(nv)),
index: None,
included: None,
}
}
None =>
ExpandedLiteral::{
value: "\{nv}",
datatype: None,
language: None,
direction: None,
raw: Some(JsonValue::Number(nv)),
index: None,
included: None,
}
}
Ok([ExpandedValue::Value(lit)])
}
// 属性位布尔 = @value 字面量保形(#t0088 oracle:true/false 保留);
// null 丢弃(1.1)
JsonValue::Bool(bv) => {
let dt = match scoped.terms.get(term) {
Some(term_definition) => term_definition.datatype
None => None
}
let lit = match dt {
Some(coercion) =>
// @none 同族不转原生值(#ttn01 同上)
if coercion is ("@id" | "@vocab" | "@none") {
// @id/@vocab coercion 不转原生值(#t0088 同族)
ExpandedLiteral::{
value: "\{bv}",
datatype: None,
language: None,
direction: None,
raw: Some(JsonValue::Bool(bv)),
index: None,
included: None,
}
} else {
ExpandedLiteral::{
value: "\{bv}",
datatype: Some(coercion),
language: None,
direction: None,
raw: Some(JsonValue::Bool(bv)),
index: None,
included: None,
}
}
None =>
ExpandedLiteral::{
value: "\{bv}",
datatype: None,
language: None,
direction: None,
raw: Some(JsonValue::Bool(bv)),
index: None,
included: None,
}
}
Ok([ExpandedValue::Value(lit)])
}
JsonValue::Null => Ok([])
}
}
///|
impl Expander for StandardExpander with fn postprocess_value(
_self,
_active : ActiveContext,
value : ExpandedValue,
) -> Result[ExpandedValue, JsonLdError] {
// 值对象归一:空语言/空方向清除(语义为无标注);xsd 派生挂 toRDF 役
match value {
ExpandedValue::Value(literal) => {
match literal.language {
Some(lang) if lang is "" => literal.language = None
_ => ()
}
match literal.direction {
Some(dir) if dir is "" => literal.direction = None
_ => ()
}
Ok(ExpandedValue::Value(literal))
}
other => Ok(other)
}
}
///|
impl Expander for StandardExpander with fn set_language(
_self,
_active : ActiveContext,
_value : JsonValue,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("set_language pending"))
}
///|
impl Expander for StandardExpander with fn set_direction(
_self,
_active : ActiveContext,
_value : JsonValue,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("set_direction pending"))
}
///|
impl Expander for StandardExpander with fn expand_list(
_self,
_active : ActiveContext,
_value : JsonValue,
_active_property : String?,
_options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("expand_list pending"))
}
///|
impl Expander for StandardExpander with fn expand_set(
_self,
_active : ActiveContext,
_value : JsonValue,
_active_property : String?,
_options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("expand_set pending"))
}
///|
impl Expander for StandardExpander with fn expand_graph(
_self,
_active : ActiveContext,
_value : JsonValue,
_active_property : String?,
_options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("expand_graph pending"))
}
///|
impl Expander for StandardExpander with fn expand_reverse(
_self,
_active : ActiveContext,
_value : JsonValue,
_active_property : String?,
_options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("expand_reverse pending"))
}
///|
impl Expander for StandardExpander with fn expand_nest(
_self,
_active : ActiveContext,
_value : JsonValue,
_active_property : String?,
_options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("expand_nest pending"))
}
///|
impl Expander for StandardExpander with fn expand_included(
_self,
_active : ActiveContext,
_value : JsonValue,
_active_property : String?,
_options : JsonLdOptions,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("expand_included pending"))
}
///|
impl Expander for StandardExpander with fn set_index(
_self,
_active : ActiveContext,
_value : JsonValue,
) -> Result[ExpandedValue, JsonLdError] {
Err(JsonLdError::Unsupported("set_index pending"))
}
///|
/// 定义期 base URL 快照(庚批——#tc031 oracle):term 所在 context 的装载
/// base URL——flags.base_url(装载链内)优先,缺省回落 documentUrl。
fn definition_base_url(
flags : ContextFlags,
options : JsonLdOptions,
) -> String? {
match flags.base_url {
Some(url) => Some(url)
None => options.base
}
}
///|
/// 应用面 base URL 覆写(庚批——#tc031 oracle):td 携带的 scoped context 在
/// 应用期以其**定义期 base URL** 作 flags.base_url——远程 context 内定义的
/// scoping 引用锚定源 context 装载位(新建面 base_url=None 会回落 documentUrl,
/// 即 #tc031 的报错路径)。
fn ContextFlags::with_context_base_url(
self : ContextFlags,
context_base_url : String?,
) -> ContextFlags {
{ ..self, base_url: context_base_url, }
}
///|
/// context 引用解析共用件(REC 4.1.2 步 5.2.1——远程 String 臂与 @import 预处理
/// 共用):引用对**context 装载 base URL** 合并。base 取 flags.base_url(装载链
/// 内 = context 自身 URL),缺省回落 options.base(documentUrl)——**不回落
/// active.base**:引用解析是算法输入位,不随 active @base 覆写走(#tc031
/// oracle:数组 context 首项 `@base` 覆写后,次项引用仍锚定文档位置;REC:
/// base URL "is often not the same as base or the base IRI of the active
/// context")。
fn resolve_context_reference(
context_ref : String,
flags : ContextFlags,
options : JsonLdOptions,
) -> Result[String, JsonLdError] {
match flags.base_url {
Some(url) => Ok(merge_base_reference(url, context_ref))
None =>
match options.base {
Some(url) => Ok(merge_base_reference(url, context_ref))
None =>
Err(
JsonLdError::InvalidContext(
"loading remote context failed: no base URL to resolve \{context_ref}",
),
)
}
}
}
///|
/// 远程 context 装载共用件(REC 4.1.2 步 5.2.5 / 步 5.4 共用):loader 装载 →
/// JSON 解析 → 解包顶层 @context 成员。装载失败 → loading remote context
/// failed;非对象或无该成员 → invalid remote context。返回解包后的内联 context
/// (调用面不再判 None——共用件已保证形态)。
fn load_context_document(
resolved : String,
options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
let loader = match options.load_context {
Some(loader) => loader
None => return Err(JsonLdError::Unsupported("context loader not provided"))
}
let text = match loader(resolved) {
Ok(text) => text
Err(msg) =>
return Err(
JsonLdError::InvalidContext("loading remote context failed: \{msg}"),
)
}
let loaded = match parse_json(text) {
Ok(value) => value
Err(_e) =>
return Err(
JsonLdError::InvalidContext(
"loading remote context failed: invalid JSON at \{resolved}",
),
)
}
// 装载结果须含 @context 成员(步 5.2.5):非对象或无该成员 → invalid remote context
let inner : JsonValue? = match loaded {
JsonValue::Object(pairs) => {
let mut found : JsonValue? = None
for pair in pairs {
if pair.0 is "@context" {
found = if found is None { Some(pair.1) } else { found }
}
}
found
}
_ => None
}
match inner {
Some(inner_value) => Ok(inner_value)
None =>
Err(JsonLdError::InvalidContext("invalid remote context: \{resolved}"))
}
}
///|
/// @import 条目合并件(REC 4.1.2 步 5.4;庚批——#tso05/06/08/09/11 oracle):
/// 装载 imported context 并做形态/链式校验,与本 context 条目**条目级合并**:
/// 「merging context into import context, **replacing common entries**」——
/// imported 条目在前、同名条目以本 context 侧**替换**(不并存;#tso11 oracle:
/// imported 的 term 定义不参与处理,故不受本 context `@protected` 追溯保护)、
/// @import 条目剥离(合并后主循环不再见)。校验:1.0 模式 →
/// invalid context entry;值非串 → invalid @import value;imported 文档须为
/// context 定义 **map**(数组/标量 → invalid remote context);imported 自身含
/// @import → invalid context entry(链式装载禁——REC 步 5.4)。
fn merge_import_entries(
import_value : JsonValue,
local_pairs : Array[(String, JsonValue)],
flags : ContextFlags,
options : JsonLdOptions,
) -> Result[Array[(String, JsonValue)], JsonLdError] {
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid context entry"))
}
let import_ref = match import_value {
JsonValue::String(context_ref) => context_ref
_ => return Err(JsonLdError::InvalidContext("invalid @import value"))
}
let resolved = match resolve_context_reference(import_ref, flags, options) {
Ok(url) => url
Err(e) => return Err(e)
}
let imported = match load_context_document(resolved, options) {
Ok(value) => value
Err(e) => return Err(e)
}
let imported_pairs = match imported {
JsonValue::Object(pairs) => pairs
_ =>
return Err(
JsonLdError::InvalidContext("invalid remote context: \{resolved}"),
)
}
for imported_pair in imported_pairs {
if imported_pair.0 is "@import" {
return Err(JsonLdError::InvalidContext("invalid context entry"))
}
}
// 本 context 键集(去 @import 后)——imported 侧同名条目被替换剔除
let local_keys : Map[String, Bool] = Map([])
for pair in local_pairs {
if !(pair.0 is "@import") {
local_keys[pair.0] = true
}
}
let merged : Array[(String, JsonValue)] = []
for imported_pair in imported_pairs {
if !local_keys.contains(imported_pair.0) {
merged.push(imported_pair)
}
}
for pair in local_pairs {
if !(pair.0 is "@import") {
merged.push(pair)
}
}
Ok(merged)
}
///|
/// ContextProcessor 落码(③批):@context 声明 → ActiveContext 构建/继承(简单形态)。
/// 继承:terms/vocab/base 父副本(ADR-JL-4 不可变语义 = 副本);数组 context = 逐项合并。
/// 落码范围:term→IRI 字符串定义、@vocab(相对先经 base)、@base、term=null(移除)。
/// 未落(红而非错):term 定义对象形态(@type/@container——⑤批)、@language/@direction
/// 默认(后由丙批落)。
/// **己批**:远程 context String 臂(REC 4.1.2 步 5.2:resolve → skip → overflow
/// → load → recurse;装载链终止两机制)。
/// **庚批**:@import 预处理(步 5.4:装载 + 条目级合并 + 链式禁)+ 引用解析
/// base 锚定 context 装载位(resolve_context_reference——不随 active @base 覆写)。
/// **type-scoped 批**:`propagate` 参数(调用方默认;local context 的 @propagate
/// 成员优先)——propagate=false 时置 previous context,供新节点对象回退
/// (REC 4.1.2 步 3)。
/// **@protected 批**:上下文级 `@protected` 成员为 td 默认保护位;`override
/// protected` 由调用面旗标传入(property-scoped/定义期校验 = true,type-scoped/
/// 元素级 = false);`null` 空化遇保护 term 且 override=false → invalid context
/// nullification。
impl ContextProcessor for StandardExpander with fn process_context(
_self,
active : ActiveContext,
local_context : JsonValue,
flags : ContextFlags,
options : JsonLdOptions,
) -> Result[ActiveContext, JsonLdError] {
let mut vocab = active.vocab
let mut base = active.base
match local_context {
// context nullification(REC 4.1.2:数组项为 null = 重置为新初始 context,
// **base IRI 取原 base URL**)——scoped-context 批勘定件:#tc014(type-scoped
// `[null]`)/ #tc018(`[null, {...}]`)的 scoped context 形态若不接受,定义期
// 校验会误判 invalid scoped context。**@protected 批门**:override protected
// = false 且 active context 含任何保护 term → invalid context nullification
// (#pr05/#pr17/#pr18/#pr20/#pr21 oracle)
JsonValue::Null => {
if !flags.override_protected && has_protected_term(active) {
return Err(JsonLdError::InvalidContext("invalid context nullification"))
}
// nullification 保 base URL;propagate=false 时把 previous 带过新 result
// (REC 4.1.2 步 5.2.1:"if propagate is false, previous context in result
// to the previous value of result")
Ok(ActiveContext::{
base: initial_context(options).base,
vocab: None,
terms: Map([]),
previous: previous_after(active, flags.propagate),
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
})
}
JsonValue::String(context_ref) => {
// 远程 context 臂(己批——REC 4.1.2 步 5.2:resolve → skip → overflow → load → recurse;
// resolve/load 与 @import 预处理共用件同源——引用解析 base 口径见
// resolve_context_reference)
let resolved = match
resolve_context_reference(context_ref, flags, options) {
Ok(url) => url
Err(e) => return Err(e)
}
// 步 5.2.2:validate=false(td 定义期校验)且链上已有 → 跳过非错误(破环主机制)
if !flags.validate_scoped && flags.remote_contexts.contains(resolved) {
return Ok(active)
}
// 步 5.2.3:链深超 processor-defined limit → context overflow(validate=true 的文档级环靠此截断)
if flags.remote_contexts.length() >= CONTEXT_CHAIN_LIMIT {
return Err(
JsonLdError::ContextOverflow("context overflow: \{resolved}"),
)
}
// 步 5.2.5:装载 + 解包 @context 成员(失败 → loading remote context
// failed / invalid remote context——文案在共用件内)
let inner = match load_context_document(resolved, options) {
Ok(value) => value
Err(e) => return Err(e)
}
// 步 5.2.6:递归——local context = 装载内容、base URL = documentUrl(= resolved,loader 无重定向)、链副本(新建数组)、validate 原样
let chain : Array[String] = []
for url in flags.remote_contexts {
chain.push(url)
}
chain.push(resolved)
let inner_flags : ContextFlags = {
propagate: flags.propagate,
override_protected: flags.override_protected,
base_url: Some(resolved),
remote_contexts: chain,
validate_scoped: flags.validate_scoped,
}
ContextProcessor::process_context(
_self, active, inner, inner_flags, options,
)
}
JsonValue::Array(items) => {
// 数组 context:逐项合并(前项产出为后项输入)
let mut current = active
for item in items {
match
ContextProcessor::process_context(
_self, current, item, flags, options,
) {
Ok(next) => current = next
Err(e) => return Err(e)
}
}
Ok(current)
}
JsonValue::Object(pairs) => {
// @import 预处理(REC 4.1.2 步 5.4;庚批):见 merge_import_entries——
// 有 @import 条目时以合并对列(imported 在前、本 context 去 @import)作
// 工作对列;无则原对列直通。合并后主循环不再见 @import 键。
let mut import_found : JsonValue? = None
for pair in pairs {
if pair.0 is "@import" && import_found is None {
import_found = Some(pair.1)
}
}
let pairs = match import_found {
None => pairs
Some(import_value) =>
match merge_import_entries(import_value, pairs, flags, options) {
Ok(merged) => merged
Err(e) => return Err(e)
}
}
let keyword_owner : Map[String, String] = Map([])
// 对象形态 term 定义缓冲(两遍处理——第二遍见主循环后)
let deferred_td : Array[(String, Array[(String, JsonValue)])] = []
// 继承:父 terms 复制——**仅当本 context 有 term 条目时才复制**(只含 context
// 关键字时 terms 不可能被写,直接共享父 map,省 O(T) 复制;@vocab/@base 等
// 关键字集合见下)
let mut has_term_entries = false
for pair in pairs {
match pair.0 {
"@base"
| "@direction"
| "@import"
| "@language"
| "@propagate"
| "@protected"
| "@version"
| "@vocab" => ()
_ => has_term_entries = true
}
}
let new_terms : Map[String, TermDefinition] = if has_term_entries {
let copied : Map[String, TermDefinition] = Map([])
for k, td in active.terms {
copied[k] = td
}
copied
} else {
active.terms
}
// @propagate 成员(REC 4.1.2 步 2;校验在步 5.x):1.0 模式非法
// (invalid context entry);非布尔 → invalid @propagate value
let mut propagate = flags.propagate
// @protected 成员(REC 4.1.2 步 5 调用句:作为本 context 内 td 的**默认**
// 保护位):1.0 模式非法(先验,与 @propagate 同门);非布尔 → invalid
// @protected value
let mut protected_default = false
// 默认语言/方向(丙批——@language/@direction 臂)
let mut default_language : String? = None
let mut default_direction : String? = None
for pair in pairs {
if pair.0 is "@propagate" {
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid context entry"))
}
match pair.1 {
JsonValue::Bool(flag) => propagate = flag
_ =>
return Err(
JsonLdError::InvalidContext("invalid @propagate value"),
)
}
}
if pair.0 is "@protected" {
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid context entry"))
}
match pair.1 {
JsonValue::Bool(flag) => protected_default = flag
_ =>
return Err(
JsonLdError::InvalidContext("invalid @protected value"),
)
}
}
}
for pair in pairs {
match pair.0 {
"@vocab" =>
match pair.1 {
JsonValue::String(v) => {
// 套件 oracle(#t0092[1.1] vs #t0115/#t0116[1.0]):
// 1.0 模式 = @vocab 非绝对(含相对/空串)即 invalid——对原始值判,
// base 不补救;1.1 模式 = 对 base raw 拼接(#t0092:空串 → base
// 本身、`../` 原样不消点段)
// 1.0 校验(#t0115/#t0116 oracle:相对/空串非法)豁免 bnode
// 标签(#t0075 oracle:@vocab "_:" 合法——处理模式依赖第一实证
// 的边界细化);bnode 不落 base 拼接
if options.processing_mode is JsonLd10 &&
!(is_valid_absolute_iri(v) || is_bnode_label(v)) {
return Err(
JsonLdError::InvalidContext("invalid vocab mapping"),
)
}
// 动态形态解析(@vocab 动态形态批——#t0124/#t0125 oracle):
// ① term 精确命中 ② compact(prefix ∈ in-progress terms,
// suffix 非 "//")③ 未命中回落既有 absolute/bnode/base-join
//(t0092 语义不变:空串 → base 本身)
let mut resolved_v : String? = None
match new_terms.get(v) {
Some(td) =>
match td.iri {
Some(iri) => resolved_v = Some(iri)
None => ()
}
None => ()
}
if resolved_v is None {
match colon_index(v) {
Some(at) => {
let (prefix, suffix) = split_first_colon(v, at)
let double_slash = suffix.get_char(0) is Some('/') &&
suffix.get_char(1) is Some('/')
if !double_slash {
match new_terms.get(prefix) {
Some(td) =>
match td.iri {
Some(base_iri) =>
resolved_v = Some("\{base_iri}\{suffix}")
None => ()
}
None => ()
}
}
}
None => ()
}
}
let resolved = match resolved_v {
Some(r) => r
None =>
if is_valid_absolute_iri(v) || is_bnode_label(v) {
v
} else {
match vocab {
// 相对 @vocab 值 → 拼接现有 vocab(#t0111/#t0112
// oracle:"./rel2#" 拼在 ctx1 vocab 之后)
Some(prev) => "\{prev}\{v}"
// 无现有 vocab 且有 base → RFC merge("/x" 前导斜杠 =
// authority 路径替换——#t0110 oracle)
None =>
match base {
Some(b) => merge_base_reference(b, v)
None => v
}
}
}
}
vocab = Some(resolved)
}
JsonValue::Null => vocab = None
_ =>
return Err(
JsonLdError::InvalidContext("@vocab must be string or null"),
)
}
"@base" =>
match pair.1 {
// 相对 @base:合并 + ".." 弹段(resolve_base_value);绝对 → 原样
JsonValue::String(b) =>
base = Some(resolve_base_value(b, active.base))
JsonValue::Null => base = None
_ =>
return Err(
JsonLdError::InvalidContext("@base must be string or null"),
)
}
"@version" => {
// 1.1 API:@version 接受串 "json-ld-1.1" 与数值 1.1(套件实证
// 159 例全为数值形且期望成功——REC"须为串"被套件输入形态否决;
// 数值 1.0 → 错,#tep03 负例钉边界)。1.0 模式切换(ActiveContext
// 载入 processing_mode)挂后续批
let version_is_11 = match pair.1 {
JsonValue::String("json-ld-1.1") => true
JsonValue::Number(v) =>
if v == 1.1 {
true
} else {
return Err(
JsonLdError::InvalidContext("invalid @version value"),
)
}
JsonValue::String(v) =>
return Err(
JsonLdError::InvalidContext("invalid @version value (\{v})"),
)
_ =>
return Err(
JsonLdError::InvalidContext("invalid @version value"),
)
}
// #tep02 oracle:processingMode json-ld-1.0 与 @version:1.1 冲突 → 错
if version_is_11 && options.processing_mode is JsonLd10 {
return Err(
JsonLdError::InvalidContext(
"processingMode json-ld-1.0 conflicts with @version: 1.1",
),
)
}
}
// 未落码 context 关键字(挂账活账——@protected 原落"term definition
// must be…"的误分类,勘定批修正为 Unsupported:@protected 族 16 枚
// 的挂账去向是「@protected 批」,非 term 定义形态错误)
// @propagate 已在对象臂入口预扫描(REC 4.1.2 步 2)——此处跳过
"@propagate" => ()
// @protected 同上(对象臂入口预扫描:默认保护位)
"@protected" => ()
"@language" =>
// 默认语言(丙批——#t0022/#t0041/#ter09 oracle):串设置/null
// 重置;非串 → invalid default language
match pair.1 {
JsonValue::String(lang) => default_language = Some(lang)
JsonValue::Null => default_language = None
_ =>
return Err(
JsonLdError::InvalidContext("invalid default language"),
)
}
"@direction" =>
// 默认方向(丙批——#tdi01 oracle):ltr/rtl/null;其他 →
// invalid base direction(#tdi08 同族)
match pair.1 {
JsonValue::Null => default_direction = None
JsonValue::String(dir) =>
if dir is ("ltr" | "rtl") {
default_direction = Some(dir)
} else {
return Err(
JsonLdError::InvalidContext("invalid base direction"),
)
}
_ =>
return Err(
JsonLdError::InvalidContext("invalid base direction"),
)
}
"@import" => ()
other => {
// 关键字别名冲突(#ter26 oracle):同关键字第二别名 → colliding keywords
if other is "id" || other is "ID" {
match keyword_owner.get("@id") {
Some(prev_owner) =>
if prev_owner != other {
return Err(
JsonLdError::InvalidContext("colliding keywords"),
)
}
None => keyword_owner["@id"] = other
}
}
// term 名不得为相对 IRI 路径(#ter48 oracle:"./something" →
// invalid IRI mapping)——"." 开头的键是相对路径形态,不能作 term
if other.get_char(0) is Some('.') {
return Err(JsonLdError::InvalidIri("invalid IRI mapping"))
}
// 空 term 名(#ter52 oracle)→ invalid term definition
if other is "" {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
// term 定义(简单形态:term → IRI 字符串;null = 移除)。
// 值 IRI 展开:vocab 位、对 in-progress terms 查表
// (#t0072 oracle:重定义为自身时用 @vocab,非旧 term 定义)
match pair.1 {
JsonValue::String(iri) => {
// 循环检测(⑤后半——#ter10 oracle):值 = compact 形且 prefix =
// 自身 term → cyclic IRI mapping
if is_self_prefix_compact(iri, other) {
return Err(JsonLdError::InvalidContext("cyclic IRI mapping"))
}
// 自映射排除(#t0072/#t0071 oracle):值 = term 自身时旧定义
// 不参与查表——有 @vocab → vocab 拼接;有 prefix → compact;
// 两者皆无 → 原样
let lookup_terms = if iri == other {
terms_without(new_terms, other)
} else {
new_terms
}
let building = ActiveContext::{
base,
vocab,
terms: lookup_terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
let expanded = match
IriExpander::expand_iri(_self, building, iri, false, true) {
Ok(k) => k
Err(e) => return Err(e)
}
// keyword 项互斥(#ter01 oracle):**真关键字** term 映射到
// 异关键字 → keyword redefinition("@iri" 等 keyword 形态
// 非真关键字者忽略不报——#t0005 oracle)
if is_keyword(other) &&
is_keyword(expanded) &&
expanded != other {
return Err(
JsonLdError::InvalidContext("keyword redefinition"),
)
}
// 值为 keyword 形态非真关键字(#tpr37/#t0120 oracle 同族):
// 定义忽略——键回落 vocab 拼接(ignoreMe → vocab/ignoreMe)
if has_keyword_form(expanded) && !is_keyword(expanded) {
continue
}
// term 名一致性检查(REC 4.2.2 @id 臂;#ter44 负例 oracle;
// simple 臂与 td @id 臂同规则同源)
match
StandardExpander::check_term_name_consistency(
_self, building, other, expanded, options,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
match
StandardExpander::install_term_definition(
new_terms,
other,
TermDefinition::{
iri: Some(expanded),
datatype: None,
container: None,
reverse: None,
local_context: None,
context_base_url: definition_base_url(flags, options),
index_mapping: None,
graph_secondary: None,
is_protected: protected_default,
language_mapping: None,
direction_mapping: None,
is_prefix: None,
nest: None,
simple_form: true,
graph_set: false,
container_set: false,
},
flags.override_protected,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
}
// term: null → @id null 映射(REC 4.2.2:value 为 null 时视作
// {"@id": null})——term **保留**、IRI 映射为 null:展开期该键
// 整体丢弃(#tin06 oracle:scoped context {"data": null} 下
// "data" 键丢弃;此前按"移除定义"处理会 vocab 拼成属性,为误判)
JsonValue::Null =>
match
StandardExpander::install_term_definition(
new_terms,
other,
TermDefinition::{
iri: None,
datatype: None,
container: None,
reverse: None,
local_context: None,
context_base_url: definition_base_url(flags, options),
index_mapping: None,
graph_secondary: None,
is_protected: protected_default,
language_mapping: None,
direction_mapping: None,
is_prefix: None,
nest: None,
simple_form: false,
graph_set: false,
container_set: false,
},
flags.override_protected,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
// 对象形态 term 定义推迟到第二遍(#t0007 oracle:同 context
// 后置 prefix 定义——简单 term——须对其可用)
JsonValue::Object(td_pairs) => deferred_td.push((other, td_pairs))
_ =>
return Err(
JsonLdError::InvalidContext(
"term definition must be a string, object or null",
),
)
}
}
}
}
// 第二遍:对象形态 term 定义(#t0007 oracle——简单定义此时全部就绪,
// 后置 prefix 可用)
for deferred in deferred_td {
match
StandardExpander::process_expanded_term_definition(
_self,
new_terms,
base,
vocab,
protected_default,
flags,
options,
deferred.0,
deferred.1,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
}
Ok(ActiveContext::{
base,
vocab,
terms: new_terms,
previous: previous_after(active, propagate),
default_language,
default_direction,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
})
}
_ =>
Err(JsonLdError::InvalidContext("local context must be object or array"))
}
}
///|
/// 扩展 term 定义(对象形态,⑤批 A 组——判据①):@id 位 = vocab 展开(自映射
/// 排除旧定义——#t0071 oracle);@type 位 = 三形态 type mapping("@id"/"@vocab"/
/// 绝对 IRI——#t0011/#t0054/#t0007 oracle);@container/@language/@prefix/@nest
/// 验证(⑤后半——#tem01/#ter21/#tes01/#tes02/#ter22/#ter53/#ten05 oracle);
/// @context 位 = 属性作用域 context(scoped-context 批:定义期校验 + 存留,
/// #tc032/#tc033 oracle)。关键字 term 必须自映射(#tec02 oracle:
/// keyword redefinition;#t0026 oracle:{"@id":"@type","@type":"@id"} 合法)
fn StandardExpander::process_expanded_term_definition(
_self : StandardExpander,
new_terms : Map[String, TermDefinition],
base : String?,
vocab : String?,
protected_default : Bool,
flags : ContextFlags,
options : JsonLdOptions,
term_name : String,
td_pairs : Array[(String, JsonValue)],
) -> Result[Unit, JsonLdError] {
let mut td_iri : String? = None
let mut td_datatype : String? = None
let mut td_container : ContainerType? = None
let mut td_reverse : String? = None
let mut td_graph_secondary : ContainerType? = None
let mut td_graph_set = false
let mut td_container_set = false
let mut td_prefix = false
let mut td_nest : String? = None
let mut td_is_prefix : Bool? = None
// 简单形态位:本函数只接 map 形 td(简单 string 形在调用方单独臂)——
// REC 4.2.2 缺省 @prefix=false 只挂 map 形(#tp001 oracle 面)
let td_simple = false
let mut td_language : LanguageMember? = None
let mut td_direction : LanguageMember? = None
let mut td_local_context : JsonValue? = None
// {"@id": null} 显式空映射(scoped-context 批):区别于"无 @id 键"——
// 不落 vocab 兜底,term 保留但 IRI 映射为 null(展开期该键丢弃)
let mut td_id_null = false
// td 级 @protected 覆盖上下文级默认(REC 4.2.2 步 10;非布尔 → invalid
// @protected value、1.0 → invalid term definition)
let mut td_protected = protected_default
// 容器含 @type 标记(REC 4.2.2:container mapping includes @type 时——
// 缺省 type mapping = @id,且只允许 @id/@vocab;#tm017/#tm018/#tm020 oracle)
let mut td_container_has_type = false
// 容器含 @index 标记(REC 4.2.2:@index mapping 的适用前提)
let mut td_container_has_index = false
// 索引映射(容器含 @index 且 index key ≠ @index 时,索引键写成属性——#tpi06~10)
let mut td_index_mapping : String? = None
for td_pair in td_pairs {
match td_pair.0 {
"@id" =>
match td_pair.1 {
// {"@id": null}:term 保留、无 IRI 映射(REC 4.2.2 @id 臂)
JsonValue::Null => td_id_null = true
JsonValue::String(iri) => {
// 循环检测(⑤后半——#ter10 oracle):值 = compact 形且 prefix =
// 自身 term → cyclic IRI mapping
if is_self_prefix_compact(iri, term_name) {
return Err(JsonLdError::InvalidContext("cyclic IRI mapping"))
}
// 自映射排除(#t0071 oracle:重定义 @id = 自身时旧定义不查表)
let lookup_terms = if iri == term_name {
terms_without(new_terms, term_name)
} else {
new_terms
}
let building = ActiveContext::{
base,
vocab,
terms: lookup_terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
match IriExpander::expand_iri(_self, building, iri, false, true) {
Ok(k) => {
// @context 别名禁(#ter19 oracle:@id 映射 @context →
// invalid keyword alias)
if k is "@context" {
return Err(
JsonLdError::InvalidContext("invalid keyword alias"),
)
}
// keyword 形态非真关键字 → **整个定义放弃**(REC 4.2.2 @id 臂明文:
// "If the value associated with the @id entry is not a keyword, but
// has the form of a keyword, **return**; processors SHOULD generate
// a warning"——不是"继续走到无 @id 兜底")。#t0120 oracle:term 无定义
// ⇒ 键回落 vocab 拼接(属性 = vocab/ignoreMe)
if has_keyword_form(k) && !is_keyword(k) {
return Ok(())
}
td_iri = Some(k)
// term 名一致性检查(REC 4.2.2 @id 臂;#ter43 负例 oracle /
// #t0026 正例 oracle——1.0 无此检查)
match
StandardExpander::check_term_name_consistency(
_self, building, term_name, k, options,
) {
Ok(_) => ()
Err(e) => return Err(e)
}
// td_iri 由前述条件写入(keyword 形态非真关键字 → 保持 None)
}
Err(e) => return Err(e)
}
}
_ => return Err(JsonLdError::InvalidContext("@id must be a string"))
}
"@type" =>
match td_pair.1 {
JsonValue::String(t) =>
// "@none"(ttn02 批——REC 1.1):显式**无 coercion**——串/数/布尓
// 全按无 datatype 值对象走、@id 形照旧节点引用(tn02-out oracle:
// "string"→{@value}、true→{@value:true}、{"@id"}→节点引用)。1.0
// 模式无此关键字 → invalid type mapping。下游与 td_datatype=None
// 同路——datatype 位存 None
if t is "@none" {
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid type mapping"))
}
// keyword 形标记(役33——None 与「无 @type」双义的解;消费位
// 按形接闸:coercion 短路/object_fit 放行/bare 早退跳过)
td_datatype = Some("@none")
} else {
// 关键字形态原样("@id"/"@vocab"/"@json"——丁批);其余 vocab 展开
let expanded = if t is ("@id" | "@vocab" | "@json") {
t
} else {
let building = ActiveContext::{
base,
vocab,
terms: new_terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
match IriExpander::expand_iri(_self, building, t, false, true) {
Ok(k) => k
Err(e) => return Err(e)
}
}
// type mapping 只收关键字形态(@id/@vocab/@json——丁批)或绝对
// IRI;无 vocab 时 **curie 形**(含冒号)相对形原样收留
//(#tc024 oracle——"ex:Number" 前缀未定义,与值 datatype 同串
// 对上);路径相对形拒(#ter23 oracle——"relative/iri" 无冒号
// invalid);空节点标号恒拒(#ter13 oracle——"_:not-an-iri")
if !(expanded is ("@id" | "@vocab" | "@json" | "@none") ||
is_valid_absolute_iri(expanded) ||
(
vocab is None &&
expanded == t &&
t.contains(":") &&
!t.has_prefix("_:")
)) {
return Err(JsonLdError::InvalidContext("invalid type mapping"))
}
td_datatype = Some(expanded)
}
_ => return Err(JsonLdError::InvalidContext("@type must be a string"))
}
"@container" =>
// 容器映射验证(⑤后半——#tem01/#ter21/#tes01/#tes02 oracle):
// 串值按模式白名单、数组仅 1.1 图组合
match validate_container_value(td_pair.1, options) {
Ok(_) => {
// 存 List/Set/Language/Type 四态(@type 批——type map 应用依赖 Type);
// @id/@graph 容器应用挂账存 None
match td_pair.1 {
JsonValue::String("@language") =>
td_container = Some(ContainerType::Language)
JsonValue::String("@list") =>
td_container = Some(ContainerType::List)
JsonValue::String("@set") =>
td_container = Some(ContainerType::Set)
JsonValue::String("@type") =>
td_container = Some(ContainerType::Type)
JsonValue::String("@index") =>
td_container = Some(ContainerType::Index)
JsonValue::String("@id") => td_container = Some(ContainerType::Id)
JsonValue::String("@graph") =>
td_container = Some(ContainerType::Graph)
JsonValue::Array(combo_items) => {
// graph 组合([@graph, @id|@index|@set]——graph 容器族):
// container=Graph + 次级存 graph_secondary
let mut has_graph = false
let mut secondary : ContainerType? = None
for combo in combo_items {
match combo {
JsonValue::String("@graph") => has_graph = true
JsonValue::String("@id") =>
secondary = Some(ContainerType::Id)
JsonValue::String("@index") =>
secondary = Some(ContainerType::Index)
// @set 不占次级但置保形位(展开形态与裸 @graph 同
// ——#t0080;压缩侧出形分叉靠 graph_set——#t0078/#t0109)
JsonValue::String("@set") => td_graph_set = true
_ => ()
}
}
let mut combo_key : ContainerType? = None
for combo in combo_items {
match combo {
JsonValue::String("@id") =>
if combo_key is None {
combo_key = Some(ContainerType::Id)
}
JsonValue::String("@type") =>
if combo_key is None {
combo_key = Some(ContainerType::Type)
}
JsonValue::String("@language") =>
if combo_key is None {
combo_key = Some(ContainerType::Language)
}
JsonValue::String("@index") =>
if combo_key is None {
combo_key = Some(ContainerType::Index)
}
_ => ()
}
}
if has_graph {
td_container = Some(ContainerType::Graph)
td_graph_secondary = secondary
} else if td_graph_set && combo_key is Some(_) {
// @set 并容([#@id|@type|@language|@index, @set]——#ts002):
// container = 主键态 + container_set 保形位(map 发射保数组)
td_container = combo_key
td_container_set = true
} else if combo_items.length() == 1 {
// 单元素数组容器 = 裸形等价(#ts001 oracle——["@list"]
// 等价 "@list";REC 1.1 数组形逐值处理)
match combo_items[0] {
JsonValue::String("@list") =>
td_container = Some(ContainerType::List)
JsonValue::String("@set") =>
td_container = Some(ContainerType::Set)
JsonValue::String("@language") =>
td_container = Some(ContainerType::Language)
JsonValue::String("@type") =>
td_container = Some(ContainerType::Type)
JsonValue::String("@index") =>
td_container = Some(ContainerType::Index)
JsonValue::String("@id") =>
td_container = Some(ContainerType::Id)
_ => ()
}
}
}
_ => ()
}
// 容器含 @type 的原始形态标记(数组形态亦算——REC 4.2.2 隐式 type
// mapping 规则按"container mapping includes @type"判,非按存储态)
match td_pair.1 {
JsonValue::String("@type") => td_container_has_type = true
JsonValue::String("@index") => td_container_has_index = true
JsonValue::Array(items) =>
for item in items {
match item {
JsonValue::String("@type") => td_container_has_type = true
JsonValue::String("@index") => td_container_has_index = true
_ => ()
}
}
_ => ()
}
}
Err(e) => return Err(e)
}
"@language" =>
// 语言映射(丙批应用化——#ter22 验证保留:非串 → invalid language
// mapping;null = 显式压制默认语言)
match td_pair.1 {
JsonValue::String(lang) =>
td_language = Some(LanguageMember::Set(lang))
JsonValue::Null => td_language = Some(LanguageMember::Null)
_ =>
return Err(JsonLdError::InvalidContext("invalid language mapping"))
}
"@direction" =>
// 方向映射(丙批——#tdi01/#tdi04~#tdi07 oracle):ltr/rtl 或 null
// 压制;非法值 → invalid base direction
match td_pair.1 {
JsonValue::String(dir) =>
if dir is ("ltr" | "rtl") {
td_direction = Some(LanguageMember::Set(dir))
} else {
return Err(JsonLdError::InvalidContext("invalid base direction"))
}
JsonValue::Null => td_direction = Some(LanguageMember::Null)
_ => return Err(JsonLdError::InvalidContext("invalid base direction"))
}
"@prefix" => {
// 前缀标记验证(⑤后半——#ter53 oracle):须布尔;true 记录供
// keyword 别名组合禁(#tpr33)。1.0 门 + 冒号名禁(#tep07/#tep09
// oracle——@prefix 系 1.1 特性;带 ":" 的 term 名 + @prefix:true =
// invalid term definition)
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
match td_pair.1 {
JsonValue::Bool(flag) => {
if flag && term_name.contains(":") {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
td_prefix = flag
td_is_prefix = Some(flag)
}
_ => return Err(JsonLdError::InvalidContext("invalid @prefix value"))
}
}
"@nest" => {
// nest 验证(⑤后半——#ten05 oracle):须串且关键字位仅 "@nest"。
// 1.0 门(#tep10 oracle——@nest 系 1.1 特性,invalid term definition);
// 非关键字值须为已定义 term 且展开 = @nest(#ten01 oracle——"unknown"
// 未定义 = invalid @nest value)
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
match td_pair.1 {
JsonValue::String(nest_term) => {
let keyword_named = nest_term.get_char(0) is Some('@')
if keyword_named {
if nest_term != "@nest" {
return Err(JsonLdError::InvalidContext("invalid @nest value"))
}
} else {
// 非关键字别名:须为在册 term 且 iri = @nest(本 context 已处理
// 面——#ten01 负例无此 term 即红;跨 context 别名挂 deferred 细研)
let mut alias_ok = false
match new_terms.get(nest_term) {
Some(alias_td) =>
match alias_td.iri {
Some("@nest") => alias_ok = true
_ => ()
}
None => ()
}
if !alias_ok {
return Err(JsonLdError::InvalidContext("invalid @nest value"))
}
}
td_nest = Some(nest_term)
}
_ => return Err(JsonLdError::InvalidContext("invalid @nest value"))
}
}
"@context" => {
// 属性作用域 context(scoped-context 批——REC 4.2.2 @context 臂):
// 1.0 模式禁(invalid term definition);1.1 模式在**定义期**调用
// Context Processing 校验——结果丢弃(REC 注记),任何错误 → invalid
// scoped context(#tc032/#tc033 oracle:未被使用的嵌入 context 仍须
// 检查)。校验通过则存 local_context,展开期在该 term 的值展开前再
// 处理套用(apply_property_scoped_context)
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
let building = ActiveContext::{
base,
vocab,
terms: new_terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
match
ContextProcessor::process_context(
_self,
building,
td_pair.1,
// REC 4.2.2 步 21.3:override protected=true、remote contexts 传
// **现链**(string 臂递归从不原地改链——新建数组追加,引用共享即
// 副本语义)、validate_scoped=false(链上已有 → 跳过非错误)
{
propagate: true,
override_protected: true,
base_url: flags.base_url,
remote_contexts: flags.remote_contexts,
validate_scoped: false,
},
options,
) {
Ok(_) => td_local_context = Some(td_pair.1)
// 未落码透传(诊断口径:Unsupported = 落码活账,不当作 scoped 校验失败)
Err(JsonLdError::Unsupported(m)) =>
return Err(JsonLdError::Unsupported(m))
Err(_) =>
return Err(JsonLdError::InvalidContext("invalid scoped context"))
}
}
"@reverse" =>
// 反向映射(reverse 批——#ter15 oracle:非串 → invalid IRI mapping;
// #ter50 oracle:串经 vocab 展开后非绝对 → invalid IRI mapping——
// documentRelative=false,base 注入不救)
match td_pair.1 {
JsonValue::String(rev) => {
let building = ActiveContext::{
base,
vocab,
terms: new_terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
match IriExpander::expand_iri(_self, building, rev, false, true) {
Ok(k) =>
// keyword 形态非真关键字("@ignoreMe" 形态)→ **整个定义放弃**
// (REC 4.2.2 @reverse 臂明文:"If the value associated with the
// @reverse entry is a string having the form of a keyword, **return**;
// processors SHOULD generate a warning")。#tpr38/#tpr39 oracle:
// term 无定义 ⇒ 键回落普通语义(不落反向映射),且**不得**再走到
// 无 @id 兜底去报 invalid IRI mapping
if has_keyword_form(k) && !is_keyword(k) {
return Ok(())
} else if is_valid_absolute_iri(k) {
td_reverse = Some(k)
} else {
return Err(JsonLdError::InvalidIri("invalid IRI mapping"))
}
Err(e) => return Err(e)
}
}
_ => return Err(JsonLdError::InvalidIri("invalid IRI mapping"))
}
"@protected" => {
// td 级保护位(REC 4.2.2 步 10):非布尔 → invalid @protected value;
// 1.0 模式 → invalid term definition
match td_pair.1 {
JsonValue::Bool(flag) => td_protected = flag
_ =>
return Err(JsonLdError::InvalidContext("invalid @protected value"))
}
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
}
"@index" => {
// 索引映射(REC 4.2.2):1.0 模式或容器不含 @index → invalid term definition;
// 值须为串;IRI 展开结果须为 **IRI**(关键字/非绝对 → invalid term definition)
// oracle:#tpi01(1.0 拒)/ #tpi03(关键字值拒)/ #tpi04(非串拒)
if options.processing_mode is JsonLd10 || !td_container_has_index {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
match td_pair.1 {
JsonValue::String(index_name) => {
let building = ActiveContext::{
base,
vocab,
terms: new_terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
match
IriExpander::expand_iri(_self, building, index_name, false, true) {
Ok(expanded_index_key) =>
if is_valid_absolute_iri(expanded_index_key) {
// **存原始值**(term 名,如 "prop")——展开期两用:① Value Expansion
// 以它为 active property(取该 term 的 type mapping,#tpi08/#tpi10
// oracle:`@index: "prop"` 且 prop 有 @type:@vocab ⇒ 索引成节点引用)
// ② **expanded index key** = 用时期再 IRI 展开得属性 IRI
td_index_mapping = Some(index_name)
} else {
return Err(
JsonLdError::InvalidContext("invalid term definition"),
)
}
Err(e) => return Err(e)
}
}
_ =>
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
}
// 其余键按 REC 4.2.2 键白名单报错("any entry other than @id, @reverse,
// @container, @context, @direction, @index, @language, @nest, @prefix,
// @protected, or @type → invalid term definition")
other =>
return Err(
JsonLdError::InvalidContext("invalid term definition (\{other})"),
)
}
}
// @id 与 @reverse 同 td 互斥(#ter14 oracle:invalid reverse property);
// @container 与 @reverse 同 td 互斥(#ter17 oracle:reverse 项不带容器)
// reverse 项容器白名单(REC 4.2.2:"if its value is neither @set, nor @index, nor
// null, an invalid reverse property error")——即 reverse **允许** @set/@index
// (#t0063/#t0131 oracle:反向属性 + index 容器);@id/@list/@language/@graph 等 → 错
if td_reverse is Some(_) {
if td_iri is Some(_) || td_id_null {
return Err(JsonLdError::InvalidContext("invalid reverse property"))
}
match td_container {
Some(ContainerType::Set) | Some(ContainerType::Index) | None => ()
_ => return Err(JsonLdError::InvalidContext("invalid reverse property"))
}
}
// reverse × nest 同 td 互斥(#ten06 oracle:invalid reverse property)
// keyword 别名 × @prefix 组合禁(#tpr33 oracle):IRI 映射为真关键字的 term
// 携带 @prefix → invalid term definition
let keyword_aliased = match td_iri {
Some(k) => is_keyword(k)
None => false
}
if td_prefix && keyword_aliased {
return Err(JsonLdError::InvalidContext("invalid term definition"))
}
// reverse × nest 组合禁(#ten06 oracle):@reverse term 携带 @nest →
// invalid reverse property
if td_reverse is Some(_) && td_nest is Some(_) {
return Err(JsonLdError::InvalidContext("invalid reverse property"))
}
// 容器含 @type 的隐式 type mapping(REC 4.2.2):未声明 @type ⇒ 置 @id;
// 已声明则须为 @id/@vocab,否则 invalid type mapping(#tm017/#tm018/#tm019/
// #tm020 oracle)
if td_container_has_type {
match td_datatype {
None => td_datatype = Some("@id")
Some(existing) =>
if !(existing is "@id" || existing is "@vocab") {
return Err(JsonLdError::InvalidContext("invalid type mapping"))
}
}
}
// 关键字面(REC 4.2.2 开头三步,@protected 批——#pr30/#pr31/#pr32 oracle):
// ① keyword term 的 td 值只允许"仅 {@container:@set} 和/或 {@protected}"——
// 符合则 IRI 映射 = 该关键字本身(关键字可被保护);② @type + 1.0 → 错;
// ③ 其余关键字重定义 → keyword redefinition(#tec02 oracle)
if is_jsonld_keyword(term_name) {
if options.processing_mode is JsonLd10 && term_name is "@type" {
return Err(JsonLdError::InvalidContext("keyword redefinition"))
}
let mut only_allowed_entries = true
let mut has_allowed_entry = false
let mut keyword_container : ContainerType? = None
for td_pair in td_pairs {
match td_pair.0 {
"@container" =>
match td_pair.1 {
JsonValue::String("@set") => {
keyword_container = Some(ContainerType::Set)
has_allowed_entry = true
}
_ => only_allowed_entries = false
}
"@protected" => has_allowed_entry = true // 值校验已在 td 键臂完成
_ => only_allowed_entries = false
}
}
// 空 map 不满足("only either **or both of** following entries" ⇒ 须至少一条;
// #tec02 oracle:`{"@type": {}}` → keyword redefinition)
if !only_allowed_entries || !has_allowed_entry {
return Err(JsonLdError::InvalidContext("keyword redefinition"))
}
match
StandardExpander::install_term_definition(
new_terms,
term_name,
TermDefinition::{
iri: Some(term_name),
datatype: None,
container: keyword_container,
reverse: None,
local_context: None,
context_base_url: definition_base_url(flags, options),
index_mapping: None,
graph_secondary: None,
is_protected: td_protected,
language_mapping: None,
direction_mapping: None,
is_prefix: None,
nest: td_nest,
simple_form: false,
graph_set: false,
container_set: false,
},
flags.override_protected,
) {
Ok(_) => return Ok(())
Err(e) => return Err(e)
}
}
// 关键字**形**但非关键字(如 "@ignoreMe"):REC "return; processors SHOULD
// generate a warning"(忽略定义)——该忽略面挂 #pr34~#pr39 批;保守报错
if term_name.get_char(0) is Some('@') {
return Err(JsonLdError::InvalidContext("keyword redefinition"))
}
// 无 @id 位三路:
// ① {"@id": null} → 显式 null 映射(term 保留,展开期键丢弃);
// ② reverse-only term → IRI 映射 = reverse IRI(REC 4.2.2 @reverse 臂:
// "set the IRI mapping of definition to the result of IRI expanding";
// 作正属性由 reverse 路由拦截,非靠 IRI 缺失)
// ③ 其余 → term 名自身 vocab 展开(spec §6.1;t0011 路径),且**必须落成
// 绝对 IRI 或 bnode**——无 @vocab 的相对 term 名 → invalid IRI mapping
// (REC 4.2.2 末路;#tc032/#tc033 oracle 的触发点)
let final_iri = if td_id_null {
None
} else {
match td_iri {
Some(iri) => Some(iri)
None =>
match td_reverse {
Some(rev) => Some(rev)
None => {
let building = ActiveContext::{
base,
vocab,
terms: new_terms,
previous: None,
default_language: None,
default_direction: None,
compact_by_iri: None,
compact_reverse: None,
compact_prefixes: None,
}
match
IriExpander::expand_iri(_self, building, term_name, false, true) {
Ok(k) =>
// 无 vocab 可拼的相对 term 名 ⇒ **invalid IRI mapping**(REC 4.2.2
// 末路:"If it does not have a vocabulary mapping, an invalid IRI
// mapping error has been detected")。#tc032/#tc033 oracle:**未被
// 使用**的嵌入 context 亦须在定义期报错(invalid scoped context)。
// 〔曾经的软化(None 忽略)是为 tpr38 让路,属**修错层**——tpr38 的
// 正解在 td @reverse 臂的 REC "return"(见该臂注释)〕
if is_valid_absolute_iri(k) || is_bnode_label(k) {
Some(k)
} else {
return Err(JsonLdError::InvalidIri("invalid IRI mapping"))
}
Err(e) => return Err(e)
}
}
}
}
}
StandardExpander::install_term_definition(
new_terms,
term_name,
TermDefinition::{
iri: final_iri,
datatype: td_datatype,
container: td_container,
reverse: td_reverse,
local_context: td_local_context,
context_base_url: definition_base_url(flags, options),
index_mapping: td_index_mapping,
graph_secondary: td_graph_secondary,
language_mapping: td_language,
direction_mapping: td_direction,
is_protected: td_protected,
is_prefix: td_is_prefix,
nest: td_nest,
simple_form: td_simple,
graph_set: td_graph_set,
container_set: td_container_set,
},
flags.override_protected,
)
}
///|
/// JSON-LD 关键字全集判定(含 context 层键——元素层分派表只收 13 个元素层关键字,
/// 而关键字重定义规则要全集)
fn is_jsonld_keyword(name : String) -> Bool {
name
is ("@base"
| "@container"
| "@context"
| "@direction"
| "@graph"
| "@id"
| "@import"
| "@included"
| "@index"
| "@json"
| "@language"
| "@list"
| "@nest"
| "@none"
| "@prefix"
| "@propagate"
| "@protected"
| "@reverse"
| "@set"
| "@type"
| "@value"
| "@version"
| "@vocab")
}
///|
/// 容器映射等价(TermDefinition 等价判定用;避免给 ContainerType 加 derive(Eq))
fn container_type_matches(a : ContainerType, b : ContainerType) -> Bool {
match (a, b) {
(List, List)
| (Set, Set)
| (Language, Language)
| (Index, Index)
| (Id, Id) => true
(Type, Type) | (Graph, Graph) => true
_ => false
}
}
///|
/// term 定义等价判定(REC 4.2.2 保护面:"definition is not the same as previous
/// definition (**other than the value of protected**)")——**除 protected 位外全等**
/// (iri/datatype/container/reverse/local_context 逐个比;#pr23/#pr24/#pr25/#pr27/
/// #pr41 等价允许 与 #pr26/#pr28/#pr42 不等价即错 的分界)
/// 先验注记:local_context 按 JsonValue 结构比较(键序敏感)——同语义异序的
/// scoped context 会判不等价,本批无 oracle,挂账
fn term_definitions_equivalent(
previous : TermDefinition,
next : TermDefinition,
) -> Bool {
let iri_same = match (previous.iri, next.iri) {
(None, None) => true
(Some(a), Some(b)) => a == b
_ => false
}
let datatype_same = match (previous.datatype, next.datatype) {
(None, None) => true
(Some(a), Some(b)) => a == b
_ => false
}
let container_same = match (previous.container, next.container) {
(None, None) => true
(Some(a), Some(b)) => container_type_matches(a, b)
_ => false
}
let reverse_same = match (previous.reverse, next.reverse) {
(None, None) => true
(Some(a), Some(b)) => a == b
_ => false
}
let local_context_same = match (previous.local_context, next.local_context) {
(None, None) => true
(Some(a), Some(b)) => a == b
_ => false
}
iri_same &&
datatype_same &&
container_same &&
reverse_same &&
local_context_same
}
///|
/// 是否含任何受保护 term(空化门用:REC 4.1.2 步 5.2.1 "active context contains
/// any protected term definitions")
fn has_protected_term(active : ActiveContext) -> Bool {
for _, term_definition in active.terms {
if term_definition.is_protected {
return true
}
}
false
}
///|
/// term 定义安装(REC 4.2.2 末段,@protected 批):保护面 = `override protected =
/// false` 且已有定义受保护 ⇒ 新定义须与旧定义**等价(除 protected 位)**;
/// 等价 ⇒ **保留旧定义**("Set definition to previous definition to retain the value
/// of protected"——#pr42 oracle);不等价 ⇒ **protected term redefinition**。
/// 简单臂/td 对象臂/关键字臂三处同源(一笔一账:保护检查只有这一处)
fn StandardExpander::install_term_definition(
new_terms : Map[String, TermDefinition],
term_name : String,
definition : TermDefinition,
override_protected : Bool,
) -> Result[Unit, JsonLdError] {
match new_terms.get(term_name) {
Some(previous) =>
if !override_protected && previous.is_protected {
if term_definitions_equivalent(previous, definition) {
return Ok(()) // 保留旧定义(含 protected 位)
}
return Err(JsonLdError::InvalidContext("protected term redefinition"))
}
None => ()
}
new_terms[term_name] = definition
Ok(())
}
///|
/// 容器映射合法性验证(⑤后半——#tem01/#ter21/#tes01/#tes02 oracle):
/// 串值 = 1.1 白名单 {@list,@set,@index,@language,@id,@type,@graph} /
/// 1.0 窄名单 {@list,@set,@index,@language};数组仅 1.1 图组合
/// [@graph, @id|@index|@set];其余类型/值 → invalid container mapping。
/// 仅验证不启用应用(container 字段仅 @language 存值——#ter35 依赖)
fn validate_container_value(
value : JsonValue,
options : JsonLdOptions,
) -> Result[Unit, JsonLdError] {
let is_10 = options.processing_mode is JsonLd10
match value {
JsonValue::String(c) => {
let valid_11 = c
is ("@list"
| "@set"
| "@index"
| "@language"
| "@id"
| "@type"
| "@graph")
let valid_10 = c is ("@list" | "@set" | "@index" | "@language")
if (if is_10 { valid_10 } else { valid_11 }) {
Ok(())
} else {
Err(JsonLdError::InvalidContext("invalid container mapping"))
}
}
JsonValue::Array(items) => {
// 数组形容器三形(1.1):①单元素 = 裸形等价(#ts001 oracle——
// ["@list"] 等价 "@list");②@graph 组合(#t0083 oracle:@graph +
// 至多一个 @id|@index + 可选 @set,禁重复/未知成员);③@set 并容
//(#ts002 oracle:@set + 至多一个 @id|@type|@language|@index)。
// @list 禁组合;空数组/未知成员拒
if is_10 {
return Err(JsonLdError::InvalidContext("invalid container mapping"))
}
let mut has_graph = false
let mut has_set = false
let mut has_list = false
let mut key_count = 0
let mut valid = items.length() > 0
for item in items {
match item {
JsonValue::String("@graph") =>
if has_graph {
valid = false
} else {
has_graph = true
}
JsonValue::String("@set") =>
if has_set {
valid = false
} else {
has_set = true
}
JsonValue::String("@list") =>
if has_list {
valid = false
} else {
has_list = true
}
JsonValue::String(s) =>
if s is ("@id" | "@type" | "@language" | "@index") {
key_count += 1
} else {
valid = false
}
_ => valid = false
}
}
let graph_combo = has_graph && key_count <= 1
let set_combo = has_set && !has_list && !has_graph && key_count <= 1
if valid && (items.length() == 1 || graph_combo || set_combo) {
Ok(())
} else {
Err(JsonLdError::InvalidContext("invalid container mapping"))
}
}
_ => Err(JsonLdError::InvalidContext("invalid container mapping"))
}
}
///|
/// compact 自指环检测(⑤后半——#ter10 oracle):值含冒号且冒号前 prefix =
/// 被定义的 term 名自身 → 展开即环。**豁免**:suffix 以 "//" 开头 =
/// protocol-like IRI(scheme 形态——#t0067 oracle:"http://…" 作 term 值非环)
fn is_self_prefix_compact(value : String, term_name : String) -> Bool {
match colon_index(value) {
Some(at) =>
if at > 0 {
let prefix = split_first_colon(value, at).0
prefix == term_name &&
!(split_first_colon(value, at).1.get_char(0) is Some('/') &&
split_first_colon(value, at).1.get_char(1) is Some('/'))
} else {
false
}
None => false
}
}
///|
/// terms 拷贝并剔除单键(自映射排除用——#t0071/#t0072 oracle)
fn terms_without(
terms : Map[String, TermDefinition],
exclude : String,
) -> Map[String, TermDefinition] {
let out : Map[String, TermDefinition] = Map([])
for key, definition in terms {
if key != exclude {
out[key] = definition
}
}
out
}
///|
/// list/set 对象拦截与展开(C 组——判据①):值对象键纯 {@list} 或纯 {@set}
/// (混入他键/双键 → invalid set or list object——#ter41 oracle)。
/// @list → [List(元素)](元素经 expand_list_item——1.0 list-of-lists 门控);
/// @set → 元素摊平为属性多值(不包 wrapper——#t0004 oracle)。
/// Ok(None) = 非 list/set 对象(调用方走既有路由)
fn StandardExpander::expand_list_set_object(
self : StandardExpander,
active : ActiveContext,
term : String,
obj_pairs : Array[(String, JsonValue)],
options : JsonLdOptions,
) -> Result[Array[ExpandedValue]?, JsonLdError] {
let mut list_value : JsonValue? = None
let mut set_value : JsonValue? = None
let mut index_value : String? = None
let mut impure = false
for pair in obj_pairs {
// 键别名识别(t0014 批——REC 键归一先于对象形态判定):别名 term("set" →
// "@set")与裸键(#t0004 oracle 不变)同路命中拦截;普通属性键两查皆否
// → impure 走既有路由
if StandardExpander::key_expands_to_keyword(self, active, pair.0, "@list") {
list_value = Some(pair.1)
} else if StandardExpander::key_expands_to_keyword(
self,
active,
pair.0,
"@set",
) {
set_value = Some(pair.1)
} else if StandardExpander::key_expands_to_keyword(
self,
active,
pair.0,
"@index",
) {
// @index 随 @list 值对象合法(#t0041 oracle——index 挂 list 贯通压缩;
// 非 impure)
match pair.1 {
JsonValue::String(index_word) => index_value = Some(index_word)
_ => impure = true
}
} else {
impure = true
}
}
if list_value is None && set_value is None {
let no_values : Array[ExpandedValue]? = None
return Ok(no_values)
}
if impure || (list_value is Some(_) && set_value is Some(_)) {
return Err(JsonLdError::InvalidList("invalid set or list object"))
}
let outcome : Result[Array[ExpandedValue]?, JsonLdError] = match list_value {
Some(list_items) => {
// 非数组 @list 值 = 单元素 list(#t0004 oracle:"@list": "one item" →
// {"@list": [{"@value": ...}]})
let items = match list_items {
JsonValue::Array(items) => items
single => [single]
}
let out : Array[ExpandedValue] = []
for item in items {
match
StandardExpander::expand_list_item(self, active, term, item, options) {
Ok(Some(v)) => out.push(v)
Ok(None) => () // null 元素丢弃(1.1)
Err(e) => return Err(e)
}
}
// 1.0 模式:list 内嵌 list = list of lists(#ter32 oracle)
if options.processing_mode is JsonLd10 {
for v in out {
match v {
ExpandedValue::List(_) =>
return Err(JsonLdError::InvalidList("list of lists"))
_ => ()
}
}
}
let wrapped : Array[ExpandedValue]? = Some([
ExpandedValue::List(out, index=index_value),
])
Ok(wrapped)
}
None =>
match set_value {
Some(JsonValue::Array(items)) => {
let out : Array[ExpandedValue] = []
for item in items {
match
StandardExpander::expand_term_values_inner(
self,
active,
term,
item,
None,
options,
) {
Ok(vs) =>
for v in vs {
out.push(v)
}
Err(e) => return Err(e)
}
}
let values : Array[ExpandedValue]? = Some(out)
Ok(values)
}
Some(single) =>
match
StandardExpander::expand_term_values_inner(
self,
active,
term,
single,
None,
options,
) {
Ok(vs) => {
let values : Array[ExpandedValue]? = Some(vs)
Ok(values)
}
Err(e) => Err(e)
}
None => {
let no_values : Array[ExpandedValue]? = None
Ok(no_values)
}
}
}
outcome
}
///|
/// list 元素展开(C 组——判据②):数组 → 嵌套 List(1.1——#tli05/#tli07
/// oracle;1.0 → list of lists——#ter24 oracle);list/set 对象 → 拦截器
/// 递归(#tli01 oracle:嵌套 @list 保留);其余 → expand_term_values 单值
/// (coercion 保持 term 上下文——#t0023/#tli03 oracle)。
/// Ok(None) = null 元素丢弃(1.1);递归一律走 expand_term_values_inner
///(容器包裹仅外层壳一次——防重入)
fn StandardExpander::expand_list_item(
self : StandardExpander,
active : ActiveContext,
term : String,
item : JsonValue,
options : JsonLdOptions,
) -> Result[ExpandedValue?, JsonLdError] {
match item {
JsonValue::Array(inner) => {
if options.processing_mode is JsonLd10 {
return Err(JsonLdError::InvalidList("list of lists"))
}
let out : Array[ExpandedValue] = []
for el in inner {
match
StandardExpander::expand_list_item(self, active, term, el, options) {
Ok(Some(v)) => out.push(v)
Ok(None) => () // null 元素丢弃(1.1)
Err(e) => return Err(e)
}
}
let nested : ExpandedValue? = Some(ExpandedValue::List(out, index=None))
Ok(nested)
}
JsonValue::Object(obj_pairs) =>
if obj_pairs.length() > 0 &&
(obj_pairs[0].0 is "@list" || obj_pairs[0].0 is "@set") {
match
StandardExpander::expand_list_set_object(
self, active, term, obj_pairs, options,
) {
Ok(Some(vs)) => {
if options.processing_mode is JsonLd10 {
for v in vs {
match v {
ExpandedValue::List(_) =>
return Err(JsonLdError::InvalidList("list of lists"))
_ => ()
}
}
}
if vs.length() == 1 {
let single : ExpandedValue? = Some(vs[0])
Ok(single)
} else {
let grouped : ExpandedValue? = Some(
ExpandedValue::List(vs, index=None),
)
Ok(grouped)
}
}
Ok(None) =>
Err(JsonLdError::InvalidList("invalid set or list object"))
Err(e) => Err(e)
}
} else {
match
StandardExpander::expand_term_values_inner(
self,
active,
term,
item,
None,
options,
) {
Ok(vs) => {
let first : ExpandedValue? = if vs.length() > 0 {
Some(vs[0])
} else {
None
}
Ok(first)
}
Err(e) => Err(e)
}
}
JsonValue::Null => {
let none_value : ExpandedValue? = None
Ok(none_value)
}
_ =>
match
StandardExpander::expand_term_values_inner(
self,
active,
term,
item,
None,
options,
) {
Ok(vs) => {
let first : ExpandedValue? = if vs.length() > 0 {
Some(vs[0])
} else {
None
}
Ok(first)
}
Err(e) => Err(e)
}
}
}
///|
/// graph 容器展开(@graph 容器族——形态表见 todo §J2 开工钉):
/// 裸/@set → 逐值包成 graph 节点({"@graph": [...]}——graph 对象值也再包一层,
/// #t0102/#t0104 oracle);@id → map 键 vocab 展开写条目 @id(保留既有——
/// id map 语义,#t0085);@index → 键原样写条目 @index、"@none" 直出(#tm013);
/// 条目值 = 既有 graph 对象 → 不二次包裹仅附键(#t0084/#t0087 oracle);
/// 数组条目逐项、@index 附挂每个子项(#t0105 oracle)。值形态递归一律走
/// expand_term_values_inner(防容器壳重入——const §5)
fn StandardExpander::expand_graph_container(
self : StandardExpander,
active : ActiveContext,
term : String,
value : JsonValue,
secondary : ContainerType?,
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
match secondary {
Some(ContainerType::Id) | Some(ContainerType::Index) => {
// map 形态:值须 map("Do not expand if value is not a map" 族语义)
let entries = match value {
JsonValue::Object(entries) => entries
_ =>
return Err(JsonLdError::InvalidValue("invalid graph container value"))
}
let out : Array[ExpandedValue] = []
for entry in entries {
// @none 别名识别(vocab 位展开——term 别名 "none": "@none",
// #tm014 oracle;字面 "@none"——#tm015/#tm013 oracle):识别为 @none
// 的条目直出不附挂
let none_key = match
IriExpander::expand_iri(self, active, entry.0, false, true) {
Ok(k) => k is "@none"
Err(_) => false
}
// @id 附挂值用 @id 值位旗(vocab=false/docRel=true——@id map 同款,
// REC 明文,同形异判表「索引键展开旗位」行)
let attach_id : String? = match secondary {
Some(ContainerType::Id) =>
if none_key {
None
} else {
match
IriExpander::expand_iri(self, active, entry.0, true, false) {
Ok(k) => Some(k)
Err(e) => return Err(e)
}
}
_ => None
}
// 索引映射属性(@index mapping 语义在 graph 容器内的延伸——#tpi11):
// td 存原始名(const §5 两用值),用时 vocab 展开成属性 IRI;
// 条目键(原串)成其值对象。@none/"@none" 映射不写
let index_property : (String, ExpandedValue)? = match secondary {
Some(ContainerType::Index) =>
if none_key {
None
} else {
match active.terms.get(term) {
Some(term_definition) =>
match term_definition.index_mapping {
Some(mapping_name) =>
if mapping_name is "@none" {
None
} else {
match
IriExpander::expand_iri(
self, active, mapping_name, false, true,
) {
Ok(prop_iri) => {
let index_value : (String, ExpandedValue) = (
prop_iri,
literal_of(entry.0, None),
)
Some(index_value)
}
Err(e) => return Err(e)
}
}
None => None
}
None => None
}
}
_ => None
}
// @index 附挂值 = 原始键名(两用值存原始形态——const §5);**有索引
// 映射时键走映射属性、不写 @index**(二者互斥——#tpi11 oracle)
let attach_index : String? = if index_property is Some(_) {
None
} else {
match secondary {
Some(ContainerType::Index) =>
if none_key {
None
} else {
Some(entry.0)
}
_ => None
}
}
// 条目值:数组逐项(每项独立 graph 对象、共享键附挂——#t0105);单值单项
let items = match entry.1 {
JsonValue::Array(items) => items
_ => [entry.1]
}
for item in items {
match
StandardExpander::expand_term_values_inner(
self,
active,
entry.0,
item,
Some(entry.0),
options,
) {
Ok(vs) =>
for v in vs {
match v {
ExpandedValue::Node(node) =>
if node.graph is None {
// 普通节点条目 → 包成 graph 对象,键附挂**包裹层**
//(内层值节点不落 @index——#t0082 oracle);
// 索引映射属性同层(#tpi11 oracle:与 @graph 同级)
let entry_properties : Array[
(String, Array[ExpandedValue]),
] = match index_property {
Some((prop_iri, index_value)) =>
[(prop_iri, [index_value])]
None => []
}
let wrapped = ExpandedNode::{
id: attach_id,
types: [],
properties: entry_properties,
reverse_props: [],
graph: Some([ExpandedValue::Node(node)]),
included: None,
index: attach_index,
}
out.push(ExpandedValue::Node(wrapped))
} else {
// 既有 graph 对象:不二次包裹,键就地附挂
//(#t0084/#t0087 oracle)
match attach_id {
Some(id_key) =>
if node.id is None {
node.id = Some(id_key)
}
None => ()
}
match attach_index {
Some(index_key) => node.index = Some(index_key)
None => ()
}
match index_property {
Some((prop_iri, index_value)) =>
node.properties.push((prop_iri, [index_value]))
None => ()
}
out.push(ExpandedValue::Node(node))
}
_ => out.push(v)
}
}
Err(e) => return Err(e)
}
}
}
Ok(out)
}
_ => {
// 裸/@set:逐值包成 graph 节点(#t0079/#t0080/#t0102/#t0104 oracle——
// graph 对象值也再包一层)
let items = match value {
JsonValue::Array(items) => items
_ => [value]
}
let out : Array[ExpandedValue] = []
for item in items {
match
StandardExpander::expand_term_values_inner(
self,
active,
term,
item,
Some(term),
options,
) {
Ok(vs) =>
for v in vs {
let wrapper = ExpandedNode::{
id: None,
types: [],
properties: [],
reverse_props: [],
graph: Some([v]),
included: None,
index: None,
}
out.push(ExpandedValue::Node(wrapper))
}
Err(e) => return Err(e)
}
}
Ok(out)
}
}
}
///|
/// framing 旗 keyword 判(frameExpansion 保形面——spec §8.96 帧旗矩阵):
/// 四旗以词形入 pattern properties;@embed 值域校验随 embed 状态机批
fn is_framing_flag_key(key : String) -> Bool {
key == "@embed" ||
key == "@explicit" ||
key == "@requireAll" ||
key == "@omitDefault"
}
///|
/// @default 值保形(帧旗批——spec §8.96):`@null` 串/null ⇒ `"@null"`
/// 标桩(framing 算法 compaction 后替换为 null);其余串词形保形(不作
/// IRI 展开——#t0009 "custom-default" oracle);number/bool raw 保形;
/// 对象 → 值对象展开(Value);数组摊开(`["@null"]` 单元素形——t0063)
fn StandardExpander::frame_preserve_default(
self : StandardExpander,
active : ActiveContext,
raw : JsonValue,
options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
match raw {
JsonValue::Null => Ok([literal_of("@null", None)])
JsonValue::String(word) =>
if word == "@null" {
Ok([literal_of("@null", None)])
} else {
Ok([literal_of(word, None)])
}
JsonValue::Number(number_value) =>
Ok([literal_of("\{number_value}", Some(JsonValue::Number(number_value)))])
JsonValue::Bool(flag) =>
Ok([
literal_of(
if flag {
"true"
} else {
"false"
},
Some(JsonValue::Bool(flag)),
),
])
JsonValue::Array(items) => {
let out : Array[ExpandedValue] = []
for item in items {
match self.frame_preserve_default(active, item, options) {
Ok(values) =>
for v in values {
out.push(v)
}
Err(e) => return Err(e)
}
}
Ok(out)
}
JsonValue::Object(_) =>
match StandardExpander::expand_element(self, active, raw, None, options) {
Ok(v) => Ok([v])
Err(e) => Err(e)
}
}
}
///|
/// 值 pattern 成分保形(spec §8.97 值 pattern 批):`{}` 通配 = 空 pattern
/// 标桩;串 → 值(@type 位 IRI 经 type-scoped 展开,余词形);null = 无
/// (None);数组位由调用方摊开后逐项入此
fn StandardExpander::frame_value_pattern_component(
self : StandardExpander,
active : ActiveContext,
key : String,
raw : JsonValue,
) -> Result[ExpandedValue?, JsonLdError] {
match raw {
JsonValue::Object(default_pairs) =>
if key == "@type" {
// @type default 对象保形(t0064——值 pattern 预扫路径与关键字臂
// 同归 expand_type_default_object;坍通配即失 @default)
Ok(Some(self.expand_type_default_object(active, default_pairs)))
} else {
Ok(Some(ExpandedValue::Node(frame_empty_pattern())))
}
JsonValue::Null => Ok(None)
JsonValue::String(word) =>
if key == "@type" {
// 负例 t0053(值 pattern 预扫路径的 @type 成分同帧文法值域——
// bnode 标识符不在册 ⇒ invalid frame)
if is_bnode_label(word) {
Err(
JsonLdError::InvalidValue(
"invalid frame: @type must not include a blank node identifier",
),
)
} else {
match IriExpander::expand_iri(self, active, word, true, true) {
Ok(expanded) => Ok(Some(literal_of(expanded, None)))
Err(e) => Err(e)
}
}
} else {
Ok(Some(literal_of(word, None)))
}
_ => Ok(None)
}
}
///|
/// @type default 对象保形(t0064——REC "@type MAY be a map with a @default
/// entry, whose values are restricted to IRIs. Processors MUST preserve
/// this value when expanding. All other entries of a default object MUST
/// be ignored"):@default 串 = type-scoped IRI 展开 ⇒ Node pattern 携
/// @default 字面(匹配臂无条件配 + 输出侧缺型注入的载体);无 @default
/// ({} 通配)= 空 pattern
fn StandardExpander::expand_type_default_object(
self : StandardExpander,
active : ActiveContext,
pairs : Array[(String, JsonValue)],
) -> ExpandedValue {
let mut default_iri : String? = None
for pair in pairs {
if pair.0 is "@default" {
match pair.1 {
JsonValue::String(word) =>
match IriExpander::expand_iri(self, active, word, true, true) {
Ok(expanded) => default_iri = Some(expanded)
Err(_) => ()
}
_ => ()
}
}
}
match default_iri {
Some(iri) =>
ExpandedValue::Node({
id: None,
types: [],
properties: [("@default", [literal_of(iri, None)])],
reverse_props: [],
graph: None,
included: None,
index: None,
})
None => ExpandedValue::Node(frame_empty_pattern())
}
}