Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 323 lines (257 sloc) 9.833 kb
05eedb5 Todd Ditchendorf initial add
authored
1 // Copyright 2010 Todd Ditchendorf
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #import "TDJsonParser.h"
16 #import "ParseKit.h"
17 #import "NSString+ParseKitAdditions.h"
18
19 @interface PKParser (PKParserFactoryAdditionsFriend)
20 - (void)setTokenizer:(PKTokenizer *)t;
21 @end
22
23 @interface PKCollectionParser ()
24 @property (nonatomic, readwrite, retain) NSMutableArray *subparsers;
25 @end
26
27 @interface TDJsonParser ()
28 @property (nonatomic, retain) PKToken *curly;
29 @property (nonatomic, retain) PKToken *bracket;
30 @end
31
32 @implementation TDJsonParser
33
34 - (id)init {
35 return [self initWithIntentToAssemble:YES];
36 }
37
38
39 - (id)initWithIntentToAssemble:(BOOL)yn {
40 if (self = [super init]) {
41 shouldAssemble = yn;
42 self.curly = [PKToken tokenWithTokenType:PKTokenTypeSymbol stringValue:@"{" floatValue:0.0];
43 self.bracket = [PKToken tokenWithTokenType:PKTokenTypeSymbol stringValue:@"[" floatValue:0.0];
44
45 self.tokenizer = [PKTokenizer tokenizer];
46 [self.tokenizer setTokenizerState:self.tokenizer.symbolState from: '/' to: '/']; // JSON doesn't have slash slash or slash star comments
47 [self.tokenizer setTokenizerState:self.tokenizer.symbolState from: '\'' to: '\'']; // JSON does not have single quoted strings
48
49 [self add:self.objectParser];
50 [self add:self.arrayParser];
51 }
52 return self;
53 }
54
55
56 - (void)dealloc {
57 // yikes. this is necessary to prevent a very nasty retain cycle leak.
58 // to be safe, release the subparsers of all collection parsers (as they may have retain cycles in complex grammars like this one)
59 // technically i only need to release the valueParser.subparers in this case, but better to be paranoid than to leak.
60 booleanParser.subparsers = nil;
61 arrayParser.subparsers = nil;
62 objectParser.subparsers = nil;
63 valueParser.subparsers = nil;
64 commaValueParser.subparsers = nil;
65 propertyParser.subparsers = nil;
66 commaPropertyParser.subparsers = nil;
67
68 self.tokenizer = nil;
69 self.stringParser = nil;
70 self.numberParser = nil;
71 self.nullParser = nil;
72 self.booleanParser = nil;
73 self.arrayParser = nil;
74 self.objectParser = nil;
75 self.valueParser = nil;
76 self.propertyParser = nil;
77 self.commaPropertyParser = nil;
78 self.commaValueParser = nil;
79 self.curly = nil;
80 self.bracket = nil;
81 [super dealloc];
82 }
83
84
85 - (id)parse:(NSString *)s {
86 self.tokenizer.string = s;
87 PKTokenAssembly *a = [PKTokenAssembly assemblyWithTokenizer:self.tokenizer];
88
89 PKAssembly *result = [self completeMatchFor:a];
90 return [result pop];
91 }
92
93
94 - (PKParser *)stringParser {
95 if (!stringParser) {
96 self.stringParser = [PKQuotedString quotedString];
97 if (shouldAssemble) {
253c2e0 Todd Ditchendorf merging changes from google code
authored
98 [stringParser setAssembler:self selector:@selector(parser:didMatchString:)];
05eedb5 Todd Ditchendorf initial add
authored
99 }
100 }
101 return stringParser;
102 }
103
104
105 - (PKParser *)numberParser {
106 if (!numberParser) {
107 self.numberParser = [PKNumber number];
108 if (shouldAssemble) {
253c2e0 Todd Ditchendorf merging changes from google code
authored
109 [numberParser setAssembler:self selector:@selector(parser:didMatchNumber:)];
05eedb5 Todd Ditchendorf initial add
authored
110 }
111 }
112 return numberParser;
113 }
114
115
116 - (PKParser *)nullParser {
117 if (!nullParser) {
118 self.nullParser = [[PKLiteral literalWithString:@"null"] discard];
119 if (shouldAssemble) {
253c2e0 Todd Ditchendorf merging changes from google code
authored
120 [nullParser setAssembler:self selector:@selector(parser:didMatchNull:)];
05eedb5 Todd Ditchendorf initial add
authored
121 }
122 }
123 return nullParser;
124 }
125
126
127 - (PKCollectionParser *)booleanParser {
128 if (!booleanParser) {
129 self.booleanParser = [PKAlternation alternation];
130 [booleanParser add:[PKLiteral literalWithString:@"true"]];
131 [booleanParser add:[PKLiteral literalWithString:@"false"]];
132 if (shouldAssemble) {
253c2e0 Todd Ditchendorf merging changes from google code
authored
133 [booleanParser setAssembler:self selector:@selector(parser:didMatchBoolean:)];
05eedb5 Todd Ditchendorf initial add
authored
134 }
135 }
136 return booleanParser;
137 }
138
139
140 - (PKCollectionParser *)arrayParser {
141 if (!arrayParser) {
142
143 // array = '[' content ']'
144 // content = Empty | actualArray
145 // actualArray = value commaValue*
146
32bd9bf Todd Ditchendorf cleaning up factory method return types. adding twitter state test.
authored
147 PKTrack *actualArray = [PKTrack track];
05eedb5 Todd Ditchendorf initial add
authored
148 [actualArray add:self.valueParser];
149 [actualArray add:[PKRepetition repetitionWithSubparser:self.commaValueParser]];
150
151 PKAlternation *content = [PKAlternation alternation];
152 [content add:[PKEmpty empty]];
153 [content add:actualArray];
154
155 self.arrayParser = [PKSequence sequence];
156 [arrayParser add:[PKSymbol symbolWithString:@"["]]; // serves as fence
157 [arrayParser add:content];
158 [arrayParser add:[[PKSymbol symbolWithString:@"]"] discard]];
159
160 if (shouldAssemble) {
253c2e0 Todd Ditchendorf merging changes from google code
authored
161 [arrayParser setAssembler:self selector:@selector(parser:didMatchArray:)];
05eedb5 Todd Ditchendorf initial add
authored
162 }
163 }
164 return arrayParser;
165 }
166
167
168 - (PKCollectionParser *)objectParser {
169 if (!objectParser) {
170
171 // object = '{' content '}'
172 // content = Empty | actualObject
173 // actualObject = property commaProperty*
174 // property = QuotedString ':' value
175 // commaProperty = ',' property
176
32bd9bf Todd Ditchendorf cleaning up factory method return types. adding twitter state test.
authored
177 PKTrack *actualObject = [PKTrack track];
05eedb5 Todd Ditchendorf initial add
authored
178 [actualObject add:self.propertyParser];
179 [actualObject add:[PKRepetition repetitionWithSubparser:self.commaPropertyParser]];
180
181 PKAlternation *content = [PKAlternation alternation];
182 [content add:[PKEmpty empty]];
183 [content add:actualObject];
184
185 self.objectParser = [PKSequence sequence];
186 [objectParser add:[PKSymbol symbolWithString:@"{"]]; // serves as fence
187 [objectParser add:content];
188 [objectParser add:[[PKSymbol symbolWithString:@"}"] discard]];
189
190 if (shouldAssemble) {
253c2e0 Todd Ditchendorf merging changes from google code
authored
191 [objectParser setAssembler:self selector:@selector(parser:didMatchObject:)];
05eedb5 Todd Ditchendorf initial add
authored
192 }
193 }
194 return objectParser;
195 }
196
197
198 - (PKCollectionParser *)valueParser {
199 if (!valueParser) {
200 self.valueParser = [PKAlternation alternation];
201 [valueParser add:self.stringParser];
202 [valueParser add:self.numberParser];
203 [valueParser add:self.nullParser];
204 [valueParser add:self.booleanParser];
205 [valueParser add:self.arrayParser];
206 [valueParser add:self.objectParser];
207 }
208 return valueParser;
209 }
210
211
212 - (PKCollectionParser *)commaValueParser {
213 if (!commaValueParser) {
214 self.commaValueParser = [PKTrack sequence];
215 [commaValueParser add:[[PKSymbol symbolWithString:@","] discard]];
216 [commaValueParser add:self.valueParser];
217 }
218 return commaValueParser;
219 }
220
221
222 - (PKCollectionParser *)propertyParser {
223 if (!propertyParser) {
224 self.propertyParser = [PKSequence sequence];
225 [propertyParser add:[PKQuotedString quotedString]];
226 [propertyParser add:[[PKSymbol symbolWithString:@":"] discard]];
227 [propertyParser add:self.valueParser];
228 if (shouldAssemble) {
253c2e0 Todd Ditchendorf merging changes from google code
authored
229 [propertyParser setAssembler:self selector:@selector(parser:didMatchProperty:)];
05eedb5 Todd Ditchendorf initial add
authored
230 }
231 }
232 return propertyParser;
233 }
234
235
236 - (PKCollectionParser *)commaPropertyParser {
237 if (!commaPropertyParser) {
238 self.commaPropertyParser = [PKTrack sequence];
239 [commaPropertyParser add:[[PKSymbol symbolWithString:@","] discard]];
240 [commaPropertyParser add:self.propertyParser];
241 }
242 return commaPropertyParser;
243 }
244
245
253c2e0 Todd Ditchendorf merging changes from google code
authored
246 - (void)parser:(PKParser *)p didMatchNull:(PKAssembly *)a {
05eedb5 Todd Ditchendorf initial add
authored
247 [a push:[NSNull null]];
248 }
249
250
253c2e0 Todd Ditchendorf merging changes from google code
authored
251 - (void)parser:(PKParser *)p didMatchNumber:(PKAssembly *)a {
05eedb5 Todd Ditchendorf initial add
authored
252 PKToken *tok = [a pop];
253 [a push:[NSNumber numberWithFloat:tok.floatValue]];
254 }
255
256
253c2e0 Todd Ditchendorf merging changes from google code
authored
257 - (void)parser:(PKParser *)p didMatchString:(PKAssembly *)a {
05eedb5 Todd Ditchendorf initial add
authored
258 PKToken *tok = [a pop];
259 [a push:[tok.stringValue stringByTrimmingQuotes]];
260 }
261
262
253c2e0 Todd Ditchendorf merging changes from google code
authored
263 - (void)parser:(PKParser *)p didMatchBoolean:(PKAssembly *)a {
05eedb5 Todd Ditchendorf initial add
authored
264 PKToken *tok = [a pop];
265 [a push:[NSNumber numberWithBool:[tok.stringValue isEqualToString:@"true"] ? YES : NO]];
266 }
267
268
253c2e0 Todd Ditchendorf merging changes from google code
authored
269 - (void)parser:(PKParser *)p didMatchArray:(PKAssembly *)a {
05eedb5 Todd Ditchendorf initial add
authored
270 NSArray *elements = [a objectsAbove:self.bracket];
271 NSMutableArray *array = [NSMutableArray arrayWithCapacity:[elements count]];
272
273 for (id element in [elements reverseObjectEnumerator]) {
274 if (element) {
275 [array addObject:element];
276 }
277 }
278 [a pop]; // pop the [
279 [a push:array];
280 }
281
282
253c2e0 Todd Ditchendorf merging changes from google code
authored
283 - (void)parser:(PKParser *)p didMatchObject:(PKAssembly *)a {
05eedb5 Todd Ditchendorf initial add
authored
284 NSArray *elements = [a objectsAbove:self.curly];
285 NSMutableDictionary *d = [NSMutableDictionary dictionaryWithCapacity:[elements count] / 2.];
286
287 NSInteger i = 0;
288 for ( ; i < [elements count] - 1; i++) {
289 id value = [elements objectAtIndex:i++];
290 NSString *key = [elements objectAtIndex:i];
291 if (key && value) {
292 [d setObject:value forKey:key];
293 }
294 }
295
296 [a pop]; // pop the {
297 [a push:d];
298 }
299
300
253c2e0 Todd Ditchendorf merging changes from google code
authored
301 - (void)parser:(PKParser *)p didMatchProperty:(PKAssembly *)a {
05eedb5 Todd Ditchendorf initial add
authored
302 id value = [a pop];
303 PKToken *tok = [a pop];
304 NSString *key = [tok.stringValue stringByTrimmingQuotes];
305
306 [a push:key];
307 [a push:value];
308 }
309
310 @synthesize stringParser;
311 @synthesize numberParser;
312 @synthesize nullParser;
313 @synthesize booleanParser;
314 @synthesize arrayParser;
315 @synthesize objectParser;
316 @synthesize valueParser;
317 @synthesize commaValueParser;
318 @synthesize propertyParser;
319 @synthesize commaPropertyParser;
320 @synthesize curly;
321 @synthesize bracket;
322 @end
Something went wrong with that request. Please try again.