///|
/// JSON-LD expansion 引擎:gen 调度面(gen.mbt)+ 手写钩子(expand_standard.mbt)的装配层。
/// 引擎只做调度与顶层形态(解析 → 入口分派 → 顶层展开 → 序列化);语义全部在钩子。
/// J2 首件:#t0001 端到端(deferred → plain,见 suite_expand_test.mbt)。

///|
/// 元素形态(调度词表:scalar/array/object;null 计入 scalar)
fn shape_of(element : JsonValue) -> String {
  match element {
    JsonValue::Array(_) => "array"
    JsonValue::Object(_) => "object"
    _ => "scalar"
  }
}

///|
/// 初始 active context(t0001 片:terms 空、base 取 options)
fn initial_context(options : JsonLdOptions) -> ActiveContext {
  {
    base: options.base,
    vocab: None,
    terms: Map([]),
    previous: None,
    default_language: None,
    default_direction: None,
    compact_by_iri: None,
    compact_reverse: None,
    compact_prefixes: None,
  }
}

///|

///|
/// 顶层展开核心(J3.1 抽取——expand_document / to_rdf_document 共用):JSON
/// 文本 → 顶层存活元素(ExpandedValue)。选择规则 = serialize_top 同款:顶层
/// 值对象即弃(#t0045)/ free-floating 节点即弃(#t0001)/ 裸 @graph 根节点
/// 表面化为图内容(#t0009/#t0021——toRdf 侧即默认图,REC §6.3)。
fn expand_top_values(
  text : String,
  options : JsonLdOptions,
) -> Result[Array[ExpandedValue], JsonLdError] {
  let element = match parse_json(text) {
    Ok(value) => value
    Err(e) => return Err(e)
  }
  let expander = StandardExpander::{ }
  let active = match options.expand_context {
    // expandContext 选项(#t0077 oracle):外部 context 先于文档处理;
    // context 文档 = {"@context": inner} → 解包后处理
    Some(context_value) => {
      let inner = match context_value {
        JsonValue::Object(pairs) => {
          let mut found : JsonValue? = None
          for pair in pairs {
            if pair.0 is "@context" {
              found = Some(pair.1)
            }
          }
          found
        }
        _ => None
      }
      match inner {
        Some(context_json) =>
          match
            ContextProcessor::process_context(
              StandardExpander::{ },
              initial_context(options),
              context_json,
              ContextFlags::element(),
              options,
            ) {
            Ok(context) => context
            Err(e) => return Err(e)
          }
        None => initial_context(options)
      }
    }
    None => initial_context(options)
  }
  let out : Array[ExpandedValue] = []
  match shape_of(element) {
    "array" => {
      let items = match element {
        JsonValue::Array(items) => items
        _ => return Err(JsonLdError::InvalidValue("shape mismatch: array"))
      }
      for item in items {
        let step = match JsonLdStep::dispatch(ExpansionEntry, shape_of(item)) {
          Some(step) => step
          None => continue
        }
        let expanded = match step {
          ExpansionExpandObject =>
            StandardExpander::expand_object(
              expander,
              active,
              item,
              None,
              options,
            )
          ExpansionExpandValue =>
            StandardExpander::expand_value(expander, active, item, None).map(fn(
              v,
            ) {
              Some(v)
            })
          ExpansionExpandArray =>
            return Err(JsonLdError::Unsupported("nested array at top pending"))
          _ => continue
        }
        match expanded {
          Ok(Some(value)) =>
            // 数组成员位非根(te004 oracle)——仅@graph 包裹保留
            push_top_value(
              out,
              value,
              frame_expansion=options.frame_expansion,
              root=false,
            )
          Ok(None) => ()
          Err(e) => return Err(e)
        }
      }
    }
    "object" => {
      let expanded = StandardExpander::expand_object(
        expander,
        active,
        element,
        None,
        options,
      )
      match expanded {
        Ok(Some(value)) =>
          push_top_value(out, value, frame_expansion=options.frame_expansion)
        Ok(None) => ()
        Err(e) => return Err(e)
      }
    }
    _ => {
      let expanded = StandardExpander::expand_value(
        expander,
        active,
        element,
        None,
      )
      match expanded {
        Ok(value) =>
          push_top_value(out, value, frame_expansion=options.frame_expansion)
        Err(e) => return Err(e)
      }
    }
  }
  Ok(out)
}

///|
/// 顶层元素选择(serialize_top 同款判据,ExpandedValue 直收——J3.1 抽取注记
/// 见 expand_top_values)
fn push_top_value(
  out : Array[ExpandedValue],
  value : ExpandedValue,
  frame_expansion? : Bool = false,
  root? : Bool = true,
) -> Unit {
  match value {
    // 顶层值对象即弃(#t0045:"Top-level value objects are removed")
    ExpandedValue::Value(_) => ()
    ExpandedValue::Node(node) =>
      // 顶层 @graph 单一门(文档级批——#t0009/#t0021/#t0046 oracle):展开后
      // 仅存 @graph 的根节点对象表面化为图内容数组;带任何其他属性(含 @id、
      // @type、正属性、@reverse)则保留节点形态(#t0021:title + @graph 并存)。
      // **root 限定(J9 html 面双 oracle——t0020 根位解包 vs te004 数组成员位
      // 保留)**:仅@graph 包裹解包只属文档根位;顶层数组的成员位仅@graph map
      // 保包裹(REC 5.1 自由浮动丢弃清单=空/@value|@list/仅@id,无仅@graph 臂
      // ——te004 W3C 期待实证)
      if root &&
        node.graph is Some(graph_items) &&
        node.id is None &&
        node.types.is_empty() &&
        node.properties.is_empty() &&
        node.reverse_props.is_empty() {
        for item in graph_items {
          out.push(item)
        }
      } else if is_free_floating(node) && !frame_expansion {
        // 顶层 free-floating 节点即弃(#t0001 同族);frameExpansion 豁免
        // ——@id-only 帧根是合法 pattern(t0032 判定面:弃则空帧全配)
      } else {
        out.push(value)
      }
    other => out.push(other)
  }
}

///|
/// expansion 文档入口:JSON 文本 → 展开形态(顶层恒为数组;free-floating 顶层节点丢弃)
pub fn expand_document(
  text : String,
  options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
  let values = match expand_top_values(text, options) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  Ok(JsonValue::Array(values.map(serialize_value)))
}

///|
/// toRDF 文档入口(J3.1——库级 API,gen 调度面 handler_hook 的调用面):
/// JSON 文本 → 展开形 → 四元组。负例 = 任一段 Err。
pub fn to_rdf_document(
  text : String,
  options : JsonLdOptions,
) -> Result[Array[JsonLdQuad], JsonLdError] {
  let values = match expand_top_values(text, options) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  ToRdfProcessor::node_to_quads(StandardExpander::{ }, values, options)
}

///|
/// free-floating:无属性、无类型(@id 有无均计)——顶层展开即弃
/// (#t0001:仅 @id 丢弃;同族:{} 亦丢弃;@graph-bearing 节点不算
/// free-floating——图对象有意义,文档级批扩展)
fn is_free_floating(node : ExpandedNode) -> Bool {
  node.properties.is_empty() &&
  node.types.is_empty() &&
  node.graph is None &&
  node.included is None && // included-bearing 节点不弃(#tin03:仅 @included 的根节点保留)
  // reverse-bearing 节点不弃(@index mapping 批——#t0131 oracle:仅 @reverse 的根节点
  // 保留;@reverse 项进 reverse_props 而非 properties,故须显式豁免)
  node.reverse_props.is_empty() &&
  // index-bearing 节点不弃(#te001 oracle——flatten「Conflicting indexes」负例
  // 实证:@id+@index 裸节点须抵达节点字典合并面,弃则冲突不可见;expand/toRdf
  // 套件均无此形在册案例,零回归面)
  node.index is None
}
// 豁免清单沿革(第五位,2026-10-01 framing 役49):frameExpansion 模式下
// @id-only 帧根/子 pattern 不弃(t0032 判定面——弃则帧空全配)。
// **族核(用户核)**:与前四位**不同族**——前四位(graph/included/reverse/
// index)是「内容承载节点」**普适豁免**(任何模式下该节点都有意义);本位是
// **模式条件豁免**(同一 @id-only 节点在常规展开仍弃,仅 frameExpansion 保)。
// 故不作普适判扩展,走调用点 `!options.frame_expansion` 旁路(push_top_value
// + 集合位两门),is_free_floating 本体零改动。

///|
/// ExpandedValue → 展开形态 JsonValue(对拍通道;J2 按覆盖面扩展)
fn serialize_value(value : ExpandedValue) -> JsonValue {
  match value {
    ExpandedValue::Node(node) => serialize_node(node)
    ExpandedValue::Value(literal) => {
      let pairs : Array[(String, JsonValue)] = []
      // raw 保形:number/bool 原样输出(string 走词形)
      pairs.push(
        (
          "@value",
          match literal.raw {
            Some(raw) => raw
            None => JsonValue::String(literal.value)
          },
        ),
      )
      match literal.datatype {
        Some(dt) => pairs.push(("@type", JsonValue::String(dt)))
        None => ()
      }
      match literal.language {
        Some(lang) => pairs.push(("@language", JsonValue::String(lang)))
        None => ()
      }
      match literal.direction {
        Some(dir) => pairs.push(("@direction", JsonValue::String(dir)))
        None => ()
      }
      // 值对象位 Included Blocks 保形(#tin05 oracle:还原 "@included" 键,
      // 内层节点照常渲染)
      match literal.included {
        Some(inc_nodes) =>
          pairs.push(
            (
              "@included",
              JsonValue::Array(
                inc_nodes.map(fn(n) { serialize_value(ExpandedValue::Node(n)) }),
              ),
            ),
          )
        None => ()
      }
      // 值对象位 @index(@index mapping 批——#t0036 oracle:字面量条目的索引)
      match literal.index {
        Some(index) => pairs.push(("@index", JsonValue::String(index)))
        None => ()
      }
      JsonValue::Object(pairs)
    }
    ExpandedValue::List(items, ..) =>
      JsonValue::Object([
        ("@list", JsonValue::Array(items.map(serialize_value))),
      ])
  }
}

///|
fn serialize_node(node : ExpandedNode) -> JsonValue {
  let pairs : Array[(String, JsonValue)] = []
  match node.id {
    // EXPLICIT_NULL_ID 哨兵 = @id 被忽略的节点(t0122 oracle:字面 "@id": null)
    Some(EXPLICIT_NULL_ID) => pairs.push(("@id", JsonValue::Null))
    Some(id) => pairs.push(("@id", JsonValue::String(id)))
    None => ()
  }
  if node.types.length() > 0 {
    pairs.push(
      (
        "@type",
        JsonValue::Array(node.types.map(fn(t) { JsonValue::String(t) })),
      ),
    )
  }
  for pair in node.properties {
    pairs.push((pair.0, JsonValue::Array(pair.1.map(serialize_value))))
  }
  // @reverse 映射输出(reverse 批——#t0042 oracle:"@reverse": {iri: [values]})
  if node.reverse_props.length() > 0 {
    let rev_pairs : Array[(String, JsonValue)] = []
    for pair in node.reverse_props {
      rev_pairs.push((pair.0, JsonValue::Array(pair.1.map(serialize_value))))
    }
    pairs.push(("@reverse", JsonValue::Object(rev_pairs)))
  }
  // 节点位 @graph 输出(文档级批——#t0020 oracle:节点下 @graph 保留)
  match node.graph {
    Some(items) =>
      pairs.push(("@graph", JsonValue::Array(items.map(serialize_value))))
    None => ()
  }
  // Included Blocks 键渲染(@included 纯形批——in01-in04 oracle:@included
  // 作为节点对象键保形输出,included 节点照常渲染;嵌套 included 随递归
  // 自展——in04)
  match node.included {
    Some(inc_nodes) =>
      pairs.push(
        (
          "@included",
          JsonValue::Array(
            inc_nodes.map(fn(n) { serialize_value(ExpandedValue::Node(n)) }),
          ),
        ),
      )
    None => ()
  }
  // index map 条目 @index(@container 映射批——#tc013 oracle)
  match node.index {
    Some(index) => pairs.push(("@index", JsonValue::String(index)))
    None => ()
  }
  JsonValue::Object(pairs)
}

///|
/// 对拍归一:对象键序不敏感(键按字典序重排,递归);数组保序。
/// 定位勘正(评审役 R-1,2026-10-09):本品 = 比对归一器,**非** RFC 8785
/// JCS(0.1.0 宣称失实,下版 CHANGELOG 改口);对外 JCS 规范化用
/// jcs_serialize(to_rdf.mbt,#tjs01~23 oracle)。
pub fn json_canonical(value : JsonValue) -> JsonValue {
  match value {
    JsonValue::Object(pairs) => {
      let sorted = pairs.map(fn(pair) { (pair.0, json_canonical(pair.1)) })
      sorted.sort_by(fn(a, b) { a.0.compare(b.0) })
      JsonValue::Object(sorted)
    }
    JsonValue::Array(items) => JsonValue::Array(items.map(json_canonical))
    other => other
  }
}

///|
/// JSON-LD 文档比对(W3C README「JSON-LD Object comparison」移植;役65
/// 定位改判:功能=通用文档比对器——对象键序不敏感;数组默认无序——唯一
/// 例外 @list 键下的数组保序;标量严格相等。语言标签大小写不敏感项未采
/// (现役从严)。「suite」系历史名非用途限定。tn004 双 nest 保序输出
/// [v2,v4,v3] 与 expected [v2,v3,v4] 同判——合法置换。
pub fn canonical_for_suite(value : JsonValue) -> JsonValue {
  canonical_suite_inner(value, false)
}

///|
fn canonical_suite_inner(
  value : JsonValue,
  order_significant : Bool,
) -> JsonValue {
  match value {
    JsonValue::Object(pairs) => {
      let sorted = pairs.map(fn(pair) {
        (pair.0, canonical_suite_inner(pair.1, pair.0 is "@list"))
      })
      sorted.sort_by(fn(a, b) { a.0.compare(b.0) })
      JsonValue::Object(sorted)
    }
    JsonValue::Array(items) =>
      if order_significant {
        JsonValue::Array(
          items.map(fn(item) { canonical_suite_inner(item, false) }),
        )
      } else {
        let canon = items.map(fn(item) { canonical_suite_inner(item, false) })
        canon.sort_by(json_total_compare)
        JsonValue::Array(canon)
      }
    other => other
  }
}

///|
/// JsonValue 全序比较(套件无序数组排序用):变体秩 -> 内容逐层
fn json_total_compare(a : JsonValue, b : JsonValue) -> Int {
  let rank = fn(v : JsonValue) -> Int {
    match v {
      JsonValue::Null => 0
      JsonValue::Bool(_) => 1
      JsonValue::Number(_) => 2
      JsonValue::String(_) => 3
      JsonValue::Array(_) => 4
      JsonValue::Object(_) => 5
    }
  }
  let ra = rank(a)
  let rb = rank(b)
  if ra != rb {
    return ra - rb
  }
  match (a, b) {
    (JsonValue::Bool(x), JsonValue::Bool(y)) =>
      match (x, y) {
        (true, false) => 1
        (false, true) => -1
        _ => 0
      }
    (JsonValue::Number(x), JsonValue::Number(y)) => x.compare(y)
    (JsonValue::String(x), JsonValue::String(y)) => x.compare(y)
    (JsonValue::Array(xs), JsonValue::Array(ys)) => {
      let n = if xs.length() < ys.length() { xs.length() } else { ys.length() }
      for i in 0.. {
      let n = if xs.length() < ys.length() { xs.length() } else { ys.length() }
      for i in 0.. 0
  }
}

///|
/// JSON-LD 文档同构判定(bnode 双射兜底;役65 定位改判:功能=通用同构
/// 比对器——直比对拍不过时,bnode 词面升为存在变量——存在标签双射 f 使
/// relabel 后规范形逐字节相等 ⇒ 同构成立。词面位三钉(flatten 输出形):
/// 谓词键位 / "@id" 值位 / "@type" 数组串值位;"@value" 字面量串不重标
/// (字面量 "_:x" 非节点词面)。标签数 > 8 直接 false——**8 是排列数工作
/// 界,非经验值**:回溯最坏 = n! 次全文档重标规范渲染,8! = 40320 次为可
/// 接受上界;套件实测最大 4 标签(#t0038,24 排列),8 留 4 倍标签余量。
/// 「suite」系历史名非用途限定。
pub fn isomorphic_for_suite(ours : JsonValue, expected : JsonValue) -> Bool {
  // @graph 数组集合语义(役52 交棒钉兑现——RDF 图无序;framing unordered
  // oracle 序不判定。只松不紧:canonical 直比通道不受扰,兜底面放宽)
  let ours_norm = jv_sort_graph_arrays(ours)
  let expected_norm = jv_sort_graph_arrays(expected)
  let ours_canon = render_json(canonical_for_suite(ours_norm))
  let expected_canon = render_json(canonical_for_suite(expected_norm))
  if ours_canon == expected_canon {
    return true
  }
  let la : Map[String, Bool] = Map([])
  jv_bnode_labels(ours, la)
  let lb : Map[String, Bool] = Map([])
  jv_bnode_labels(expected, lb)
  if la.length() != lb.length() || la.length() > 8 {
    return false
  }
  let left = la.keys().collect()
  left.sort()
  let right = lb.keys().collect()
  right.sort()
  jv_bnode_assign(left, right, 0, Map([]), Map([]), ours, expected_canon)
}

///|
/// @graph 数组排序规范化(役52 序勘定低成本路线):对象含 @graph 键且值
/// 为数组 ⇒ 元素按 canonical 词形排序(深走全树);其余结构原样递归
fn jv_sort_graph_arrays(value : JsonValue) -> JsonValue {
  match value {
    JsonValue::Object(pairs) => {
      let new_pairs : Array[(String, JsonValue)] = []
      for pair in pairs {
        new_pairs.push((pair.0, jv_sort_graph_arrays(pair.1)))
      }
      for i, pair in new_pairs {
        if pair.0 == "@graph" {
          match pair.1 {
            JsonValue::Array(items) => {
              let keyed : Array[(String, JsonValue)] = []
              for item in items {
                keyed.push((render_json(canonical_for_suite(item)), item))
              }
              keyed.sort_by(fn(a, b) { String::compare(a.0, b.0) })
              let sorted : Array[JsonValue] = []
              for kv in keyed {
                sorted.push(kv.1)
              }
              new_pairs[i] = (pair.0, JsonValue::Array(sorted))
            }
            _ => ()
          }
        }
      }
      JsonValue::Object(new_pairs)
    }
    JsonValue::Array(items) => {
      let new_items : Array[JsonValue] = []
      for item in items {
        new_items.push(jv_sort_graph_arrays(item))
      }
      JsonValue::Array(new_items)
    }
    _ => value
  }
}

///|
/// bnode 词面收集(三钉位递归全走;词面去重入表——判定器同形 nq_term_is_bnode)
fn jv_bnode_labels(value : JsonValue, into : Map[String, Bool]) -> Unit {
  match value {
    JsonValue::Object(pairs) =>
      for pair in pairs {
        if nq_term_is_bnode(pair.0) {
          into[pair.0] = true
        }
        match (pair.0, pair.1) {
          ("@id", JsonValue::String(word)) =>
            if nq_term_is_bnode(word) {
              into[word] = true
            }
          ("@type", JsonValue::Array(items)) =>
            for item in items {
              match item {
                JsonValue::String(word) =>
                  if nq_term_is_bnode(word) {
                    into[word] = true
                  }
                _ => ()
              }
            }
          _ => ()
        }
        jv_bnode_labels(pair.1, into)
      }
    JsonValue::Array(items) =>
      for item in items {
        jv_bnode_labels(item, into)
      }
    _ => ()
  }
}

///|
/// bnode 词面重标(三钉位替换——与收集位对称;mapping 只含 bnode 词面,
/// 命中即换、未命中保留原形)
fn jv_relabel_bnodes(
  value : JsonValue,
  mapping : Map[String, String],
) -> JsonValue {
  match value {
    JsonValue::Object(pairs) => {
      let out : Array[(String, JsonValue)] = []
      for pair in pairs {
        let key = match mapping.get(pair.0) {
          Some(relabeled) => relabeled
          None => pair.0
        }
        let value_out = match (pair.0, pair.1) {
          ("@id", JsonValue::String(word)) =>
            match mapping.get(word) {
              Some(relabeled) => JsonValue::String(relabeled)
              None => pair.1
            }
          ("@type", JsonValue::Array(items)) =>
            JsonValue::Array(
              items.map(fn(item) {
                match item {
                  JsonValue::String(word) =>
                    match mapping.get(word) {
                      Some(relabeled) => JsonValue::String(relabeled)
                      None => item
                    }
                  _ => item
                }
              }),
            )
          _ => jv_relabel_bnodes(pair.1, mapping)
        }
        out.push((key, value_out))
      }
      JsonValue::Object(out)
    }
    JsonValue::Array(items) =>
      JsonValue::Array(items.map(fn(item) { jv_relabel_bnodes(item, mapping) }))
    other => other
  }
}

///|
/// 标签双射回溯(left 逐个向 right 未用词面派位;满位即重标对拍——
/// mapping 同位覆写无需撤销,used 撤销保双射)
fn jv_bnode_assign(
  left : Array[String],
  right : Array[String],
  depth : Int,
  mapping : Map[String, String],
  used : Map[String, Bool],
  ours : JsonValue,
  expected_canon : String,
) -> Bool {
  if depth == left.length() {
    let relabeled = jv_relabel_bnodes(ours, mapping)
    return render_json(canonical_for_suite(relabeled)) == expected_canon
  }
  for j in 0.. String {
  match node.id {
    Some(id) => id
    None => {
      let t = flatten_fresh_bnode(state)
      node.id = Some(t)
      t
    }
  }
}

///|
/// 新生 bnode(序号跳过**输入在册标签**——#t0045 oracle:输入明写 "_:b0" 与
/// 新生 "_:b0" 撞号 ⇒ 注入错体;reserved 预扫防)
fn flatten_fresh_bnode(state : FlattenState) -> String {
  let mut t = "_:b\{state.counter}"
  state.counter += 1
  while state.reserved.contains(t) {
    t = "_:b\{state.counter}"
    state.counter += 1
  }
  t
}

///|
/// bnode 标签预扫收集(ExpandedValue 树全走——#t0045 oracle)
fn flatten_reserve(value : ExpandedValue, into : Map[String, Bool]) -> Unit {
  match value {
    ExpandedValue::Node(node) => {
      match node.id {
        Some(id) => if nq_term_is_bnode(id) { into[id] = true }
        None => ()
      }
      for pair in node.properties {
        for v in pair.1 {
          flatten_reserve(v, into)
        }
      }
      for pair in node.reverse_props {
        for v in pair.1 {
          flatten_reserve(v, into)
        }
      }
      match node.graph {
        Some(items) =>
          for it in items {
            flatten_reserve(it, into)
          }
        None => ()
      }
      match node.included {
        Some(incs) =>
          for inc in incs {
            flatten_reserve(ExpandedValue::Node(inc), into)
          }
        None => ()
      }
    }
    ExpandedValue::List(items, ..) =>
      for it in items {
        flatten_reserve(it, into)
      }
    ExpandedValue::Value(_) => ()
  }
}

///|
/// JsonValue → JSON 文本(**往返保真 min-JSON**;役65 库化整形晋 pub——
/// 真调用点 = html extract_all 合并源 + isomorphic_for_suite 规范渲染 +
/// 去重键;库出口承诺 = compact/expand 返回 JsonValue 须有官方序列化器)。
/// **≠ json_canonical**:键序/数词形/数组序一概不动(规范形归
/// json_canonical,本函数只管词法保真)。字符串转义 `"` `\` 与全部控制
/// 字符(`\b\f\n\r\t` 命名形 + 其余 \u00XX);数词形 = Double 插值词形
/// (render∘parse 往返值等价,wbtest 钉)。
pub fn render_json(value : JsonValue) -> String {
  match value {
    JsonValue::Null => "null"
    JsonValue::Bool(b) => if b { "true" } else { "false" }
    JsonValue::Number(nv) => "\{nv}"
    JsonValue::String(sv) => render_json_string(sv)
    JsonValue::Array(items) => {
      let parts = StringBuilder()
      parts.write_string("[")
      for i, item in items {
        if i > 0 {
          parts.write_string(",")
        }
        parts.write_string(render_json(item))
      }
      parts.write_string("]")
      parts.to_string()
    }
    JsonValue::Object(pairs) => {
      let parts = StringBuilder()
      parts.write_string("{")
      for i, pair in pairs {
        if i > 0 {
          parts.write_string(",")
        }
        parts.write_string(render_json(JsonValue::String(pair.0)))
        parts.write_string(":")
        parts.write_string(render_json(pair.1))
      }
      parts.write_string("}")
      parts.to_string()
    }
  }
}

///|
/// JSON 字符串词形(引号包裹 + 转义门:`"` `\` 控制字符——役65 晋 pub 前
/// 补,此前零转义渲染含 `"` 串值产非法 JSON;html 50/50 纯因无此形输入)
fn render_json_string(sv : String) -> String {
  let parts = StringBuilder()
  parts.write_char('"')
  for ch in sv {
    let code = ch.to_int()
    if ch == '"' {
      parts.write_string("\\\"")
    } else if ch == '\\' {
      parts.write_string("\\\\")
    } else if code == 8 {
      parts.write_string("\\b")
    } else if code == 12 {
      parts.write_string("\\f")
    } else if code == 10 {
      parts.write_string("\\n")
    } else if code == 13 {
      parts.write_string("\\r")
    } else if code == 9 {
      parts.write_string("\\t")
    } else if code < 0x20 {
      // 其余控制字符 → \u00XX(大写十六进制,JSON 惯例)
      parts.write_string("\\u00")
      parts.write_char(render_json_hex_digit(code / 16))
      parts.write_char(render_json_hex_digit(code % 16))
    } else {
      parts.write_char(ch)
    }
  }
  parts.write_char('"')
  parts.to_string()
}

///|
/// 0-15 → 十六进制位字符(大写;控制字符 \u 转义用)
fn render_json_hex_digit(n : Int) -> Char {
  if n < 10 {
    Int::unsafe_to_char(48 + n)
  } else {
    Int::unsafe_to_char(55 + n)
  }
}

///|
/// 属性值行走分发(节点 → 词面引用 + 作用域注册;字面量/列表 → 词面对象)。
/// None = null 条目弃。
fn flatten_walk_value(
  state : FlattenState,
  value : ExpandedValue,
  scope : String,
) -> JsonValue? {
  match value {
    ExpandedValue::Node(child) => {
      let term = flatten_walk_node(state, child, scope)
      Some(JsonValue::Object([("@id", JsonValue::String(term))]))
    }
    ExpandedValue::Value(l) => Some(serialize_value(ExpandedValue::Value(l)))
    ExpandedValue::List(items, ..) => {
      // @list 保序保重复(#t0027 oracle:list 2,2 保留)
      let arr : Array[JsonValue] = []
      for it in items {
        match flatten_walk_value(state, it, scope) {
          Some(jv) => arr.push(jv)
          None => ()
        }
      }
      Some(JsonValue::Object([("@list", JsonValue::Array(arr))]))
    }
  }
}

///|
/// 属性值数组组装(**非 @list 值去重**——RDF 集合语义:#t0027 myset 2,2,3 →
/// 1,2,3;@list 值对象为单值不受影响)
fn flatten_vals(
  state : FlattenState,
  values : Array[ExpandedValue],
  scope : String,
) -> Array[JsonValue] {
  let vals : Array[JsonValue] = []
  let seen : Map[String, Bool] = Map([])
  for v in values {
    match v {
      ExpandedValue::List(_) =>
        match flatten_walk_value(state, v, scope) {
          Some(jv) => vals.push(jv)
          None => ()
        }
      _ =>
        match flatten_walk_value(state, v, scope) {
          Some(jv) => {
            let key = render_json(jv)
            if !seen.contains(key) {
              seen[key] = true
              vals.push(jv)
            }
          }
          None => ()
        }
    }
  }
  vals
}

///|
/// 同 id 节点图载体判定(@graph 在场——图载体恒顶层成员,#t0021 oracle)
fn nq_has_graph(node : ExpandedNode) -> Bool {
  node.graph is Some(_)
}

///|
/// 成员值级并集(役56 tin06——同 id 全形再现并入既有成员:types 未见追加、
/// 属性同键值级去重续 append(JsonValue 结构等值)、键缺失即新建。**只在
/// visited 命中且全形时调用**——裸引用/环再入不走此臂)
fn flatten_member_merge(
  state : FlattenState,
  scope : String,
  term : String,
  node : ExpandedNode,
) -> Unit {
  let own_scope = if nq_has_graph(node) {
    "" // 图载体恒顶层成员(与 flatten_walk_node own_scope 同规)
  } else {
    scope
  }
  let lists = match state.members.get(own_scope) {
    Some(lists) => lists
    None => return
  }
  // 定位成员(@id 对 = String(term))
  let mut target : Array[(String, JsonValue)]? = None
  for entry in lists {
    if entry.length() > 0 {
      match entry[0] {
        ("@id", JsonValue::String(id_word)) =>
          if id_word == term {
            target = Some(entry)
          }
        _ => ()
      }
    }
  }
  let member_pairs = match target {
    Some(pairs) => pairs
    None => return
  }
  // types 未见追加("@type" 对缺省 = 末位新建;canonical 键序归一不挑位)
  if node.types.length() > 0 {
    let mut type_array : Array[JsonValue]? = None
    for member_entry in member_pairs {
      if member_entry.0 == "@type" {
        match member_entry.1 {
          JsonValue::Array(values) => type_array = Some(values)
          _ => ()
        }
      }
    }
    match type_array {
      Some(values) => {
        let seen_t : Map[String, Bool] = Map([])
        for v in values {
          match v {
            JsonValue::String(word) => seen_t[word] = true
            _ => ()
          }
        }
        for t in node.types {
          if !seen_t.contains(t) {
            values.push(JsonValue::String(t))
            seen_t[t] = true
          }
        }
      }
      None => {
        let values : Array[JsonValue] = []
        for t in node.types {
          values.push(JsonValue::String(t))
        }
        member_pairs.push(("@type", JsonValue::Array(values)))
      }
    }
  }
  // 属性:同键值级去重续 append(@value/@id 渲染形结构等值判重);缺键新建
  for pair in node.properties {
    let rendered = flatten_vals(state, pair.1, own_scope)
    let mut slot : Array[JsonValue]? = None
    for member_pair in member_pairs {
      if member_pair.0 == pair.0 {
        match member_pair.1 {
          JsonValue::Array(values) => slot = Some(values)
          _ => ()
        }
      }
    }
    match slot {
      Some(values) =>
        for v in rendered {
          if !values.contains(v) {
            values.push(v)
          }
        }
      None => member_pairs.push((pair.0, JsonValue::Array(rendered)))
    }
  }
}

///|
/// 节点行走 + 作用域成员注册(返回主体词面):
/// - **图载体节点(@graph 在场)= 恒顶层 "" 成员**(#t0021 oracle:_:b0/_:b1
///   恒顶层;其 @graph 内容 = 新具名作用域 "g:词面" 的成员——book 提升入
///   图内、chapters 内联 _:b1);自身属性值节点行走于 ""。
/// - 普通节点 = 当前作用域成员(嵌套节点提升入所在图——#t0021 book;
///   同作用域同 id 一次——#flatten-0005 oracle:裸引用不实体化)。
/// - **@reverse 反转注入**(#t0037/#t0045/#t0046 oracle——flatten 为反转形,
///   与 expand t0042 保形分叉):reverse 值节点作主体、注入 (pred, 本体引用)
///   至其成员对列表。
fn flatten_walk_node(
  state : FlattenState,
  node : ExpandedNode,
  scope : String,
) -> String {
  let term = flatten_term(state, node)
  let visit_key = "\{scope}|\{term}"
  let is_graph = nq_has_graph(node)
  if state.visited.contains(visit_key) {
    // 全形再现 = 成员值级并集(役56 tin06 勘定——REC Generate Node Map
    // 语义:同 id 每次出现都并入节点图成员;visited 早退只应吞裸引用/
    // 环再入。included 先行序下后到全形携带的属性不得整弃)
    if node.types.length() > 0 ||
      node.properties.length() > 0 ||
      node.reverse_props.length() > 0 {
      flatten_member_merge(state, scope, term, node)
    }
    return term
  }
  state.visited[visit_key] = true
  let own_scope = if is_graph {
    "" // 图载体恒顶层成员(#t0021 oracle)
  } else {
    scope
  }
  // included:同作用域成员行走(#tin01 oracle——flatten 不渲染 @included 键,
  // included 节点提升为所在作用域成员;in01 键渲染保形是 expand 套件面)
  match node.included {
    Some(incs) =>
      for inc in incs {
        let _ = flatten_walk_node(state, inc, own_scope)
      }
    None => ()
  }
  // 同谓词跨对合并(F2 拼接产生同名多对——#t0038 oracle:types/props 去重)
  let prop_map : Map[String, Array[ExpandedValue]] = Map([])
  let prop_order : Array[String] = []
  for pair in node.properties {
    match prop_map.get(pair.0) {
      Some(existing) =>
        for v in pair.1 {
          existing.push(v)
        }
      None => {
        let vals : Array[ExpandedValue] = []
        for v in pair.1 {
          vals.push(v)
        }
        prop_map[pair.0] = vals
        prop_order.push(pair.0)
      }
    }
  }
  let pairs : Array[(String, JsonValue)] = [("@id", JsonValue::String(term))]
  // @type(去重)
  if node.types.length() > 0 {
    let seen_t : Map[String, Bool] = Map([])
    let types : Array[JsonValue] = []
    for t in node.types {
      if !seen_t.contains(t) {
        seen_t[t] = true
        types.push(JsonValue::String(t))
      }
    }
    pairs.push(("@type", JsonValue::Array(types)))
  }
  // 属性(值去重经 flatten_vals)
  for pred in prop_order {
    match prop_map.get(pred) {
      Some(vals) =>
        pairs.push(
          (pred, JsonValue::Array(flatten_vals(state, vals, own_scope))),
        )
      None => ()
    }
  }
  // @index
  match node.index {
    Some(ix) => pairs.push(("@index", JsonValue::String(ix)))
    None => ()
  }
  // 图内容:新作用域续走(成员入 "g:词面")+ @graph 成员装配(内联——
  // #flatten-0020-out 实证)
  match node.graph {
    Some(items) => {
      let graph_scope = "g:" + term
      if !state.scopes.contains(graph_scope) {
        state.scopes[graph_scope] = true
        state.members[graph_scope] = []
        for it in items {
          let _ = flatten_walk_value(state, it, graph_scope)
        }
      }
      let graph_arr : Array[JsonValue] = []
      match state.members.get(graph_scope) {
        Some(member_lists) =>
          for mentry in member_lists {
            graph_arr.push(JsonValue::Object(mentry))
          }
        None => ()
      }
      pairs.push(("@graph", JsonValue::Array(graph_arr)))
    }
    None => ()
  }
  // 裸 {@id} 引用不实体化(#flatten-0005/#t0020 oracle)——引用即词面;
  // 带 reverse 者例外(反转注入使其非裸)
  if pairs.length() == 1 && node.reverse_props.length() == 0 {
    return term
  }
  // 注册成员对列表(reverse 注入可寻址——数组引用原地变异)
  match state.members.get(own_scope) {
    Some(lists) => lists.push(pairs)
    None => {
      let lists : Array[Array[(String, JsonValue)]] = []
      lists.push(pairs)
      state.members[own_scope] = lists
    }
  }
  // @reverse 反转注入(flatten 为反转形——#t0037/#t0045/#t0046 oracle,
  // 与 expand t0042 保形分叉):值节点作主体,注入 (pred, 本体引用);
  // 裸跳过者 ensure {@id} 占位成员
  for pair in node.reverse_props {
    for v in pair.1 {
      match v {
        ExpandedValue::Node(child) => {
          let ct = flatten_term(state, child)
          let ckey = "\{own_scope}|\{ct}"
          if !state.visited.contains(ckey) {
            let _ = flatten_walk_node(state, child, own_scope)
          }
          let mut found : Array[(String, JsonValue)]? = None
          match state.members.get(own_scope) {
            Some(lists) =>
              for target in lists {
                if found is None && target.length() > 0 {
                  match target[0].1 {
                    JsonValue::String(id_term) =>
                      if id_term == ct {
                        found = Some(target)
                      }
                    _ => ()
                  }
                }
              }
            None => ()
          }
          // 注入目标 = child 成员对列表(**并入既有同谓词对**——#t0037/#t0039
          // oracle:@reverse 注入与显式 knows 同值只留一份,RDF 集合语义)
          let value_obj = JsonValue::Object([("@id", JsonValue::String(term))])
          let value_key = render_json(value_obj)
          let child_pairs : Array[(String, JsonValue)] = match found {
            Some(target) => target
            None => {
              let fresh : Array[(String, JsonValue)] = [
                ("@id", JsonValue::String(ct)),
              ]
              state.members[own_scope].push(fresh)
              fresh
            }
          }
          let mut merged = false
          for i in 0.. {
                  let mut dup = false
                  for existing in vals {
                    if render_json(existing) == value_key {
                      dup = true
                    }
                  }
                  if !dup {
                    vals.push(value_obj)
                  }
                  merged = true
                }
                _ => ()
              }
            }
          }
          if !merged {
            child_pairs.push((pair.0, JsonValue::Array([value_obj])))
          }
        }
        _ => ()
      }
    }
  }
  // 收账:reverse 注入成员(child_pairs 引用已在 members 中——此处按词面收)
  term
}

///|
/// flatten 文档入口(J5——spec §8.40):JSON 文本 → 展开全链 → F2 同 id
/// 预合并 → 作用域成员注册 → 顶层作用域枚举。负例 = 任一段 Err。
/// flatten 唯一入口(REC flatten(input, context, options) 同形——context
/// 可选;役65 库化整形:原 flatten_document/with_context 双入口合一,
/// 兑现役56「暴露面随 harness 裁量收缩」承诺)。context 缺省 = 展平直出
/// (数组形);context 给定 = 展平后以该 context 压缩(flatten_with_context_inner)。
pub fn flatten_document(
  text : String,
  context? : String,
  options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
  match context {
    Some(context_text) =>
      flatten_with_context_inner(text, context_text, options)
    None => {
      let top = match flatten_core(text, options) {
        Ok(top) => top
        Err(e) => return Err(e)
      }
      let out : Array[JsonValue] = []
      for pairs in top {
        out.push(JsonValue::Object(pairs))
      }
      Ok(JsonValue::Array(out))
    }
  }
}

///|
/// flatten + compaction 合成(REC flatten(input, context)——context 非
/// null ⇒ 展平后以该 context 压缩;役56 t0044。成员**再展开**(flatten
/// 展开形幂等——绝对 IRI 键/引用/@value 形不变形)后走压缩正机
/// (compact_node_jv);wrap 三钉同 compact_document,compactArrays=false
/// 单节点也 @graph 包裹(#t0091 同款)。
fn flatten_with_context_inner(
  text : String,
  context_text : String,
  options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
  let top = match flatten_core(text, options) {
    Ok(top) => top
    Err(e) => return Err(e)
  }
  let ctx_doc = match parse_json(context_text) {
    Ok(doc) => doc
    Err(e) => return Err(e)
  }
  let inner : JsonValue = match ctx_doc {
    JsonValue::Object(pairs) => {
      let mut found : JsonValue? = None
      for pair in pairs {
        if pair.0 is "@context" {
          found = Some(pair.1)
        }
      }
      match found {
        Some(v) => v
        None => ctx_doc
      }
    }
    _ => ctx_doc
  }
  let active = match
    ContextProcessor::process_context(
      StandardExpander::{ },
      initial_context(options),
      inner,
      ContextFlags::element(),
      options,
    ) {
    Ok(context) => context
    Err(e) => return Err(e)
  }
  // 成员再展开 + 逐节点压缩
  let expander = StandardExpander::{ }
  let expansion_active = initial_context(options)
  let compacted : Array[JsonValue] = []
  for member_pairs in top {
    let element = JsonValue::Object(member_pairs)
    match
      StandardExpander::expand_object(
        expander,
        expansion_active,
        element,
        None,
        options,
      ) {
      Ok(Some(ExpandedValue::Node(member_node))) =>
        match compact_node_jv(member_node, active, options) {
          Ok(jv) => compacted.push(jv)
          Err(e) => return Err(e)
        }
      Ok(_) => ()
      Err(e) => return Err(e)
    }
  }
  // 顶层包裹(**flatten API 固定形制**,与 compact_document 的"单节点解包"分道):
  // API compact() 明文"The final output is a map derived from compacted output.
  // If compacted output is an array, it is included with an entry of (a possibly
  // aliased) @graph"——flatten 的输入恒为**展平数组**,故顶层恒 `@graph` 包裹、
  // **不适用单节点解包**(oracle:html #tf001 单节点 ⇒ 仍 @graph;#tf003 两节点 ⇒
  // 条目内部单值仍按 compactArrays 出标量——即"顶层解包禁用、内部照旧")
  let body_pairs : Array[(String, JsonValue)] = []
  if compacted.length() == 0 {
    return Ok(JsonValue::Object([]))
  } else {
    let graph_key = compact_iri_word(
      "@graph", true, false, true, active, options,
    )
    body_pairs.push((graph_key, JsonValue::Array(compacted)))
  }
  body_pairs.insert(0, ("@context", inner))
  Ok(JsonValue::Object(body_pairs))
}

///|
/// flatten 核心(展开 → F2 同 id 预合并 → 走图)——渲染成员对列;输出
/// 形分叉(直出/带 context 压缩)由入口裁定
fn flatten_core(
  text : String,
  options : JsonLdOptions,
) -> Result[Array[Array[(String, JsonValue)]], JsonLdError] {
  let values = match expand_top_values(text, options) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  // F2 同 id 预合并复用:同一 ctx 贯穿全值(跨值同 id 对象并集到首见);
  // @index 冲突同点检出(#te001 oracle——同 id 双 @index 不等 = colliding
  // indexes 错,REC Generate Node Map 合并面)
  let merge_ctx : MergeCtx = { anon: 0, seen: Map([]), }
  for value in values {
    match nq_merge_same_id(value, merge_ctx, "") {
      Err(e) => return Err(e)
      Ok(_) => ()
    }
  }
  let state : FlattenState = {
    counter: 0,
    visited: Map([]),
    members: Map([("", [])]),
    scopes: Map([("", true)]),
    reserved: Map([]),
  }
  // bnode 标签预扫(#t0045 oracle——新生序号避开输入在册标签;全值一遍)
  for value in values {
    flatten_reserve(value, state.reserved)
  }
  for value in values {
    let _ = flatten_walk_value(state, value, "")
  }
  match state.members.get("") {
    Some(arr) => Ok(arr)
    None => Ok([])
  }
}

///|
/// compaction 文档入口(J6——spec §8.41 钉一):JSON 文本 + 主动 context
/// 文本 → 展开全链 → context 处理 → 压缩序列化(compact_standard.mbt)。
/// 负例 = 任一段 Err。输出形三钉:展开空 → {};单节点 → 对象;n 节点 →
/// @graph 包裹。@context 键 = **原文逐字**发射(223/223 实证),context 空
/// 不发射(6 例实证——ctx {} 族)。
pub fn compact_document(
  input_text : String,
  context_text : String,
  options : JsonLdOptions,
) -> Result[JsonValue, JsonLdError] {
  let values = match expand_top_values(input_text, options) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let ctx_doc = match parse_json(context_text) {
    Ok(doc) => doc
    Err(e) => return Err(e)
  }
  // context 文档形:{"@context": inner} 包装解包,裸形直用
  let inner : JsonValue = match ctx_doc {
    JsonValue::Object(pairs) => {
      let mut found : JsonValue? = None
      for pair in pairs {
        if pair.0 is "@context" {
          found = Some(pair.1)
        }
      }
      match found {
        Some(v) => v
        None => ctx_doc
      }
    }
    _ => ctx_doc
  }
  let active = match
    ContextProcessor::process_context(
      StandardExpander::{ },
      initial_context(options),
      inner,
      ContextFlags::element(),
      options,
    ) {
    Ok(context) => context
    Err(e) => return Err(e)
  }
  // 压缩主体(顶层形三钉;单节点解包随 compactArrays——#t0091 oracle:
  // compactArrays=false 时单节点也 @graph 包裹)
  let body_pairs : Array[(String, JsonValue)] = []
  if values.length() == 1 && options.compact_arrays {
    match values[0] {
      ExpandedValue::Node(node) =>
        match compact_node_jv(node, active, options) {
          Ok(JsonValue::Object(pairs)) =>
            for pair in pairs {
              body_pairs.push(pair)
            }
          Ok(_) => ()
          Err(e) => return Err(e)
        }
      _ => ()
    }
  } else if values.length() == 1 {
    // 单节点未塌缩(compactArrays=false)→ @graph 包裹(REC §9.1 步 9.2——
    // compact 输出为数组时包 @graph 键;#t0091 oracle)
    match values[0] {
      ExpandedValue::Node(node) =>
        match compact_node_jv(node, active, options) {
          Ok(jv) => {
            let graph_key = compact_iri_word(
              "@graph", true, false, true, active, options,
            )
            body_pairs.push((graph_key, JsonValue::Array([jv])))
          }
          Err(e) => return Err(e)
        }
      _ => ()
    }
  } else if values.length() > 1 {
    let arr : Array[JsonValue] = []
    for value in values {
      match value {
        ExpandedValue::Node(node) =>
          match compact_node_jv(node, active, options) {
            Ok(jv) => arr.push(jv)
            Err(e) => return Err(e)
          }
        _ => ()
      }
    }
    // @graph 包裹键走 term 词形(关键字别名——#t0014/#t0052 oracle:
    // "data":"@graph"/"graph":"@graph" → 别名作键;无别名回落 "@graph")
    let graph_key = compact_iri_word(
      "@graph", true, false, true, active, options,
    )
    body_pairs.push((graph_key, JsonValue::Array(arr)))
  }
  // @context 原文发射(context 空 / 主体空均不发射)
  let context_empty = match inner {
    JsonValue::Object(pairs) => pairs.is_empty()
    JsonValue::Null => true
    _ => false
  }
  if !context_empty && body_pairs.length() > 0 {
    body_pairs.insert(0, ("@context", inner))
  }
  Ok(JsonValue::Object(body_pairs))
}