]> git.wincent.com - WOTest.git/blob - NSScanner+WOTest.m
Fix object-to-pointer comparisons on Leopard
[WOTest.git] / NSScanner+WOTest.m
1 //
2 //  NSScanner+WOTest.m
3 //  WOTest
4 //
5 //  Created by Wincent Colaiuta on 12 June 2005.
6 //
7 //  Copyright 2005-2007 Wincent Colaiuta.
8 //  This program is free software: you can redistribute it and/or modify
9 //  it under the terms of the GNU General Public License as published by
10 //  the Free Software Foundation, either version 3 of the License, or
11 //  (at your option) any later version.
12 //
13 //  This program is distributed in the hope that it will be useful,
14 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //  GNU General Public License for more details.
17 //
18 //  You should have received a copy of the GNU General Public License
19 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 //
21
22 #import "NSScanner+WOTest.h"
23 #import <objc/objc-class.h>
24 #import "NSString+WOTest.h"
25 #import "NSValue+WOTest.h"
26
27 #define WO_LEGAL_IDENTIFIER_CHARACTERS  @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
28
29 @implementation NSScanner (WOTest)
30
31 - (BOOL)WOTest_peekCharacter:(unichar *)value;
32 {
33     NSParameterAssert(value != NULL);
34     unsigned scanLocation   = [self scanLocation];
35     NSString *string        = [self string];
36     if (string && ([string length] > scanLocation))
37     {
38         *value = [string characterAtIndex:scanLocation];
39         return YES;
40     }   
41     return NO;
42 }
43
44 - (BOOL)WOTest_scanCharacter:(unichar *)value;
45 {    
46     unichar  character;
47     if ([self WOTest_peekCharacter:&character])
48     {
49         if (value)
50             *value = character;
51         [self setScanLocation:[self scanLocation] + 1];
52         return YES;
53     }
54     return NO;
55 }
56
57 - (BOOL)WOTest_scanCharacterFromSet:(NSCharacterSet *)scanSet intoChar:(unichar *)value;
58 {
59     if (!scanSet) return NO; // nothing to do
60     
61     unsigned    scanLocation = [self scanLocation];
62     unichar     character;
63     if ([self WOTest_scanCharacter:&character] && [scanSet characterIsMember:character])
64     {
65         if (value)
66             *value = character;
67         return YES;
68     }
69     
70     [self setScanLocation:scanLocation]; // revert
71     return NO;
72 }
73
74 - (BOOL)WOTest_scanReturnTypeIntoString:(NSString **)stringValue
75 {
76     if ([self scanLocation] != 0)
77         return NO; // the return type must be at the start of the string
78     
79     // scan a single type (the first one will be the return type)
80     return [self WOTest_scanTypeIntoString:stringValue];
81 }
82
83 - (BOOL)WOTest_scanTypeIntoString:(NSString **)stringValue
84 {
85     unsigned    scanLocation        = [self scanLocation];
86     NSString    *qualifiers         = nil;
87     NSString    *type               = nil;
88     
89     // scan any qualifers that are present
90     if (![self WOTest_scanQualifiersIntoString:&qualifiers]) qualifiers = @"";
91     
92     // scan type
93     if ([self WOTest_scanBitfieldIntoString:&type]          ||
94         [self scanPointerIntoString:&type]                  ||
95         [self WOTest_scanArrayIntoString:&type]             ||
96         [self WOTest_scanStructIntoString:&type]            ||
97         [self WOTest_scanUnionIntoString:&type]             ||
98         [self WOTest_scanNonCompoundTypeIntoString:&type])
99     {
100         // success
101         if (stringValue)
102             *stringValue = 
103                 [NSString stringWithFormat:@"%@%@", qualifiers, type];
104         return YES;        
105     }
106     
107     [self setScanLocation:scanLocation]; // revert
108     return NO;
109 }
110
111 - (BOOL)WOTest_scanQualifiersIntoString:(NSString **)stringValue
112 {    
113     NSCharacterSet *qualifiersSet = 
114     [NSCharacterSet characterSetWithCharactersInString:
115         [NSString stringWithFormat:@"%C%C%C%C%C%C%C",
116             WO_ENCODING_QUALIFIER_CONST,    WO_ENCODING_QUALIFIER_IN,
117             WO_ENCODING_QUALIFIER_INOUT,    WO_ENCODING_QUALIFIER_OUT,
118             WO_ENCODING_QUALIFIER_BYCOPY,   WO_ENCODING_QUALIFIER_BYREF,
119             WO_ENCODING_QUALIFIER_ONEWAY]];
120     
121     return [self scanCharactersFromSet:qualifiersSet
122                             intoString:stringValue];
123 }
124
125 - (BOOL)WOTest_scanNonCompoundTypeIntoString:(NSString **)stringValue
126 {
127     NSCharacterSet *nonCompoundSet = 
128     [NSCharacterSet characterSetWithCharactersInString:
129         [NSString stringWithFormat:@"%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C%C",
130             _C_ID,      _C_CLASS,   _C_SEL,     _C_CHR,     _C_UCHR,    
131             _C_SHT,     _C_USHT,    _C_INT,     _C_UINT,    _C_LNG,
132             _C_ULNG,    _C_LNGLNG,  _C_ULNGLNG, _C_FLT,     _C_DBL,     
133             _C_99BOOL,  _C_VOID,    _C_UNDEF,   _C_CHARPTR]];
134     
135     unichar character;
136     if ([self WOTest_scanCharacterFromSet:nonCompoundSet intoChar:&character])
137     {
138         if (stringValue)
139             *stringValue = [NSString WOTest_stringWithCharacter:character]; 
140         return YES;
141     }
142      
143     return NO;
144 }
145
146 - (BOOL)WOTest_scanBitfieldIntoString:(NSString **)stringValue
147 {
148     unsigned scanLocation = [self scanLocation];
149     
150     unichar marker; // look for bitfield marker
151     int num;        // scan number of bits
152     if ([self WOTest_scanCharacter:&marker] && (marker == _C_BFLD) && [self scanInt:&num])
153     {
154         if (stringValue)
155             *stringValue = [NSString stringWithFormat:@"%C%d", marker, num];
156         return YES;
157     }
158     
159     [self setScanLocation:scanLocation]; // revert
160     return NO;
161 }
162
163 - (BOOL)WOTest_scanArrayIntoString:(NSString **)stringValue
164 {
165     unsigned scanLocation = [self scanLocation];
166     
167     unichar startMarker, endMarker; // look for array start marker
168     int num;                        // scan number of elements
169     NSString *type;                 // scan type of elements
170     
171     if ([self WOTest_scanCharacter:&startMarker] && (startMarker == _C_ARY_B) &&
172         [self scanInt:&num] && [self WOTest_scanTypeIntoString:&type] &&
173         [self WOTest_scanCharacter:&endMarker] && (endMarker == _C_ARY_E))
174     {
175         if (stringValue)
176             *stringValue = [NSString stringWithFormat:@"%C%d%@%C", startMarker, num, type, endMarker];
177         return YES;
178     }
179     
180     
181     [self setScanLocation:scanLocation]; // revert
182     return NO;
183 }
184
185 - (BOOL)WOTest_scanIdentifierIntoString:(NSString **)stringValue
186 {
187     unsigned scanLocation = [self scanLocation];
188     
189     unichar firstChar, equalsChar;
190     if ([self WOTest_peekCharacter:&firstChar])
191     {
192         // identifiers must begin with a letter or underscore (no numbers!)
193         if (((firstChar >= 'a') && (firstChar <= 'z')) || 
194             ((firstChar >= 'A') && (firstChar <= 'Z')) ||
195             (firstChar == '_'))
196         {
197             NSString *identifier;   // scan identifier
198             if ([self scanCharactersFromSet:
199                 [NSCharacterSet characterSetWithCharactersInString:
200                     WO_LEGAL_IDENTIFIER_CHARACTERS]
201                                  intoString:&identifier])
202             {
203                 if ([self WOTest_peekCharacter:&equalsChar] && (equalsChar == '='))
204                 {
205                     if (stringValue)
206                         *stringValue = identifier;
207                     return YES;
208                 }
209             }
210         }
211         else // special case: check for anonymous/unknown identifiers ('?')
212         {
213             if ([self WOTest_scanCharacter:&firstChar] && (firstChar == _C_UNDEF) &&
214                 [self WOTest_peekCharacter:&equalsChar] && (equalsChar == '='))
215             {
216                 if (stringValue)
217                     *stringValue = [NSString WOTest_stringWithCharacter:_C_UNDEF];
218                 return YES;
219             }
220         }
221     }
222     
223     [self setScanLocation:scanLocation]; // revert
224     return NO;
225 }
226
227 - (BOOL)WOTest_scanStructIntoString:(NSString **)stringValue
228 {
229     unsigned scanLocation = [self scanLocation];
230     
231     unichar startMarker, endMarker;
232     NSString *identifier;
233     if ([self WOTest_scanCharacter:&startMarker] && (startMarker == _C_STRUCT_B))
234     {
235         // scan optional identifier
236         if ([self WOTest_scanIdentifierIntoString:&identifier])
237         {
238             [self WOTest_scanCharacter:NULL]; // scan past "="
239             
240             // prepare identifier for later insertion (append equals sign)
241             identifier = [NSString stringWithFormat:@"%@=", identifier];
242         }
243         else // optional identifier not found
244             identifier = @"";
245         
246         // scan types until you hit end marker
247         NSMutableString *types = [NSMutableString string];
248         NSString *type;
249         while ([self WOTest_scanTypeIntoString:&type])
250         {
251             [types appendString:type];
252             [self scanInt:nil]; // skip over any superfluous numbers in string
253         }
254         
255         // scan end marker
256         if ([self WOTest_scanCharacter:&endMarker] && (endMarker == _C_STRUCT_E))
257         {
258             if (stringValue)
259                 *stringValue = [NSString stringWithFormat:@"%C%@%@%C", startMarker, identifier, types, endMarker];
260             return YES;
261         }
262     }
263     [self setScanLocation:scanLocation]; // revert
264     return NO;
265 }
266
267 - (BOOL)WOTest_scanUnionIntoString:(NSString **)stringValue
268 {
269     unsigned scanLocation = [self scanLocation];
270     
271     unichar startMarker, equalsMarker, endMarker;
272     NSString *identifier;
273     if ([self WOTest_scanCharacter:&startMarker] && (startMarker == _C_UNION_B))
274     {
275         // scan optional identifier
276         unsigned identifierLocation = [self scanLocation];
277         if ([self WOTest_scanIdentifierIntoString:&identifier] &&
278             [self WOTest_scanCharacter:&equalsMarker] && (equalsMarker == '='))
279             // prepare identifier for later insertion (append equals sign)
280             identifier = [NSString stringWithFormat:@"%@=", identifier];
281         else // optional identifier not found
282         {
283             identifier = @"";
284             [self setScanLocation:identifierLocation];
285         }
286
287         // scan types until you hit end marker
288         NSMutableString *types = [NSMutableString string];
289         NSString *type;
290         while ([self WOTest_scanTypeIntoString:&type])
291         {
292             [types appendString:type];
293             [self scanInt:nil]; // skip over any superfluous numbers in string
294         }
295         
296         // scan end marker
297         if ([self WOTest_scanCharacter:&endMarker] && (endMarker == _C_UNION_E))
298         {
299             if (stringValue)
300                 *stringValue = [NSString stringWithFormat:@"%C%@%@%C", startMarker, identifier, types, endMarker];
301             return YES;
302         }
303     }
304         
305     [self setScanLocation:scanLocation]; // revert
306     return NO;    
307 }
308
309 - (BOOL)scanPointerIntoString:(NSString **)stringValue
310 {
311     unsigned scanLocation = [self scanLocation];
312     
313     unichar marker; // look for pointer marker
314     NSString *type; // scan type to which pointer points
315     if ([self WOTest_scanCharacter:&marker] && (marker == _C_PTR) && [self WOTest_scanTypeIntoString:&type])
316     {
317         if (stringValue)
318             *stringValue = [NSString stringWithFormat:@"%C%@", marker, type];
319         return YES;
320     }
321     
322     [self setScanLocation:scanLocation]; // revert
323     return NO;
324 }
325
326 @end