///|
/// A projected logical node with its original input location.
priv enum LKind {
LNull
LBool(Bool)
LNumber(@smithy.NodeNumber)
LString(String)
LList(Array[LNode])
LStruct(Array[(String, LNode)])
LUnion(String, LNode)
LMap(Array[(String, LNode)])
LObject(Array[(String, LNode)])
LInvalid
}
///|
priv struct LNode {
kind : LKind
source : @validation.InstancePath
}
///|
impl @validation.ValueView for LNode with fn inspect(self) {
let located = fn(fields : Array[(String, LNode)]) {
@validation.ValueNode::LocatedObject(
fields.map(f => {
@validation.LocatedField::new(
f.0,
f.1,
@validation.InstancePath::root(),
)
}),
)
}
match self.kind {
LNull | LInvalid => Null
LBool(b) => Boolean(b)
LNumber(n) => Number(n)
LString(s) => String(s)
LList(items) => Array(items)
LStruct(fields) | LMap(fields) | LObject(fields) => located(fields)
LUnion(name, payload) => located([(name, payload)])
}
}
///|
impl @validation.ValueView for LNode with fn source(self) {
Some(self.source)
}
///|
impl @validation.ValueView for LogicalValue with fn inspect(self) {
match self {
Null => Null
Boolean(b) => Boolean(b)
Number(n) => Number(n)
String(s) => String(s)
List(items) => Array(items)
Structure(fields) | Map(fields) | Object(fields) => Object(fields)
Union(name, payload) => Object([(name, payload)])
}
}
///|
fn LKind::is_empty(self : LKind) -> Bool {
match self {
LList(items) => items.is_empty()
LStruct(fields) | LMap(fields) | LObject(fields) => fields.is_empty()
_ => false
}
}
///|
fn LogicalValue::is_empty(self : LogicalValue) -> Bool {
match self {
List(items) => items.is_empty()
Structure(fields) | Map(fields) | Object(fields) => fields.is_empty()
_ => false
}
}
///|
priv enum Strip {
Enter(LNode)
Exit(LKind, Int32)
}
///|
/// Drop source locations. Uses an explicit stack.
fn LNode::to_logical(self : LNode) -> LogicalValue {
let tasks : Array[Strip] = [Enter(self)]
let out : Array[LogicalValue] = []
let take = fn(n : Int32) -> Array[LogicalValue] {
let items : Array[LogicalValue] = []
for _ in 0..
match node.kind {
LNull | LInvalid => out.push(Null)
LBool(b) => out.push(Boolean(b))
LNumber(n) => out.push(Number(n))
LString(s) => out.push(String(s))
LList(items) => {
tasks.push(Exit(node.kind, items.length()))
for i = items.length() - 1; i >= 0; i = i - 1 {
tasks.push(Enter(items[i]))
}
}
LStruct(fields) | LMap(fields) | LObject(fields) => {
tasks.push(Exit(node.kind, fields.length()))
for i = fields.length() - 1; i >= 0; i = i - 1 {
tasks.push(Enter(fields[i].1))
}
}
LUnion(_, payload) => {
tasks.push(Exit(node.kind, 1))
tasks.push(Enter(payload))
}
}
Exit(kind, n) => {
let items = take(n)
let named = fn(fields : Array[(String, LNode)]) {
let out : Array[(String, LogicalValue)] = []
for i, f in fields {
out.push((f.0, items[i]))
}
out
}
out.push(
match kind {
LList(_) => List(items)
LStruct(fields) => Structure(named(fields))
LMap(fields) => Map(named(fields))
LObject(fields) => Object(named(fields))
LUnion(name, _) => Union(name, items[0])
_ => Null
},
)
}
}
}
out.pop().unwrap()
}
///|
fn field_path(
path : @validation.InstancePath,
name : String,
) -> @validation.InstancePath {
path.field(name) catch {
_ => path
}
}
///|
fn index_path(
path : @validation.InstancePath,
index : Int32,
) -> @validation.InstancePath {
path.index(index) catch {
_ => path
}
}