///|
/// JSON Value representation
pub(all) enum JsonValue {
Null
Boolean(Bool)
Number(Double)
String(String)
Array(Array[JsonValue])
Object(Map[String, JsonValue])
} derive(Show, Eq)
///|
/// JSON parsing errors
pub(all) suberror ParseError {
UnexpectedChar(Int, Char)
UnexpectedEof
InvalidNumber(String)
InvalidEscape(Char)
InvalidUnicodeEscape(String)
} derive(Eq, Show)
///|
/// Parser state
priv struct Parser {
mut pos : Int
input : String
}
///|
/// Create a new parser
fn Parser::new(input : String) -> Parser {
{ pos: 0, input }
}
///|
/// Check if we're at the end of input
fn Parser::is_eof(self : Parser) -> Bool {
self.pos >= self.input.length()
}
///|
/// Peek current character without consuming
fn Parser::peek(self : Parser) -> Char? {
if self.is_eof() {
None
} else {
self.input.get_char(self.pos)
}
}
///|
/// Consume and return current character
fn Parser::next(self : Parser) -> Char raise ParseError {
match self.peek() {
Some(ch) => {
self.pos += 1
ch
}
None => raise ParseError::UnexpectedEof
}
}
///|
/// Skip whitespace characters
fn Parser::skip_whitespace(self : Parser) -> Unit {
while true {
match self.peek() {
Some(' ') | Some('\t') | Some('\n') | Some('\r') => self.pos += 1
_ => break
}
}
}
///|
/// Expect and consume a specific character
fn Parser::expect(self : Parser, expected : Char) -> Unit raise ParseError {
let ch = self.next()
if ch != expected {
raise ParseError::UnexpectedChar(self.pos - 1, ch)
}
}
///|
/// Parse a JSON value
fn Parser::parse_value(self : Parser) -> JsonValue raise ParseError {
self.skip_whitespace()
match self.peek() {
Some('n') => self.parse_null()
Some('t') | Some('f') => self.parse_boolean()
Some('"') => JsonValue::String(self.parse_string())
Some('[') => self.parse_array()
Some('{') => self.parse_object()
Some(ch) if ch is ('0'..='9') || ch == '-' => self.parse_number()
Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
None => raise ParseError::UnexpectedEof
}
}
///|
/// Parse null
fn Parser::parse_null(self : Parser) -> JsonValue raise ParseError {
self.expect('n')
self.expect('u')
self.expect('l')
self.expect('l')
JsonValue::Null
}
///|
/// Parse boolean
fn Parser::parse_boolean(self : Parser) -> JsonValue raise ParseError {
match self.peek() {
Some('t') => {
self.expect('t')
self.expect('r')
self.expect('u')
self.expect('e')
JsonValue::Boolean(true)
}
Some('f') => {
self.expect('f')
self.expect('a')
self.expect('l')
self.expect('s')
self.expect('e')
JsonValue::Boolean(false)
}
Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
None => raise ParseError::UnexpectedEof
}
}
///|
/// Parse number
fn Parser::parse_number(self : Parser) -> JsonValue raise ParseError {
let start = self.pos
// Handle negative sign
if self.peek() is Some('-') {
ignore(self.next())
}
// Parse integer part
if self.peek() is Some('0') {
ignore(self.next())
// Parse digits
} else if self.peek() is Some(ch) && ch is ('1'..='9') {
ignore(self.next())
while self.peek() is Some(ch) && ch is ('0'..='9') {
ignore(self.next())
}
} else {
raise ParseError::InvalidNumber("Expected digit")
}
// Parse fractional part
if self.peek() is Some('.') {
ignore(self.next())
if self.peek() is Some(ch) && ch is ('0'..='9') {
while self.peek() is Some(ch) && ch is ('0'..='9') {
ignore(self.next())
}
} else {
raise ParseError::InvalidNumber("Expected digit after decimal point")
}
}
// Parse exponent
if self.peek() is Some('e') || self.peek() is Some('E') {
ignore(self.next())
if self.peek() is Some('+') || self.peek() is Some('-') {
ignore(self.next())
}
if self.peek() is Some(ch) && ch is ('0'..='9') {
while self.peek() is Some(ch) && ch is ('0'..='9') {
ignore(self.next())
}
} else {
raise ParseError::InvalidNumber("Expected digit in exponent")
}
}
let num_str = try! self.input[start:self.pos]
let n = @strconv.parse_double(num_str) catch {
_ => raise ParseError::InvalidNumber(num_str.to_string())
}
JsonValue::Number(n)
}
///|
/// Parse string
fn Parser::parse_string(self : Parser) -> String raise ParseError {
self.expect('"')
let buf = @buffer.new()
while true {
match self.peek() {
Some('"') => {
ignore(self.next())
break
}
Some('\\') => {
ignore(self.next())
let escaped = self.next()
match escaped {
'"' => buf.write_char('"')
'\\' => buf.write_char('\\')
'/' => buf.write_char('/')
'b' => buf.write_char('\b')
'f' => buf.write_char('\u{000C}')
'n' => buf.write_char('\n')
'r' => buf.write_char('\r')
't' => buf.write_char('\t')
'u' => {
// Parse unicode escape \uXXXX
let hex = @buffer.new()
for i = 0; i < 4; i = i + 1 {
let ch = self.next()
if ch is ('0'..='9') || ch is ('a'..='f') || ch is ('A'..='F') {
hex.write_char(ch)
} else {
raise ParseError::InvalidUnicodeEscape(hex.to_string())
}
}
// Convert hex to character
let hex_str = hex.to_string()
let code = @strconv.parse_int(hex_str, base=16) catch {
_ => raise ParseError::InvalidUnicodeEscape(hex_str)
}
buf.write_char(code.unsafe_to_char())
}
ch => raise ParseError::InvalidEscape(ch)
}
}
Some(ch) => {
buf.write_char(ch)
ignore(self.next())
}
None => raise ParseError::UnexpectedEof
}
}
buf.to_string()
}
///|
/// Parse array
fn Parser::parse_array(self : Parser) -> JsonValue raise ParseError {
self.expect('[')
self.skip_whitespace()
let arr : Array[JsonValue] = []
if self.peek() is Some(']') {
ignore(self.next())
return JsonValue::Array(arr)
}
while true {
arr.push(self.parse_value())
self.skip_whitespace()
match self.peek() {
Some(',') => {
ignore(self.next())
self.skip_whitespace()
}
Some(']') => {
ignore(self.next())
break
}
Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
None => raise ParseError::UnexpectedEof
}
}
JsonValue::Array(arr)
}
///|
/// Parse object
fn Parser::parse_object(self : Parser) -> JsonValue raise ParseError {
self.expect('{')
self.skip_whitespace()
let obj : Map[String, JsonValue] = {}
if self.peek() is Some('}') {
ignore(self.next())
return JsonValue::Object(obj)
}
while true {
self.skip_whitespace()
let key = self.parse_string()
self.skip_whitespace()
self.expect(':')
let value = self.parse_value()
obj[key] = value
self.skip_whitespace()
match self.peek() {
Some(',') => {
ignore(self.next())
self.skip_whitespace()
}
Some('}') => {
ignore(self.next())
break
}
Some(ch) => raise ParseError::UnexpectedChar(self.pos, ch)
None => raise ParseError::UnexpectedEof
}
}
JsonValue::Object(obj)
}
///|
/// Parse a JSON string into a JsonValue
pub fn parse(input : String) -> JsonValue raise ParseError {
let parser = Parser::new(input)
let value = parser.parse_value()
parser.skip_whitespace()
if not(parser.is_eof()) {
match parser.peek() {
Some(ch) => raise ParseError::UnexpectedChar(parser.pos, ch)
None => ()
}
}
value
}
///|
/// Convert JsonValue to string representation
pub fn JsonValue::to_json_string(self : JsonValue) -> String {
let buf = @buffer.new()
self.write_to(buf)
buf.to_string()
}
///|
/// Write JsonValue to buffer
fn JsonValue::write_to(self : JsonValue, buf : @buffer.Buffer) -> Unit {
match self {
Null => buf.write_string("null")
Boolean(b) => buf.write_string(if b { "true" } else { "false" })
Number(n) => buf.write_string(n.to_string())
String(s) => {
buf.write_char('"')
for ch in s {
match ch {
'"' => buf.write_string("\\\"")
'\\' => buf.write_string("\\\\")
'\b' => buf.write_string("\\b")
'\u{000C}' => buf.write_string("\\f")
'\n' => buf.write_string("\\n")
'\r' => buf.write_string("\\r")
'\t' => buf.write_string("\\t")
ch if ch.to_int() < 32 => {
buf.write_string("\\u")
let code = ch.to_int()
let hex = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f',
]
buf.write_char(hex[code / 4096 % 16])
buf.write_char(hex[code / 256 % 16])
buf.write_char(hex[code / 16 % 16])
buf.write_char(hex[code % 16])
}
ch => buf.write_char(ch)
}
}
buf.write_char('"')
}
Array(arr) => {
buf.write_char('[')
for i, item in arr {
if i > 0 {
buf.write_char(',')
}
item.write_to(buf)
}
buf.write_char(']')
}
Object(obj) => {
buf.write_char('{')
let mut first = true
for key, value in obj {
if not(first) {
buf.write_char(',')
}
first = false
JsonValue::String(key).write_to(buf)
buf.write_char(':')
value.write_to(buf)
}
buf.write_char('}')
}
}
}