Skip to content

Instantly share code, notes, and snippets.

@Arthraim
Created November 24, 2020 08:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Arthraim/7a0b2f73978cd25513f5d3f89a8766a9 to your computer and use it in GitHub Desktop.
Save Arthraim/7a0b2f73978cd25513f5d3f89a8766a9 to your computer and use it in GitHub Desktop.
Incomplete JSON parser in Swift
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