Created
November 24, 2020 08:38
Incomplete JSON parser in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let JSON_COMMA = "," | |
let JSON_COLON = ":" | |
let JSON_LEFTBRACKET = "[" | |
let JSON_RIGHTBRACKET = "]" | |
let JSON_LEFTBRACE = "{" | |
let JSON_RIGHTBRACE = "}" | |
let JSON_QUOTE = "\"" | |
let JSON_WHITESPACES: [String] = [" ", "\t", "\n", "\r"] | |
let JSON_SYNTAX: [String] = [JSON_COMMA, JSON_COLON, | |
JSON_LEFTBRACE, JSON_RIGHTBRACE, | |
JSON_LEFTBRACKET, JSON_RIGHTBRACKET] | |
func lex(_ string: String) -> [String] { | |
var tokens: [String] = [] | |
var buffer: String = string | |
while buffer.count > 0 { | |
let stringResult = lexString(buffer) | |
buffer = stringResult.1 | |
if let jsonString = stringResult.0 { | |
tokens.append(jsonString) | |
continue | |
} | |
// TODO: lex booleans, nulls, numbers | |
let firstChar = String(buffer.prefix(1)) | |
if JSON_WHITESPACES.contains(firstChar) { // ignore whitespaces | |
let index = buffer.index(buffer.startIndex, offsetBy: 1) | |
buffer = String(buffer.suffix(from: index)) | |
} else if JSON_SYNTAX.contains(firstChar) { | |
tokens.append(firstChar) | |
let index = buffer.index(buffer.startIndex, offsetBy: 1) | |
buffer = String(buffer.suffix(from: index)) | |
} else { | |
fatalError("Unexpected character: \(firstChar)") | |
} | |
} | |
return tokens | |
} | |
func lexString(_ string: String) -> (String?, String) { | |
var jsonString: String = "" | |
var mutableString = string | |
let firstByte = String(mutableString.prefix(1)) | |
if firstByte == JSON_QUOTE { | |
let index = mutableString.index(mutableString.startIndex, offsetBy: 1) | |
mutableString = String(mutableString.suffix(from: index)) | |
} else { | |
return (nil, mutableString) | |
} | |
for c in mutableString { | |
if String(c) == JSON_QUOTE { | |
let index = mutableString.index(mutableString.startIndex, offsetBy: jsonString.count + 1) | |
return (jsonString, String(mutableString.suffix(from: index))) | |
} else { | |
jsonString += String(c) | |
} | |
} | |
fatalError("Expected end-of-string quote") | |
} | |
let jsonExample = #"{"foo":"bar", "baz":"qux"}"# | |
print(jsonExample) | |
let lexResult = lex(jsonExample) | |
print(lexResult) | |
//====================================================================================================================== | |
func parse(tokens: [String], isRoot: Bool = false) -> (Any, [String]) { | |
let t = tokens[0] | |
if isRoot, t != JSON_LEFTBRACE { | |
fatalError("Root must be an object") | |
} | |
// if t == JSON_LEFTBRACKET { | |
// return parseArray(Array<String>(tokens[1...])) | |
// } else | |
if t == JSON_LEFTBRACE { | |
return parseObject(Array<String>(tokens[1...])) | |
// else if t == JSON_LEFTBRACKET { | |
// return parseArray(Array<String>(tokens[1...])) | |
} else { | |
return (t, Array<String>(tokens[1...])) | |
} | |
} | |
func parseObject(_ tokens: [String]) -> (Any, [String]) { | |
var jsonObject: [String: Any] = [:] | |
var mutableTokens = tokens | |
var t = mutableTokens[0] | |
if t == JSON_RIGHTBRACE { | |
return (jsonObject, Array<String>(mutableTokens[1...])) | |
} | |
while true { | |
let jsonKey = mutableTokens[0] | |
if let jsonKey = jsonKey as? String { | |
mutableTokens = Array<String>(mutableTokens[1...]) | |
} else { | |
fatalError("Expected string key, got: \(mutableTokens[0])") | |
} | |
if mutableTokens[0] != JSON_COLON { | |
fatalError("Expected colon afater key in object, got \(mutableTokens[0])") | |
} | |
let tuple = parse(tokens: Array<String>(mutableTokens[1...])) | |
let jsonValue = tuple.0 | |
mutableTokens = tuple.1 | |
jsonObject[jsonKey] = jsonValue | |
t = mutableTokens[0] | |
if t == JSON_RIGHTBRACE { | |
return (jsonObject, Array<String>(mutableTokens[1...])) | |
} else if t != JSON_COMMA { | |
fatalError("Expected comma afater pair in object, got \(t)") | |
} | |
mutableTokens = Array<String>(mutableTokens[1...]) | |
} | |
fatalError("Expected end-of-object bracket") | |
} | |
let parseResult = parse(tokens: lexResult) | |
print(parseResult.0) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment