Skip to content
This repository has been archived by the owner on Mar 23, 2021. It is now read-only.

Commit

Permalink
added keeping source information during parsing, see #12
Browse files Browse the repository at this point in the history
  • Loading branch information
czechboy0 committed Mar 17, 2016
1 parent ad17bf8 commit 5559ad3
Show file tree
Hide file tree
Showing 19 changed files with 252 additions and 114 deletions.
13 changes: 9 additions & 4 deletions Sources/Jay/ArrayParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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)
}
Expand Down
14 changes: 9 additions & 5 deletions Sources/Jay/BooleanParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 4 additions & 7 deletions Sources/Jay/ByteReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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..<self.cursor.advancedBy(take)
return Array(self.content[range])
}

func curr() -> JChar {
precondition(!self.isDone())
return self.content[self.cursor]
}

func currIndex() -> Int {
return self.cursor
}

func isDone() -> Bool {
return self.cursor == self.content.endIndex
}
Expand Down
24 changes: 24 additions & 0 deletions Sources/Jay/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

12 changes: 11 additions & 1 deletion Sources/Jay/Jay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
}
}



4 changes: 2 additions & 2 deletions Sources/Jay/NativeParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
34 changes: 34 additions & 0 deletions Sources/Jay/NativeTypeConversions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 4 additions & 2 deletions Sources/Jay/NullParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
10 changes: 7 additions & 3 deletions Sources/Jay/NumberParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
}

Expand Down
19 changes: 11 additions & 8 deletions Sources/Jay/ObjectParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@

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)

//detect opening brace
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
Expand All @@ -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 :
Expand All @@ -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()
Expand All @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/Jay/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 {
Expand Down
11 changes: 7 additions & 4 deletions Sources/Jay/Reader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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..<self.currIndex()
}

mutating func readNext(next: Int) throws -> [JChar] {
try self.ensureNotDone()
var buff = [JChar]()
Expand Down
4 changes: 2 additions & 2 deletions Sources/Jay/RootParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 5559ad3

Please sign in to comment.