///|
/// The type is known for a delayed object even when its members are unresolved.
pub fn value_type(value : Value) -> String raise ParseError {
if known_object(value) {
return "OBJECT"
}
match value {
Text(_) | Bare(_) => "STRING"
Number(_) => "NUMBER"
Boolean(_) => "BOOLEAN"
Null => "NULL"
List(_) => "LIST"
Bound(_, _, body) => value_type(body)
_ => raise Invalid("value type is unresolved")
}
}
///|
/// Return detached data, rejecting unresolved children at any depth.
pub fn value_unwrapped(value : Value) -> Value raise ParseError {
unwrapped_tree(value, 0, tree_work())
}
///|
/// Literal map lookup; None means missing, Some(Null) is a present null value.
pub fn object_get(value : Value, key : String) -> Value? raise ParseError {
object_fields(value).get(key).map(copy_value)
}
///|
pub fn object_keys(value : Value) -> Array[String] raise ParseError {
object_iteration_keys(object_fields(value))
}
///|
pub fn list_get(value : Value, index : Int) -> Value raise ParseError {
guard value is List(items) else { raise Invalid("expected list") }
if index < 0 || index >= items.length() {
raise Invalid("list index out of bounds")
}
copy_value(items[index])
}
///|
pub fn value_size(value : Value) -> Int raise ParseError {
match value {
List(items) => items.length()
_ => object_fields(value).length()
}
}