From 5559ad35e5d1f3ff7e9ef3e8636fac2554eb2cc6 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 17 Mar 2016 11:17:45 +0000 Subject: [PATCH] added keeping source information during parsing, see #12 --- Sources/Jay/ArrayParser.swift | 13 ++-- Sources/Jay/BooleanParser.swift | 14 ++-- Sources/Jay/ByteReader.swift | 11 ++- Sources/Jay/Extensions.swift | 24 +++++++ Sources/Jay/Jay.swift | 12 +++- Sources/Jay/NativeParser.swift | 4 +- Sources/Jay/NativeTypeConversions.swift | 34 ++++++++++ Sources/Jay/NullParser.swift | 6 +- Sources/Jay/NumberParser.swift | 10 ++- Sources/Jay/ObjectParser.swift | 19 +++--- Sources/Jay/Parser.swift | 4 +- Sources/Jay/Reader.swift | 11 +-- Sources/Jay/RootParser.swift | 4 +- Sources/Jay/StringParser.swift | 11 +-- Sources/Jay/Types.swift | 24 +++++++ Sources/Jay/ValueParser.swift | 4 +- Tests/Jay/ParsingTests.swift | 90 ++++++++++++++++--------- Tests/Jay/ReaderTests.swift | 41 +++++------ Tests/Jay/TestUtils.swift | 30 ++++++--- 19 files changed, 252 insertions(+), 114 deletions(-) diff --git a/Sources/Jay/ArrayParser.swift b/Sources/Jay/ArrayParser.swift index 2755bed..72bd79e 100644 --- a/Sources/Jay/ArrayParser.swift +++ b/Sources/Jay/ArrayParser.swift @@ -8,9 +8,10 @@ struct ArrayParser: JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { var reader = try self.prepareForReading(withReader: r) + let start = reader.currIndex() //detect opening bracket guard reader.curr() == Const.BeginArray else { @@ -25,11 +26,12 @@ struct ArrayParser: JsonParser { if reader.curr() == Const.EndArray { //empty array reader.next() - return (JsonValue.Array([]), reader) + let range = reader.rangeFrom(start) + return (ParsedJsonToken(.Array([]), range), reader) } //now start scanning for values - var values = [JsonValue]() + var values = [ParsedJsonToken]() repeat { //scan for value @@ -41,7 +43,10 @@ struct ArrayParser: JsonParser { //value OR for a closing bracket reader = try self.prepareForReading(withReader: reader) switch reader.curr() { - case Const.EndArray: reader.next(); return (JsonValue.Array(values), reader) + case Const.EndArray: + reader.next() + let range = reader.rangeFrom(start) + return (ParsedJsonToken(.Array(values), range), reader) case Const.ValueSeparator: reader.next(); break //comma, so another value must come. let the loop repeat. default: throw Error.UnexpectedCharacter(reader) } diff --git a/Sources/Jay/BooleanParser.swift b/Sources/Jay/BooleanParser.swift index 9630951..79b4943 100644 --- a/Sources/Jay/BooleanParser.swift +++ b/Sources/Jay/BooleanParser.swift @@ -8,20 +8,24 @@ struct BooleanParser: JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { - func parseTrue(rr: Reader) throws -> (JsonValue, Reader) { + func parseTrue(rr: Reader) throws -> (ParsedJsonToken, Reader) { var rd = rr + let start = rd.currIndex() //try to read the "true" literal, throw if anything goes wrong try rd.stopAtFirstDifference(ByteReader(content: Const.True)) - return (JsonValue.Boolean(JsonBoolean.True), rd) + let range = rd.rangeFrom(start) + return (ParsedJsonToken(.Boolean(JsonBoolean.True), range), rd) } - func parseFalse(rr: Reader) throws -> (JsonValue, Reader) { + func parseFalse(rr: Reader) throws -> (ParsedJsonToken, Reader) { var rd = rr + let start = rd.currIndex() //try to read the "false" literal, throw if anything goes wrong try rd.stopAtFirstDifference(ByteReader(content: Const.False)) - return (JsonValue.Boolean(JsonBoolean.False), rd) + let range = rd.rangeFrom(start) + return (ParsedJsonToken(.Boolean(JsonBoolean.False), range), rd) } let reader = try self.prepareForReading(withReader: r) diff --git a/Sources/Jay/ByteReader.swift b/Sources/Jay/ByteReader.swift index fa526d1..29b1497 100644 --- a/Sources/Jay/ByteReader.swift +++ b/Sources/Jay/ByteReader.swift @@ -21,18 +21,15 @@ struct ByteReader: Reader { self.cursor = self.cursor.successor() } - func peek(next: Int) -> [JChar] { - - let take = min(next, self.content.endIndex - self.cursor) - let range = self.cursor.. JChar { precondition(!self.isDone()) return self.content[self.cursor] } + func currIndex() -> Int { + return self.cursor + } + func isDone() -> Bool { return self.cursor == self.content.endIndex } diff --git a/Sources/Jay/Extensions.swift b/Sources/Jay/Extensions.swift index 8ea40dc..9399b74 100644 --- a/Sources/Jay/Extensions.swift +++ b/Sources/Jay/Extensions.swift @@ -95,3 +95,27 @@ public func ==(lhs: JsonValue, rhs: JsonValue) -> Bool { default: return false } } + +extension ParsedJsonToken: Equatable { } +public func ==(lhs: ParsedJsonToken, rhs: ParsedJsonToken) -> Bool { + return lhs.value == rhs.value && lhs.range == rhs.range +} + +extension ParsedJsonValue: Equatable { } +public func ==(lhs: ParsedJsonValue, rhs: ParsedJsonValue) -> Bool { + switch (lhs, rhs) { + case (.Null, .Null): return true + case (.Boolean(let l), .Boolean(let r)): return l == r + case (.String(let l), .String(let r)): return l == r + case (.Array(let l), .Array(let r)): return l == r + case (.Object(let l), .Object(let r)): return l == r + case (.Number(let l), .Number(let r)): + switch (l, r) { + case (.JsonInt(let ll), .JsonInt(let rr)): return ll == rr + case (.JsonDbl(let ll), .JsonDbl(let rr)): return ll == rr + default: return false + } + default: return false + } +} + diff --git a/Sources/Jay/Jay.swift b/Sources/Jay/Jay.swift index 4d2e00d..bfe070d 100644 --- a/Sources/Jay/Jay.swift +++ b/Sources/Jay/Jay.swift @@ -53,7 +53,7 @@ extension Jay { //manually unwrapping each value recursively. If you just want //Swift types with less type-information, use `jsonFromData()` above. public func typesafeJsonFromData(data: [UInt8]) throws -> JsonValue { - return try Parser().parseJsonFromData(data) + return try Parser().parseJsonFromData(data).stripAnnotations() } //Formats your JSON-compatible object into data or throws an error. @@ -62,5 +62,15 @@ extension Jay { } } +//Typesafe with location annotations +extension Jay { + + //Returns JSON representation in a typesafe manner, also includes + //source location information (range in which each token was found). + public func annotatedTypesafeJsonFromData(data: [UInt8]) throws -> ParsedJsonToken { + return try Parser().parseJsonFromData(data) + } +} + diff --git a/Sources/Jay/NativeParser.swift b/Sources/Jay/NativeParser.swift index 4af754c..b7cd8ad 100644 --- a/Sources/Jay/NativeParser.swift +++ b/Sources/Jay/NativeParser.swift @@ -13,9 +13,9 @@ struct NativeParser { func parse(data: [UInt8]) throws -> Any { let jsonValue = try Parser().parseJsonFromData(data) - + //recursively convert into native types - let native = jsonValue.toNative() + let native = jsonValue.stripAnnotations().toNative() return native } } diff --git a/Sources/Jay/NativeTypeConversions.swift b/Sources/Jay/NativeTypeConversions.swift index a2668a8..d45e78a 100644 --- a/Sources/Jay/NativeTypeConversions.swift +++ b/Sources/Jay/NativeTypeConversions.swift @@ -19,6 +19,40 @@ extension JaySON { public init(_ array: [Any]) { self.json = array } } +extension ParsedJsonToken { + + func stripAnnotations() -> JsonValue { + return self.value.stripAnnotations() + } +} + +extension ParsedJsonValue { + func stripAnnotations() -> JsonValue { + switch self { + + case .Object(let obj): + var out: [Swift.String: JsonValue] = [:] + for i in obj { out[i.0] = i.1.stripAnnotations() } + return .Object(out) + + case .Array(let arr): + return .Array(arr.map({ $0.stripAnnotations() })) + + case .Boolean(let bool): + return .Boolean(bool) + + case .Null: + return .Null + + case .Number(let num): + return .Number(num) + + case .String(let str): + return .String(str) + } + } +} + extension JsonValue { func toNative() -> Any { diff --git a/Sources/Jay/NullParser.swift b/Sources/Jay/NullParser.swift index a0d79e6..d38f55d 100644 --- a/Sources/Jay/NullParser.swift +++ b/Sources/Jay/NullParser.swift @@ -8,12 +8,14 @@ struct NullParser: JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { var reader = try self.prepareForReading(withReader: r) + let start = reader.currIndex() //try to read the "null" literal, throw if anything goes wrong try reader.stopAtFirstDifference(ByteReader(content: Const.Null)) - return (JsonValue.Null, reader) + let range = reader.rangeFrom(start) + return (ParsedJsonToken(.Null, range), reader) } } diff --git a/Sources/Jay/NumberParser.swift b/Sources/Jay/NumberParser.swift index c09bd9e..190cfc0 100644 --- a/Sources/Jay/NumberParser.swift +++ b/Sources/Jay/NumberParser.swift @@ -20,9 +20,10 @@ struct NumberParser: JsonParser { //frac = decimal-point 1*DIGIT //int = zero / ( digit1-9 *DIGIT ) - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { var reader = try self.prepareForReading(withReader: r) + let start = reader.currIndex() //1. Optional minus let negative: Bool @@ -44,7 +45,8 @@ struct NumberParser: JsonParser { //now if any number terminator is here, finish up with 0 if Const.NumberTerminators.contains(reader.curr()) { - return (JsonValue.Number(JsonNumber.JsonInt(0)), reader) + let range = reader.rangeFrom(start) + return (ParsedJsonToken(.Number(JsonNumber.JsonInt(0)), range), reader) } //else there MUST be a frac part @@ -66,7 +68,9 @@ struct NumberParser: JsonParser { //Generate the final number let number = self.generateNumber(negative: negative, integer: integer, frac: frac, exp: exp) - let value = JsonValue.Number(number) + let range = reader.rangeFrom(start) + + let value = ParsedJsonToken(.Number(number), range) return (value, reader) } diff --git a/Sources/Jay/ObjectParser.swift b/Sources/Jay/ObjectParser.swift index f7a99b1..ab1895b 100644 --- a/Sources/Jay/ObjectParser.swift +++ b/Sources/Jay/ObjectParser.swift @@ -8,7 +8,7 @@ struct ObjectParser: JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { var reader = try self.prepareForReading(withReader: r) @@ -16,6 +16,7 @@ struct ObjectParser: JsonParser { guard reader.curr() == Const.BeginObject else { throw Error.UnexpectedCharacter(reader) } + let start = reader.currIndex() try reader.nextAndCheckNotDone() //move along, now start looking for name/value pairs @@ -25,20 +26,21 @@ struct ObjectParser: JsonParser { if reader.curr() == Const.EndObject { //empty object reader.next() - return (JsonValue.Object([:]), reader) + let range = reader.rangeFrom(start) + return (ParsedJsonToken(.Object([:]), range), reader) } //now start scanning for name/value pairs - var pairs = [(JsonString, JsonValue)]() + var pairs = [(JsonString, ParsedJsonToken)]() repeat { //scan for name let nameRet = try StringParser().parse(withReader: reader) reader = nameRet.1 let name: JsonString - switch nameRet.0 { + switch nameRet.0.value { case .String(let n): name = n; break - default: fatalError("Logic error: Should have returned a dictionary") + default: fatalError("Logic error: Dictionary key must be a string") } //scan for name separator : @@ -63,7 +65,8 @@ struct ObjectParser: JsonParser { case Const.EndObject: reader.next() let exported = self.exportArray(pairs) - return (JsonValue.Object(exported), reader) + let range = reader.rangeFrom(start) + return (ParsedJsonToken(.Object(exported), range), reader) case Const.ValueSeparator: //comma, so another value must come. let the loop repeat. reader.next() @@ -73,9 +76,9 @@ struct ObjectParser: JsonParser { } while true } - func exportArray(pairs: [(JsonString, JsonValue)]) -> [JsonString: JsonValue] { + func exportArray(pairs: [(JsonString, ParsedJsonToken)]) -> [JsonString: ParsedJsonToken] { - var object = [JsonString: JsonValue]() + var object = [JsonString: ParsedJsonToken]() for i in pairs { object[i.0] = i.1 } diff --git a/Sources/Jay/Parser.swift b/Sources/Jay/Parser.swift index bff35fb..baaab00 100644 --- a/Sources/Jay/Parser.swift +++ b/Sources/Jay/Parser.swift @@ -9,7 +9,7 @@ struct Parser { //assuming data [Int8] - func parseJsonFromData(data: [JChar]) throws -> JsonValue { + func parseJsonFromData(data: [JChar]) throws -> ParsedJsonToken { //create a reader for this data let reader = ByteReader(content: data) @@ -30,7 +30,7 @@ struct Parser { } protocol JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) } extension JsonParser { diff --git a/Sources/Jay/Reader.swift b/Sources/Jay/Reader.swift index 7d50987..30fde26 100644 --- a/Sources/Jay/Reader.swift +++ b/Sources/Jay/Reader.swift @@ -11,19 +11,22 @@ protocol Reader { // Returns the currently pointed-at char func curr() -> JChar + // Returns the index of the cursor + func currIndex() -> JsonRange.Index + // Moves cursor to the next char mutating func next() - // Returns the `next` next characters, if not enough chars, returns - // less characters. - func peek(next: Int) -> [JChar] - // Returns `true` if all characters have been read func isDone() -> Bool } extension Reader { + func rangeFrom(start: JsonRange.Index) -> JsonRange { + return start.. [JChar] { try self.ensureNotDone() var buff = [JChar]() diff --git a/Sources/Jay/RootParser.swift b/Sources/Jay/RootParser.swift index 9554fa2..fb6d3e5 100644 --- a/Sources/Jay/RootParser.swift +++ b/Sources/Jay/RootParser.swift @@ -8,13 +8,13 @@ struct RootParser: JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { var reader = try self.prepareForReading(withReader: r) //the standard doesn't require handling of fragments, so here //we'll assume we're only parsing valid structured types (object/array) - let root: JsonValue + let root: ParsedJsonToken switch reader.curr() { case Const.BeginObject: (root, reader) = try ObjectParser().parse(withReader: reader) diff --git a/Sources/Jay/StringParser.swift b/Sources/Jay/StringParser.swift index 0cfdfa1..1763263 100644 --- a/Sources/Jay/StringParser.swift +++ b/Sources/Jay/StringParser.swift @@ -14,9 +14,10 @@ struct StringParser: JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { var reader = try self.prepareForReading(withReader: r) + let start = reader.currIndex() //ensure we're starting with a quote guard reader.curr() == Const.QuotationMark else { @@ -27,13 +28,15 @@ struct StringParser: JsonParser { //if another quote, it's just an empty string if reader.curr() == Const.QuotationMark { try reader.nextAndCheckNotDone() - return (JsonValue.String(""), reader) + let range = reader.rangeFrom(start) + return (ParsedJsonToken(.String(""), range), reader) } let str: String (str, reader) = try self.parseString(reader) - let obj = JsonValue.String(str) - return (obj, reader) + let obj = ParsedJsonValue.String(str) + let range = reader.rangeFrom(start) + return (ParsedJsonToken(obj, range), reader) } func parseString(r: Reader) throws -> (String, Reader) { diff --git a/Sources/Jay/Types.swift b/Sources/Jay/Types.swift index 83b58d9..61df36a 100644 --- a/Sources/Jay/Types.swift +++ b/Sources/Jay/Types.swift @@ -40,7 +40,31 @@ public enum JsonValue { case Null } +// Types with additional metadata attached by parser +public typealias JsonRange = Range + +public typealias ParsedJsonObject = [String: ParsedJsonToken] +public typealias ParsedJsonArray = [ParsedJsonToken] + +public enum ParsedJsonValue { + case Object(ParsedJsonObject) + case Array(ParsedJsonArray) + case Number(JsonNumber) + case String(JsonString) + case Boolean(JsonBoolean) + case Null +} + +public struct ParsedJsonToken { + public let value: ParsedJsonValue + public let range: JsonRange + + init(_ value: ParsedJsonValue, _ range: JsonRange) { + self.value = value + self.range = range + } +} diff --git a/Sources/Jay/ValueParser.swift b/Sources/Jay/ValueParser.swift index 5e79e50..506001a 100644 --- a/Sources/Jay/ValueParser.swift +++ b/Sources/Jay/ValueParser.swift @@ -8,7 +8,7 @@ struct ValueParser: JsonParser { - func parse(withReader r: Reader) throws -> (JsonValue, Reader) { + func parse(withReader r: Reader) throws -> (ParsedJsonToken, Reader) { var reader = try self.prepareForReading(withReader: r) @@ -30,7 +30,7 @@ struct ValueParser: JsonParser { throw Error.UnexpectedCharacter(reader) } - let val: JsonValue + let val: ParsedJsonToken (val, reader) = try parser.parse(withReader: reader) return (val, reader) } diff --git a/Tests/Jay/ParsingTests.swift b/Tests/Jay/ParsingTests.swift index 61713ad..3667ef2 100644 --- a/Tests/Jay/ParsingTests.swift +++ b/Tests/Jay/ParsingTests.swift @@ -64,6 +64,7 @@ import Foundation ("testString_SpecialCharacter_EmojiComplex", testString_SpecialCharacter_EmojiComplex), ("testObject_Empty", testObject_Empty), ("testObject_Example1", testObject_Example1), + ("testObject_Example1_Annotated", testObject_Example1_Annotated), ("testNative_Example1", testNative_Example1), ("test_Example2", test_Example2) ] @@ -89,7 +90,7 @@ class ParsingTests:XCTestCase { let reader = ByteReader(content: "null, heyo") let ret = try! ValueParser().parse(withReader: reader) - ensureNull(ret.0) + ensureNull(ret.0.value) XCTAssert(ret.1.curr() == ",".char()) } @@ -104,7 +105,7 @@ class ParsingTests:XCTestCase { let reader = ByteReader(content: "true, ") let ret = try! ValueParser().parse(withReader: reader) - ensureBool(ret.0, exp: JsonBoolean.True) + ensureBool(ret.0.value, exp: JsonBoolean.True) XCTAssert(ret.1.curr() == ",".char()) } @@ -119,7 +120,7 @@ class ParsingTests:XCTestCase { let reader = ByteReader(content: "false, ") let ret = try! ValueParser().parse(withReader: reader) - ensureBool(ret.0, exp: JsonBoolean.False) + ensureBool(ret.0.value, exp: JsonBoolean.False) XCTAssert(ret.1.curr() == ",".char()) } @@ -142,7 +143,7 @@ class ParsingTests:XCTestCase { JsonValue.Number(JsonNumber.JsonDbl(-24.3)), JsonValue.Number(JsonNumber.JsonDbl(18200000000)), ] - ensureArray(ret.0, exp: exp) + ensureArray(ret.0.value, exp: exp) XCTAssert(ret.1.isDone()) } @@ -156,7 +157,7 @@ class ParsingTests:XCTestCase { JsonValue.Number(JsonNumber.JsonDbl(-12.3)), JsonValue.Boolean(JsonBoolean.False) ] - ensureArray(ret.0, exp: exp) + ensureArray(ret.0.value, exp: exp) XCTAssert(ret.1.curr() == "\n".char()) } @@ -191,43 +192,43 @@ class ParsingTests:XCTestCase { func testNumber_Int_Zero() { let reader = ByteReader(content: "0 ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonInt(0)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonInt(0)) } func testNumber_Int_One() { let reader = ByteReader(content: "1 ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonInt(1)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonInt(1)) } func testNumber_Int_Basic() { let reader = ByteReader(content: "24 ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonInt(24)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonInt(24)) } func testNumber_Int_Negative() { let reader = ByteReader(content: "24 , ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonInt(24)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonInt(24)) } func testNumber_Dbl_Basic() { let reader = ByteReader(content: "24.34, ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(24.34)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(24.34)) } func testNumber_Dbl_ZeroSomething() { let reader = ByteReader(content: "0.34, ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(0.34)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(0.34)) } func testNumber_Dbl_MinusZeroSomething() { let reader = ByteReader(content: "-0.34, ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(-0.34)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(-0.34)) } func testNumber_Dbl_Incomplete() { @@ -239,7 +240,7 @@ class ParsingTests:XCTestCase { func testNumber_Dbl_Negative() { let reader = ByteReader(content: "-24.34]") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(-24.34)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(-24.34)) } func testNumber_Dbl_Negative_WrongChar() { @@ -263,25 +264,25 @@ class ParsingTests:XCTestCase { func testNumber_Double_Exp_Normal() { let reader = ByteReader(content: "-24.3245e2, ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(-2432.45)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(-2432.45)) } func testNumber_Double_Exp_Positive() { let reader = ByteReader(content: "-24.3245e+2, ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(-2432.45)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(-2432.45)) } func testNumber_Double_Exp_Negative() { let reader = ByteReader(content: "-24.3245e-2, ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(-0.243245)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(-0.243245)) } func testNumber_Double_Exp_NoFrac() { let reader = ByteReader(content: "24E2, ") let ret = try! ValueParser().parse(withReader: reader) - ensureNumber(ret.0, exp: JsonNumber.JsonDbl(2400)) + ensureNumber(ret.0.value, exp: JsonNumber.JsonDbl(2400)) } func testNumber_Double_Exp_TwoEs() { @@ -364,86 +365,86 @@ class ParsingTests:XCTestCase { func testString_Empty() { let reader = ByteReader(content: "\"\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "") + ensureString(ret.0.value, exp: "") } func testString_Normal() { let reader = ByteReader(content: "\"hello world\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "hello world") + ensureString(ret.0.value, exp: "hello world") } func testString_Normal_WhitespaceInside() { let reader = ByteReader(content: "\"he \\r\\n l \\t l \\n o wo\\rrld \" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "he \r\n l \t l \n o wo\rrld ") + ensureString(ret.0.value, exp: "he \r\n l \t l \n o wo\rrld ") } func testString_StartEndWithSpaces() { let reader = ByteReader(content: "\" hello world \" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: " hello world ") + ensureString(ret.0.value, exp: " hello world ") } func testString_Unicode_RegularChar() { let reader = ByteReader(content: "\"hel\\u006co world\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "hello world") + ensureString(ret.0.value, exp: "hello world") } func testString_Unicode_SpecialCharacter_CoolA() { let reader = ByteReader(content: "\"h\\u01cdw\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "hǍw") + ensureString(ret.0.value, exp: "hǍw") } func testString_Unicode_SpecialCharacter_HebrewShin() { let reader = ByteReader(content: "\"h\\u05e9w\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "hשw") + ensureString(ret.0.value, exp: "hשw") } func testString_Unicode_SpecialCharacter_QuarterTo() { let reader = ByteReader(content: "\"h\\u25d5w\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "h◕w") + ensureString(ret.0.value, exp: "h◕w") } func testString_Unicode_SpecialCharacter_EmojiSimple() { let reader = ByteReader(content: "\"h\\ud83d\\ude3bw\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "h😻w") + ensureString(ret.0.value, exp: "h😻w") } func testString_Unicode_SpecialCharacter_EmojiComplex() { let reader = ByteReader(content: "\"h\\ud83c\\udde8\\ud83c\\uddffw\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "h🇨🇿w") + ensureString(ret.0.value, exp: "h🇨🇿w") } func testString_SpecialCharacter_QuarterTo() { let reader = ByteReader(content: "\"h◕w\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "h◕w") + ensureString(ret.0.value, exp: "h◕w") } func testString_SpecialCharacter_EmojiSimple() { let reader = ByteReader(content: "\"h😻w\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "h😻w") + ensureString(ret.0.value, exp: "h😻w") } func testString_SpecialCharacter_EmojiComplex() { let reader = ByteReader(content: "\"h🇨🇿w\" ") let ret = try! ValueParser().parse(withReader: reader) - ensureString(ret.0, exp: "h🇨🇿w") + ensureString(ret.0.value, exp: "h🇨🇿w") } func testObject_Empty() { let reader = ByteReader(content: "{}") let ret = try! ValueParser().parse(withReader: reader) - let exp: JsonObject = [:] - ensureObject(ret.0, exp: exp) + let exp: ParsedJsonObject = [:] + ensureObject(ret.0.value, exp: exp) } func testObject_Example1() { @@ -464,7 +465,30 @@ class ParsingTests:XCTestCase { ]), "name": JsonValue.Boolean(.True) ] - ensureObject(ret.0, exp: exp) + ensureObject(ret.0.value, exp: exp) + } + + func testObject_Example1_Annotated() { + let data = "{\t\"hello\" : \"wor🇨🇿ld\", \n\t \"val\": 1234, \"many\": [\n-12.32, null, \"yo\"\r], \"emptyDict\": {}, \"dict\": {\"arr\":[]}, \"name\": true}".chars() + let reader = ByteReader(content: data) + let ret = try! ValueParser().parse(withReader: reader) + + let exp: ParsedJsonToken = ParsedJsonToken(.Object([ + "hello": ParsedJsonToken(.String("wor🇨🇿ld"), 12..<27), + "val": ParsedJsonToken(.Number(.JsonInt(1234)), 39..<43), + "many": ParsedJsonToken(.Array([ + ParsedJsonToken(.Number(.JsonDbl(-12.32)), 55..<61), + ParsedJsonToken(.Null, 63..<67), + ParsedJsonToken(.String("yo"), 69..<73) + ]), 53..<75), + "emptyDict": ParsedJsonToken(.Object([:]), 90..<92), + "dict": ParsedJsonToken(.Object([ + "arr": ParsedJsonToken(.Array([]), 109..<111) + ]), 102..<112), + "name": ParsedJsonToken(.Boolean(.True), 122..<126) + ]), 0..<127) + let res = (ret.0 == exp) + XCTAssert(res, "Ret: \(ret.0), exp: \(exp)") } func testNative_Example1() { diff --git a/Tests/Jay/ReaderTests.swift b/Tests/Jay/ReaderTests.swift index f65863a..1513a04 100644 --- a/Tests/Jay/ReaderTests.swift +++ b/Tests/Jay/ReaderTests.swift @@ -20,11 +20,10 @@ import XCTest ("testStopAtFirstDifference_EmptyMain", testStopAtFirstDifference_EmptyMain), ("testStopAtFirstDifference_EmptyExpected", testStopAtFirstDifference_EmptyExpected), ("testStopAtFirstDifference_Normal", testStopAtFirstDifference_Normal), - ("testPeek_EnoughAvailable", testPeek_EnoughAvailable), - ("testPeek_LessAvailable", testPeek_LessAvailable), - ("testPeek_NoAvailable", testPeek_NoAvailable), ("testReadNext_EnoughAvailable", testReadNext_EnoughAvailable), - ("testReadNext_LessAvailable", testReadNext_LessAvailable) + ("testReadNext_LessAvailable", testReadNext_LessAvailable), + ("testCurrIndex_Start", testCurrIndex_Start), + ("testCurrIndex_End", testCurrIndex_End) ] } } @@ -117,28 +116,6 @@ class ReaderTests: XCTestCase { XCTFail() } } - - func testPeek_EnoughAvailable() { - var mainReader = ByteReader(content: "hello world") - mainReader.next() - mainReader.next() - XCTAssert(mainReader.peek(5) == "llo w".chars()) - } - - func testPeek_LessAvailable() { - var mainReader = ByteReader(content: "hello world") - mainReader.next() - mainReader.next() - XCTAssert(mainReader.peek(12) == "llo world".chars()) - } - - func testPeek_NoAvailable() { - var mainReader = ByteReader(content: "hey") - mainReader.next() - mainReader.next() - mainReader.next() - XCTAssert(mainReader.peek(5) == []) - } func testReadNext_EnoughAvailable() { var mainReader = ByteReader(content: "hello world") @@ -153,4 +130,16 @@ class ReaderTests: XCTestCase { XCTAssertNil(try? mainReader.readNext(12)) } + func testCurrIndex_Start() { + let r = ByteReader(content: "hello world") + XCTAssertEqual(r.currIndex(), 0) + } + + func testCurrIndex_End() { + var r = ByteReader(content: "hey") + r.next() + r.next() + r.next() + XCTAssertEqual(r.currIndex(), 3) + } } diff --git a/Tests/Jay/TestUtils.swift b/Tests/Jay/TestUtils.swift index 1f2033b..0b2c564 100644 --- a/Tests/Jay/TestUtils.swift +++ b/Tests/Jay/TestUtils.swift @@ -9,19 +9,27 @@ import XCTest @testable import Jay -func ensureNull(val: JsonValue) { - XCTAssertEqual(val, JsonValue.Null) +func ensureNull(val: ParsedJsonValue) { + XCTAssertEqual(val, ParsedJsonValue.Null) } -func ensureBool(val: JsonValue, exp: JsonBoolean) { - XCTAssertEqual(val, JsonValue.Boolean(exp)) +func ensureBool(val: ParsedJsonValue, exp: JsonBoolean) { + XCTAssertEqual(val, ParsedJsonValue.Boolean(exp)) } func ensureArray(val: JsonValue, exp: JsonArray) { XCTAssertEqual(val, JsonValue.Array(exp)) } -func ensureNumber(val: JsonValue, exp: JsonNumber) { +func ensureArray(val: ParsedJsonValue, exp: JsonArray) { + XCTAssertEqual(val.stripAnnotations(), JsonValue.Array(exp)) +} + +func ensureArray(val: ParsedJsonValue, exp: ParsedJsonArray) { + XCTAssertEqual(val, ParsedJsonValue.Array(exp)) +} + +func ensureNumber(val: ParsedJsonValue, exp: JsonNumber) { switch val { case .Number(let num): @@ -36,11 +44,15 @@ func ensureNumber(val: JsonValue, exp: JsonNumber) { } } -func ensureString(val: JsonValue, exp: JsonString) { - XCTAssertEqual(val, JsonValue.String(exp)) +func ensureString(val: ParsedJsonValue, exp: JsonString) { + XCTAssertEqual(val, ParsedJsonValue.String(exp)) +} + +func ensureObject(val: ParsedJsonValue, exp: JsonObject) { + XCTAssertEqual(val.stripAnnotations(), JsonValue.Object(exp)) } -func ensureObject(val: JsonValue, exp: JsonObject) { - XCTAssertEqual(val, JsonValue.Object(exp)) +func ensureObject(val: ParsedJsonValue, exp: ParsedJsonObject) { + XCTAssertEqual(val, ParsedJsonValue.Object(exp)) }