///|
/// JSON Value representation
pub enum JsonValue {
Null
Bool(Bool)
Number(Double)
String(String)
Array(Array[JsonValue])
Object(Map[String, JsonValue])
} derive(Show, Eq)
///|
/// Position in the input for error reporting
struct Position {
line : Int
column : Int
} derive(Show, Eq)
///|
/// Parse errors with position information
pub(all) suberror ParseError {
UnexpectedChar(Position, Char)
UnexpectedEof(Position)
InvalidNumber(Position, String)
InvalidEscape(Position)
InvalidUnicodeEscape(Position)
} derive(Eq, Show)
///|
/// Lexer state
struct Lexer {
input : String
mut pos : Int
mut line : Int
mut column : Int
}
///|
/// Create a new lexer
fn Lexer::new(input : String) -> Lexer {
{ input, pos: 0, line: 1, column: 1 }
}
///|
/// Get current position
fn Lexer::position(self : Lexer) -> Position {
{ line: self.line, column: self.column }
}
///|
/// Peek at the current character without consuming it
fn Lexer::peek(self : Lexer) -> Char? {
if self.pos >= self.input.length() {
None
} else {
self.input.get_char(self.pos)
}
}
///|
/// Consume and return the current character
fn Lexer::advance(self : Lexer) -> Char? {
match self.peek() {
Some(ch) => {
self.pos += ch.to_string().length()
if ch == '\n' {
self.line += 1
self.column = 1
} else {
self.column += 1
}
Some(ch)
}
None => None
}
}
///|
/// Skip whitespace characters
fn Lexer::skip_whitespace(self : Lexer) -> Unit {
while true {
match self.peek() {
Some(ch) if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' =>
self.advance() |> ignore
_ => break
}
}
}
///|
/// Expect a specific character
fn Lexer::expect(self : Lexer, expected : Char) -> Unit raise ParseError {
match self.advance() {
Some(ch) if ch == expected => ()
Some(ch) => raise ParseError::UnexpectedChar(self.position(), ch)
None => raise ParseError::UnexpectedEof(self.position())
}
}
///|
/// Parse a JSON value
pub fn parse(input : String) -> JsonValue raise ParseError {
let lexer = Lexer::new(input)
let value = parse_value(lexer)
lexer.skip_whitespace()
match lexer.peek() {
Some(ch) => raise ParseError::UnexpectedChar(lexer.position(), ch)
None => value
}
}
///|
/// Parse any JSON value
fn parse_value(lexer : Lexer) -> JsonValue raise ParseError {
lexer.skip_whitespace()
match lexer.peek() {
Some('n') => parse_null(lexer)
Some('t') | Some('f') => parse_bool(lexer)
Some('"') => String(parse_string(lexer))
Some('[') => parse_array(lexer)
Some('{') => parse_object(lexer)
Some(ch) if ch == '-' || is_digit(ch) => parse_number(lexer)
Some(ch) => raise ParseError::UnexpectedChar(lexer.position(), ch)
None => raise ParseError::UnexpectedEof(lexer.position())
}
}
///|
/// Parse null literal
fn parse_null(lexer : Lexer) -> JsonValue raise ParseError {
lexer.expect('n')
lexer.expect('u')
lexer.expect('l')
lexer.expect('l')
Null
}
///|
/// Parse boolean literal
fn parse_bool(lexer : Lexer) -> JsonValue raise ParseError {
match lexer.peek() {
Some('t') => {
lexer.expect('t')
lexer.expect('r')
lexer.expect('u')
lexer.expect('e')
Bool(true)
}
Some('f') => {
lexer.expect('f')
lexer.expect('a')
lexer.expect('l')
lexer.expect('s')
lexer.expect('e')
Bool(false)
}
Some(ch) => raise ParseError::UnexpectedChar(lexer.position(), ch)
None => raise ParseError::UnexpectedEof(lexer.position())
}
}
///|
/// Check if a character is a decimal digit
fn is_digit(ch : Char) -> Bool {
ch >= '0' && ch <= '9'
}
///|
/// Parse a number
fn parse_number(lexer : Lexer) -> JsonValue raise ParseError {
let pos = lexer.position()
let mut num_str = ""
// Optional minus sign
if lexer.peek() == Some('-') {
num_str += "-"
lexer.advance() |> ignore
}
// Integer part
match lexer.peek() {
Some('0') => {
num_str += "0"
lexer.advance() |> ignore
}
Some(ch) if is_digit(ch) =>
while true {
match lexer.peek() {
Some(ch) if is_digit(ch) => {
num_str += ch.to_string()
lexer.advance() |> ignore
}
_ => break
}
}
_ => raise ParseError::InvalidNumber(pos, num_str)
}
// Fractional part
if lexer.peek() == Some('.') {
num_str += "."
lexer.advance() |> ignore
let mut has_digit = false
while true {
match lexer.peek() {
Some(ch) if is_digit(ch) => {
num_str += ch.to_string()
lexer.advance() |> ignore
has_digit = true
}
_ => break
}
}
if not(has_digit) {
raise ParseError::InvalidNumber(pos, num_str)
}
}
// Exponent part
match lexer.peek() {
Some('e') | Some('E') => {
num_str += "e"
lexer.advance() |> ignore
match lexer.peek() {
Some('+') => lexer.advance() |> ignore
Some('-') => {
num_str += "-"
lexer.advance() |> ignore
}
_ => ()
}
let mut has_digit = false
while true {
match lexer.peek() {
Some(ch) if is_digit(ch) => {
num_str += ch.to_string()
lexer.advance() |> ignore
has_digit = true
}
_ => break
}
}
if not(has_digit) {
raise ParseError::InvalidNumber(pos, num_str)
}
}
_ => ()
}
try @strconv.parse_double(num_str) catch {
_ => raise ParseError::InvalidNumber(pos, num_str)
} noraise {
n => Number(n)
}
}
///|
/// Parse a string
fn parse_string(lexer : Lexer) -> String raise ParseError {
let pos = lexer.position()
lexer.expect('"')
let mut result = ""
while true {
match lexer.advance() {
Some('"') => break
Some('\\') =>
match lexer.advance() {
Some('"') => result += "\""
Some('\\') => result += "\\"
Some('/') => result += "/"
Some('b') => result += "\u{8}" // backspace
Some('f') => result += "\u{c}" // form feed
Some('n') => result += "\n"
Some('r') => result += "\r"
Some('t') => result += "\t"
Some('u') => {
// Parse unicode escape \uXXXX
let mut hex = ""
for i = 0; i < 4; i = i + 1 {
match lexer.advance() {
Some(ch) if is_hex_digit(ch) => hex += ch.to_string()
_ => raise ParseError::InvalidUnicodeEscape(pos)
}
}
match parse_hex(hex) {
Some(code) =>
match code.to_char() {
Some(ch) => result += ch.to_string()
None => raise ParseError::InvalidUnicodeEscape(pos)
}
None => raise ParseError::InvalidUnicodeEscape(pos)
}
}
_ => raise ParseError::InvalidEscape(pos)
}
Some(ch) if ch.to_int() < 0x20 =>
raise ParseError::UnexpectedChar(pos, ch)
Some(ch) => result += ch.to_string()
None => raise ParseError::UnexpectedEof(pos)
}
}
result
}
///|
/// Check if character is a hex digit
fn is_hex_digit(ch : Char) -> Bool {
(ch >= '0' && ch <= '9') ||
(ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F')
}
///|
/// Parse hexadecimal string to integer
fn parse_hex(s : String) -> Int? {
let mut result = 0
for i = 0; i < s.length(); i = i + 1 {
let ch = s[i]
let digit = if ch >= '0' && ch <= '9' {
ch - '0'
} else if ch >= 'a' && ch <= 'f' {
ch - 'a' + 10
} else if ch >= 'A' && ch <= 'F' {
ch - 'A' + 10
} else {
return None
}
result = result * 16 + digit
}
Some(result)
}
///|
/// Parse an array
fn parse_array(lexer : Lexer) -> JsonValue raise ParseError {
lexer.expect('[')
lexer.skip_whitespace()
let arr : Array[JsonValue] = []
// Check for empty array
if lexer.peek() == Some(']') {
lexer.advance() |> ignore
return Array(arr)
}
while true {
arr.push(parse_value(lexer))
lexer.skip_whitespace()
match lexer.advance() {
Some(',') => {
lexer.skip_whitespace()
continue
}
Some(']') => break
Some(ch) => raise ParseError::UnexpectedChar(lexer.position(), ch)
None => raise ParseError::UnexpectedEof(lexer.position())
}
}
Array(arr)
}
///|
/// Parse an object
fn parse_object(lexer : Lexer) -> JsonValue raise ParseError {
lexer.expect('{')
lexer.skip_whitespace()
let obj : Map[String, JsonValue] = {}
// Check for empty object
if lexer.peek() == Some('}') {
lexer.advance() |> ignore
return Object(obj)
}
while true {
// Parse key
let key = parse_string(lexer)
lexer.skip_whitespace()
// Expect colon
lexer.expect(':')
lexer.skip_whitespace()
// Parse value
let value = parse_value(lexer)
obj[key] = value
lexer.skip_whitespace()
match lexer.advance() {
Some(',') => {
lexer.skip_whitespace()
continue
}
Some('}') => break
Some(ch) => raise ParseError::UnexpectedChar(lexer.position(), ch)
None => raise ParseError::UnexpectedEof(lexer.position())
}
}
Object(obj)
}