///|
/// Parser state
priv struct Parser {
lexer : Lexer
mut current : Token
}
///|
/// Create new parser
fn Parser::new(input : String) -> Parser raise JsonError {
let lexer = Lexer::new(input)
let current = lexer.next_token()
{ lexer, current }
}
///|
/// Advance to next token
fn Parser::advance(self : Parser) -> Unit raise JsonError {
self.current = self.lexer.next_token()
}
///|
/// Parse a JSON value
fn Parser::parse_value(self : Parser) -> JsonValue raise JsonError {
match self.current {
TNull => {
self.advance()
Null
}
TTrue => {
self.advance()
Bool(true)
}
TFalse => {
self.advance()
Bool(false)
}
TNumber(n) => {
self.advance()
Number(n)
}
TString(s) => {
self.advance()
String(s)
}
TLeftBracket => self.parse_array()
TLeftBrace => self.parse_object()
_ => {
let pos = self.lexer.position()
let msg = "Expected value, got \{self.current}"
raise JsonError::UnexpectedToken(pos, msg)
}
}
}
///|
/// Parse array
fn Parser::parse_array(self : Parser) -> JsonValue raise JsonError {
self.advance() // consume [
let values : Array[JsonValue] = []
// Empty array
if self.current == TRightBracket {
self.advance()
return Array(values)
}
while true {
let value = self.parse_value()
values.push(value)
match self.current {
TComma => {
self.advance()
// Check for trailing comma
if self.current == TRightBracket {
let pos = self.lexer.position()
raise JsonError::TrailingComma(pos)
}
continue
}
TRightBracket => {
self.advance()
break
}
_ => {
let pos = self.lexer.position()
let msg = "Expected comma or ] in array"
raise JsonError::UnexpectedToken(pos, msg)
}
}
}
Array(values)
}
///|
/// Parse object
fn Parser::parse_object(self : Parser) -> JsonValue raise JsonError {
self.advance() // consume {
let obj : Map[String, JsonValue] = {}
// Empty object
if self.current == TRightBrace {
self.advance()
return Object(obj)
}
while true {
// Parse key (must be string)
let key = match self.current {
TString(s) => {
self.advance()
s
}
_ => {
let pos = self.lexer.position()
let msg = "Expected string key in object"
raise JsonError::UnexpectedToken(pos, msg)
}
}
// Expect colon
match self.current {
TColon => self.advance()
_ => {
let pos = self.lexer.position()
let msg = "Expected : after key"
raise JsonError::UnexpectedToken(pos, msg)
}
}
// Parse value
let value = self.parse_value()
// Check for duplicate key
if obj.contains(key) {
let pos = self.lexer.position()
let msg = "Duplicate key: \{key}"
raise JsonError::DuplicateKey(pos, msg)
}
obj[key] = value
match self.current {
TComma => {
self.advance()
// Check for trailing comma
if self.current == TRightBrace {
let pos = self.lexer.position()
raise JsonError::TrailingComma(pos)
}
continue
}
TRightBrace => {
self.advance()
break
}
_ => {
let pos = self.lexer.position()
let msg = "Expected comma or } in object"
raise JsonError::UnexpectedToken(pos, msg)
}
}
}
Object(obj)
}
///|
/// Main parse function
pub fn parse(input : String) -> JsonValue raise JsonError {
let parser = Parser::new(input)
let value = parser.parse_value()
// Ensure no trailing content
if parser.current != TEof {
let pos = parser.lexer.position()
let msg = "Unexpected content after JSON value"
raise JsonError::UnexpectedToken(pos, msg)
}
value
}