///|
/// framing 面落码(J5——spec §8.93 勘定 / §8.94 开工第一笔 / 本役 = 首役
/// #t0001 冒烟)。REC JSON-LD 1.1 Framing:管线五段复用(expand 全链 /
/// F2 同 id 预合并 / graph map / compaction 主体),新算法主体 = Frame
/// Matching + Framing 两递归。首役骨架覆盖 1.0 基础族单链嵌套(#t0001);
/// wildcard / match-none / value pattern / 帧旗覆写(@embed/@explicit/
/// @requireAll/@omitDefault——frameExpansion 展开批)/ named graph 递归
/// / @reverse / list pattern / @preserve 发射 / prune bnodes / 环检测
/// 逐簇役落码,每批以套件例为钉(禁无判定面落码)。

///|
/// framing 状态(REC framing state——骨架面:旗字段随 frameExpansion 批进)
priv struct FrameState {
  /// "@default"(frame 顶层 @graph ⇒ frameDefault)| "@merged"(缺省);
  /// mut(named-graph 递归位换名——§8.103,变异-恢复)
  mut graph_name : String
  /// graph map(REC Node Map Generation 产物):graph 名 → subject id → 节点
  graph_map : Map[String, Map[String, ExpandedNode]]
  /// 无 id 节点铸号器(flatten_fresh_bnode 同款 "_:bN";输入标签避撞预扫
  /// 随 bnode 簇批补)
  mut bnode_counter : Int
  /// embedded 旗(REC "copy of state with embedded=true" 的变异-恢复等价
  /// ——DFS 单链无交织面;§8.99 embed 状态机)
  mut embedded : Bool
  /// embed 路径栈(环检测 = 目标 id ∈ 栈;顶位主体入栈、递归推入/弹出)
  embed_stack : Array[String]
  /// 已嵌账(键 = graph|id;**每顶层匹配树重置**——t0014 bnode 两树各嵌
  /// 全形 oracle 定音辖域)
  embedded_map : Map[String, Bool]
  /// @last 前嵌位账(键 = graph|id → (parent 节点, 性质)——替换走性质位
  /// 值组扫描)
  last_locations : Map[String, (ExpandedNode, String)]
  /// 压缩词表(term 键 → IRI 映射;役55 t0010 缺位发射「term 键同指」豁免
  /// 的判定面——帧 document 构造,debug 镜像可空表)
  compaction_terms : Map[String, String]
  mut probe : String?
}

///|
fn frame_fresh_bnode(state : FrameState) -> String {
  let term = "_:b\{state.bnode_counter}"
  state.bnode_counter += 1
  match state.probe {
    Some(t) => state.probe = Some(t + "MINT " + term + "\n")
    None => ()
  }
  term
}

///|
fn frame_trace(state : FrameState, line : String) -> Unit {
  match state.probe {
    Some(t) => state.probe = Some(t + line + "\n")
    None => ()
  }
}

///|
/// subject map:当前 graph 名下的 subject 集合;"@merged" = 全 graph 并集
/// (REC Merge Node Maps 真语义——同 id 跨图**逐位并**而非整节点替换;
/// t0068 勘定:图序排序迭代保确定序,"@default" 恒先)
fn frame_subject_map(state : FrameState) -> Map[String, ExpandedNode] {
  if state.graph_name == "@merged" {
    let merged : Map[String, ExpandedNode] = Map([])
    let graph_names = state.graph_map.keys().collect()
    graph_names.sort()
    for graph_name in graph_names {
      match state.graph_map.get(graph_name) {
        Some(nodes) =>
          for id, node in nodes {
            match merged.get(id) {
              Some(base) => merged[id] = frame_merged_union(base, node)
              None => merged[id] = node
            }
          }
        None => ()
      }
    }
    merged
  } else {
    match state.graph_map.get(state.graph_name) {
      Some(nodes) => nodes
      None => {
        let empty : Map[String, ExpandedNode] = Map([])
        empty
      }
    }
  }
}

///|
/// @merged 逐位并集(t0068——types 保序去重、properties 同键值列续append
/// 不去重(对齐 jsonld.js merge 语义)、graph/included/reverse 缺位补位、
/// index 缺位补。值与子节点共享引用——framing 对输入树只读,新容器 =
/// 并集载体。t0048 外层裸壳面被本式自然覆盖:壳无位可并,全节点不丢)
fn frame_merged_union(
  base : ExpandedNode,
  extra : ExpandedNode,
) -> ExpandedNode {
  let merged_types : Array[String] = []
  for t in base.types {
    if !merged_types.contains(t) {
      merged_types.push(t)
    }
  }
  for t in extra.types {
    if !merged_types.contains(t) {
      merged_types.push(t)
    }
  }
  let merged_props : Array[(String, Array[ExpandedValue])] = []
  for pair in base.properties {
    merged_props.push((pair.0, pair.1.copy()))
  }
  for pair in extra.properties {
    let mut found = false
    for i in 0.. {
      let both = graph_a.copy()
      for v in graph_b {
        both.push(v)
      }
      Some(both)
    }
    (Some(graph_a), None) => Some(graph_a.copy())
    (None, Some(graph_b)) => Some(graph_b.copy())
    (None, None) => None
  }
  let merged_included : Array[ExpandedNode]? = match
    (base.included, extra.included) {
    (Some(included_a), Some(included_b)) => {
      let both = included_a.copy()
      for node in included_b {
        both.push(node)
      }
      Some(both)
    }
    (Some(included_a), None) => Some(included_a.copy())
    (None, Some(included_b)) => Some(included_b.copy())
    (None, None) => None
  }
  {
    id: if base.id is Some(_) {
      base.id
    } else {
      extra.id
    },
    types: merged_types,
    properties: merged_props,
    reverse_props: merged_reverse,
    graph: merged_graph,
    included: merged_included,
    index: if base.index is Some(_) {
      base.index
    } else {
      extra.index
    },
  }
}

///|
/// graph map 构建(REC Node Map Generation 骨架):嵌套节点对象抽出注册、
/// 原位留 node reference;值对象原位;@list 逐项抽取(index 槽原位保形);
/// named graph 内容注册到以其 @id 命名的图。同 id 首见注册(F2 预合并已
/// 保证图内同 id 单点)
fn frame_register(
  state : FrameState,
  graph_name : String,
  value : ExpandedValue,
) -> ExpandedValue {
  match value {
    ExpandedValue::Value(_) => value
    ExpandedValue::List(items, ..) => {
      for i, item in items {
        items[i] = frame_register(state, graph_name, item)
      }
      value
    }
    ExpandedValue::Node(node) => {
      // 自身 id 先铸(t0049 oracle 定音——无 id 的图承载节点:图名**复用
      // 自身 id** 一枚,不另铸图名)
      let self_id = match node.id {
        Some(id) =>
          if id == EXPLICIT_NULL_ID {
            frame_fresh_bnode(state)
          } else {
            id
          }
        None => frame_fresh_bnode(state)
      }
      // named graph 内容注册(内容节点属以其 @id 命名的图)
      match node.graph {
        Some(items) => {
          let graph_id = match node.id {
            Some(id) => id
            None => self_id
          }
          for item in items {
            let _ = frame_register(state, graph_id, item)
          }
        }
        None => ()
      }
      // 自身注册**先于性质下走**(后向边勘定——tg003 根因:后向
      // reference 在祖先自注册前递归,把裸引用抢注进图字典,first-wins
      // 随即弃真节点 ⇒ subject map 空壳、匹配全空)。先注册真节点,后向
      // 边命中 contains 即跳过;F2 预合并已保证同 id 全节点单点
      let nodes = match state.graph_map.get(graph_name) {
        Some(existing) => existing
        None => {
          let fresh : Map[String, ExpandedNode] = Map([])
          state.graph_map[graph_name] = fresh
          fresh
        }
      }
      frame_trace(
        state,
        "PLACE [" +
        self_id +
        "] graph=" +
        graph_name +
        " types=" +
        node.types.length().to_string() +
        " props=" +
        node.properties.length().to_string(),
      )
      if !nodes.contains(self_id) {
        nodes[self_id] = node
      } else {
        // 最全者胜(后向边占位勘定二——tg005 根因:后向 reference 先注
        // 册成裸壳,真节点后到被 first-wins 挡外 ⇒ 空壳匹配不上。F2 预
        // 合并保证同 id 至多一个全节点 ⇒ 全节点到即换、裸引用互不换)
        let existing = match nodes.get(self_id) {
          Some(e) => e
          None => node
        }
        let existing_bare = existing.types.is_empty() &&
          existing.properties.is_empty() &&
          existing.reverse_props.is_empty() &&
          existing.graph is None &&
          existing.included is None
        let node_bare = node.types.is_empty() &&
          node.properties.is_empty() &&
          node.reverse_props.is_empty() &&
          node.graph is None &&
          node.included is None
        if existing_bare && !node_bare {
          nodes[self_id] = node
        }
      }
      // 性质位抽取(原位替换为 reference;map 条目 = 同对象,原位变更可见)
      for pair in node.properties {
        for i, item in pair.1 {
          pair.1[i] = frame_register(state, graph_name, item)
        }
      }
      // 原位留 node reference
      ExpandedValue::Node({
        id: Some(self_id),
        types: [],
        properties: [],
        reverse_props: [],
        graph: None,
        included: None,
        index: None,
      })
    }
  }
}

///|
/// 父位(REC parent):顶层 results 数组 | output 节点性质位
priv enum FrameParent {
  Top(Array[ExpandedValue])
  Prop(ExpandedNode, String)
  /// @reverse 配得数组 / list 递归输出数组(§8.101)
  Values(Array[ExpandedValue])
  /// named graph 递归父位(§8.103——值入节点 graph 槽)
  Graph(ExpandedNode)
}

///|
/// output 性质位追加(同性质多值累积——REC output[property] 数组语义)
fn frame_output_add(
  node : ExpandedNode,
  property : String,
  value : ExpandedValue,
) -> Unit {
  for pair in node.properties {
    if pair.0 == property {
      // Value 精确同形去重(t0059 oracle——F2 并集把两处同 id 的同名值并
      // 出重复;REC node map 语义值本集;framing 局部去重不动共享合并面)
      match value {
        ExpandedValue::Value(new_lit) =>
          for existing in pair.1 {
            match existing {
              ExpandedValue::Value(old_lit) =>
                if old_lit.value == new_lit.value &&
                  old_lit.datatype == new_lit.datatype &&
                  old_lit.language == new_lit.language {
                  return
                }
              _ => ()
            }
          }
        _ => ()
      }
      pair.1.push(value)
      return
    }
  }
  node.properties.push((property, [value]))
}

///|
fn frame_parent_add(parent : FrameParent, value : ExpandedValue) -> Unit {
  match parent {
    Top(arr) => arr.push(value)
    Prop(node, property) => frame_output_add(node, property, value)
    Values(arr) => arr.push(value)
    Graph(node) =>
      match node.graph {
        Some(items) => items.push(value)
        None => node.graph = Some([value])
      }
  }
}

///|
/// 帧性质值查取
fn frame_property_values(
  frame_node : ExpandedNode,
  property : String,
) -> Array[ExpandedValue]? {
  for pair in frame_node.properties {
    if pair.0 == property {
      return Some(pair.1)
    }
  }
  None
}

///|
/// 节点性质值查取(缺性质 = 空数组——REC 匹配语义 "values is empty")
fn frame_node_values(
  node : ExpandedNode,
  property : String,
) -> Array[ExpandedValue] {
  for pair in node.properties {
    if pair.0 == property {
      return pair.1
    }
  }
  []
}

///|
/// 合成空帧(帧缺性质位 / 帧展开为空时的全配 pattern——explicit=false 下
/// 全性质随嵌递归收编)
fn frame_empty_pattern() -> ExpandedNode {
  {
    id: None,
    types: [],
    properties: [],
    reverse_props: [],
    graph: None,
    included: None,
    index: None,
  }
}

///|
/// 合成帧(REC:"create a new frame using a new map with properties for
/// @embed, @explicit and @requireAll **taken from** embed, explicit and
/// requireAll"——子帧缺位时合成帧继承当前旗——tg007 bookmark 副本 topics
/// 全嵌 oracle 定音:非继承则 @once 化引用)
fn frame_synthesized_pattern(
  embed : EmbedPolicy,
  explicit : Bool,
  require_all : Bool,
) -> ExpandedNode {
  let pattern = frame_empty_pattern()
  let embed_word = match embed {
    Always => "@always"
    Once => "@once"
    Never => "@never"
    Last => "@last"
  }
  pattern.properties.push(("@embed", [literal_of(embed_word, None)]))
  pattern.properties.push(
    ("@explicit", [literal_of(if explicit { "true" } else { "false" }, None)]),
  )
  pattern.properties.push(
    (
      "@requireAll",
      [literal_of(if require_all { "true" } else { "false" }, None)],
    ),
  )
  pattern
}

///|
/// 帧旗读取(frameExpansion 保形词形——spec §8.96):pattern properties 的
/// keyword 键首值词形("true"/"false"/"@always"…)
fn frame_flag_word(frame_node : ExpandedNode, keyword : String) -> String? {
  match frame_property_values(frame_node, keyword) {
    Some(values) =>
      match values.get(0) {
        Some(ExpandedValue::Value(lit)) => Some(lit.value)
        _ => None
      }
    None => None
  }
}

///|
/// Frame Matching 主入口:subjects 过滤 + 码点序。确定性骨架 = 恒排
/// (REC ordered 旗「code point order if ordered」的骨架化——unordered
/// 语义 oracle 差异面随对拍役校)
fn frame_matching(
  state : FrameState,
  subjects : Array[String],
  frame_node : ExpandedNode,
  require_all : Bool,
) -> Array[String] {
  let subject_map = frame_subject_map(state)
  let matched : Array[String] = []
  for id in subjects {
    match subject_map.get(id) {
      Some(node) =>
        if frame_node_matches(state, node, frame_node, require_all) {
          matched.push(id)
        }
      None => ()
    }
  }
  matched.sort()
  matched
}

///|
/// Frame Matching 单节点(REC 骨架三臂):@id 位 IRI 直配 / @type 位
/// IRI∈types / node pattern 递归(值位 subjects 下代匹配非空)。
/// @id/@type 失配恒否(硬过滤,处理即断);余性质 requireAll 分 all/any;
/// framing 旗/@default 键不参与匹配(REC "no other keywords are
/// considered")。wildcard / match-none / value pattern / @default 缺位配
/// 形随批落码
fn frame_node_matches(
  state : FrameState,
  node : ExpandedNode,
  frame_node : ExpandedNode,
  require_all : Bool,
) -> Bool {
  let node_id = node_id_of(node)
  // match-none 性质位节点级预检(REC:"node does not match if values is
  // not empty and the value of property in frame is match none"——#t0023
  // Sub2 出局 oracle;值缺位不否决,走缺位发射)
  for pair in frame_node.properties {
    if pair.0.get_char(0) is Some('@') {
      continue
    }
    if pair.1.is_empty() && !frame_node_values(node, pair.0).is_empty() {
      return false
    }
  }
  // @id 位硬过滤:pattern 形(frameExpansion 保形——t0032/33)优先,单 IRI
  // 形(id 字段)回落
  let id_patterned = frame_property_values(frame_node, "@id") is Some(_)
  match frame_property_values(frame_node, "@id") {
    Some(id_patterns) => {
      let mut id_match = false
      for pattern in id_patterns {
        match pattern {
          ExpandedValue::Value(lit) =>
            if lit.value == node_id {
              id_match = true
            }
          ExpandedValue::Node(wild) =>
            if frame_is_wildcard_pattern(wild) {
              id_match = true
            }
          _ => ()
        }
      }
      if !id_match {
        return false
      }
    }
    None =>
      match frame_node.id {
        Some(frame_id) => if frame_id != node_id { return false }
        None => ()
      }
  }
  // @type 位硬过滤:pattern 形(IRI 清单任配 / {} 通配须有型 / 空清单
  // match-none 须无型——t0031)优先,types 字段形回落(t0005/t0026)
  let type_patterned = frame_property_values(frame_node, "@type") is Some(_)
  match frame_property_values(frame_node, "@type") {
    Some(type_patterns) => {
      let mut type_match = type_patterns.is_empty() && node.types.is_empty()
      for pattern in type_patterns {
        match pattern {
          ExpandedValue::Value(lit) =>
            if node.types.contains(lit.value) {
              type_match = true
            }
          ExpandedValue::Node(pattern_node) =>
            if frame_is_wildcard_pattern(pattern_node) {
              if !node.types.is_empty() {
                type_match = true
              }
            } else if frame_property_values(pattern_node, "@default") is Some(_) {
              // @type default 对象 = 无条件配(t0064——REC 匹配 default
              // 臂;jsonld.js matchThis=true 不视节点类型有无)
              type_match = true
            }
          _ => ()
        }
      }
      if !type_match {
        return false
      }
    }
    None =>
      if !frame_node.types.is_empty() {
        let mut type_match = false
        for wanted in frame_node.types {
          if node.types.contains(wanted) {
            type_match = true
            break
          }
        }
        if !type_match {
          return false
        }
      }
  }
  // 语义位 = 非 @ 键性质 ∪ pattern 形 @id/@type(旗/@default 键不算——REC
  // "no other keywords are considered";全无 = 帧空/旗-only 即配)
  let has_types_or_id = !frame_node.types.is_empty() || frame_node.id is Some(_)
  let mut has_semantic = id_patterned || type_patterned || has_types_or_id
  for pair in frame_node.properties {
    if !(pair.0.get_char(0) is Some('@')) {
      has_semantic = true
      break
    }
  }
  if !has_semantic {
    return true
  }
  // 性质位记账:@id/@type 到此必已配(计入 any/all);pattern 递归判余位
  let mut all_match = true
  let mut any_match = id_patterned ||
    type_patterned ||
    !frame_node.types.is_empty() ||
    frame_node.id is Some(_)
  let mut saw_semantic = id_patterned || type_patterned
  for pair in frame_node.properties {
    if pair.0.get_char(0) is Some('@') {
      continue
    }
    saw_semantic = true
    if frame_property_matches(state, node, pair.0, pair.1, require_all) {
      any_match = true
    } else {
      all_match = false
    }
  }
  // 仅 @id/@type(旗已剔)——硬过滤已过即配
  if !saw_semantic {
    return true
  }
  if require_all {
    all_match
  } else {
    any_match
  }
}

///|
fn node_id_of(node : ExpandedNode) -> String {
  match node.id {
    Some(id) => id
    None => ""
  }
}

///|
/// 性质位匹配(REC 骨架 = node pattern 臂):值位 node references 的 id
/// 作 subjects 下代递归,非空即配。value pattern / wildcard / match-none
/// / @default 缺位配随 frameExpansion 批落码
fn frame_property_matches(
  state : FrameState,
  node : ExpandedNode,
  property : String,
  patterns : Array[ExpandedValue],
  require_all : Bool,
) -> Bool {
  let values = frame_node_values(node, property)
  // match-none(空 pattern 清单):值缺位 = 配(有值已在节点级预检出局
  // ——t0023 双向 oracle)
  if patterns.is_empty() {
    return values.is_empty()
  }
  for pattern in patterns {
    match pattern {
      ExpandedValue::List(patterns_in_list, ..) =>
        // list pattern 臂(§8.101——t0056-58 any-item-any-pattern 定音;
        // 整表输出不滤)。list-vs-list 交叉,任项配即配
        for item in values {
          match item {
            ExpandedValue::List(items, ..) => {
              let mut list_matched = false
              for one_pattern in patterns_in_list {
                for listitem in items {
                  if frame_list_item_matches(
                      state, one_pattern, listitem, require_all,
                    ) {
                    list_matched = true
                    break
                  }
                }
                if list_matched {
                  break
                }
              }
              if list_matched {
                return true
              }
            }
            _ => ()
          }
        }
      ExpandedValue::Value(pattern_lit) =>
        // 串/词形 pattern = 精确值 pattern(t0036:v/t/l 全等)
        for item in values {
          match item {
            ExpandedValue::Value(lit) =>
              if frame_literal_matches(pattern_lit, lit) {
                return true
              }
            _ => ()
          }
        }
      ExpandedValue::Node(pattern_node) =>
        if frame_is_value_pattern(pattern_node) {
          // 值 pattern(@value/@type/@language 成分——t0037-45)
          for item in values {
            match item {
              ExpandedValue::Value(lit) =>
                if frame_value_pattern_matches(pattern_node, lit) {
                  return true
                }
              _ => ()
            }
          }
        } else {
          let mut has_iri_props = false
          for pp in pattern_node.properties {
            if !(pp.0.get_char(0) is Some('@')) {
              has_iri_props = true
              break
            }
          }
          let has_id_or_type = pattern_node.id is Some(_) ||
            !pattern_node.types.is_empty()
          if !has_iri_props && !has_id_or_type {
            // 通配/默认-only 形:{} 有值即配(t0023 ex:q / t0024);@-键
            // -only(@default/@旗)恒配——缺位走发射(t0025 requireAll
            // 三节点全配 oracle;#t0009 p4 复验)
            if pattern_node.properties.is_empty() {
              if !values.is_empty() {
                return true
              }
            } else {
              return true
            }
          } else {
            // node pattern 递归:值位 references 的 id 下代匹配非空
            let value_subjects : Array[String] = []
            for item in values {
              match item {
                ExpandedValue::Node(ref_node) =>
                  match ref_node.id {
                    Some(ref_id) => value_subjects.push(ref_id)
                    None => ()
                  }
                _ => ()
              }
            }
            let matched = frame_matching(
              state, value_subjects, pattern_node, require_all,
            )
            if !matched.is_empty() {
              return true
            }
          }
        }
    }
  }
  false
}

///|
/// 通配 pattern 判(`{}`——无 id/types/properties 全空)
fn frame_is_wildcard_pattern(pattern_node : ExpandedNode) -> Bool {
  pattern_node.id is None &&
  pattern_node.types.is_empty() &&
  pattern_node.properties.is_empty()
}

///|
/// 值 pattern 判(含 @value/@type/@language 保形键——frameExpansion 值
/// pattern 模式产物)
fn frame_is_value_pattern(pattern_node : ExpandedNode) -> Bool {
  for pair in pattern_node.properties {
    match pair.0 {
      "@value" | "@type" | "@language" => return true
      _ => ()
    }
  }
  false
}

///|
/// 精确值 pattern 配(串 pattern 展开 Value——v/t/l 全等;语言折叠比对)
fn frame_literal_matches(
  pattern : ExpandedLiteral,
  lit : ExpandedLiteral,
) -> Bool {
  let value_ok = pattern.value == lit.value
  let type_ok = match (pattern.datatype, lit.datatype) {
    (Some(a), Some(b)) => a == b
    (None, None) => true
    _ => false
  }
  let lang_ok = match (pattern.language, lit.language) {
    (Some(a), Some(b)) => lang_tag_eq(a, b)
    (None, None) => true
    _ => false
  }
  value_ok && type_ok && lang_ok
}

///|
/// 值 pattern 配(REC Value Pattern Matching——成分缺位 = 值须无该成分
///(t0045 p 位只留 plain oracle);{} 通配位 = 有成分即配(须值位非空成
/// 分);空清单 = match-none(值须无成分——t0040/t0041);清单 = 任配
///(t0042);语言折叠(t0045 "R"↔"r"))
fn frame_value_pattern_matches(
  pattern_node : ExpandedNode,
  lit : ExpandedLiteral,
) -> Bool {
  // v 位(v1 恒非空 ⇒ 无 @value 键 = 不配)
  let v_ok = match frame_property_values(pattern_node, "@value") {
    None => false
    Some(components) => {
      let mut wildcard = false
      let mut listed = false
      for component in components {
        match component {
          ExpandedValue::Value(component_lit) =>
            if component_lit.value == lit.value {
              listed = true
            }
          ExpandedValue::Node(wild) =>
            if frame_is_wildcard_pattern(wild) {
              wildcard = true
            }
          _ => ()
        }
      }
      wildcard || listed
    }
  }
  // t 位
  let t_ok = match frame_property_values(pattern_node, "@type") {
    None => lit.datatype is None
    Some(components) => {
      let mut wildcard = false
      let mut listed = false
      for component in components {
        match component {
          ExpandedValue::Value(component_lit) =>
            match lit.datatype {
              Some(dt) => if component_lit.value == dt { listed = true }
              None => ()
            }
          ExpandedValue::Node(wild) =>
            if frame_is_wildcard_pattern(wild) {
              wildcard = true
            }
          _ => ()
        }
      }
      (wildcard && lit.datatype is Some(_)) ||
      listed ||
      (lit.datatype is None && components.is_empty())
    }
  }
  // l 位(折叠)
  let l_ok = match frame_property_values(pattern_node, "@language") {
    None => lit.language is None
    Some(components) => {
      let mut wildcard = false
      let mut listed = false
      for component in components {
        match component {
          ExpandedValue::Value(component_lit) =>
            match lit.language {
              Some(tag) =>
                if lang_tag_eq(component_lit.value, tag) {
                  listed = true
                }
              None => ()
            }
          ExpandedValue::Node(wild) =>
            if frame_is_wildcard_pattern(wild) {
              wildcard = true
            }
          _ => ()
        }
      }
      (wildcard && lit.language is Some(_)) ||
      listed ||
      (lit.language is None && components.is_empty())
    }
  }
  v_ok && t_ok && l_ok
}

///|
/// 值位 pattern 过滤(invoke 收值门——t0045 非配值不收):无 pattern 全收;
/// match-none 不收;精确/值 pattern 按配收;通配/node pattern 位值照收
fn frame_value_kept(
  frame_node : ExpandedNode,
  property : String,
  item : ExpandedValue,
) -> Bool {
  let lit = match item {
    ExpandedValue::Value(value_lit) => value_lit
    _ => return true
  }
  match frame_property_values(frame_node, property) {
    None => true
    Some(patterns) =>
      match patterns.get(0) {
        None => false
        Some(ExpandedValue::Value(pattern_lit)) =>
          frame_literal_matches(pattern_lit, lit)
        Some(ExpandedValue::Node(pattern_node)) =>
          if frame_is_value_pattern(pattern_node) {
            frame_value_pattern_matches(pattern_node, lit)
          } else {
            true
          }
        Some(_) => true
      }
  }
}

///|
/// list 项配(§8.101——值字面精确/值 pattern/node pattern 递归/通配)
fn frame_list_item_matches(
  state : FrameState,
  pattern : ExpandedValue,
  listitem : ExpandedValue,
  require_all : Bool,
) -> Bool {
  match pattern {
    ExpandedValue::Value(pattern_lit) =>
      match listitem {
        ExpandedValue::Value(lit) => frame_literal_matches(pattern_lit, lit)
        _ => false
      }
    ExpandedValue::Node(pattern_node) =>
      if frame_is_value_pattern(pattern_node) {
        match listitem {
          ExpandedValue::Value(lit) =>
            frame_value_pattern_matches(pattern_node, lit)
          _ => false
        }
      } else if frame_is_wildcard_pattern(pattern_node) {
        true
      } else {
        match listitem {
          ExpandedValue::Node(ref_node) =>
            match ref_node.id {
              Some(ref_id) =>
                !frame_matching(state, [ref_id], pattern_node, require_all).is_empty()
              None => false
            }
          _ => false
        }
      }
    ExpandedValue::List(_, ..) => false
  }
}

///|
/// embed 策略(§8.99 状态机——帧旗 @embed per-invocation 读帧;词形归一:
/// @always→Always / @once|true|缺省→Once / @never|false→Never / @last→Last
/// ——Last 仅 1.0(t0059 oracle);值域校验随负例批)
priv enum EmbedPolicy {
  Always
  Once
  Never
  Last
}

///|
fn frame_embed_policy(frame_node : ExpandedNode) -> EmbedPolicy {
  match frame_flag_word(frame_node, "@embed") {
    Some("@always") => Always
    Some("@never") => Never
    Some("false") => Never
    Some("@last") => Last
    _ => Once
  }
}

///|
/// node reference 装配({@id} 形——embed 三支的引用发射)
fn frame_reference(id : String) -> ExpandedValue {
  ExpandedValue::Node({
    id: Some(id),
    types: [],
    properties: [],
    reverse_props: [],
    graph: None,
    included: None,
    index: None,
  })
}

///|
/// @last 前嵌位替换:性质位值组中 id 全形节点原地换引用(t0059 oracle——
/// "replaces previous embed values with node reference")
fn frame_replace_embed_with_ref(
  parent_node : ExpandedNode,
  property : String,
  id : String,
) -> Unit {
  for pair in parent_node.properties {
    if pair.0 == property {
      for i, item in pair.1 {
        match item {
          ExpandedValue::Node(embedded) =>
            match embedded.id {
              Some(embedded_id) =>
                if embedded_id == id {
                  pair.1[i] = frame_reference(id)
                }
              None => ()
            }
          _ => ()
        }
      }
      return
    }
  }
}

///|
/// 输入 bnode 重铸号(役52——tp021/t0015 oracle:输入 bnode 标签全换新
/// _:bN——节点 id 位与 @type 位同一映射保持一致;seen 表跨树一致;铸号
/// 器与 frame_register 共享(state 先建)⇒ 零碰撞)
fn frame_relabel_bnodes(
  state : FrameState,
  value : ExpandedValue,
  seen : Map[String, String],
) -> Unit {
  match value {
    ExpandedValue::Node(n) => {
      match n.id {
        Some(id) =>
          if nq_term_is_bnode(id) {
            let fresh = match seen.get(id) {
              Some(f) => f
              None => {
                let f = frame_fresh_bnode(state)
                // fresh 标签身份自映(键统一勘定——已铸标签再遇 = 命中自身,
                // 不再二次铸号改写共享节点 ⇒ 双键分裂根因封堵)
                seen[f] = f
                seen[id] = f
                f
              }
            }
            n.id = Some(fresh)
          }
        None => ()
      }
      for i, t in n.types {
        if nq_term_is_bnode(t) {
          let fresh = match seen.get(t) {
            Some(f) => f
            None => {
              let f = frame_fresh_bnode(state)
              seen[f] = f
              seen[t] = f
              f
            }
          }
          n.types[i] = fresh
        }
      }
      for pair in n.properties {
        for v in pair.1 {
          frame_relabel_bnodes(state, v, seen)
        }
      }
      match n.graph {
        Some(items) =>
          for item in items {
            frame_relabel_bnodes(state, item, seen)
          }
        None => ()
      }
      match n.included {
        Some(incs) =>
          for inc in incs {
            frame_relabel_bnodes(state, ExpandedValue::Node(inc), seen)
          }
        None => ()
      }
    }
    ExpandedValue::List(items, ..) =>
      for item in items {
        frame_relabel_bnodes(state, item, seen)
      }
    ExpandedValue::Value(_) => ()
  }
}

///|
/// Framing Algorithm(REC 骨架):matched 码点序枚举 → output 节点装配
/// (keyword 位拷贝 + 性质位:值对象直追加 / node reference 子帧递归嵌入)
/// → parent 收账。旗覆写(requireAll/explicit)+ explicit 收编门 + 帧缺位
/// 性质 @preserve 发射本批已落;embed 旗三分支(@never / @once 已嵌 /
/// 环检测)、named graph 递归(graph_map 有 id 条目)、@included、@reverse:
/// 随各簇役落码
fn frame_invoke(
  state : FrameState,
  subjects : Array[String],
  frame_node : ExpandedNode,
  parent : FrameParent,
) -> Unit {
  // 帧旗覆写(REC 步 2——帧内 @requireAll/@explicit/@embed 覆写本帧子树;
  // 词形 "true"/"false" 自 frameExpansion 保形)
  let require_all = match frame_flag_word(frame_node, "@requireAll") {
    Some("true") => true
    _ => false
  }
  let explicit = match frame_flag_word(frame_node, "@explicit") {
    Some("true") => true
    _ => false
  }
  let embed = frame_embed_policy(frame_node)
  let matched = frame_matching(state, subjects, frame_node, require_all)
  let subject_map = frame_subject_map(state)
  for id in matched {
    match subject_map.get(id) {
      Some(node) => {
        // @once/@last 辖域 = 每顶层匹配树(t0014 bnode 两树各嵌全形 oracle
        // ——顶位进树前重置三账)
        match parent {
          Top(_) => {
            state.embedded_map.clear()
            state.embed_stack.clear()
            state.last_locations.clear()
          }
          _ => ()
        }
        let output : ExpandedNode = {
          id: Some(id),
          types: [],
          properties: [],
          reverse_props: [],
          graph: None,
          included: None,
          index: None,
        }
        // embed 三支(REC Framing Algorithm 序;§8.99 状态机)
        let embed_key = state.graph_name + "|" + id
        match state.probe {
          Some(t) => {
            let line = "TURN id=" +
              id +
              " map=" +
              state.embedded_map.length().to_string() +
              " stack=" +
              state.embed_stack.length().to_string() +
              "\n"
            state.probe = Some(t + line)
          }
          None => ()
        }
        // 支一:顶位(embedded=false)且已嵌 ⇒ 跳过不添(named-graph 递归
        // 位——本批不触,graph 批落)
        if !state.embedded && state.embedded_map.contains(embed_key) {
          continue
        }
        // 支二/三:递归位(embedded=true)
        if state.embedded {
          let circular = state.embed_stack.contains(id)
          if embed is Never || circular {
            match state.probe {
              Some(t) => {
                let line = "  REF(never-or-circ) id=" +
                  id +
                  " circ=" +
                  circular.to_string() +
                  "\n"
                state.probe = Some(t + line)
              }
              None => ()
            }
            // 恒引用 / 环上 ⇒ {@id} 引用发射(t0011/30 never、g003 环 oracle)
            frame_parent_add(parent, frame_reference(id))
            continue
          }
          if embed is Once && state.embedded_map.contains(embed_key) {
            // 树内已嵌 ⇒ 引用(前嵌留全形;t0013 oracle)
            frame_parent_add(parent, frame_reference(id))
            continue
          }
        }
        // @last:前嵌位原地换引用(t0059 oracle),新位全嵌
        if embed is Last {
          match state.last_locations.get(embed_key) {
            Some((old_parent, old_property)) =>
              frame_replace_embed_with_ref(old_parent, old_property, id)
            None => ()
          }
        }
        // named graph 递归(§8.103——REC:"If graph map in state has an
        // entry for id":帧无 @graph ⇒ 递归除非 graphName=@merged;帧有
        // @graph ⇒ 子帧 = 首值(缺 ⇒ 合成),id 为 @merged/@default 不递归;
        // 递归 = graphName=id + embedded=false + 子 subjects + Graph 父位)
        if state.graph_map.contains(id) {
          let frame_has_graph = frame_node.graph is Some(_)
          let mut recurse = true
          let graph_subframe : ExpandedNode = if !frame_has_graph {
            if state.graph_name == "@merged" {
              recurse = false
              frame_empty_pattern()
            } else {
              frame_synthesized_pattern(embed, explicit, require_all)
            }
          } else {
            match frame_node.graph {
              Some(items) =>
                match items.get(0) {
                  Some(ExpandedValue::Node(pattern)) => pattern
                  _ => frame_synthesized_pattern(embed, explicit, require_all)
                }
              None => frame_synthesized_pattern(embed, explicit, require_all)
            }
          }
          if recurse {
            let saved_graph_name = state.graph_name
            let saved_embedded = state.embedded
            state.graph_name = id
            state.embedded = false
            let graph_subjects = match state.graph_map.get(id) {
              Some(nodes) => {
                let keys = nodes.keys().collect()
                keys.sort()
                keys
              }
              None => []
            }
            frame_invoke(state, graph_subjects, graph_subframe, Graph(output))
            state.embedded = saved_embedded
            state.graph_name = saved_graph_name
          }
        }
        // @included 帧(REC 步 6——同 subjects 复用、embedded=false 拷贝态、
        // output 复用为父、active = @included;子帧 = @included 首值节点
        // pattern。tin01-03 面——included 键别名/@container @set 随压缩面)
        match frame_node.included {
          Some(included_items) =>
            match included_items.get(0) {
              Some(included_pattern) => {
                let saved_embedded_included = state.embedded
                state.embedded = false
                frame_invoke(
                  state,
                  subjects,
                  included_pattern,
                  Prop(output, "@included"),
                )
                state.embedded = saved_embedded_included
              }
              None => ()
            }
          None => ()
        }
        state.embed_stack.push(id)
        // keyword 位拷贝(REC "If property is a keyword, add"——展开 IR
        // 中 @type/@index 居专字段);types 去重(F2 并集重复——t0057
        // "ex:T","ex:T" oracle;匹配 contains 语义本集)
        for wanted in node.types {
          if !output.types.contains(wanted) {
            output.types.push(wanted)
          }
        }
        if node.index is Some(_) {
          output.index = node.index
        }
        // 性质位走查
        for pair in node.properties {
          let property = pair.0
          // explicit 收编门(REC:性质不在帧 + explicit ⇒ MUST NOT 加值
          // ——#t0005 oracle:contributor 弃、title/contains 帧内保)
          let frame_values = frame_property_values(frame_node, property)
          if explicit && frame_values is None {
            continue
          }
          for item in pair.1 {
            match item {
              ExpandedValue::Node(ref_node) => {
                // node reference → 子帧递归(帧缺位 = 合成空帧全嵌;
                // REC "first value from property in frame as frame")
                let sub_frame : ExpandedNode = match frame_values {
                  Some(fvs) =>
                    match fvs[0] {
                      ExpandedValue::Node(pattern) => pattern
                      _ =>
                        frame_synthesized_pattern(embed, explicit, require_all)
                    }
                  None =>
                    frame_synthesized_pattern(embed, explicit, require_all)
                }
                match ref_node.id {
                  Some(ref_id) => {
                    // REC "copy of state with embedded=true"——变异-恢复
                    // 等价(DFS 单链)
                    let saved_embedded = state.embedded
                    state.embedded = true
                    frame_invoke(
                      state,
                      [ref_id],
                      sub_frame,
                      Prop(output, property),
                    )
                    state.embedded = saved_embedded
                  }
                  None => ()
                }
              }
              ExpandedValue::Value(_) =>
                // 值位 pattern 过滤(值 pattern 精确/通配/match-none——
                // t0036-45/t0023 判定面)
                if frame_value_kept(frame_node, property, item) {
                  // 语言 pattern 配值小写规范形(t0045 oracle——REC Value
                  // Pattern Matching 双侧小写规范的输出侧显形;拷贝发,
                  // 不扰 subject map 原值)
                  let mut lowered = false
                  match frame_property_values(frame_node, property) {
                    Some(patterns) =>
                      match patterns.get(0) {
                        Some(ExpandedValue::Node(pattern_node)) =>
                          if frame_is_value_pattern(pattern_node) &&
                            frame_property_values(pattern_node, "@language")
                            is Some(_) {
                            match item {
                              ExpandedValue::Value(value_lit) => {
                                frame_output_add(
                                  output,
                                  property,
                                  ExpandedValue::Value(
                                    frame_language_lowered_lit(value_lit),
                                  ),
                                )
                                lowered = true
                              }
                              _ => ()
                            }
                          }
                        Some(ExpandedValue::Value(pattern_lit)) =>
                          if pattern_lit.language is Some(_) {
                            match item {
                              ExpandedValue::Value(value_lit) => {
                                frame_output_add(
                                  output,
                                  property,
                                  ExpandedValue::Value(
                                    frame_language_lowered_lit(value_lit),
                                  ),
                                )
                                lowered = true
                              }
                              _ => ()
                            }
                          }
                        _ => ()
                      }
                    None => ()
                  }
                  if !lowered {
                    frame_output_add(output, property, item)
                  }
                }
              ExpandedValue::List(items, ..) => {
                // list 位(§8.101——REC 逐项:ref 递归嵌(子帧 = 帧 list
                // pattern 首值,缺 ⇒ 合成空帧);值项收;t0055-58)
                let list_patterns : Array[ExpandedValue] = match
                  frame_property_values(frame_node, property) {
                  Some(fvs) =>
                    match fvs.get(0) {
                      Some(ExpandedValue::List(ps, ..)) => ps
                      _ => []
                    }
                  None => []
                }
                let out_items : Array[ExpandedValue] = []
                frame_trace(
                  state,
                  "  LIST " +
                  property +
                  " patterns=" +
                  list_patterns.length().to_string() +
                  " items=" +
                  items.length().to_string(),
                )
                for listitem in items {
                  match listitem {
                    ExpandedValue::Node(ref_node) =>
                      match ref_node.id {
                        Some(ref_id) => {
                          // 子帧 = 帧 list pattern 首个 node pattern;pattern
                          // 全为值 pattern ⇒ 节点项不配不收(t0057 node2
                          // oracle——值 pattern 不嵌节点);pattern 缺 ⇒ 合成
                          // 帧继承旗(t0055)
                          let mut node_pattern : ExpandedNode? = None
                          for one_pattern in list_patterns {
                            match one_pattern {
                              ExpandedValue::Node(pn) =>
                                if !frame_is_value_pattern(pn) {
                                  node_pattern = Some(pn)
                                  break
                                }
                              _ => ()
                            }
                          }
                          match node_pattern {
                            Some(_) =>
                              frame_trace(
                                state,
                                "  LISTITEM node-embed " + ref_id,
                              )
                            None =>
                              if list_patterns.is_empty() {
                                frame_trace(
                                  state,
                                  "  LISTITEM synth-embed " + ref_id,
                                )
                              } else {
                                frame_trace(state, "  LISTITEM DROP " + ref_id)
                              }
                          }
                          let sub_frame : ExpandedNode = match node_pattern {
                            Some(pattern) => pattern
                            None =>
                              if list_patterns.is_empty() {
                                frame_synthesized_pattern(
                                  embed, explicit, require_all,
                                )
                              } else {
                                continue
                              }
                          }
                          let saved_embedded = state.embedded
                          state.embedded = true
                          frame_invoke(
                            state,
                            [ref_id],
                            sub_frame,
                            Values(out_items),
                          )
                          state.embedded = saved_embedded
                        }
                        None => ()
                      }
                    ExpandedValue::Value(_) => out_items.push(listitem)
                    _ => out_items.push(listitem)
                  }
                }
                match state.probe {
                  Some(t) => {
                    let n_s = StringBuilder()
                    for oi in out_items {
                      match oi {
                        ExpandedValue::Value(lit) => {
                          n_s.write_string("V(")
                          n_s.write_string(lit.value)
                          n_s.write_string(") ")
                        }
                        ExpandedValue::Node(nn) => {
                          n_s.write_string("N(")
                          n_s.write_string(probe_id_text(nn))
                          n_s.write_string(") ")
                        }
                        ExpandedValue::List(_, ..) => n_s.write_string("L ")
                      }
                    }
                    state.probe = Some(
                      t +
                      "  LIST-OUT " +
                      property +
                      " n=" +
                      out_items.length().to_string() +
                      " [" +
                      n_s.to_string() +
                      "]\n",
                    )
                  }
                  None => ()
                }
                frame_output_add(
                  output,
                  property,
                  ExpandedValue::List(out_items, index=None),
                )
              }
            }
          }
        }
        // 帧缺位性质 @preserve 发射(REC:frame 非 keyword 性质(@type
        // 走 types 字段不在列)不 in output ⇒ 性质帧首值判 @omitDefault,
        // 未跳则发 {@preserve: @default 值拷贝 | "@null"}——#t0009 四臂
        // / #t0005 ex:null oracle)。@reverse 臂随 reverse 簇批
        for frame_pair in frame_node.properties {
          let property = frame_pair.0
          if property.get_char(0) is Some('@') {
            // @type default 注入(t0064——jsonld.js "allow through default
            // types":节点无型时帧 @type default IRI 即其型;有型 = 已在
            // output 不注入。expanded 形 Node pattern 携 @default IRI)
            if property is "@type" && output.types.is_empty() {
              for type_value in frame_pair.1 {
                match type_value {
                  ExpandedValue::Node(pattern_node) =>
                    match frame_property_values(pattern_node, "@default") {
                      Some(default_values) =>
                        match default_values.get(0) {
                          Some(ExpandedValue::Value(lit)) =>
                            if !output.types.contains(lit.value) {
                              output.types.push(lit.value)
                            }
                          _ => ()
                        }
                      None => ()
                    }
                  _ => ()
                }
              }
            }
            continue
          }
          let mut already = match frame_property_values(output, property) {
            Some(existing) => !existing.is_empty()
            None => false
          }
          // term 键同指豁免(役55 t0010——帧展开 concat IRI vs 输入键自
          // 映射 IRI:output 键为在册 term 键且其 IRI 映射 = 帧性质 IRI
          // ⇒ 同指已在册,不按缺位发 @preserve null)
          if !already {
            for out_pair in output.properties {
              if !out_pair.1.is_empty() {
                match state.compaction_terms.get(out_pair.0) {
                  Some(term_iri) => if term_iri == property { already = true }
                  None => ()
                }
              }
            }
          }
          if already {
            continue
          }
          // 性质帧:pattern 首值;match-none 形(空清单——t0023 ex:p)=
          // 无帧(无 @default/@omitDefault 可读 ⇒ 发 "@null")
          let property_frame : ExpandedNode? = match frame_pair.1.get(0) {
            Some(ExpandedValue::Node(pattern)) => Some(pattern)
            _ => None
          }
          let omit = match property_frame {
            Some(pf) => frame_flag_word(pf, "@omitDefault") is Some("true")
            None => false
          }
          if omit {
            continue
          }
          let preserved : Array[ExpandedValue] = match property_frame {
            Some(pf) =>
              match frame_property_values(pf, "@default") {
                Some(default_values) => {
                  let copy : Array[ExpandedValue] = []
                  for v in default_values {
                    copy.push(v)
                  }
                  copy
                }
                None => [literal_of("@null", None)]
              }
            None => [literal_of("@null", None)]
          }
          frame_output_add(
            output,
            property,
            ExpandedValue::Node({
              id: None,
              types: [],
              properties: [("@preserve", preserved)],
              reverse_props: [],
              graph: None,
              included: None,
              index: None,
            }),
          )
        }
        // @reverse 臂(§8.101——flattened subjects 反扫:正性质 rev_prop
        // 含指向 id 之 reference 者入配得数组;帧侧 reverse_props 汇流
        // 字面键与别名 term——t0028/29)
        let reverse_subjects = subject_map.keys().collect()
        reverse_subjects.sort()
        for rev_pair in frame_node.reverse_props {
          let rev_prop = rev_pair.0
          let sub_frame : ExpandedNode = match rev_pair.1.get(0) {
            Some(ExpandedValue::Node(pattern)) => pattern
            _ => frame_empty_pattern()
          }
          let rev_items : Array[ExpandedValue] = []
          for other_id in reverse_subjects {
            if other_id == id {
              continue
            }
            match subject_map.get(other_id) {
              Some(other_node) => {
                let mut points = false
                for v in frame_node_values(other_node, rev_prop) {
                  match v {
                    ExpandedValue::Node(ref_node) =>
                      match ref_node.id {
                        Some(ref_id) => if ref_id == id { points = true }
                        None => ()
                      }
                    _ => ()
                  }
                }
                if points {
                  let saved_embedded = state.embedded
                  state.embedded = true
                  frame_invoke(state, [other_id], sub_frame, Values(rev_items))
                  state.embedded = saved_embedded
                }
              }
              None => ()
            }
          }
          output.reverse_props.push((rev_prop, rev_items))
        }
        frame_parent_add(parent, ExpandedValue::Node(output))
        let _ = state.embed_stack.pop()
        state.embedded_map[embed_key] = true
        match parent {
          Prop(parent_node, parent_property) =>
            if embed is Last {
              state.last_locations[embed_key] = (parent_node, parent_property)
            }
          _ => ()
        }
      }
      None => ()
    }
  }
}

///|
/// @preserve 解包(REC frame() 步——compaction 前递归替换):仅 @preserve
/// 单性质的 id-less 节点摊开为其值组;其余节点/List 深走原位重写。返回
/// 替换后 0..n 值(摊开位父数组重排,原位位恒单值)
fn frame_unwrap_preserve(value : ExpandedValue) -> Array[ExpandedValue] {
  match value {
    ExpandedValue::Node(node) => {
      let is_preserve_wrapper = node.id is None &&
        node.types.is_empty() &&
        node.index is None &&
        node.graph is None &&
        node.included is None &&
        node.properties.length() == 1 &&
        node.properties[0].0 == "@preserve"
      if is_preserve_wrapper {
        let out : Array[ExpandedValue] = []
        for preserved_value in node.properties[0].1 {
          for replaced in frame_unwrap_preserve(preserved_value) {
            out.push(replaced)
          }
        }
        return out
      }
      for pair in node.properties {
        let replaced : Array[ExpandedValue] = []
        for item in pair.1 {
          for rewritten in frame_unwrap_preserve(item) {
            replaced.push(rewritten)
          }
        }
        pair.1.clear()
        for item in replaced {
          pair.1.push(item)
        }
      }
      [ExpandedValue::Node(node)]
    }
    ExpandedValue::List(items, ..) => {
      let replaced : Array[ExpandedValue] = []
      for item in items {
        for rewritten in frame_unwrap_preserve(item) {
          replaced.push(rewritten)
        }
      }
      items.clear()
      for item in replaced {
        items.push(item)
      }
      [value]
    }
    ExpandedValue::Value(_) => [value]
  }
}

///|
/// `"@null"`→null 递归(REC frame() 末步——**compaction 后**;null-only
/// 数组清空留空数组——#t0009 ex:p7 @set oracle;对象位 null 保留——
/// #t0005 ex:null oracle)
fn frame_null_walk(value : JsonValue) -> JsonValue {
  match value {
    JsonValue::String(word) =>
      if word == "@null" {
        JsonValue::Null
      } else {
        value
      }
    JsonValue::Array(items) => {
      let walked : Array[JsonValue] = []
      let mut only_null = true
      for item in items {
        let rewritten = frame_null_walk(item)
        if !(rewritten is JsonValue::Null) {
          only_null = false
        }
        walked.push(rewritten)
      }
      if only_null {
        JsonValue::Array([])
      } else {
        JsonValue::Array(walked)
      }
    }
    JsonValue::Object(pairs) => {
      let walked_pairs : Array[(String, JsonValue)] = []
      for pair in pairs {
        walked_pairs.push((pair.0, frame_null_walk(pair.1)))
      }
      JsonValue::Object(walked_pairs)
    }
    _ => value
  }
}

///|
/// 语言标签小写(ASCII 折叠——lang_tag_eq 同族;语言子序列恒 ASCII)
fn frame_lower_tag(tag : String) -> String {
  let builder = StringBuilder()
  for c in tag {
    builder.write_char(ascii_fold_lower(c).unsafe_to_char())
  }
  builder.to_string()
}

///|
/// 语言小写规范值拷贝(framing 发射位——不扰 subject map 原值)
fn frame_language_lowered_lit(lit : ExpandedLiteral) -> ExpandedLiteral {
  {
    value: lit.value,
    datatype: lit.datatype,
    language: match lit.language {
      Some(tag) => Some(frame_lower_tag(tag))
      None => None
    },
    direction: lit.direction,
    raw: lit.raw,
    index: lit.index,
    included: lit.included,
  }
}

///|
/// framing 文档入口(J5——spec §8.93 勘定 / §8.94 立桩 / §8.95 首役落码):
/// ①expand(input) ②F2 同 id 预合并(flatten 同源)③frame 原文 @context
/// 抽取 + 展开(骨架 = 常规展开;frameExpansion 保形批随帧旗簇)④
/// process_context 建 ActiveContext ⑤graph map(Node Map Generation 骨架
/// + frame 顶层 @graph ⇒ @default 否则 @merged)⑥匹配嵌套递归 ⑦prune
/// bnodes(mode≠1.0——tp 簇批)⑧@preserve 替换(发射批同笔)⑨compaction
/// (frame @context,compact_node_jv 复用)⑩@graph 包裹 + @context 回填
/// +"@null"→null(发射批同笔)
pub fn frame_document(
  input_text : String,
  frame_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 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(_) => ()
    }
  }
  // frame 原文扫读:@context(compaction context + ActiveContext 双用)
  // + 顶层 @graph(frameDefault 判定)
  let frame_json = match parse_json(frame_text) {
    Ok(doc) => doc
    Err(e) => return Err(e)
  }
  // 帧无 @context ⇒ compaction context = 空 map(t0018 勘定——原样落整帧
  // 对象会被 process_context 当 context 处理而炸:@type 数组项即 invalid
  // context entry)
  let mut frame_inner : JsonValue = JsonValue::Object([])
  let mut has_top_graph = false
  match frame_json {
    JsonValue::Object(pairs) => {
      let mut found : JsonValue? = None
      for pair in pairs {
        if pair.0 is "@context" {
          found = Some(pair.1)
        }
        if pair.0 is "@graph" {
          has_top_graph = true
        }
      }
      match found {
        Some(v) => frame_inner = v
        None => ()
      }
    }
    _ => ()
  }
  // frame 展开走 frameExpansion=true(REC frame() API——frame 文档保形
  // 展开;输入展开恒走原 options)
  let frame_options : JsonLdOptions = {
    base: options.base,
    expand_context: options.expand_context,
    processing_mode: options.processing_mode,
    load_context: options.load_context,
    rdf_direction: options.rdf_direction,
    produce_generalized_rdf: options.produce_generalized_rdf,
    compact_arrays: options.compact_arrays,
    compact_to_relative: options.compact_to_relative,
    frame_expansion: true,
    omit_graph: false,
    use_native_types: false,
    use_rdf_type: false,
  }
  let frame_expanded = match expand_top_values(frame_text, frame_options) {
    Ok(v) => v
    Err(e) => return Err(e)
  }
  let active = match
    ContextProcessor::process_context(
      StandardExpander::{ },
      initial_context(options),
      frame_inner,
      ContextFlags::element(),
      options,
    ) {
    Ok(context) => context
    Err(e) => return Err(e)
  }
  // 压缩词表(term 键 → IRI;缺位发射 term 键同指豁免判定面——役55 t0010)
  let compaction_terms : Map[String, String] = Map([])
  for term_name, term_definition in active.terms {
    match term_definition.iri {
      Some(iri) => compaction_terms[term_name] = iri
      None => ()
    }
  }
  let state : FrameState = {
    graph_name: if has_top_graph {
      "@default"
    } else {
      "@merged"
    },
    graph_map: Map([]),
    bnode_counter: 0,
    embedded: false,
    embed_stack: [],
    embedded_map: Map([]),
    last_locations: Map([]),
    compaction_terms,
    probe: None,
  }
  // 输入 bnode 重铸号(先于注册——tp021/t0015 面)
  let relabel_seen : Map[String, String] = Map([])
  for value in values {
    frame_relabel_bnodes(state, value, relabel_seen)
  }
  for value in values {
    let _ = frame_register(state, "@default", value)
  }
  // frame 根 pattern:展开首值取 Node(frame 顶必 map;空展开/负例校验
  // 随批——骨架空展开回落空 pattern 全配)
  let frame_root : ExpandedNode = match frame_expanded.get(0) {
    Some(ExpandedValue::Node(pattern)) => pattern
    _ => frame_empty_pattern()
  }
  let results : Array[ExpandedValue] = []
  let subjects = frame_subject_map(state).keys().collect()
  subjects.sort()
  frame_invoke(state, subjects, frame_root, Top(results))
  // ⑧ @preserve 解包(compaction 前——REC frame() 步递归替换)
  let unwrapped : Array[ExpandedValue] = []
  for result in results {
    for rewritten in frame_unwrap_preserve(result) {
      unwrapped.push(rewritten)
    }
  }
  // ⑦ prune bnodes(REC——mode≠1.0:仅现一次于性质值位的 bnode @id 摘除
  // ——tp 族 + t0056 id-less 节点面;0 次现(无值位引用)亦摘)
  let is_10 = match options.processing_mode {
    JsonLd10 => true
    JsonLd11 => false
  }
  if !is_10 {
    let counts : Map[String, Int] = Map([])
    fn count_values(value : ExpandedValue) -> Unit {
      match value {
        ExpandedValue::Node(n) => {
          match n.id {
            Some(id) =>
              if nq_term_is_bnode(id) {
                counts[id] = counts.get(id).unwrap_or(0) + 1
              }
            None => ()
          }
          // @type 位 bnode 引用同计入(tp021 oracle——_:b0 被 republic 的
          // @type 引用 + 自身 @id = 2 ⇒ 不摘)
          for t in n.types {
            if nq_term_is_bnode(t) {
              counts[t] = counts.get(t).unwrap_or(0) + 1
            }
          }
          for pair in n.properties {
            for v in pair.1 {
              count_values(v)
            }
          }
          match n.graph {
            Some(items) =>
              for item in items {
                count_values(item)
              }
            None => ()
          }
        }
        ExpandedValue::List(items, ..) =>
          for item in items {
            count_values(item)
          }
        ExpandedValue::Value(_) => ()
      }
    }
    for result in results {
      match result {
        ExpandedValue::Node(n) => {
          // 自身 @id 计入(tp020 oracle 定音——top 步骤 bnode 两处现
          // (自身 @id + instructions 值位)⇒ 不摘;t0056 无值位引用者
          // 计 1 ⇒ 摘)
          match n.id {
            Some(id) =>
              if nq_term_is_bnode(id) {
                counts[id] = counts.get(id).unwrap_or(0) + 1
              }
            None => ()
          }
          for pair in n.properties {
            for v in pair.1 {
              count_values(v)
            }
          }
        }
        _ => ()
      }
    }
    fn prune_ids(value : ExpandedValue) -> Unit {
      match value {
        ExpandedValue::Node(n) => {
          match n.id {
            Some(id) =>
              if nq_term_is_bnode(id) && counts.get(id).unwrap_or(0) <= 1 {
                n.id = None
              }
            None => ()
          }
          for pair in n.properties {
            for v in pair.1 {
              prune_ids(v)
            }
          }
          match n.graph {
            Some(items) =>
              for item in items {
                prune_ids(item)
              }
            None => ()
          }
        }
        ExpandedValue::List(items, ..) =>
          for item in items {
            prune_ids(item)
          }
        ExpandedValue::Value(_) => ()
      }
    }
    for result in results {
      prune_ids(result)
    }
  }
  // ⑨ compaction(frame @context;compactArrays/compactToRelative 旗透传)
  let compacted_items : Array[JsonValue] = []
  for result in unwrapped {
    match result {
      ExpandedValue::Node(out_node) =>
        match compact_node_jv(out_node, active, options) {
          Ok(jv) => compacted_items.push(frame_null_walk(jv))
          Err(e) => return Err(e)
        }
      _ => ()
    }
  }
  // ⑩ wrap 三分(oracle 规则——1.0 恒 @graph 包裹:t0001/02/04/05/09;
  // 1.1:空 ⇒ `@graph:[]`(t0003)、单 ⇒ 解包直出(t0023/26/31/32/34/35)、
  // 多 ⇒ 包裹(t0033)——1.1 单解包 = compaction compactArrays 单节点
  // 解包行为经 frame() 管线的显形)
  let body_pairs : Array[(String, JsonValue)] = []
  let is_10 = match options.processing_mode {
    JsonLd10 => true
    JsonLd11 => false
  }
  if compacted_items.length() == 1 && !is_10 {
    match compacted_items[0] {
      JsonValue::Object(pairs) =>
        for pair in pairs {
          body_pairs.push(pair)
        }
      other => {
        let graph_key = compact_iri_word(
          "@graph", true, false, true, active, options,
        )
        body_pairs.push((graph_key, JsonValue::Array([other])))
      }
    }
  } else {
    let graph_key = compact_iri_word(
      "@graph", true, false, true, active, options,
    )
    body_pairs.push((graph_key, JsonValue::Array(compacted_items)))
  }
  // @context 原文逐字回填(空 context 不发射——compact_document 同款;
  // t0018 无帧 context 例对拍时校)
  let context_empty = match frame_inner {
    JsonValue::Object(pairs) => pairs.is_empty()
    JsonValue::Null => true
    _ => false
  }
  if !context_empty && body_pairs.length() > 0 {
    body_pairs.insert(0, ("@context", frame_inner))
  }
  // "@null"→null 递归(无发射路径——@preserve 发射批同笔)
  Ok(JsonValue::Object(body_pairs))
}

///|
fn probe_id_text(n : ExpandedNode) -> String {
  match n.id {
    Some(i) => i
    None => "None"
  }
}