Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 323 lines (257 sloc) 9.833 kB
05eedb5 @itod 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 @itod merging changes from google code
authored
98 [stringParser setAssembler:self selector:@selector(parser:didMatchString:)];
05eedb5 @itod 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 @itod merging changes from google code
authored
109 [numberParser setAssembler:self selector:@selector(parser:didMatchNumber:)];
05eedb5 @itod 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 @itod merging changes from google code
authored
120 [nullParser setAssembler:self selector:@selector(parser:didMatchNull:)];
05eedb5 @itod 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 @itod merging changes from google code
authored
133 [booleanParser setAssembler:self selector:@selector(parser:didMatchBoolean:)];
05eedb5 @itod 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 @itod cleaning up factory method return types. adding twitter state test.
authored
147 PKTrack *actualArray = [PKTrack track];
05eedb5 @itod 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 @itod merging changes from google code
authored
161 [arrayParser setAssembler:self selector:@selector(parser:didMatchArray:)];
05eedb5 @itod 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 @itod cleaning up factory method return types. adding twitter state test.
authored
177 PKTrack *actualObject = [PKTrack track];
05eedb5 @itod 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 @itod merging changes from google code
authored
191 [objectParser setAssembler:self selector:@selector(parser:didMatchObject:)];
05eedb5 @itod 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 @itod merging changes from google code
authored
229 [propertyParser setAssembler:self selector:@selector(parser:didMatchProperty:)];
05eedb5 @itod 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 @itod merging changes from google code
authored
246 - (void)parser:(PKParser *)p didMatchNull:(PKAssembly *)a {
05eedb5 @itod initial add
authored
247 [a push:[NSNull null]];
248 }
249
250
253c2e0 @itod merging changes from google code
authored
251 - (void)parser:(PKParser *)p didMatchNumber:(PKAssembly *)a {
05eedb5 @itod initial add
authored
252 PKToken *tok = [a pop];
253 [a push:[NSNumber numberWithFloat:tok.floatValue]];
254 }
255
256
253c2e0 @itod merging changes from google code
authored
257 - (void)parser:(PKParser *)p didMatchString:(PKAssembly *)a {
05eedb5 @itod initial add
authored
258 PKToken *tok = [a pop];
259 [a push:[tok.stringValue stringByTrimmingQuotes]];
260 }
261
262
253c2e0 @itod merging changes from google code
authored
263 - (void)parser:(PKParser *)p didMatchBoolean:(PKAssembly *)a {
05eedb5 @itod initial add
authored
264 PKToken *tok = [a pop];
265 [a push:[NSNumber numberWithBool:[tok.stringValue isEqualToString:@"true"] ? YES : NO]];
266 }
267
268
253c2e0 @itod merging changes from google code
authored
269 - (void)parser:(PKParser *)p didMatchArray:(PKAssembly *)a {
05eedb5 @itod 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 @itod merging changes from google code
authored
283 - (void)parser:(PKParser *)p didMatchObject:(PKAssembly *)a {
05eedb5 @itod 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 @itod merging changes from google code
authored
301 - (void)parser:(PKParser *)p didMatchProperty:(PKAssembly *)a {
05eedb5 @itod 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.