///|
/// JSON value representation
pub(all) enum JsonValue {
Object(Map[String, JsonValue])
Array(Array[JsonValue])
String(String)
Number(Double)
Boolean(Bool)
Null
} derive(Eq, Show)
///|
/// Token types for JSON lexer
enum Token {
LeftBrace // {
RightBrace // }
LeftBracket // [
RightBracket // ]
Colon // :
Comma // ,
String(String)
Number(Double)
True
False
Null
Eof
} derive(Eq, Show)
///|
/// Position in source for error reporting
struct Position {
line : Int
column : Int
} derive(Show, Eq)
///|
/// Parser errors
pub(all) suberror ParseError {
UnexpectedToken(Position, Token, String)
UnexpectedChar(Position, Char)
UnexpectedEof(Position)
InvalidNumber(Position, String)
InvalidEscape(Position, Char)
InvalidUnicode(Position, String)
} derive(Show, Eq)
///|
/// Lexer state
priv 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 current character without consuming
fn Lexer::peek(self : Lexer) -> Char? {
if self.pos < self.input.length() {
self.input.get_char(self.pos)
} else {
None
}
}
///|
/// Advance to next character
fn Lexer::advance(self : Lexer) -> Unit {
if self.pos < self.input.length() {
match self.input.get_char(self.pos) {
Some('\n') => {
self.line += 1
self.column = 1
}
Some(_) => self.column += 1
None => ()
}
self.pos += 1
}
}
///|
/// Skip whitespace
fn Lexer::skip_whitespace(self : Lexer) -> Unit {
while true {
match self.peek() {
Some(c) if c is (' ' | '\t' | '\n' | '\r') => self.advance()
_ => break
}
}
}
///|
/// Parse a string literal
fn Lexer::parse_string(self : Lexer) -> String raise ParseError {
let start_pos = self.position()
self.advance() // Skip opening quote
let buf = @buffer.new()
let mut done = false
while not(done) {
match self.peek() {
None => raise ParseError::UnexpectedEof(start_pos)
Some('"') => {
self.advance()
done = true
}
Some('\\') => {
self.advance()
match self.peek() {
None => raise ParseError::UnexpectedEof(self.position())
Some('\"') => {
buf.write_char('"')
self.advance()
}
Some('\\') => {
buf.write_char('\\')
self.advance()
}
Some('/') => {
buf.write_char('/')
self.advance()
}
Some('b') => {
buf.write_char('\b')
self.advance()
}
Some('f') => {
buf.write_char('\u000C')
self.advance()
}
Some('n') => {
buf.write_char('\n')
self.advance()
}
Some('r') => {
buf.write_char('\r')
self.advance()
}
Some('t') => {
buf.write_char('\t')
self.advance()
}
Some('u') => {
self.advance()
let hex_start = self.pos
let mut hex_value = 0
for i = 0; i < 4; i = i + 1 {
match self.peek() {
Some(c) if c is ('0'..='9') => {
hex_value = hex_value * 16 + (c.to_int() - '0'.to_int())
self.advance()
}
Some(c) if c is ('a'..='f') => {
hex_value = hex_value * 16 + (c.to_int() - 'a'.to_int() + 10)
self.advance()
}
Some(c) if c is ('A'..='F') => {
hex_value = hex_value * 16 + (c.to_int() - 'A'.to_int() + 10)
self.advance()
}
_ => {
let hex_str = try! self.input[hex_start:self.pos].to_string()
raise ParseError::InvalidUnicode(self.position(), hex_str)
}
}
}
buf.write_char(hex_value.unsafe_to_char())
}
Some(c) => raise ParseError::InvalidEscape(self.position(), c)
}
}
Some(c) => {
buf.write_char(c)
self.advance()
}
}
}
buf.to_string()
}
///|
/// Parse a number literal
fn Lexer::parse_number(self : Lexer) -> Double raise ParseError {
let start_pos = self.position()
let start = self.pos
// Optional minus
if self.peek() is Some('-') {
self.advance()
}
// Integer part
match self.peek() {
Some('0') => self.advance()
Some(c) if c is ('1'..='9') => {
self.advance()
while true {
match self.peek() {
Some(c) if c is ('0'..='9') => self.advance()
_ => break
}
}
}
_ => raise ParseError::InvalidNumber(start_pos, "Expected digit")
}
// Fractional part
if self.peek() is Some('.') {
self.advance()
let mut has_digit = false
while true {
match self.peek() {
Some(c) if c is ('0'..='9') => {
has_digit = true
self.advance()
}
_ => break
}
}
if not(has_digit) {
raise ParseError::InvalidNumber(
start_pos, "Expected digit after decimal point",
)
}
}
// Exponent part
if self.peek() is Some('e' | 'E') {
self.advance()
if self.peek() is Some('+' | '-') {
self.advance()
}
let mut has_digit = false
while true {
match self.peek() {
Some(c) if c is ('0'..='9') => {
has_digit = true
self.advance()
}
_ => break
}
}
if not(has_digit) {
raise ParseError::InvalidNumber(start_pos, "Expected digit in exponent")
}
}
let num_str = try! self.input[start:self.pos].to_string()
@strconv.parse_double(num_str) catch {
_ => raise ParseError::InvalidNumber(start_pos, num_str)
}
}
///|
/// Get next token
fn Lexer::next_token(self : Lexer) -> Token raise ParseError {
self.skip_whitespace()
match self.peek() {
None => Token::Eof
Some('{') => {
self.advance()
Token::LeftBrace
}
Some('}') => {
self.advance()
Token::RightBrace
}
Some('[') => {
self.advance()
Token::LeftBracket
}
Some(']') => {
self.advance()
Token::RightBracket
}
Some(':') => {
self.advance()
Token::Colon
}
Some(',') => {
self.advance()
Token::Comma
}
Some('"') => Token::String(self.parse_string())
Some(c) if c is ('-' | '0'..='9') => Token::Number(self.parse_number())
Some('t') => {
let pos = self.position()
self.advance()
if self.peek() is Some('r') {
self.advance()
if self.peek() is Some('u') {
self.advance()
if self.peek() is Some('e') {
self.advance()
Token::True
} else {
raise ParseError::UnexpectedChar(pos, 'e')
}
} else {
raise ParseError::UnexpectedChar(pos, 'u')
}
} else {
raise ParseError::UnexpectedChar(pos, 'r')
}
}
Some('f') => {
let pos = self.position()
self.advance()
if self.peek() is Some('a') {
self.advance()
if self.peek() is Some('l') {
self.advance()
if self.peek() is Some('s') {
self.advance()
if self.peek() is Some('e') {
self.advance()
Token::False
} else {
raise ParseError::UnexpectedChar(pos, 'e')
}
} else {
raise ParseError::UnexpectedChar(pos, 's')
}
} else {
raise ParseError::UnexpectedChar(pos, 'l')
}
} else {
raise ParseError::UnexpectedChar(pos, 'a')
}
}
Some('n') => {
let pos = self.position()
self.advance()
if self.peek() is Some('u') {
self.advance()
if self.peek() is Some('l') {
self.advance()
if self.peek() is Some('l') {
self.advance()
Token::Null
} else {
raise ParseError::UnexpectedChar(pos, 'l')
}
} else {
raise ParseError::UnexpectedChar(pos, 'l')
}
} else {
raise ParseError::UnexpectedChar(pos, 'u')
}
}
Some(c) => raise ParseError::UnexpectedChar(self.position(), c)
}
}
///|
/// Parser state
priv struct Parser {
lexer : Lexer
mut current : Token
}
///|
/// Create a new parser
fn Parser::new(input : String) -> Parser raise ParseError {
let lexer = Lexer::new(input)
let current = lexer.next_token()
{ lexer, current }
}
///|
/// Advance to next token
fn Parser::advance(self : Parser) -> Unit raise ParseError {
self.current = self.lexer.next_token()
}
///|
/// Expect and consume a specific token
fn Parser::expect(self : Parser, expected : Token) -> Unit raise ParseError {
if self.current == expected {
self.advance()
} else {
raise ParseError::UnexpectedToken(
self.lexer.position(),
self.current,
"Expected \{expected}",
)
}
}
///|
/// Parse a JSON value
fn Parser::parse_value(self : Parser) -> JsonValue raise ParseError {
match self.current {
Token::LeftBrace => self.parse_object()
Token::LeftBracket => self.parse_array()
Token::String(s) => {
self.advance()
JsonValue::String(s)
}
Token::Number(n) => {
self.advance()
JsonValue::Number(n)
}
Token::True => {
self.advance()
JsonValue::Boolean(true)
}
Token::False => {
self.advance()
JsonValue::Boolean(false)
}
Token::Null => {
self.advance()
JsonValue::Null
}
tok =>
raise ParseError::UnexpectedToken(
self.lexer.position(),
tok,
"Expected JSON value",
)
}
}
///|
/// Parse a JSON object
fn Parser::parse_object(self : Parser) -> JsonValue raise ParseError {
self.expect(Token::LeftBrace)
let obj : Map[String, JsonValue] = {}
if self.current != Token::RightBrace {
while true {
// Parse key
let key = match self.current {
Token::String(s) => {
self.advance()
s
}
tok =>
raise ParseError::UnexpectedToken(
self.lexer.position(),
tok,
"Expected string key",
)
}
// Expect colon
self.expect(Token::Colon)
// Parse value
let value = self.parse_value()
obj[key] = value
// Check for continuation
if self.current == Token::Comma {
self.advance()
} else {
break
}
}
}
self.expect(Token::RightBrace)
JsonValue::Object(obj)
}
///|
/// Parse a JSON array
fn Parser::parse_array(self : Parser) -> JsonValue raise ParseError {
self.expect(Token::LeftBracket)
let arr : Array[JsonValue] = []
if self.current != Token::RightBracket {
while true {
arr.push(self.parse_value())
if self.current == Token::Comma {
self.advance()
} else {
break
}
}
}
self.expect(Token::RightBracket)
JsonValue::Array(arr)
}
///|
/// Parse JSON string into a JsonValue
pub fn parse(input : String) -> JsonValue raise ParseError {
let parser = Parser::new(input)
let value = parser.parse_value()
if parser.current != Token::Eof {
raise ParseError::UnexpectedToken(
parser.lexer.position(),
parser.current,
"Expected end of input",
)
}
value
}
///|
/// Stringify a JsonValue with optional indentation
pub fn stringify(value : JsonValue, indent? : Int = 0) -> String {
let buf = @buffer.new()
stringify_impl(value, buf, 0, indent)
buf.to_string()
}
///|
/// Internal stringify implementation
fn stringify_impl(
value : JsonValue,
buf : @buffer.Buffer,
depth : Int,
indent : Int,
) -> Unit {
match value {
JsonValue::Null => buf.write_string("null")
JsonValue::Boolean(true) => buf.write_string("true")
JsonValue::Boolean(false) => buf.write_string("false")
JsonValue::Number(n) =>
if n.is_inf() || n.is_nan() {
buf.write_string("null")
} else {
buf.write_string(n.to_string())
}
JsonValue::String(s) => {
buf.write_char('"')
for char in s {
match char {
'"' => buf.write_string("\\\"")
'\\' => buf.write_string("\\\\")
'\b' => buf.write_string("\\b")
'\u000C' => buf.write_string("\\f")
'\n' => buf.write_string("\\n")
'\r' => buf.write_string("\\r")
'\t' => buf.write_string("\\t")
c => buf.write_char(c)
}
}
buf.write_char('"')
}
JsonValue::Array(arr) => {
buf.write_char('[')
if arr.length() > 0 {
if indent > 0 {
buf.write_char('\n')
}
for i, item in arr {
if indent > 0 {
let mut j = 0
while j < (depth + 1) * indent {
buf.write_char(' ')
j = j + 1
}
}
stringify_impl(item, buf, depth + 1, indent)
if i < arr.length() - 1 {
buf.write_char(',')
}
if indent > 0 {
buf.write_char('\n')
}
}
if indent > 0 {
let mut j = 0
while j < depth * indent {
buf.write_char(' ')
j = j + 1
}
}
}
buf.write_char(']')
}
JsonValue::Object(obj) => {
buf.write_char('{')
let keys = obj.keys().collect()
if keys.length() > 0 {
if indent > 0 {
buf.write_char('\n')
}
for i, key in keys {
if indent > 0 {
let mut j = 0
while j < (depth + 1) * indent {
buf.write_char(' ')
j = j + 1
}
}
buf.write_char('"')
buf.write_string(key)
buf.write_char('"')
buf.write_char(':')
if indent > 0 {
buf.write_char(' ')
}
match obj.get(key) {
Some(v) => stringify_impl(v, buf, depth + 1, indent)
None => ()
}
if i < keys.length() - 1 {
buf.write_char(',')
}
if indent > 0 {
buf.write_char('\n')
}
}
if indent > 0 {
let mut j = 0
while j < depth * indent {
buf.write_char(' ')
j = j + 1
}
}
}
buf.write_char('}')
}
}
}
///|
/// Convert JsonValue to pretty-printed string
pub fn JsonValue::to_pretty_string(self : JsonValue) -> String {
stringify(self, indent=2)
}