///|
fn depth_limit() -> @parsec.Parser[Char, RawJson] {
@parsec.Parser::choice([
raw_char('[', expected="JSON nesting depth limit"),
raw_char('{', expected="JSON nesting depth limit"),
])
.look_ahead()
.then_right(@parsec.Parser::cut())
.then_right(fail_at(depth_limit_label))
}
///|
fn array_items(
limits : JsonLimits,
depth : Int,
) -> @parsec.Parser[Char, Array[RawJson]] {
let item = @parsec.Parser::delay(() => value(limits, depth + 1))
if limits.max_array_items <= 0 {
item
.look_ahead()
.not_followed_by(expected=array_limit_label)
.then_right(@parsec.Parser::pure([]))
} else {
let nonempty = item.flat_map(first => {
token(',', expected="comma")
.then_right(item)
.repeat_0_to_n(limits.max_array_items - 1)
.map(rest => {
let items = [first]
for item in rest {
items.push(item)
}
items
})
})
let over_limit = token(',', expected="comma")
.then_right(item.look_ahead().then_right(fail_at(array_limit_label)))
.or_else(@parsec.Parser::pure(()))
nonempty.or_else(@parsec.Parser::pure([])).then_left(over_limit)
}
}
///|
fn array(limits : JsonLimits, depth : Int) -> @parsec.Parser[Char, RawJson] {
token('[', expected="opening bracket")
.then_right(array_items(limits, depth))
.then_left(token(']', expected="closing bracket"))
.map(items => Array(items))
}
///|
fn property(
limits : JsonLimits,
depth : Int,
) -> @parsec.Parser[Char, RawMember] {
@parsec.Parser::position()
.then(string(limits))
.flat_map(key => {
token(':', expected="colon")
.then_right(@parsec.Parser::delay(() => value(limits, depth + 1)))
.map(value => { key: key.1, key_offset: key.0, value, })
})
}
///|
fn object_members(
limits : JsonLimits,
depth : Int,
) -> @parsec.Parser[Char, Array[RawMember]] {
let current_property = property(limits, depth)
if limits.max_object_members <= 0 {
current_property
.look_ahead()
.not_followed_by(expected=object_limit_label)
.then_right(@parsec.Parser::pure([]))
} else {
let nonempty = current_property.flat_map(first => {
token(',', expected="comma")
.then_right(current_property)
.repeat_0_to_n(limits.max_object_members - 1)
.map(rest => {
let properties = [first]
for property in rest {
properties.push(property)
}
properties
})
})
let over_limit = token(',', expected="comma")
.then_right(
current_property.look_ahead().then_right(fail_at(object_limit_label)),
)
.or_else(@parsec.Parser::pure(()))
nonempty.or_else(@parsec.Parser::pure([])).then_left(over_limit)
}
}
///|
fn object(limits : JsonLimits, depth : Int) -> @parsec.Parser[Char, RawJson] {
token('{', expected="opening brace")
.then_right(object_members(limits, depth))
.then_left(token('}', expected="closing brace"))
.map(members => Object(members))
}
///|
fn scalar(limits : JsonLimits) -> @parsec.Parser[Char, RawJson] {
@parsec.Parser::choice([
text("null", expected="null").replace(Null),
text("true", expected="true").replace(Bool(true)),
text("false", expected="false").replace(Bool(false)),
string(limits).map(value => Text(value)),
number().map(raw => Number(raw)),
])
}
///|
fn value(limits : JsonLimits, depth : Int) -> @parsec.Parser[Char, RawJson] {
let scalar = scalar(limits)
if depth >= limits.max_depth {
scalar.or_else(depth_limit())
} else {
scalar
.or_else(@parsec.Parser::delay(() => array(limits, depth)))
.or_else(@parsec.Parser::delay(() => object(limits, depth)))
}
}