2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22 #include "WODParser.h"
25 @implementation WODParser
27 static Class StrClass = Nil;
28 static Class DictClass = Nil;
29 static Class NumberClass = Nil;
30 static NSNumber *yesNum = nil;
31 static NSNumber *noNum = nil;
32 static BOOL useUTF8 = NO;
35 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
37 StrClass = [NSString class];
38 DictClass = [NSMutableDictionary class];
39 NumberClass = [NSNumber class];
41 if (yesNum == nil) yesNum = [[NumberClass numberWithBool:YES] retain];
42 if (noNum == nil) noNum = [[NumberClass numberWithBool:NO] retain];
44 useUTF8 = [ud boolForKey:@"WOParsersUseUTF8"];
47 - (id)initWithHandler:(id<WODParserHandler,NSObject>)_handler {
48 ASSIGN(self->callback, _handler);
52 [self->callback release];
58 - (id)associationWithValue:(id)_value {
59 return [self->callback parser:self makeAssociationWithValue:_value];
61 - (id)associationWithKeyPath:(NSString *)_keyPath {
62 return [self->callback parser:self makeAssociationWithKeyPath:_keyPath];
65 - (id)elementDefinitionForComponent:(NSString *)_cname
66 associations:(id)_entry
67 elementName:(NSString *)_elemName
69 return [self->callback parser:self
70 makeDefinitionForComponentNamed:_cname
72 elementName:_elemName];
77 static id _parseProperty(NSZone *_zone, const unichar *_buffer, unsigned *_idx,
78 unsigned _len, NSException **_exception,
79 BOOL _allowAssoc, id self);
80 static id _parseWodEntry(NSZone *_zone, const unichar *_buffer, unsigned *_idx,
81 unsigned _len, NSException **_exception,
82 NSString **_name, NSString **_class,
85 - (NSException *)parseDefinitionsFromBuffer:(const unichar *)_buffer
87 mappings:(NSMutableDictionary *)_mappings
89 NSException *exception = nil;
90 NSString *elementName = nil;
91 NSString *componentName = nil;
95 [_mappings removeAllObjects];
97 while ((entry = _parseWodEntry(NULL, _buffer, &idx, _len, &exception,
98 &elementName, &componentName,
103 [entry release]; entry = nil;
104 [elementName release]; elementName = nil;
105 [componentName release]; componentName = nil;
109 if ([_mappings objectForKey:elementName] != nil)
110 [self warnWithFormat:@"duplicate definition of element %@ !",
113 def = [self elementDefinitionForComponent:componentName
115 elementName:elementName];
117 [componentName release]; componentName = nil;
118 [entry release]; entry = nil;
120 if ((def != nil) && (elementName != nil))
121 [_mappings setObject:def forKey:elementName];
123 NSLog(@"defined element %@ definition=%@", elementName, def);
125 [elementName release]; elementName = nil;
133 - (NSStringEncoding)stringEncodingForData:(NSData *)_data {
134 // TODO: we could check for UTF-16 marker in front of data
135 return useUTF8 ? NSUTF8StringEncoding : [NSString defaultCStringEncoding];
138 - (NSDictionary *)parseDeclarationData:(NSData *)_decl {
139 NSMutableDictionary *defs;
145 if (![self->callback parser:self willParseDeclarationData:_decl])
148 /* recode buffer using NSString */
150 s = [[NSString alloc] initWithData:_decl
151 encoding:[self stringEncodingForData:_decl]];
153 buf = calloc(bufLen + 2, sizeof(unichar));
154 [s getCharacters:buf];
155 [s release]; s = nil;
156 buf[bufLen] = 0; /* null-terminate buffer, parser might need that */
160 defs = [NSMutableDictionary dictionaryWithCapacity:100];
162 ex = [self parseDefinitionsFromBuffer:buf length:bufLen mappings:defs];
164 if (buf != NULL) free(buf); buf = NULL;
169 [self->callback parser:self failedParsingDeclarationData:_decl
173 [self->callback parser:self finishedParsingDeclarationData:_decl
181 static int _numberOfLines(const unichar *_buffer, unsigned _lastIdx) {
182 register unsigned pos, lineCount = 1;
184 for (pos = 0; (pos < _lastIdx) && (_buffer[pos] != '\0'); pos++) {
185 if (_buffer[pos] == '\n')
191 static inline BOOL _isBreakChar(const unichar _c) {
193 case ' ': case '\t': case '\n': case '\r':
194 case '=': case ';': case ',':
195 case '{': case '(': case '"': case '<':
204 static inline BOOL _isIdChar(const unichar _c) {
205 return (_isBreakChar(_c) && (_c != '.')) ? NO : YES;
208 static inline int _valueOfHexChar(const unichar _c) {
210 case '0': case '1': case '2': case '3': case '4':
211 case '5': case '6': case '7': case '8': case '9':
212 return (_c - 48); // 0-9 (ascii-char)'0' - 48 => (int)0
214 case 'A': case 'B': case 'C':
215 case 'D': case 'E': case 'F':
216 return (_c - 55); // A-F, A=10..F=15, 'A'=65..'F'=70
218 case 'a': case 'b': case 'c':
219 case 'd': case 'e': case 'f':
220 return (_c - 87); // a-f, a=10..F=15, 'a'=97..'f'=102
226 static inline BOOL _isHexDigit(const unichar _c) {
228 case '0': case '1': case '2': case '3': case '4':
229 case '5': case '6': case '7': case '8': case '9':
230 case 'A': case 'B': case 'C':
231 case 'D': case 'E': case 'F':
232 case 'a': case 'b': case 'c':
233 case 'd': case 'e': case 'f':
241 static NSException *_makeException(NSException *_exception,
242 const unichar *_buffer, unsigned _idx,
243 unsigned _len, NSString *_text)
245 NSMutableDictionary *ui = nil;
246 NSException *exception = nil;
250 numLines = _numberOfLines(_buffer, _idx);
251 atEof = (_idx >= _len) ? YES : NO;
254 // error resulted from a previous error (exception already set)
258 _text = [@"Unexpected end: " stringByAppendingString:[_text stringValue]];
260 _text = [StrClass stringWithFormat:@"Syntax error in line %i: %@",
265 ui = [[exception userInfo] mutableCopy];
267 ui = [[DictClass alloc] initWithCapacity:8];
269 [ui setObject:[NumberClass numberWithInt:numLines] forKey:@"line"];
270 [ui setObject:[NumberClass numberWithInt:_len] forKey:@"size"];
271 [ui setObject:[NumberClass numberWithInt:_idx] forKey:@"position"];
273 if (!atEof && (_idx > 0)) {
274 register unsigned pos;
275 const unichar *startPos, *endPos;
277 for (pos = _idx; (pos >= 0) && (_buffer[pos] != '\n'); pos--)
279 startPos = &(_buffer[pos + 1]);
281 for (pos = _idx; ((pos < _len) && (_buffer[pos] != '\n')); pos++)
283 endPos = &(_buffer[pos - 1]);
285 if (startPos < endPos) {
288 ll = [[StrClass alloc] initWithCharacters:startPos
289 length:endPos - startPos];
290 [ui setObject:ll forKey:@"lastLine"];
294 NSLog(@"%s: startPos=0x%08X endPos=0x%08X", __PRETTY_FUNCTION__,
299 exception = [NSException exceptionWithName:@"SyntaxError"
302 [ui release]; ui = nil;
307 static BOOL _skipComments(const unichar *_buffer,
308 unsigned *_idx, unsigned _len,
309 NSException **_exception)
311 register unsigned pos = *_idx;
317 //NSLog(@"start at '%c' (%i)", _buffer[pos], pos);
319 do { // until all comments are filtered ..
322 if ((_buffer[pos] == '/') && (pos + 1 < _len)) {
323 if (_buffer[pos + 1] == '/') { // single line comments
324 pos += 2; // skip '//'
326 // search for '\n' ..
327 while ((pos < _len) && (_buffer[pos] != '\n'))
330 if ((pos < _len) && (_buffer[pos] == '\n')) {
331 pos++; // skip newline, otherwise EOF was reached
335 else if (_buffer[pos + 1] == '*') { /* multiline comments */
336 BOOL commentIsClosed = NO;
338 pos += 2; // skip '/*'
340 do { // search for '*/'
341 while ((pos < _len) && (_buffer[pos] != '*'))
344 if (pos < _len) { // found '*'
345 if ((pos + 1) < _len) {
346 if (_buffer[pos + 1] == '/') { // found '*/'
347 commentIsClosed = YES;
348 pos += 2; // skip '*/'
357 if (!commentIsClosed) {
358 // EOF found, comment wasn't closed
360 _makeException(*_exception, _buffer, *_idx, _len,
361 @"comment was not closed (expected '*/')");
366 else if (isspace((int)_buffer[pos])) {
371 while (lookAgain && (pos < _len));
375 //NSLog(@"end at '%c' (%i)", _buffer[pos], pos);
380 static NSString *_parseIdentifier(NSZone *_zone,
381 const unichar *_buffer, unsigned *_idx,
382 unsigned _len, NSException **_exception)
384 register unsigned pos = *_idx;
385 register unsigned len = 0;
386 unsigned startPos = pos;
388 // skip comments and spaces
389 if (!_skipComments(_buffer, _idx, _len, _exception)) {
390 // EOF reached during comment-skipping
391 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
392 @"did not find an id (expected 'a-zA-Z0-9') !");
396 // loop until break char
397 while (_isIdChar(_buffer[pos]) && (pos < _len)) {
402 if (len == 0) { // wasn't a string ..
404 _makeException(*_exception, _buffer, *_idx, _len,
405 @"did not find an id (expected 'a-zA-Z0-9') !");
410 return [[StrClass alloc] initWithCharacters:&(_buffer[startPos])
414 static NSString *_parseKeyPath(NSZone *_zone,
415 const unichar *_buffer, unsigned *_idx,
416 unsigned _len, NSException **_exception,
419 NSMutableString *keypath = nil;
420 NSString *component = nil;
422 if (!_skipComments(_buffer, _idx, _len, _exception)) {
423 // EOF reached during comment-skipping
424 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
425 @"did not find keypath (expected id)");
429 component = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
430 if (component == nil) {
431 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
432 @"did not find keypath (expected id)");
435 if (_buffer[*_idx] != '.') // single id-keypath
438 keypath = [[NSMutableString allocWithZone:_zone] init];
439 [keypath appendString:component];
441 while ((_buffer[*_idx] == '.') && (component != nil)) {
442 *_idx += 1; // skip '.'
443 [keypath appendString:@"."];
445 [component release]; component = nil;
446 component = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
448 if (component == nil) {
449 [keypath release]; keypath = nil;
451 _makeException(*_exception, _buffer, *_idx, _len,
452 @"expected component after '.' in keypath !");
456 [keypath appendString:component];
458 [component release]; component = nil;
463 static NSString *_parseQString(NSZone *_zone,
464 const unichar *_buffer, unsigned *_idx,
465 unsigned _len, NSException **_exception,
468 // skip comments and spaces
469 if (!_skipComments(_buffer, _idx, _len, _exception)) {
470 // EOF reached during comment-skipping
471 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
472 @"did not find a quoted string (expected '\"') !");
476 if (_buffer[*_idx] != '"') { // it's not a quoted string that's follows
478 _makeException(*_exception, _buffer, *_idx, _len,
479 @"did not find quoted string (expected '\"')");
482 else { // a quoted string
483 register unsigned pos = *_idx;
484 register unsigned len = 0;
485 unsigned startPos = pos + 1;
486 BOOL containsEscaped = NO;
488 pos++; // skip starting quote
490 // loop until closing quote
491 while ((_buffer[pos] != '"') && (pos < _len)) {
492 if (_buffer[pos] == '\\') {
493 containsEscaped = YES;
494 pos++; // skip following char
497 _makeException(*_exception, _buffer, *_idx, _len,
498 @"escape in quoted string not finished !");
506 if (pos == _len) { // syntax error, quote not closed
509 _makeException(*_exception, _buffer, *_idx, _len,
510 @"quoted string not closed (expected '\"')");
514 pos++; // skip closing quote
515 *_idx = pos; // store pointer
518 if (len == 0) /* empty string */
521 if (containsEscaped) {
522 register unsigned pos2;
526 NSCAssert(len > 0, @"invalid length ..");
527 str = calloc(len + 3, sizeof(unichar));
529 for (pos = startPos, pos2 = 0; _buffer[pos] != '"'; pos++, pos2++) {
530 //NSLog(@"char=%c pos=%i pos2=%i", _buffer[pos], pos2);
531 if (_buffer[pos] == '\\') {
533 switch (_buffer[pos]) {
534 case 'a': str[pos2] = '\a'; break;
535 case 'b': str[pos2] = '\b'; break;
536 case 'f': str[pos2] = '\f'; break;
537 case 'n': str[pos2] = '\n'; break;
538 case 't': str[pos2] = '\t'; break;
539 case 'v': str[pos2] = '\v'; break;
540 case '\\': str[pos2] = '\\'; break;
543 str[pos2] = _buffer[pos];
548 str[pos2] = _buffer[pos];
552 NSCAssert(pos2 == len, @"invalid unescape ..");
554 ostr = [[StrClass alloc] initWithCharacters:str length:pos2];
555 if (str != NULL) free(str); str = NULL;
560 NSCAssert(len > 0, @"invalid length ..");
561 return [[StrClass alloc] initWithCharacters:&(_buffer[startPos])
567 static NSData *_parseData(NSZone *_zone, const unichar *_buffer,
568 unsigned *_idx, unsigned _len,
569 NSException **_exception,
572 if (!_skipComments(_buffer, _idx, _len, _exception)) {
573 // EOF reached during comment-skipping
574 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
575 @"did not find a data (expected '<') !");
579 if (_buffer[*_idx] != '<') { // it's not a data that's follows
580 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
581 @"did not find a data (expected '<') !");
585 register unsigned pos = *_idx + 1;
586 register unsigned len = 0;
588 NSMutableData *data = nil;
590 *_idx += 1; // skip '<'
592 if (!_skipComments(_buffer, _idx, _len, _exception)) {
593 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
594 @"data was not closed (expected '>') ..");
598 if (_buffer[*_idx] == '>') { // empty data
599 *_idx += 1; // skip '>'
600 return [[NSData allocWithZone:_zone] init];
603 // count significant chars
604 while ((_buffer[pos] != '>') && (pos < _len)) {
605 if ((_buffer[pos] == ' ') || (_buffer[pos] == '\t'))
607 else if (_isHexDigit(_buffer[pos]))
611 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
612 @"invalid char in data property");
619 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
620 @"data was not closed (expected '>')");
623 endPos = pos; // store position of closing '>'
625 // if odd, then add one byte for trailing nibble
626 len = (len % 2 == 1) ? len / 2 + 1 : len / 2;
627 data = [[NSMutableData allocWithZone:_zone] initWithLength:len];
629 /* now copy bytes ... */
632 register int pending = -1;
633 char *buf = [data mutableBytes];
635 for (pos = *_idx, i = 0; (pos < endPos) && (i < len); pos++) {
636 int value = _valueOfHexChar(_buffer[pos]);
642 value = value * 16 + pending;
650 if (pending != -1) { // was odd, now add the trailer ..
651 NSCAssert(i < len, @"invalid length ..");
652 buf[i] = pending * 16;
656 // update global position
657 *_idx = endPos + 1; // endPos + 1 (*endPos == '>', 1 => skips '>')
663 static NSDictionary *_parseDict(NSZone *_zone,
664 const unichar *_buffer, unsigned *_idx,
665 unsigned _len, NSException **_exception,
668 if (!_skipComments(_buffer, _idx, _len, _exception)) {
669 // EOF reached during comment-skipping
670 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
671 @"did not find dictionary (expected '{')");
675 if (_buffer[*_idx] != '{') { // it's not a dict that's follows
676 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
677 @"did not find dictionary (expected '{')");
681 NSMutableDictionary *result = nil;
686 *_idx += 1; // skip '{'
688 if (!_skipComments(_buffer, _idx, _len, _exception)) {
689 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
690 @"dictionary was not closed (expected '}')");
694 if (_buffer[*_idx] == '}') { // an empty dictionary
695 *_idx += 1; // skip the '}'
696 return [[DictClass allocWithZone:_zone] init];
699 result = [[DictClass allocWithZone:_zone] init];
704 if (!_skipComments(_buffer, _idx, _len, _exception)) {
706 _makeException(*_exception, _buffer, *_idx, _len,
707 @"dictionary was not closed (expected '}')");
709 break; // unexpected EOF
712 if (_buffer[*_idx] == '}') { // dictionary closed
713 *_idx += 1; // skip the '}'
718 key = _parseProperty(_zone, _buffer, _idx, _len, _exception, NO, self);
719 if (key == nil) { // syntax error
720 if (*_exception == nil) {
721 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
722 @"got nil-key in dictionary ..");
728 /* The following parses: (comment|space)* '=' (comment|space)* */
729 if (!_skipComments(_buffer, _idx, _len, _exception)) {
730 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
731 @"expected '=' after key in dictionary");
733 break; // unexpected EOF
735 // no we need a '=' assignment
736 if (_buffer[*_idx] != '=') {
737 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
738 @"expected '=' after key in dictionary");
742 *_idx += 1; // skip '='
743 if (!_skipComments(_buffer, _idx, _len, _exception)) {
745 _makeException(*_exception, _buffer, *_idx, _len,
746 @"expected value after key '=' in dictionary");
748 break; // unexpected EOF
751 // read value property
752 value = _parseProperty(_zone, _buffer, _idx, _len, _exception, NO, self);
759 if (value == nil) { // syntax error
760 if (*_exception == nil) {
761 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
762 @"got nil-value in dictionary");
769 if ((key != nil) && (value != nil))
770 [result setObject:value forKey:key];
772 // release key and value
773 RELEASE(key); key = nil;
774 RELEASE(value); value = nil;
776 // read trailing ';' if available
777 if (!_skipComments(_buffer, _idx, _len, _exception)) {
779 _makeException(*_exception, _buffer, *_idx, _len,
780 @"dictionary was not closed (expected '}')");
782 break; // unexpected EOF
784 if (_buffer[*_idx] == ';') {
785 *_idx += 1; // skip ';'
787 else { // no ';' at end of pair, only allowed at end of dictionary
788 if (!_skipComments(_buffer, _idx, _len, _exception)) {
790 _makeException(*_exception, _buffer, *_idx, _len,
791 @"dictionary was not closed (expected '}')");
793 break; // unexpected EOF
796 if (_buffer[*_idx] != '}') { // dictionary wasn't closed
798 _makeException(*_exception, _buffer, *_idx, _len,
799 @"key-value pair without ';' at the end");
805 while ((*_idx < _len) && (result != nil) && !didFail);
807 RELEASE(key); key = nil;
808 RELEASE(value); value = nil;
810 [result release]; result = nil;
818 static NSArray *_parseArray(NSZone *_zone, const unichar *_buffer, unsigned *_idx,
819 unsigned _len, NSException **_exception,
822 if (!_skipComments(_buffer, _idx, _len, _exception)) {
823 // EOF reached during comment-skipping
824 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
825 @"did not find array (expected '(')");
829 if (_buffer[*_idx] != '(') { // it's not an array that's follows
830 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
831 @"did not find array (expected '(')");
835 NSMutableArray *result = nil;
838 *_idx += 1; // skip '('
840 if (!_skipComments(_buffer, _idx, _len, _exception)) {
841 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
842 @"array was not closed (expected ')')");
846 if (_buffer[*_idx] == ')') { // an empty array
847 *_idx += 1; // skip the ')'
848 return [[NSArray allocWithZone:_zone] init];
851 result = [[NSMutableArray allocWithZone:_zone] init];
853 element = _parseProperty(_zone, _buffer, _idx, _len, _exception, NO, self);
854 if (element == nil) {
855 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
856 @"expected element in array");
857 [result release]; result = nil;
860 [result addObject:element];
861 [element release]; element = nil;
863 if (!_skipComments(_buffer, _idx, _len, _exception)) {
865 _makeException(*_exception, _buffer, *_idx, _len,
866 @"array was not closed (expected ')' or ',')");
872 if (_buffer[*_idx] == ')') { // closed array
873 *_idx += 1; // skip ')'
876 else if (_buffer[*_idx] == ',') { // next element
877 *_idx += 1; // skip ','
879 if (!_skipComments(_buffer, _idx, _len, _exception)) {
880 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
881 @"array was not closed (expected ')')");
886 if (_buffer[*_idx] == ')') { // closed array, like this '(1,2,)'
887 *_idx += 1; // skip ')'
891 else { // syntax error
893 _makeException(*_exception, _buffer, *_idx, _len,
894 @"expected ')' or ',' after array element");
895 [result release]; result = nil;
899 while ((*_idx < _len) && (result != nil));
901 [element release]; element = nil;
907 static NSNumber *_parseDigitPath(NSString *digitPath) {
910 r = [digitPath rangeOfString:@"."];
912 ? [NumberClass numberWithDouble:[digitPath doubleValue]]
913 : [NumberClass numberWithInt:[digitPath intValue]];
916 static BOOL _ucIsEqual(const unichar *s, char *tok, unsigned len) {
919 case 1: return (s[0] == tok[0]) ? YES : NO;
921 if (s[0] != tok[0] || s[0] == 0) return NO;
922 if (s[1] != tok[1]) return NO;
925 if (s[0] != tok[0] || s[0] == 0) return NO;
926 if (s[1] != tok[1] || s[1] == 0) return NO;
927 if (s[2] != tok[2]) return NO;
930 register unsigned int i;
932 for (i = 0; i < len; i++) {
933 if (s[i] != tok[i] || s[i] == 0) return NO;
941 static id _parseProperty(NSZone *_zone, const unichar *_buffer, unsigned *_idx,
943 NSException **_exception, BOOL _allowAssoc,
946 BOOL valueProperty = YES;
949 if (!_skipComments(_buffer, _idx, _len, _exception))
952 switch (_buffer[*_idx]) {
953 case '"': // quoted string
954 NSCAssert(result == nil, @"result is already set ..");
955 result = _parseQString(_zone, _buffer, _idx, _len, _exception, self);
958 case '{': // dictionary
959 NSCAssert(result == nil, @"result is already set ..");
960 result = _parseDict(_zone, _buffer, _idx, _len, _exception, self);
964 NSCAssert(result == nil, @"result is already set ..");
965 result = _parseArray(_zone, _buffer, _idx, _len, _exception, self);
969 NSCAssert(result == nil, @"result is already set ..");
970 result = _parseData(_zone, _buffer, _idx, _len, _exception, self);
974 NSCAssert(result == nil, @"result is already set ..");
976 if (isdigit((int)_buffer[*_idx]) || (_buffer[*_idx] == '-')) {
978 NSCAssert(result == nil, @"result is already set ..");
980 digitPath = _parseKeyPath(_zone, _buffer, _idx, _len, _exception,self);
981 result = [_parseDigitPath(digitPath) retain];
982 [digitPath release]; digitPath = nil;
985 else if (_isIdChar(_buffer[*_idx])) {
988 if ((_buffer[*_idx] == 'Y') || (_buffer[*_idx] == 'N')) {
990 if ((*_idx + 4) < _len) {
991 if (_ucIsEqual(&(_buffer[*_idx]), "YES", 3) &&
992 _isBreakChar(_buffer[*_idx + 3])) {
993 result = [yesNum retain];
995 *_idx += 3; // skip the YES
998 if (((*_idx + 3) < _len) && !valueProperty) {
999 if (_ucIsEqual(&(_buffer[*_idx]), "NO", 2) &&
1000 _isBreakChar(_buffer[*_idx + 2])) {
1001 result = [noNum retain];
1002 valueProperty = YES;
1003 *_idx += 2; // skip the NO
1007 else if ((_buffer[*_idx] == 't') || (_buffer[*_idx] == 'f')) {
1008 // parse true and false
1009 if ((*_idx + 5) < _len) {
1010 if (_ucIsEqual(&(_buffer[*_idx]), "true", 4) &&
1011 _isBreakChar(_buffer[*_idx + 4])) {
1012 result = [yesNum retain];
1013 valueProperty = YES;
1014 *_idx += 4; // skip the true
1017 if (((*_idx + 6) < _len) && !valueProperty) {
1018 if (_ucIsEqual(&(_buffer[*_idx]), "false", 5) &&
1019 _isBreakChar(_buffer[*_idx + 5])) {
1020 result = [noNum retain];
1021 valueProperty = YES;
1022 *_idx += 5; // skip the false
1027 if (!valueProperty) {
1028 NSCAssert(result == nil, @"result already set ..");
1029 result = _parseKeyPath(_zone, _buffer, _idx, _len, _exception, self);
1033 NSCAssert(result == nil, @"result already set ..");
1035 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1044 if (result == nil) {
1045 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1046 @"error in property value");
1049 NSCAssert(result, @"missing property value ..");
1054 result = valueProperty
1055 ? [self associationWithValue:result]
1056 : [self associationWithKeyPath:result];
1059 NSCAssert(result, @"got no association for property ..");
1061 [old release]; old = nil;
1063 return [result retain];
1066 /* result is already retained */
1070 static NSDictionary *_parseWodConfig(NSZone *_zone, const unichar *_buffer,
1071 unsigned *_idx, unsigned _len,
1072 NSException **_exception,
1075 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1076 // EOF reached during comment-skipping
1077 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1078 @"did not find element configuration (expected '{')");
1082 if (_buffer[*_idx] != '{') { // it's not a dict that's follows
1083 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1084 @"did not find element configuration (expected '{')");
1088 NSMutableDictionary *result = nil;
1089 NSString *key = nil;
1093 *_idx += 1; // skip '{'
1095 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1096 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1097 @"element configuration was not closed (expected '}')");
1101 if (_buffer[*_idx] == '}') { // an empty configuration
1102 *_idx += 1; // skip the '}'
1103 return [[DictClass allocWithZone:_zone] init];
1106 result = [[DictClass allocWithZone:_zone] init];
1111 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1113 _makeException(*_exception, _buffer, *_idx, _len,
1114 @"dictionary was not closed (expected '}')");
1116 break; // unexpected EOF
1119 if (_buffer[*_idx] == '}') { // dictionary closed
1120 *_idx += 1; // skip the '}'
1124 // read key property
1125 key = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
1126 if (key == nil) { // syntax error
1127 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1128 @"expected identifier in element configuration ..");
1133 /* The following parses: (comment|space)* '=' (comment|space)* */
1134 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1135 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1136 @"expected '=' after id in element configuration");
1138 break; // unexpected EOF
1140 // no we need a '=' assignment
1141 if (_buffer[*_idx] != '=') {
1142 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1143 @"expected '=' after id in element configuration");
1147 *_idx += 1; // skip '='
1148 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1150 _makeException(*_exception, _buffer, *_idx, _len,
1151 @"expected value after id '=' in "
1152 @"element configuration");
1154 break; // unexpected EOF
1157 // read value property
1158 value = _parseProperty(_zone, _buffer, _idx, _len, _exception, YES, self);
1165 if (value == nil) { // syntax error
1166 if (*_exception == nil) {
1168 _makeException(*_exception, _buffer, *_idx, _len,
1169 @"got nil-value in element configuration");
1174 NSCAssert(key, @"invalid key ..");
1175 NSCAssert(value, @"invalid value ..");
1178 if ((value != nil) && (key != nil))
1179 [result setObject:value forKey:key];
1181 // release key and value
1182 RELEASE(key); key = nil;
1183 RELEASE(value); value = nil;
1185 // read trailing ';' if available
1186 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1188 _makeException(*_exception, _buffer, *_idx, _len,
1189 @"element configuration was not "
1190 @"closed (expected '}')");
1192 break; // unexpected EOF
1194 if (_buffer[*_idx] == ';') {
1195 *_idx += 1; // skip ';'
1197 else { // no ';' at end of pair, only allowed at end of dictionary
1198 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1200 _makeException(*_exception, _buffer, *_idx, _len,
1201 @"element configuration was not "
1202 @"closed (expected '}')");
1204 break; // unexpected EOF
1207 if (_buffer[*_idx] != '}') { // config wasn't closed
1209 _makeException(*_exception, _buffer, *_idx, _len,
1210 @"key-value pair without ';' at the end");
1216 while ((*_idx < _len) && (result != nil) && !didFail);
1218 [key release]; key = nil;
1219 [value release]; value = nil;
1222 [result release]; result = nil;
1230 static id _parseWodEntry(NSZone *_zone, const unichar *_buffer, unsigned *_idx,
1231 unsigned _len, NSException **_exception,
1232 NSString **_name, NSString **_class, id self)
1234 NSString *elementName = nil;
1235 NSString *componentName = nil;
1236 NSDictionary *config = nil;
1241 if (!_skipComments(_buffer, _idx, _len, _exception))
1245 elementName = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
1246 if (elementName == nil) {
1247 *_exception = _makeException(nil, _buffer, *_idx, _len,
1248 @"expected element name");
1252 if (!_skipComments(_buffer, _idx, _len, _exception))
1255 // Element/Component separator
1256 if (_buffer[*_idx] == ':') {
1257 *_idx += 1; // skip ':'
1260 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1261 @"expected ':' after element name");
1265 if (!_skipComments(_buffer, _idx, _len, _exception))
1269 componentName = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
1270 if (componentName == nil) {
1271 *_exception = _makeException(nil, _buffer, *_idx, _len,
1272 @"expected component name");
1276 if (!_skipComments(_buffer, _idx, _len, _exception))
1280 config = _parseWodConfig(_zone, _buffer, _idx, _len, _exception, self);
1284 //NSLog(@"%@ : %@ %@", elementName, componentName, config);
1286 // read trailing ';' if available
1287 if (_skipComments(_buffer, _idx, _len, _exception)) {
1288 if (_buffer[*_idx] == ';') {
1289 *_idx += 1; // skip ';'
1293 *_name = elementName;
1294 *_class = componentName;
1299 NSLog(@"failed at %@:%@ ..", elementName, componentName);
1301 [elementName release]; elementName = nil;
1302 [componentName release]; componentName = nil;
1303 [config release]; config = nil;
1307 @end /* WODParser */