use crate::{json_err, token::Token, JSONError};
pub struct Lexer {
pub json: String,
}
impl Lexer {
pub fn new(json: String) -> Self {
Lexer { json }
}
pub fn get_tokens(&self) -> Result<Vec<Token>, JSONError> {
let mut tokens = vec![];
let mut json = self.json.clone();
while json.len() > 0 {
if let Some(result) = self.lex_string(&mut json) {
if let Ok(string_token) = result {
tokens.push(string_token);
continue;
} else {
json_err!(result.unwrap_err().get_message());
}
}
if let Some(result) = self.lex_number(&mut json) {
if let Ok(number_token) = result {
tokens.push(number_token);
continue;
} else {
json_err!(result.unwrap_err().get_message());
}
}
if let Some(result) = self.lex_boolean(&mut json) {
if let Ok(boolean_token) = result {
tokens.push(boolean_token);
continue;
} else {
json_err!(result.unwrap_err().get_message());
}
}
if let Some(result) = self.lex_null(&mut json) {
if let Ok(null_token) = result {
tokens.push(null_token);
continue;
} else {
json_err!(result.unwrap_err().get_message());
}
}
let first_char = json[..1].to_string();
match &first_char[..] {
" " | "\t" | "\r" | "\n" => {
json = json[1..].to_string();
}
"{" => {
tokens.push(Token::OpenCurlyBracket);
json = json[1..].to_string();
}
"}" => {
tokens.push(Token::CloseCurlyBracket);
json = json[1..].to_string();
}
"[" => {
tokens.push(Token::OpenSquareBracket);
json = json[1..].to_string();
}
"]" => {
tokens.push(Token::CloseSquareBracket);
json = json[1..].to_string();
}
"," => {
tokens.push(Token::Comma);
json = json[1..].to_string();
}
":" => {
tokens.push(Token::Colon);
json = json[1..].to_string();
}
"\"" => {
json_err!("Unexpected end of file: {}", json);
}
char => {
json_err!("Unexpected character: {}", char);
}
}
}
Ok(tokens)
}
fn lex_string(&self, string: &mut String) -> Option<Result<Token, JSONError>> {
let mut data = String::new();
if &string[..1] != "\"" {
return None;
}
let mut escape = false;
for char in string[1..].chars() {
if escape {
if ['r', 'n', 't', '\\', '\"'].contains(&char) {
data.push(char);
escape = false;
} else {
json_err!(
Some,
"Invalid JSON: Invalid escape of character: <{}>",
char.encode_utf8(&mut [0, 0])
);
}
} else {
if char == '\\' {
data.push(char);
escape = true;
} else if char == '"' {
*string = string[data.len() + 2..].to_string();
return Some(Ok(Token::String(data)));
} else {
data.push(char);
}
}
}
None
}
fn lex_number(&self, string: &mut String) -> Option<Result<Token, JSONError>> {
let mut data = String::new();
for char in string.chars() {
let last_char = data.chars().last();
if char.is_digit(10) {
data.push(char);
continue;
}
if char == '-' {
if data == "" || last_char.unwrap() == 'e' || last_char.unwrap() == 'E' {
data.push(char);
continue;
}
json_err!(
Some,
"Invalid JSON: Invalid position for character \"-\" in number"
);
}
if char == 'e' || char == 'E' {
if data != ""
&& last_char.unwrap().is_digit(10)
&& !data.contains('e')
&& !data.contains('E')
{
data.push(char);
continue;
}
json_err!(
Some,
"Invalid JSON: Invalid position for character \"e\" in number"
);
}
if char == '.' {
if data != ""
&& last_char.unwrap().is_digit(10)
&& !data.contains("e")
&& !data.contains('E')
{
data.push(char);
continue;
}
json_err!(
Some,
"Invalid JSON: Invalid position for character \".\" in number"
);
}
break;
}
if data.len() == 0 {
return None;
}
*string = string[data.len()..].to_string();
Some(Ok(Token::Number(data.parse().unwrap())))
}
fn lex_boolean(&self, string: &mut String) -> Option<Result<Token, JSONError>> {
if string.len() > 4 && string.starts_with("true") {
*string = string[4..].to_string();
Some(Ok(Token::Boolean(true)))
} else if string.len() > 5 && string.starts_with("false") {
*string = string[5..].to_string();
Some(Ok(Token::Boolean(false)))
} else {
None
}
}
fn lex_null(&self, string: &mut String) -> Option<Result<Token, JSONError>> {
if string.len() > 4 && string.starts_with("null") {
*string = string[4..].to_string();
Some(Ok(Token::Null))
} else {
None
}
}
}