///|
/// Parse one complete strict JSON document. Object keys are checked while they
/// are still textual so duplicate-key diagnostics do not depend on a map's
/// overwrite behaviour.
pub fn parse(input : String) -> Result[JsonValue, ParseError] {
let parser = Parser::new(input)
parser.skip_whitespace()
match parser.value() {
Err(error) => Err(error)
Ok(value) => {
parser.skip_whitespace()
if parser.position == parser.chars.length() {
Ok(value)
} else {
Err(TrailingContent(parser.position))
}
}
}
}
///|
fn Parser::value(self : Parser) -> Result[JsonValue, ParseError] {
match self.peek() {
None => Err(UnexpectedEnd(self.position))
Some('n') => self.literal("null", Null)
Some('t') => self.literal("true", Bool(true))
Some('f') => self.literal("false", Bool(false))
Some('"') => self.string().map(value => String(value))
Some('[') => self.array()
Some('{') => self.object()
Some('-')
| Some('0')
| Some('1')
| Some('2')
| Some('3')
| Some('4')
| Some('5')
| Some('6')
| Some('7')
| Some('8')
| Some('9') => self.number().map(value => Number(value))
Some(char) => Err(UnexpectedCharacter(self.position, char))
}
}
///|
fn Parser::array(self : Parser) -> Result[JsonValue, ParseError] {
self.position = self.position + 1
self.skip_whitespace()
let values : Array[JsonValue] = []
if self.take_if(']') {
return Ok(Array(values))
}
match self.value() {
Err(error) => return Err(error)
Ok(value) => values.push(value)
}
self.skip_whitespace()
if self.take_if(']') {
return Ok(Array(values))
}
if !self.take_if(',') {
return Err(Expected(self.position, "',' or ']"))
}
self.skip_whitespace()
self.array_more(values)
}
///|
fn Parser::array_more(
self : Parser,
values : Array[JsonValue],
) -> Result[JsonValue, ParseError] {
if self.peek() is Some(']') {
return Err(Expected(self.position, "a value after ','"))
}
match self.value() {
Err(error) => return Err(error)
Ok(value) => values.push(value)
}
self.skip_whitespace()
if self.take_if(']') {
Ok(Array(values))
} else if self.take_if(',') {
self.skip_whitespace()
self.array_more(values)
} else {
Err(Expected(self.position, "',' or ']"))
}
}
///|
fn Parser::object(self : Parser) -> Result[JsonValue, ParseError] {
self.position = self.position + 1
self.skip_whitespace()
let entries : Array[(String, JsonValue)] = []
if self.take_if('}') {
return Ok(Object(entries))
}
self.object_more(entries)
}
///|
fn Parser::object_more(
self : Parser,
entries : Array[(String, JsonValue)],
) -> Result[JsonValue, ParseError] {
let key_position = self.position
let key = match self.string() {
Ok(value) => value
Err(_) => return Err(Expected(self.position, "an object key string"))
}
for entry in entries {
let (existing, _) = entry
if existing == key {
return Err(DuplicateKey(key_position, key))
}
}
self.skip_whitespace()
if !self.take_if(':') {
return Err(Expected(self.position, "':'"))
}
self.skip_whitespace()
let value = match self.value() {
Ok(value) => value
Err(error) => return Err(error)
}
entries.push((key, value))
self.skip_whitespace()
if self.take_if('}') {
Ok(Object(entries))
} else if self.take_if(',') {
self.skip_whitespace()
if self.peek() is Some('}') {
Err(Expected(self.position, "an object key after ','"))
} else {
self.object_more(entries)
}
} else {
Err(Expected(self.position, "',' or '}'"))
}
}
///|
fn Parser::literal(
self : Parser,
text : String,
value : JsonValue,
) -> Result[JsonValue, ParseError] {
for char in text {
if self.peek() != Some(char) {
return Err(Expected(self.position, text))
}
self.position = self.position + 1
}
Ok(value)
}
///|
fn Parser::number(self : Parser) -> Result[String, ParseError] {
let start = self.position
let mut text = ""
if self.take_if('-') {
text = "-"
}
match self.peek() {
Some('0') => {
text = text + "0"
self.position = self.position + 1
if self.peek().map(char => char >= '0' && char <= '9').unwrap_or(false) {
return Err(InvalidNumber(start, "leading zero"))
}
}
Some(char) if char >= '1' && char <= '9' =>
while self.peek().map(is_digit).unwrap_or(false) {
match self.next() {
Some(digit) => text = text + digit.to_string()
None => ()
}
}
_ => return Err(InvalidNumber(start, "integer part required"))
}
if self.take_if('.') {
text = text + "."
let fraction_start = self.position
while self.peek().map(is_digit).unwrap_or(false) {
match self.next() {
Some(digit) => text = text + digit.to_string()
None => ()
}
}
if self.position == fraction_start {
return Err(InvalidNumber(start, "fraction digits required"))
}
}
if self.take_if('e') || self.take_if('E') {
text = text + "e"
if self.take_if('+') {
text = text + "+"
} else if self.take_if('-') {
text = text + "-"
}
let exponent_start = self.position
while self.peek().map(is_digit).unwrap_or(false) {
match self.next() {
Some(digit) => text = text + digit.to_string()
None => ()
}
}
if self.position == exponent_start {
return Err(InvalidNumber(start, "exponent digits required"))
}
}
Ok(text)
}
///|
fn Parser::string(self : Parser) -> Result[String, ParseError] {
if !self.take_if('"') {
return Err(Expected(self.position, "a string"))
}
let mut output = ""
while true {
match self.next() {
None => return Err(UnexpectedEnd(self.position))
Some('"') => return Ok(output)
Some('\\') =>
match self.escape() {
Ok(char) => output = output + char.to_string()
Err(error) => return Err(error)
}
Some(char) if char < ' ' =>
return Err(InvalidEscape(self.position - 1, "control character"))
Some(char) => output = output + char.to_string()
}
} nobreak {
Err(UnexpectedEnd(self.position))
}
}
///|
fn Parser::escape(self : Parser) -> Result[Char, ParseError] {
let position = self.position
match self.next() {
Some('"') => Ok('"')
Some('\\') => Ok('\\')
Some('/') => Ok('/')
Some('b') => Ok('\b')
Some('f') => Ok('\f')
Some('n') => Ok('\n')
Some('r') => Ok('\r')
Some('t') => Ok('\t')
Some('u') => self.unicode_escape(position)
Some(char) =>
Err(InvalidEscape(position, "unknown escape '" + char.to_string() + "'"))
None => Err(UnexpectedEnd(position))
}
}
///|
fn Parser::unicode_escape(
self : Parser,
position : Int,
) -> Result[Char, ParseError] {
let first = match self.hex_code_unit(position) {
Ok(value) => value
Err(error) => return Err(error)
}
if first >= 0xd800 && first <= 0xdbff {
if !self.take_if('\\') || !self.take_if('u') {
return Err(
InvalidEscape(position, "high surrogate without low surrogate"),
)
}
let second = match self.hex_code_unit(position) {
Ok(value) => value
Err(error) => return Err(error)
}
if second < 0xdc00 || second > 0xdfff {
return Err(
InvalidEscape(position, "high surrogate without low surrogate"),
)
}
Ok((0x10000 + (first - 0xd800) * 0x400 + second - 0xdc00).unsafe_to_char())
} else if first >= 0xdc00 && first <= 0xdfff {
Err(InvalidEscape(position, "lone low surrogate"))
} else {
Ok(first.unsafe_to_char())
}
}
///|
fn Parser::hex_code_unit(
self : Parser,
position : Int,
) -> Result[Int, ParseError] {
let mut value = 0
let mut count = 0
while count < 4 {
match self.next() {
Some(char) if char >= '0' && char <= '9' =>
value = value * 16 + char.to_int() - '0'.to_int()
Some(char) if char >= 'a' && char <= 'f' =>
value = value * 16 + char.to_int() - 'a'.to_int() + 10
Some(char) if char >= 'A' && char <= 'F' =>
value = value * 16 + char.to_int() - 'A'.to_int() + 10
_ =>
return Err(InvalidEscape(position, "four hexadecimal digits required"))
}
count = count + 1
}
Ok(value)
}
///|
fn Parser::peek(self : Parser) -> Char? {
if self.position < self.chars.length() {
Some(self.chars[self.position])
} else {
None
}
}
///|
fn Parser::next(self : Parser) -> Char? {
match self.peek() {
None => None
Some(char) => {
self.position = self.position + 1
Some(char)
}
}
}
///|
fn Parser::take_if(self : Parser, expected : Char) -> Bool {
if self.peek() == Some(expected) {
self.position = self.position + 1
true
} else {
false
}
}
///|
fn Parser::skip_whitespace(self : Parser) -> Unit {
while self.peek().map(is_json_whitespace).unwrap_or(false) {
self.position = self.position + 1
}
}
///|
fn is_digit(char : Char) -> Bool {
char >= '0' && char <= '9'
}
///|
fn is_json_whitespace(char : Char) -> Bool {
char == ' ' || char == '\n' || char == '\r' || char == '\t'
}