use super::state::*;
use crate::error::Result;
use crate::values::{JsonNum, JsonValue};
use std::collections::HashMap;
const UNEXPECTED_END_OF_STRING: &str = "Invalid JSON\t unexpected end of string";
pub fn parse_number(state: &mut ParserState) -> Result<JsonValue> {
let number_string = consume_number(state);
if number_string.contains('.') {
match number_string.parse() {
Ok(float) => Ok(JsonValue::Num(JsonNum::Float(float))),
Err(e) => Err(format!("failed to parse number as f64 {}", e).into()),
}
} else {
match number_string.parse() {
Ok(int) => Ok(JsonValue::Num(JsonNum::Int(int))),
Err(e) => Err(format!("failed to parse number as i128 {}", e).into()),
}
}
}
pub fn parse_escape_sequence(state: &mut ParserState) -> Result<char> {
match advance(state) {
Some('n') => Ok('\n'),
Some('t') => Ok('\t'),
Some('r') => Ok('\r'),
Some('u') => todo!("add escape for unicode"),
Some('"') => Ok('"'),
Some('\\') => Ok('\\'),
None => Err(UNEXPECTED_END_OF_STRING.into()),
Some(c) => Err(format!(
"invalid character escape at {}\tattempted escape character`{}`",
state
.peek()
.map_or("UNKOWN".into(), |(pos, _)| pos.to_string()),
c
)
.into()),
}
}
pub fn parse_string(state: &mut ParserState) -> Result<String> {
assert_char(state, '"', false)?;
let mut working_stirng = String::new();
loop {
let c = match advance(state) {
Some(c) => c,
None => return Err(UNEXPECTED_END_OF_STRING.into()),
};
match c {
'"' => break,
'\\' => working_stirng.push(parse_escape_sequence(state)?),
c => working_stirng.push(c),
}
}
Ok(working_stirng)
}
pub fn parse_object(state: &mut ParserState) -> Result<JsonValue> {
assert_char(state, '{', false)?;
consume_whitespace(state);
let mut json_map: HashMap<String, JsonValue> = HashMap::new();
if let Some('}') = peek(state) {
return Ok(JsonValue::Obj(json_map));
}
loop {
let key = parse_string(state)?;
consume_whitespace(state);
assert_char(state, ':', false)?;
consume_whitespace(state);
json_map.insert(key, main_parse(state)?);
consume_whitespace(state);
match advance(state){
Some(',') => consume_whitespace(state),
Some('}') => break,
None => return Err(UNEXPECTED_END_OF_STRING.into()),
Some(c) => return Err(format!("Invalid json string error at position {} expected either `,` or `}}` instead found {}", state.peek().map_or("UNKOWN".into(), |(pos, _)|pos.to_string()), c).into())
}
}
Ok(JsonValue::Obj(json_map))
}
pub fn parse_array(state: &mut ParserState) -> Result<JsonValue> {
assert_char(state, '[', false)?;
consume_whitespace(state);
let mut json_list: Vec<JsonValue> = Vec::new();
if let Some(']') = peek(state) {
return Ok(JsonValue::Array(json_list));
}
loop {
json_list.push(main_parse(state)?);
consume_whitespace(state);
match advance(state){
Some(',') => consume_whitespace(state),
Some(']') => break,
None => return Err(UNEXPECTED_END_OF_STRING.into()),
Some(c) => return Err(format!("Invalid json string error at position {} expected either `,` or `]` instead found {}", state.peek().map_or("UNKOWN".into(), |(pos, _)|pos.to_string()), c).into())
}
}
Ok(JsonValue::Array(json_list))
}
pub fn main_parse(state: &mut ParserState) -> Result<JsonValue> {
consume_whitespace(state);
match peek(state) {
Some('t' | 'T') => {
assert_string(state, "true", true)?;
Ok(JsonValue::Bool(true))
}
Some('f' | 'F') => {
assert_string(state, "false", true)?;
Ok(JsonValue::Bool(false))
}
Some('n') => {
assert_string(state, "null", true)?;
Ok(JsonValue::Null)
}
Some('.' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') => {
parse_number(state)
}
Some('"') => Ok(JsonValue::String(parse_string(state)?)),
Some('[') => parse_array(state),
Some('{') => parse_object(state),
None => Err(UNEXPECTED_END_OF_STRING.into()),
Some(c) => Err(format!(
"Invalid JSON\tunknown character at position: {} `{c}`",
state
.peek()
.map_or("UNKOWN".into(), |(pos, _)| pos.to_string())
)
.into()),
}
}