///|
/// What the parser expects next.
#derive(Eq)
priv enum State {
/// A value: at the top level, after `:`, or after `,` in an array.
Value
/// After `[`: the first element or `]`.
ArrayFirst
/// After an array element: `,` or `]`.
ArrayNext
/// After `{`: the first member or `}`.
ObjectFirst
/// After a member value: `,` or `}`.
ObjectNext
/// After a complete top-level value.
TopEnd
/// The input is exhausted.
Finished
}
///|
/// An asynchronous pull parser for JSON text read from an
/// `@lexbuf.AsyncLexbuf`.
///
/// The parser keeps one entry per open container (`true` for an object), so
/// its memory is proportional to the nesting depth, not to the document size.
/// Tokens are scanned whole by `lexscan`, so a single token, such as a long
/// string, is buffered in full unless `max_string_length` is set.
///
/// After a syntax error or an error raised by the input source, every later
/// call raises the same error again. Events before a syntax error are returned
/// first. When the source raises, the events of the token group being scanned
/// may be lost. After a cancellation, later calls raise `Interrupted`.
pub struct Parser {
priv lexbuf : @lexbuf.AsyncLexbuf
priv max_depth : Int32
priv max_string_length : Int32
priv multiple_values : Bool
priv stack : Array[Bool]
priv mut state : State
priv pending : Array[Event]
priv mut pending_head : Int32
priv mut offset : Int32
priv mut line : Int32
priv mut line_start : Int32
priv mut need_separator : Bool
priv mut error : Error?
}
///|
/// Creates a parser reading from `lexbuf`.
///
/// - `max_depth`: the deepest nesting of objects and arrays accepted. The
/// default matches `@json.parse`.
/// - `max_string_length`: if set, strings, property names and number literals
/// longer than this many UTF-16 code units are rejected with `TooLong`. The
/// parser then scans a token in pieces of at most 64 characters, so its
/// memory is bounded by the depth, about one input chunk and the token being
/// decoded (at most the limit). This mode is slower.
/// - `multiple_values`: if `true`, the input is a sequence of JSON values, such
/// as newline-delimited JSON or concatenated documents, separated by optional
/// whitespace; a top-level number, `true`, `false` or `null` must be followed
/// by whitespace or the end of input. The default expects exactly one value.
pub fn Parser::new(
lexbuf : @lexbuf.AsyncLexbuf,
max_depth~ : Int32 = 1024,
max_string_length? : Int32,
multiple_values~ : Bool = false,
) -> Parser {
{
lexbuf,
max_depth,
max_string_length: match max_string_length {
Some(n) => if n < 0 { 0 } else { n }
None => -1
},
multiple_values,
stack: [],
state: Value,
pending: [],
pending_head: 0,
offset: 0,
line: 1,
line_start: 0,
need_separator: false,
error: None,
}
}
///|
/// Creates a parser reading text chunks from `reader`, for example
/// `@stdio.stdin.text`. The options are those of `Parser::new`.
pub fn[R : @io.TextReader] Parser::from_reader(
reader : R,
max_depth~ : Int32 = 1024,
max_string_length? : Int32,
multiple_values~ : Bool = false,
) -> Parser {
Parser::new(
@lexbuf.AsyncLexbuf::from_fn(() => reader.read()),
max_depth~,
max_string_length?,
multiple_values~,
)
}
///|
/// The position of the next unconsumed input. Events already scanned but not
/// yet returned by `next_event` are behind this position.
pub fn Parser::position(self : Parser) -> Position {
{
offset: self.offset,
line: self.line,
column: self.offset - self.line_start,
}
}
///|
/// The number of objects and arrays open after the events returned so far,
/// counting events that are scanned but not yet returned.
pub fn Parser::depth(self : Parser) -> Int32 {
self.stack.length()
}
///|
/// Returns the next event, or `None` at the end of the input.
pub async fn Parser::next_event(self : Parser) -> Event? {
if self.pending_head < self.pending.length() {
return Some(self.take_pending())
}
if self.error is Some(e) {
raise e
}
// A step interrupted by an error that is not caught below, such as a
// cancellation, leaves the input in an unknown state.
// Events it queued are dropped: they may start a group that will never be
// completed.
errdefer (if self.error is None {
self.error = Some(Interrupted)
self.pending.clear()
self.pending_head = 0
})
while self.state != Finished {
let failed = try {
if self.max_string_length >= 0 {
self.slow_step()
} else {
match self.state {
Value | ArrayFirst => self.scan_value()
ArrayNext => self.scan_array_next()
ObjectFirst => self.scan_member()
ObjectNext => self.scan_object_next()
TopEnd | Finished => self.slow_step()
}
}
None
} catch {
e => Some(e)
}
if failed is Some(e) {
self.error = Some(e)
}
// Events scanned before an error are still returned first.
if self.pending_head < self.pending.length() {
return Some(self.take_pending())
}
if failed is Some(e) {
raise e
}
}
None
}
///|
fn Parser::take_pending(self : Parser) -> Event {
let event = self.pending[self.pending_head]
self.pending_head += 1
if self.pending_head == self.pending.length() {
self.pending.clear()
self.pending_head = 0
}
event
}
///|
/// Returns the next complete value.
///
/// At the top level this is the next document. Inside an array it is the
/// next element. It returns `None` at the end of the input, or when the
/// enclosing array or object ends (the end event is consumed). Inside an
/// object, where the next event is a `Key`, use `next_member` instead; calling
/// `next_value` there aborts.
pub async fn Parser::next_value(self : Parser) -> Json? {
match self.next_event() {
None | Some(EndArray | EndObject) => None
Some(Key(_)) =>
abort("Parser::next_value: the next event is a key; use next_member")
Some(first) => Some(self.build_value(first))
}
}
///|
/// Returns the next member of the enclosing object as a key and its complete
/// value, or `None` when the object ends (the `EndObject` event is consumed).
/// Aborts unless the next event is a `Key` or `EndObject`.
pub async fn Parser::next_member(self : Parser) -> (String, Json)? {
match self.next_event() {
Some(EndObject) => None
Some(Key(key)) => {
guard self.next_event() is Some(first) else {
abort("unreachable: a key is always followed by a value")
}
Some((key, self.build_value(first)))
}
_ => abort("Parser::next_member: not inside an object")
}
}
///|
/// Skips the next complete value without building it, with the same contract
/// as `next_value`: returns `false` at the end of the input or when the
/// enclosing container ends. Aborts if the next event is a `Key`.
pub async fn Parser::skip_value(self : Parser) -> Bool {
let mut open = match self.next_event() {
None | Some(EndArray | EndObject) => return false
Some(Key(_)) => abort("Parser::skip_value: the next event is a key")
Some(StartArray | StartObject) => 1
Some(Value(_)) => 0
}
while open > 0 {
match self.next_event() {
Some(StartArray | StartObject) => open += 1
Some(EndArray | EndObject) => open -= 1
Some(_) => ()
None =>
abort("unreachable: the input ended inside a value without an error")
}
}
true
}
///|
/// Calls `f` with each remaining value, as returned by `next_value`.
pub async fn Parser::each_value(
self : Parser,
f : async (Json) -> Unit,
) -> Unit {
while self.next_value() is Some(value) {
f(value)
}
}
///|
/// A container being built by `build_value`.
priv enum Builder {
ArrayBuilder(Array[Json])
ObjectBuilder(Map[String, Json], mut key~ : String)
}
///|
/// Builds the value that starts with `first`, reading the rest of its events.
/// Uses an explicit stack, so deep nesting does not deepen the call stack.
async fn Parser::build_value(self : Parser, first : Event) -> Json {
let stack : Array[Builder] = []
let mut event = first
for ;; {
let value = match event {
Value(v) => v
StartArray => {
stack.push(ArrayBuilder([]))
event = self.next_event_in_value()
continue
}
StartObject => {
stack.push(ObjectBuilder(Map([]), key=""))
event = self.next_event_in_value()
continue
}
Key(k) => {
guard stack.last() is Some(ObjectBuilder(_) as b) else {
abort("unreachable: key outside an object")
}
b.key = k
event = self.next_event_in_value()
continue
}
EndArray =>
match stack.pop() {
Some(ArrayBuilder(elements)) => Json::array(elements)
_ => abort("unreachable: unbalanced array")
}
EndObject =>
match stack.pop() {
Some(ObjectBuilder(members, ..)) => Json::object(members)
_ => abort("unreachable: unbalanced object")
}
}
match stack.last() {
None => return value
Some(ArrayBuilder(elements)) => elements.push(value)
Some(ObjectBuilder(members, key~)) => members[key] = value
}
event = self.next_event_in_value()
}
}
///|
/// Events already scanned are taken without an async call.
async fn Parser::next_event_in_value(self : Parser) -> Event {
if self.pending_head < self.pending.length() {
return self.take_pending()
}
guard self.next_event() is Some(event) else {
abort("unreachable: the input ended inside a value without an error")
}
event
}
///|
/// The current position, for errors.
fn Parser::pos(self : Parser) -> Position {
self.position()
}
///|
/// Advances over `n` code units that contain no line break.
fn Parser::advance(self : Parser, n : Int32) -> Unit {
self.offset += n
}
///|
/// Advances over `text`, which may contain line breaks.
fn Parser::advance_text(self : Parser, text : StringView) -> Unit {
let base = self.offset
for i in 0.. Unit {
self.state = match self.stack.last() {
None => TopEnd
Some(true) => ObjectNext
Some(false) => ArrayNext
}
}
///|
/// Opens an object (`true`) or array at `pos`, checking the depth limit.
fn Parser::open(
self : Parser,
is_object : Bool,
pos : Position,
) -> Unit raise ParseError {
if self.stack.length() >= self.max_depth {
raise DepthLimitExceeded(pos, self.max_depth)
}
self.stack.push(is_object)
if is_object {
self.pending.push(StartObject)
self.state = ObjectFirst
} else {
self.pending.push(StartArray)
self.state = ArrayFirst
}
}
///|
/// Closes the innermost container.
fn Parser::close(self : Parser) -> Unit {
match self.stack.pop() {
Some(true) => self.pending.push(EndObject)
Some(false) => self.pending.push(EndArray)
None => abort("unreachable: close at the top level")
}
self.value_done()
}
///|
/// Emits a complete scalar value.
fn Parser::scalar(self : Parser, value : Json, needs_separator : Bool) -> Unit {
self.pending.push(Value(value))
if self.stack.is_empty() && self.multiple_values {
self.need_separator = needs_separator
}
self.value_done()
}
///|
/// At the top level of a multi-value stream, checks that whitespace separates
/// a number or literal from the next value. `ws` is the length of the
/// whitespace before the value, which starts with `c` at the current position.
fn Parser::check_separator(
self : Parser,
ws : Int32,
c : Char,
) -> Unit raise ParseError {
if self.need_separator && ws == 0 && self.stack.is_empty() {
raise UnexpectedChar(self.pos(), c)
}
self.need_separator = false
}