@@ -30,7 +30,7 @@ package struct JSONParser {
3030 package static func parse ( _ buffer : UnsafeBufferPointer < UInt8 > ) throws( Error ) -> JSONValue {
3131 var p = JSONParser ( buffer )
3232 p . skipWhitespace ( )
33- let value = try p . parseValue ( depth : 0 )
33+ let value = try p . parseValue ( )
3434 p . skipWhitespace ( )
3535 if p . i != p . end { throw p . error ( " Unexpected non-whitespace character after JSON " ) }
3636 return value
@@ -57,89 +57,128 @@ package struct JSONParser {
5757 }
5858 }
5959
60- private mutating func parseValue ( depth : Int ) throws( Error ) -> JSONValue {
61- guard i < end else { throw error ( " Unexpected end of JSON input " ) }
62- switch b [ i ] {
63- case UInt8 ( ascii : " { " ) : return try parseObject ( depth : depth )
64- case UInt8 ( ascii : " [ " ) : return try parseArray ( depth : depth )
65- case UInt8 ( ascii : " \" " ) : return . string ( try parseString ( ) )
66- case UInt8 ( ascii : " t " ) : try expectLiteral ( " true " ) ; return . bool ( true )
67- case UInt8 ( ascii : " f " ) : try expectLiteral ( " false " ) ; return . bool ( false )
68- case UInt8 ( ascii : " n " ) : try expectLiteral ( " null " ) ; return . null
69- case UInt8 ( ascii : " - " ) , UInt8 ( ascii : " 0 " ) ... UInt8 ( ascii : " 9 " ) : return . number ( try parseNumber ( ) )
70- default : throw error ( " Unexpected token " )
71- }
60+ /// An array or object that a value being parsed is in.
61+ private enum Container {
62+ case array ( [ JSONValue ] )
63+ /// An object and the key of the value being parsed.
64+ case object ( JSONObject , key : String )
7265 }
7366
74- private mutating func expectLiteral ( _ literal : StaticString ) throws( Error ) {
75- let n = literal . utf8CodeUnitCount
76- guard end - i >= n else { throw error ( " Unexpected end of JSON input " ) }
77- let p = literal . utf8Start
78- for k in 0 ..< n where b [ i + k ] != p [ k ] {
79- throw error ( " Unexpected token " )
80- }
81- i += n
82- }
83-
84- private mutating func parseObject ( depth : Int ) throws( Error ) -> JSONValue {
85- if depth >= Self . maxDepth { throw error ( " JSON nested too deeply " ) }
86- i += 1 // {
87- var object = JSONObject ( )
88- skipWhitespace ( )
89- if i < end && b [ i ] == UInt8 ( ascii : " } " ) {
90- i += 1
91- return . object ( object )
92- }
67+ /// Parses a value without recursion, so nesting takes heap, not stack.
68+ /// The innermost open array or object is `array` or `object` (with
69+ /// `key`); the ones it is in are on `parents`. They move between the two
70+ /// (`take()`), so each stays uniquely referenced and adding to it doesn't
71+ /// copy it.
72+ private mutating func parseValue ( ) throws( Error ) -> JSONValue {
73+ var parents : [ Container ] = [ ]
74+ var array : [ JSONValue ] ?
75+ var object : JSONObject ?
76+ var key = " "
9377 while true {
94- skipWhitespace ( )
9578 guard i < end else { throw error ( " Unexpected end of JSON input " ) }
96- guard b [ i ] == UInt8 ( ascii : " \" " ) else { throw error ( " Expected property name " ) }
97- let key = try parseString ( )
98- skipWhitespace ( )
99- guard i < end , b [ i ] == UInt8 ( ascii : " : " ) else {
100- throw error ( " Expected ':' after property name " )
101- }
102- i += 1
103- skipWhitespace ( )
104- object [ key ] = try parseValue ( depth : depth + 1 )
105- skipWhitespace ( )
106- guard i < end else { throw error ( " Unexpected end of JSON input " ) }
107- if b [ i ] == UInt8 ( ascii : " , " ) {
79+ var value : JSONValue
80+ switch b [ i ] {
81+ case UInt8 ( ascii : " { " ) , UInt8 ( ascii : " [ " ) :
82+ let isObject = b [ i ] == UInt8 ( ascii : " { " )
83+ if parents . count + ( array == nil && object == nil ? 0 : 1 ) >= Self . maxDepth {
84+ throw error ( " JSON nested too deeply " )
85+ }
10886 i += 1
87+ skipWhitespace ( )
88+ if i < end && b [ i ] == ( isObject ? UInt8 ( ascii : " } " ) : UInt8 ( ascii : " ] " ) ) {
89+ i += 1
90+ value = isObject ? . object ( JSONObject ( ) ) : . array ( [ ] )
91+ break
92+ }
93+ if let a = array . take ( ) {
94+ parents . append ( . array ( a ) )
95+ } else if let o = object . take ( ) {
96+ parents . append ( . object ( o , key : key ) )
97+ }
98+ if isObject {
99+ object = JSONObject ( )
100+ key = try parseKey ( )
101+ } else {
102+ array = [ ]
103+ }
109104 continue
105+ case UInt8 ( ascii : " \" " ) : value = . string ( try parseString ( ) )
106+ case UInt8 ( ascii : " t " ) : try expectLiteral ( " true " ) ; value = . bool ( true )
107+ case UInt8 ( ascii : " f " ) : try expectLiteral ( " false " ) ; value = . bool ( false )
108+ case UInt8 ( ascii : " n " ) : try expectLiteral ( " null " ) ; value = . null
109+ case UInt8 ( ascii : " - " ) , UInt8 ( ascii : " 0 " ) ... UInt8 ( ascii : " 9 " ) :
110+ value = . number ( try parseNumber ( ) )
111+ default : throw error ( " Unexpected token " )
110112 }
111- if b [ i ] == UInt8 ( ascii : " } " ) {
112- i += 1
113- return . object ( object )
113+ // Adds the value to its container, closing the containers that end
114+ // after it, until one has another value.
115+ while true {
116+ skipWhitespace ( )
117+ if array != nil {
118+ array ! . append ( value )
119+ guard i < end else { throw error ( " Unexpected end of JSON input " ) }
120+ if b [ i ] == UInt8 ( ascii : " , " ) {
121+ i += 1
122+ skipWhitespace ( )
123+ break
124+ }
125+ guard b [ i ] == UInt8 ( ascii : " ] " ) else {
126+ throw error ( " Expected ',' or ']' after array element " )
127+ }
128+ i += 1
129+ value = . array ( array . take ( ) ! )
130+ } else if object != nil {
131+ // A repeated key keeps its first place and its last value.
132+ object ! [ key ] = value
133+ guard i < end else { throw error ( " Unexpected end of JSON input " ) }
134+ if b [ i ] == UInt8 ( ascii : " , " ) {
135+ i += 1
136+ key = try parseKey ( )
137+ break
138+ }
139+ guard b [ i ] == UInt8 ( ascii : " } " ) else {
140+ throw error ( " Expected ',' or '}' after property value " )
141+ }
142+ i += 1
143+ value = . object ( object . take ( ) ! )
144+ } else {
145+ return value
146+ }
147+ guard let parent = parents . popLast ( ) else { continue }
148+ switch consume parent {
149+ case . array ( let a) :
150+ array = consume a
151+ case . object ( let o, let k) :
152+ object = consume o
153+ key = consume k
154+ }
114155 }
115- throw error ( " Expected ',' or '}' after property value " )
116156 }
117157 }
118158
119- private mutating func parseArray ( depth : Int ) throws( Error ) -> JSONValue {
120- if depth >= Self . maxDepth { throw error ( " JSON nested too deeply " ) }
121- i += 1 // [
122- var array : [ JSONValue ] = [ ]
159+ /// A property name and its colon, and the whitespace around them.
160+ private mutating func parseKey ( ) throws( Error ) -> String {
123161 skipWhitespace ( )
124- if i < end && b [ i ] == UInt8 ( ascii : " ] " ) {
125- i += 1
126- return . array ( array )
162+ guard i < end else { throw error ( " Unexpected end of JSON input " ) }
163+ guard b [ i ] == UInt8 ( ascii : " \" " ) else { throw error ( " Expected property name " ) }
164+ let key = try parseString ( )
165+ skipWhitespace ( )
166+ guard i < end , b [ i ] == UInt8 ( ascii : " : " ) else {
167+ throw error ( " Expected ':' after property name " )
127168 }
128- while true {
129- skipWhitespace ( )
130- array . append ( try parseValue ( depth : depth + 1 ) )
131- skipWhitespace ( )
132- guard i < end else { throw error ( " Unexpected end of JSON input " ) }
133- if b [ i ] == UInt8 ( ascii : " , " ) {
134- i += 1
135- continue
136- }
137- if b [ i ] == UInt8 ( ascii : " ] " ) {
138- i += 1
139- return . array ( array )
140- }
141- throw error ( " Expected ',' or ']' after array element " )
169+ i += 1
170+ skipWhitespace ( )
171+ return key
172+ }
173+
174+ private mutating func expectLiteral ( _ literal : StaticString ) throws( Error ) {
175+ let n = literal . utf8CodeUnitCount
176+ guard end - i >= n else { throw error ( " Unexpected end of JSON input " ) }
177+ let p = literal . utf8Start
178+ for k in 0 ..< n where b [ i + k ] != p [ k ] {
179+ throw error ( " Unexpected token " )
142180 }
181+ i += n
143182 }
144183
145184 private mutating func parseString ( ) throws( Error ) -> String {
0 commit comments