///|
/// Resolve an exact path. Duplicate matching keys are intentionally ambiguous.
pub fn find_node(root : Node, path : Array[PathSegment]) -> Node? {
let mut current = root
for segment in path {
match segment {
Index(index) => {
if current.kind != ArrayNode ||
index < 0 ||
index >= current.children.length() {
return None
}
current = current.children[index]
}
Key(name) => {
if current.kind != ObjectNode {
return None
}
let mut found : Node? = None
for property in current.children {
if property.kind == PropertyNode && property.children.length() == 2 {
match property.children[0].value {
String(key) =>
if key == name {
if found is Some(_) {
return None
}
found = Some(property.children[1])
}
_ => ()
}
}
}
match found {
Some(node) => current = node
None => return None
}
}
}
}
Some(current)
}
///|
/// Cached JSON projection of a parsed node. Keep source spans for exact numbers;
/// a Double estimate cannot represent every valid JSON numeric spelling.
pub fn node_value(node : Node) -> Json {
node.value
}
///|
/// Return the deepest containing node. Ranges are half-open unless explicitly
/// including the right endpoint; ties prefer the earlier source child.
pub fn node_at_offset(
node : Node,
offset : Int,
include_right_bound? : Bool = false,
) -> Node? {
fn contains(value : Node) -> Bool {
offset >= value.offset &&
(
offset < value.offset + value.length ||
(include_right_bound && offset == value.offset + value.length)
)
}
if !contains(node) {
return None
}
let mut current = node
// Parsed trees are bounded; this also prevents hostile, caller-constructed
// cyclic child arrays from trapping an inspection operation forever.
for _ in 0..<1024 {
let mut next : Node? = None
for child in current.children {
if contains(child) {
next = Some(child)
break
}
}
match next {
None => return Some(current)
Some(child) => current = child
}
}
Some(current)
}