///|
// Strategy: jq-frame-chunks
///|
/// Frames JSON values without waiting for the end of the entire input stream.
/// A frame is parsed by the standard JSON parser before it reaches the query.
priv struct JsonInput {
reader : &@io.Reader
mut chunk : Bytes
mut position : Int32
}
///|
fn JsonInput::new(reader : &@io.Reader) -> JsonInput {
{ reader, chunk: b"", position: 0, }
}
///|
priv struct JsonFrame {
bytes : Array[Byte]
mut compound : Bool
mut depth : Int32
mut quoted : Bool
mut escaped : Bool
}
///|
fn json_space(byte : Byte) -> Bool {
byte is (b' ' | b'\t' | b'\r' | b'\n')
}
///|
fn JsonInput::scan(self : JsonInput, frame : JsonFrame) -> Bool {
while self.position < self.chunk.length() {
let byte = self.chunk[self.position]
self.position += 1
if frame.bytes.is_empty() {
if json_space(byte) {
continue
}
frame.bytes.push(byte)
frame.compound = byte is (b'{' | b'[')
frame.depth = if frame.compound { 1 } else { 0 }
frame.quoted = byte == b'"'
continue
}
if frame.quoted {
frame.bytes.push(byte)
if frame.escaped {
frame.escaped = false
} else if byte == b'\\' {
frame.escaped = true
} else if byte == b'"' {
frame.quoted = false
if !frame.compound {
return true
}
}
} else if frame.compound {
frame.bytes.push(byte)
match byte {
b'"' => frame.quoted = true
b'{' | b'[' => frame.depth += 1
b'}' | b']' => {
frame.depth -= 1
if frame.depth == 0 {
return true
}
}
_ => ()
}
} else if json_space(byte) {
return true
} else if byte is (b'{' | b'[' | b'"') {
self.position -= 1
return true
} else {
frame.bytes.push(byte)
}
}
false
}
///|
async fn JsonInput::next(self : JsonInput) -> Json? {
let frame : JsonFrame = {
bytes: [],
compound: false,
depth: 0,
quoted: false,
escaped: false,
}
while !self.scan(frame) {
match self.reader.read_some(max_len=65536) {
None => break
Some(chunk) => {
self.chunk = chunk
self.position = 0
}
}
}
if frame.bytes.is_empty() {
None
} else {
Some(@json.parse(@utf8.decode(Bytes::from_array(frame.bytes))))
}
}
///|
priv struct Evaluation {
query : @ast.Expr
options : CliOptions
colors : Array[String]?
mut last : Json?
values : Array[Json]
raw : Array[Byte]
}
///|
async fn Evaluation::emit(self : Evaluation, input : Json) -> Unit {
let options = self.options
let last = write_results(
@ast.eval(self.query, input),
options.raw_output || options.join_output,
options.compact_output,
options.join_output,
options.sort_keys,
options.indent,
options.tab,
self.colors,
)
if last is Some(_) {
self.last = last
}
}
///|
async fn Evaluation::consume(self : Evaluation, value : Json) -> Unit {
if self.options.slurp {
self.values.push(value)
} else {
self.emit(value)
}
}
///|
async fn Evaluation::read(self : Evaluation, reader : &@io.Reader) -> Unit {
if self.options.raw_input && self.options.slurp {
while reader.read_some(max_len=65536) is Some(chunk) {
for byte in chunk {
self.raw.push(byte)
}
}
} else if self.options.raw_input || self.options.logs {
let lines = @stream.LineScanner::new(reader)
while lines.next() is Some(line) {
let text = @utf8.decode(line.data)
let value = if self.options.raw_input {
Json::string(text)
} else {
@json.parse(text) catch {
_ => continue
}
}
self.consume(value)
}
} else {
let input = JsonInput::new(reader)
while input.next() is Some(value) {
self.consume(value)
}
}
}