///|
fn parse_cli_inputs(
cfg : CliArgs,
stdin_text : String,
) -> Array[@lib.Value] raise @lib.ParseError {
if cfg.no_input {
[@lib.Value::null()]
} else {
let text = match cfg.input {
Some(v) => v
None => stdin_text
}
if cfg.raw_input {
if cfg.slurp {
[@lib.Value::string(text)]
} else {
parse_raw_lines(text)
}
} else {
parse_json_inputs(text, cfg.slurp)
}
}
}
///|
fn parse_json_inputs(
text : String,
slurp : Bool,
) -> Array[@lib.Value] raise @lib.ParseError {
if slurp {
[@lib.Value::array(parse_slurp_values(text))]
} else {
parse_json_stream_values(text)
}
}
///|
fn parse_slurp_values(text : String) -> Array[@lib.Value] raise @lib.ParseError {
parse_json_stream_values(text)
}
///|
fn parse_json_stream_values(
text : String,
) -> Array[@lib.Value] raise @lib.ParseError {
let values = []
let mut start = 0
let len = text.length()
while start < len {
start = skip_ascii_ws_offset(text, start)
if start >= len {
return values
}
let end_ = parse_json_value_end(text, start)
values.push(parse_json_piece(text, start, end_))
start = end_
}
values
}
///|
fn skip_ascii_ws_offset(text : String, start : Int32) -> Int32 {
let mut i = start
while i < text.length() {
let ch = text.unsafe_get(i).to_int().unsafe_to_char()
if is_ascii_ws(ch) || ch.to_int() == 0xFEFF {
i += 1
} else {
return i
}
}
i
}
///|
fn is_scalar_boundary(ch : Char) -> Bool {
is_ascii_ws(ch) || ch == '{' || ch == '[' || ch == '"'
}
///|
fn parse_json_value_end(
text : String,
start : Int32,
) -> Int32 raise @lib.ParseError {
let first = text.unsafe_get(start).to_int().unsafe_to_char()
match first {
'{' | '[' => parse_structured_value_end(text, start)
'"' => parse_string_value_end(text, start)
_ => parse_scalar_value_end(text, start)
}
}
///|
fn parse_structured_value_end(
text : String,
start : Int32,
) -> Int32 raise @lib.ParseError {
let stack = []
let mut i = start
let mut in_string = false
let mut escaped = false
while i < text.length() {
let ch = text.unsafe_get(i).to_int().unsafe_to_char()
if in_string {
if escaped {
escaped = false
} else if ch == '\\' {
escaped = true
} else if ch == '"' {
in_string = false
}
i += 1
continue
}
if ch == '"' {
in_string = true
} else if ch == '{' {
stack.push('}')
} else if ch == '[' {
stack.push(']')
} else if ch == '}' || ch == ']' {
if stack.length() == 0 || stack[stack.length() - 1] != ch {
return raise_parse_error_end_from_offset(text, start)
}
ignore(stack.pop())
if stack.length() == 0 {
return i + 1
}
}
i += 1
}
raise_parse_error_end_from_offset(text, start)
}
///|
fn parse_string_value_end(
text : String,
start : Int32,
) -> Int32 raise @lib.ParseError {
let mut i = start + 1
let mut escaped = false
while i < text.length() {
let ch = text.unsafe_get(i).to_int().unsafe_to_char()
if escaped {
escaped = false
} else if ch == '\\' {
escaped = true
} else if ch == '"' {
return i + 1
}
i += 1
}
raise_parse_error_end_from_offset(text, start)
}
///|
fn parse_scalar_value_end(text : String, start : Int32) -> Int32 {
let mut i = start
while i < text.length() {
let ch = text.unsafe_get(i).to_int().unsafe_to_char()
if i > start && is_scalar_boundary(ch) {
return i
}
i += 1
}
i
}
///|
fn parse_json_piece(
text : String,
start_offset : Int32,
end_ : Int32,
) -> @lib.Value raise @lib.ParseError {
@lib.parse(text.view(start_offset~, end_offset=end_).to_owned())
}
///|
fn raise_parse_error_end_from_offset(
text : String,
start : Int32,
) -> Int32 raise @lib.ParseError {
let piece = text.view(start_offset=start).to_owned()
ignore(@lib.parse(piece))
return start
}
///|
fn parse_raw_lines(text : String) -> Array[@lib.Value] {
if text.length() == 0 {
return []
}
let values = []
let mut start = 0
for i in 0.. String {
if s.length() > 0 && s.unsafe_get(s.length() - 1) == '\r' {
s.view(end_offset=s.length() - 1).to_owned()
} else {
s
}
}
///|
fn is_ascii_ws(ch : Char) -> Bool {
ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'
}