2 Copyright (C) 2000-2004 SKYRIX Software AG
4 This file is part of OpenGroupware.org.
6 OGo 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 OGo 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 OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
23 #include "WODParser.h"
26 @implementation WODParser
28 static Class StrClass = Nil;
29 static Class DictClass = Nil;
30 static Class NumberClass = Nil;
31 static NSNumber *yesNum = nil;
32 static NSNumber *noNum = nil;
33 static BOOL useUTF8 = NO;
36 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
38 StrClass = [NSString class];
39 DictClass = [NSMutableDictionary class];
40 NumberClass = [NSNumber class];
42 if (yesNum == nil) yesNum = [[NumberClass numberWithBool:YES] retain];
43 if (noNum == nil) noNum = [[NumberClass numberWithBool:NO] retain];
45 useUTF8 = [ud boolForKey:@"WOParsersUseUTF8"];
48 - (id)initWithHandler:(id<WODParserHandler,NSObject>)_handler {
49 ASSIGN(self->callback, _handler);
53 [self->callback release];
59 - (id)associationWithValue:(id)_value {
60 return [self->callback parser:self makeAssociationWithValue:_value];
62 - (id)associationWithKeyPath:(NSString *)_keyPath {
63 return [self->callback parser:self makeAssociationWithKeyPath:_keyPath];
66 - (id)elementDefinitionForComponent:(NSString *)_cname
67 associations:(id)_entry
68 elementName:(NSString *)_elemName
70 return [self->callback parser:self
71 makeDefinitionForComponentNamed:_cname
73 elementName:_elemName];
78 static id _parseProperty(NSZone *_zone, const char *_buffer, unsigned *_idx,
79 unsigned _len, NSException **_exception,
80 BOOL _allowAssoc, id self);
81 static id _parseWodEntry(NSZone *_zone, const char *_buffer, unsigned *_idx,
82 unsigned _len, NSException **_exception,
83 NSString **_name, NSString **_class,
86 static NSString *_makeStringForBuffer(const unsigned char *_buf, unsigned _l) {
87 // TODO: duplicate code with WOHTMLParser, but probably isn't worth a
96 return [[StrClass alloc] initWithCString:_buf length:_l];
98 // Note: we cast the pointer because we are not going to modify _buf for the
99 // duration and we are never going to write the data - should work
100 // with any Foundation, but isn't strictly API compatible
101 data = [[NSData alloc] initWithBytesNoCopy:(void *)_buf length:_l
103 r = [[StrClass alloc] initWithData:data encoding:NSUTF8StringEncoding];
108 - (NSException *)parseDefinitionsFromBuffer:(const char *)_buffer
109 length:(unsigned)_len
110 mappings:(NSMutableDictionary *)_mappings
112 NSException *exception = nil;
113 NSString *elementName = nil;
114 NSString *componentName = nil;
118 [_mappings removeAllObjects];
120 while ((entry = _parseWodEntry(NULL, _buffer, &idx, _len, &exception,
121 &elementName, &componentName,
126 RELEASE(entry); entry = nil;
127 RELEASE(elementName); elementName = nil;
128 RELEASE(componentName); componentName = nil;
132 if ([_mappings objectForKey:elementName] != nil)
133 NSLog(@"WARNING: duplicate definition of element %@ !", elementName);
135 def = [self elementDefinitionForComponent:componentName
137 elementName:elementName];
139 RELEASE(componentName); componentName = nil;
140 RELEASE(entry); entry = nil;
142 if ((def != nil) && (elementName != nil))
143 [_mappings setObject:def forKey:elementName];
145 NSLog(@"defined element %@ definition=%@", elementName, def);
147 RELEASE(elementName); elementName = nil;
155 - (NSDictionary *)parseDeclarationData:(NSData *)_decl {
156 NSMutableDictionary *defs;
159 if (![self->callback parser:self willParseDeclarationData:_decl])
162 defs = [NSMutableDictionary dictionaryWithCapacity:100];
164 ex = [self parseDefinitionsFromBuffer:[_decl bytes]
165 length:[_decl length]
169 [self->callback parser:self failedParsingDeclarationData:_decl exception:ex];
171 [self->callback parser:self finishedParsingDeclarationData:_decl
179 static int _numberOfLines(const char *_buffer, unsigned _lastIdx) {
180 register unsigned pos, lineCount = 1;
182 for (pos = 0; (pos < _lastIdx) && (_buffer[pos] != '\0'); pos++) {
183 if (_buffer[pos] == '\n')
189 static inline BOOL _isBreakChar(unsigned char _c) {
191 case ' ': case '\t': case '\n': case '\r':
192 case '=': case ';': case ',':
193 case '{': case '(': case '"': case '<':
202 static inline BOOL _isIdChar(unsigned char _c) {
203 return (_isBreakChar(_c) && (_c != '.')) ? NO : YES;
206 static inline int _valueOfHexChar(unsigned char _c) {
208 case '0': case '1': case '2': case '3': case '4':
209 case '5': case '6': case '7': case '8': case '9':
210 return (_c - 48); // 0-9 (ascii-char)'0' - 48 => (int)0
212 case 'A': case 'B': case 'C':
213 case 'D': case 'E': case 'F':
214 return (_c - 55); // A-F, A=10..F=15, 'A'=65..'F'=70
216 case 'a': case 'b': case 'c':
217 case 'd': case 'e': case 'f':
218 return (_c - 87); // a-f, a=10..F=15, 'a'=97..'f'=102
224 static inline BOOL _isHexDigit(unsigned char _c) {
226 case '0': case '1': case '2': case '3': case '4':
227 case '5': case '6': case '7': case '8': case '9':
228 case 'A': case 'B': case 'C':
229 case 'D': case 'E': case 'F':
230 case 'a': case 'b': case 'c':
231 case 'd': case 'e': case 'f':
239 static NSException *_makeException(NSException *_exception,
240 const char *_buffer, unsigned _idx,
241 unsigned _len, NSString *_text)
243 NSMutableDictionary *ui = nil;
244 NSException *exception = nil;
248 numLines = _numberOfLines(_buffer, _idx);
249 atEof = (_idx >= _len) ? YES : NO;
252 // error resulted from a previous error (exception already set)
256 _text = [@"Unexpected end: " stringByAppendingString:[_text stringValue]];
258 _text = [StrClass stringWithFormat:@"Syntax error in line %i: %@",
263 ui = [[exception userInfo] mutableCopy];
265 ui = [[DictClass alloc] initWithCapacity:8];
267 [ui setObject:[NumberClass numberWithInt:numLines] forKey:@"line"];
268 [ui setObject:[NumberClass numberWithInt:_len] forKey:@"size"];
269 [ui setObject:[NumberClass numberWithInt:_idx] forKey:@"position"];
271 if (!atEof && (_idx > 0)) {
272 register unsigned pos;
273 const char *startPos, *endPos;
275 for (pos = _idx; (pos >= 0) && (_buffer[pos] != '\n'); pos--)
277 startPos = &(_buffer[pos + 1]);
279 for (pos = _idx; ((pos < _len) && (_buffer[pos] != '\n')); pos++)
281 endPos = &(_buffer[pos - 1]);
283 if (startPos < endPos) {
286 ll = _makeStringForBuffer(startPos, endPos - startPos);
287 [ui setObject:ll forKey:@"lastLine"];
291 NSLog(@"%s: startPos=0x%08X endPos=0x%08X", __PRETTY_FUNCTION__,
296 exception = [NSException exceptionWithName:@"SyntaxError"
299 [ui release]; ui = nil;
304 static BOOL _skipComments(const char *_buffer, unsigned *_idx, unsigned _len,
305 NSException **_exception)
307 register unsigned pos = *_idx;
313 //NSLog(@"start at '%c' (%i)", _buffer[pos], pos);
315 do { // until all comments are filtered ..
318 if ((_buffer[pos] == '/') && (pos + 1 < _len)) {
319 if (_buffer[pos + 1] == '/') { // single line comments
320 pos += 2; // skip '//'
322 // search for '\n' ..
323 while ((pos < _len) && (_buffer[pos] != '\n'))
326 if ((pos < _len) && (_buffer[pos] == '\n')) {
327 pos++; // skip newline, otherwise EOF was reached
331 else if (_buffer[pos + 1] == '*') { /* multiline comments */
332 BOOL commentIsClosed = NO;
334 pos += 2; // skip '/*'
336 do { // search for '*/'
337 while ((pos < _len) && (_buffer[pos] != '*'))
340 if (pos < _len) { // found '*'
341 if ((pos + 1) < _len) {
342 if (_buffer[pos + 1] == '/') { // found '*/'
343 commentIsClosed = YES;
344 pos += 2; // skip '*/'
353 if (!commentIsClosed) {
354 // EOF found, comment wasn't closed
356 _makeException(*_exception, _buffer, *_idx, _len,
357 @"comment was not closed (expected '*/')");
362 else if (isspace((int)_buffer[pos])) {
367 while (lookAgain && (pos < _len));
371 //NSLog(@"end at '%c' (%i)", _buffer[pos], pos);
376 static NSString *_parseIdentifier(NSZone *_zone,
377 const char *_buffer, unsigned *_idx,
378 unsigned _len, NSException **_exception)
380 register unsigned pos = *_idx;
381 register unsigned len = 0;
382 unsigned startPos = pos;
384 // skip comments and spaces
385 if (!_skipComments(_buffer, _idx, _len, _exception)) {
386 // EOF reached during comment-skipping
387 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
388 @"did not find an id (expected 'a-zA-Z0-9') !");
392 // loop until break char
393 while (_isIdChar(_buffer[pos]) && (pos < _len)) {
398 if (len == 0) { // wasn't a string ..
400 _makeException(*_exception, _buffer, *_idx, _len,
401 @"did not find an id (expected 'a-zA-Z0-9') !");
406 return _makeStringForBuffer(&(_buffer[startPos]), len);
409 static NSString *_parseKeyPath(NSZone *_zone,
410 const char *_buffer, unsigned *_idx,
411 unsigned _len, NSException **_exception,
414 NSMutableString *keypath = nil;
415 NSString *component = nil;
417 if (!_skipComments(_buffer, _idx, _len, _exception)) {
418 // EOF reached during comment-skipping
419 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
420 @"did not find keypath (expected id)");
424 component = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
425 if (component == nil) {
426 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
427 @"did not find keypath (expected id)");
430 if (_buffer[*_idx] != '.') // single id-keypath
433 keypath = [[NSMutableString allocWithZone:_zone] init];
434 [keypath appendString:component];
436 while ((_buffer[*_idx] == '.') && (component != nil)) {
437 *_idx += 1; // skip '.'
438 [keypath appendString:@"."];
440 RELEASE(component); component = nil;
441 component = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
443 if (component == nil) {
444 RELEASE(keypath); keypath = nil;
446 _makeException(*_exception, _buffer, *_idx, _len,
447 @"expected component after '.' in keypath !");
451 [keypath appendString:component];
453 RELEASE(component); component = nil;
458 static NSString *_parseQString(NSZone *_zone,
459 const char *_buffer, unsigned *_idx,
460 unsigned _len, NSException **_exception,
463 // skip comments and spaces
464 if (!_skipComments(_buffer, _idx, _len, _exception)) {
465 // EOF reached during comment-skipping
466 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
467 @"did not find a quoted string (expected '\"') !");
471 if (_buffer[*_idx] != '"') { // it's not a quoted string that's follows
472 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
473 @"did not find quoted string (expected '\"')");
476 else { // a quoted string
477 register unsigned pos = *_idx;
478 register unsigned len = 0;
479 unsigned startPos = pos + 1;
480 BOOL containsEscaped = NO;
482 pos++; // skip starting quote
484 // loop until closing quote
485 while ((_buffer[pos] != '"') && (pos < _len)) {
486 if (_buffer[pos] == '\\') {
487 containsEscaped = YES;
488 pos++; // skip following char
491 _makeException(*_exception, _buffer, *_idx, _len,
492 @"escape in quoted string not finished !");
500 if (pos == _len) { // syntax error, quote not closed
502 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
503 @"quoted string not closed (expected '\"')");
507 pos++; // skip closing quote
508 *_idx = pos; // store pointer
511 if (len == 0) { // empty string
514 else if (containsEscaped) {
515 register unsigned pos2;
519 NSCAssert(len > 0, @"invalid length ..");
520 str = malloc(len + 3);
522 for (pos = startPos, pos2 = 0; _buffer[pos] != '"'; pos++, pos2++) {
523 //NSLog(@"char=%c pos=%i pos2=%i", _buffer[pos], pos2);
524 if (_buffer[pos] == '\\') {
526 switch (_buffer[pos]) {
527 case 'a': str[pos2] = '\a'; break;
528 case 'b': str[pos2] = '\b'; break;
529 case 'f': str[pos2] = '\f'; break;
530 case 'n': str[pos2] = '\n'; break;
531 case 't': str[pos2] = '\t'; break;
532 case 'v': str[pos2] = '\v'; break;
533 case '\\': str[pos2] = '\\'; break;
536 str[pos2] = _buffer[pos];
541 str[pos2] = _buffer[pos];
545 NSCAssert(pos2 == len, @"invalid unescape ..");
547 ostr = _makeStringForBuffer(str, pos2);
548 if (str) free(str); str = NULL;
553 NSCAssert(len > 0, @"invalid length ..");
554 return _makeStringForBuffer(&(_buffer[startPos]), len);
559 static NSData *_parseData(NSZone *_zone, const char *_buffer,
560 unsigned *_idx, unsigned _len,
561 NSException **_exception,
564 if (!_skipComments(_buffer, _idx, _len, _exception)) {
565 // EOF reached during comment-skipping
566 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
567 @"did not find a data (expected '<') !");
571 if (_buffer[*_idx] != '<') { // it's not a data that's follows
572 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
573 @"did not find a data (expected '<') !");
577 register unsigned pos = *_idx + 1;
578 register unsigned len = 0;
580 NSMutableData *data = nil;
582 *_idx += 1; // skip '<'
584 if (!_skipComments(_buffer, _idx, _len, _exception)) {
585 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
586 @"data was not closed (expected '>') ..");
590 if (_buffer[*_idx] == '>') { // empty data
591 *_idx += 1; // skip '>'
592 return [[NSData allocWithZone:_zone] init];
595 // count significant chars
596 while ((_buffer[pos] != '>') && (pos < _len)) {
597 if ((_buffer[pos] == ' ') || (_buffer[pos] == '\t'))
599 else if (_isHexDigit(_buffer[pos]))
603 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
604 @"invalid char in data property");
611 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
612 @"data was not closed (expected '>')");
615 endPos = pos; // store position of closing '>'
617 // if odd, then add one byte for trailing nibble
618 len = (len % 2 == 1) ? len / 2 + 1 : len / 2;
619 data = [[NSMutableData allocWithZone:_zone] initWithLength:len];
621 /* now copy bytes ... */
624 register int pending = -1;
625 char *buf = [data mutableBytes];
627 for (pos = *_idx, i = 0; (pos < endPos) && (i < len); pos++) {
628 int value = _valueOfHexChar(_buffer[pos]);
634 value = value * 16 + pending;
642 if (pending != -1) { // was odd, now add the trailer ..
643 NSCAssert(i < len, @"invalid length ..");
644 buf[i] = pending * 16;
648 // update global position
649 *_idx = endPos + 1; // endPos + 1 (*endPos == '>', 1 => skips '>')
655 static NSDictionary *_parseDict(NSZone *_zone,
656 const char *_buffer, unsigned *_idx,
657 unsigned _len, NSException **_exception,
660 if (!_skipComments(_buffer, _idx, _len, _exception)) {
661 // EOF reached during comment-skipping
662 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
663 @"did not find dictionary (expected '{')");
667 if (_buffer[*_idx] != '{') { // it's not a dict that's follows
668 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
669 @"did not find dictionary (expected '{')");
673 NSMutableDictionary *result = nil;
678 *_idx += 1; // skip '{'
680 if (!_skipComments(_buffer, _idx, _len, _exception)) {
681 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
682 @"dictionary was not closed (expected '}')");
686 if (_buffer[*_idx] == '}') { // an empty dictionary
687 *_idx += 1; // skip the '}'
688 return [[DictClass allocWithZone:_zone] init];
691 result = [[DictClass allocWithZone:_zone] init];
696 if (!_skipComments(_buffer, _idx, _len, _exception)) {
698 _makeException(*_exception, _buffer, *_idx, _len,
699 @"dictionary was not closed (expected '}')");
701 break; // unexpected EOF
704 if (_buffer[*_idx] == '}') { // dictionary closed
705 *_idx += 1; // skip the '}'
710 key = _parseProperty(_zone, _buffer, _idx, _len, _exception, NO, self);
711 if (key == nil) { // syntax error
712 if (*_exception == nil) {
713 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
714 @"got nil-key in dictionary ..");
720 /* The following parses: (comment|space)* '=' (comment|space)* */
721 if (!_skipComments(_buffer, _idx, _len, _exception)) {
722 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
723 @"expected '=' after key in dictionary");
725 break; // unexpected EOF
727 // no we need a '=' assignment
728 if (_buffer[*_idx] != '=') {
729 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
730 @"expected '=' after key in dictionary");
734 *_idx += 1; // skip '='
735 if (!_skipComments(_buffer, _idx, _len, _exception)) {
737 _makeException(*_exception, _buffer, *_idx, _len,
738 @"expected value after key '=' in dictionary");
740 break; // unexpected EOF
743 // read value property
744 value = _parseProperty(_zone, _buffer, _idx, _len, _exception, NO, self);
751 if (value == nil) { // syntax error
752 if (*_exception == nil) {
753 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
754 @"got nil-value in dictionary");
761 if ((key != nil) && (value != nil))
762 [result setObject:value forKey:key];
764 // release key and value
765 RELEASE(key); key = nil;
766 RELEASE(value); value = nil;
768 // read trailing ';' if available
769 if (!_skipComments(_buffer, _idx, _len, _exception)) {
771 _makeException(*_exception, _buffer, *_idx, _len,
772 @"dictionary was not closed (expected '}')");
774 break; // unexpected EOF
776 if (_buffer[*_idx] == ';') {
777 *_idx += 1; // skip ';'
779 else { // no ';' at end of pair, only allowed at end of dictionary
780 if (!_skipComments(_buffer, _idx, _len, _exception)) {
782 _makeException(*_exception, _buffer, *_idx, _len,
783 @"dictionary was not closed (expected '}')");
785 break; // unexpected EOF
788 if (_buffer[*_idx] != '}') { // dictionary wasn't closed
790 _makeException(*_exception, _buffer, *_idx, _len,
791 @"key-value pair without ';' at the end");
797 while ((*_idx < _len) && (result != nil) && !didFail);
799 RELEASE(key); key = nil;
800 RELEASE(value); value = nil;
802 [result release]; result = nil;
810 static NSArray *_parseArray(NSZone *_zone, const char *_buffer, unsigned *_idx,
811 unsigned _len, NSException **_exception,
814 if (!_skipComments(_buffer, _idx, _len, _exception)) {
815 // EOF reached during comment-skipping
816 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
817 @"did not find array (expected '(')");
821 if (_buffer[*_idx] != '(') { // it's not an array that's follows
822 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
823 @"did not find array (expected '(')");
827 NSMutableArray *result = nil;
830 *_idx += 1; // skip '('
832 if (!_skipComments(_buffer, _idx, _len, _exception)) {
833 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
834 @"array was not closed (expected ')')");
838 if (_buffer[*_idx] == ')') { // an empty array
839 *_idx += 1; // skip the ')'
840 return [[NSArray allocWithZone:_zone] init];
843 result = [[NSMutableArray allocWithZone:_zone] init];
845 element = _parseProperty(_zone, _buffer, _idx, _len, _exception, NO, self);
846 if (element == nil) {
847 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
848 @"expected element in array");
849 [result release]; result = nil;
852 [result addObject:element];
853 [element release]; element = nil;
855 if (!_skipComments(_buffer, _idx, _len, _exception)) {
857 _makeException(*_exception, _buffer, *_idx, _len,
858 @"array was not closed (expected ')' or ',')");
864 if (_buffer[*_idx] == ')') { // closed array
865 *_idx += 1; // skip ')'
868 else if (_buffer[*_idx] == ',') { // next element
869 *_idx += 1; // skip ','
871 if (!_skipComments(_buffer, _idx, _len, _exception)) {
872 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
873 @"array was not closed (expected ')')");
878 if (_buffer[*_idx] == ')') { // closed array, like this '(1,2,)'
879 *_idx += 1; // skip ')'
883 else { // syntax error
885 _makeException(*_exception, _buffer, *_idx, _len,
886 @"expected ')' or ',' after array element");
887 [result release]; result = nil;
891 while ((*_idx < _len) && (result != nil));
893 [element release]; element = nil;
899 static NSNumber *_parseDigitPath(NSString *digitPath) {
902 r = [digitPath rangeOfString:@"."];
904 ? [NumberClass numberWithDouble:[digitPath doubleValue]]
905 : [NumberClass numberWithInt:[digitPath intValue]];
908 static id _parseProperty(NSZone *_zone, const char *_buffer, unsigned *_idx,
910 NSException **_exception, BOOL _allowAssoc,
913 BOOL valueProperty = YES;
916 if (!_skipComments(_buffer, _idx, _len, _exception))
919 switch (_buffer[*_idx]) {
920 case '"': // quoted string
921 NSCAssert(result == nil, @"result is already set ..");
922 result = _parseQString(_zone, _buffer, _idx, _len, _exception, self);
925 case '{': // dictionary
926 NSCAssert(result == nil, @"result is already set ..");
927 result = _parseDict(_zone, _buffer, _idx, _len, _exception, self);
931 NSCAssert(result == nil, @"result is already set ..");
932 result = _parseArray(_zone, _buffer, _idx, _len, _exception, self);
936 NSCAssert(result == nil, @"result is already set ..");
937 result = _parseData(_zone, _buffer, _idx, _len, _exception, self);
941 NSCAssert(result == nil, @"result is already set ..");
943 if (isdigit((int)_buffer[*_idx]) || (_buffer[*_idx] == '-')) {
945 NSCAssert(result == nil, @"result is already set ..");
947 digitPath = _parseKeyPath(_zone, _buffer, _idx, _len, _exception,self);
948 result = [_parseDigitPath(digitPath) retain];
949 [digitPath release]; digitPath = nil;
952 else if (_isIdChar(_buffer[*_idx])) {
955 if ((_buffer[*_idx] == 'Y') || (_buffer[*_idx] == 'N')) {
957 if ((*_idx + 4) < _len) {
958 if (strncmp(&(_buffer[*_idx]), "YES", 3) == 0 &&
959 _isBreakChar(_buffer[*_idx + 3])) {
960 result = [yesNum retain];
962 *_idx += 3; // skip the YES
965 if (((*_idx + 3) < _len) && !valueProperty) {
966 if (strncmp(&(_buffer[*_idx]), "NO", 2) == 0 &&
967 _isBreakChar(_buffer[*_idx + 2])) {
968 result = [noNum retain];
970 *_idx += 2; // skip the NO
974 else if ((_buffer[*_idx] == 't') || (_buffer[*_idx] == 'f')) {
975 // parse true and false
976 if ((*_idx + 5) < _len) {
977 if (strncmp(&(_buffer[*_idx]), "true", 4) == 0 &&
978 _isBreakChar(_buffer[*_idx + 4])) {
979 result = [yesNum retain];
981 *_idx += 4; // skip the true
984 if (((*_idx + 6) < _len) && !valueProperty) {
985 if (strncmp(&(_buffer[*_idx]), "false", 5) == 0 &&
986 _isBreakChar(_buffer[*_idx + 5])) {
987 result = [noNum retain];
989 *_idx += 5; // skip the false
994 if (!valueProperty) {
995 NSCAssert(result == nil, @"result already set ..");
996 result = _parseKeyPath(_zone, _buffer, _idx, _len, _exception, self);
1000 NSCAssert(result == nil, @"result already set ..");
1002 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1011 if (result == nil) {
1012 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1013 @"error in property value");
1016 NSCAssert(result, @"missing property value ..");
1021 result = valueProperty
1022 ? [self associationWithValue:result]
1023 : [self associationWithKeyPath:result];
1026 NSCAssert(result, @"got no association for property ..");
1028 [old release]; old = nil;
1030 return [result retain];
1033 /* result is already retained */
1037 static NSDictionary *_parseWodConfig(NSZone *_zone, const char *_buffer,
1038 unsigned *_idx, unsigned _len,
1039 NSException **_exception,
1042 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1043 // EOF reached during comment-skipping
1044 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1045 @"did not find element configuration (expected '{')");
1049 if (_buffer[*_idx] != '{') { // it's not a dict that's follows
1050 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1051 @"did not find element configuration (expected '{')");
1055 NSMutableDictionary *result = nil;
1056 NSString *key = nil;
1060 *_idx += 1; // skip '{'
1062 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1063 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1064 @"element configuration was not closed (expected '}')");
1068 if (_buffer[*_idx] == '}') { // an empty configuration
1069 *_idx += 1; // skip the '}'
1070 return [[DictClass allocWithZone:_zone] init];
1073 result = [[DictClass allocWithZone:_zone] init];
1078 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1080 _makeException(*_exception, _buffer, *_idx, _len,
1081 @"dictionary was not closed (expected '}')");
1083 break; // unexpected EOF
1086 if (_buffer[*_idx] == '}') { // dictionary closed
1087 *_idx += 1; // skip the '}'
1091 // read key property
1092 key = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
1093 if (key == nil) { // syntax error
1094 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1095 @"expected identifier in element configuration ..");
1100 /* The following parses: (comment|space)* '=' (comment|space)* */
1101 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1102 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1103 @"expected '=' after id in element configuration");
1105 break; // unexpected EOF
1107 // no we need a '=' assignment
1108 if (_buffer[*_idx] != '=') {
1109 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1110 @"expected '=' after id in element configuration");
1114 *_idx += 1; // skip '='
1115 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1117 _makeException(*_exception, _buffer, *_idx, _len,
1118 @"expected value after id '=' in "
1119 @"element configuration");
1121 break; // unexpected EOF
1124 // read value property
1125 value = _parseProperty(_zone, _buffer, _idx, _len, _exception, YES, self);
1132 if (value == nil) { // syntax error
1133 if (*_exception == nil) {
1135 _makeException(*_exception, _buffer, *_idx, _len,
1136 @"got nil-value in element configuration");
1141 NSCAssert(key, @"invalid key ..");
1142 NSCAssert(value, @"invalid value ..");
1145 if ((value != nil) && (key != nil))
1146 [result setObject:value forKey:key];
1148 // release key and value
1149 RELEASE(key); key = nil;
1150 RELEASE(value); value = nil;
1152 // read trailing ';' if available
1153 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1155 _makeException(*_exception, _buffer, *_idx, _len,
1156 @"element configuration was not "
1157 @"closed (expected '}')");
1159 break; // unexpected EOF
1161 if (_buffer[*_idx] == ';') {
1162 *_idx += 1; // skip ';'
1164 else { // no ';' at end of pair, only allowed at end of dictionary
1165 if (!_skipComments(_buffer, _idx, _len, _exception)) {
1167 _makeException(*_exception, _buffer, *_idx, _len,
1168 @"element configuration was not "
1169 @"closed (expected '}')");
1171 break; // unexpected EOF
1174 if (_buffer[*_idx] != '}') { // config wasn't closed
1176 _makeException(*_exception, _buffer, *_idx, _len,
1177 @"key-value pair without ';' at the end");
1183 while ((*_idx < _len) && (result != nil) && !didFail);
1185 [key release]; key = nil;
1186 [value release]; value = nil;
1189 [result release]; result = nil;
1197 static id _parseWodEntry(NSZone *_zone, const char *_buffer, unsigned *_idx,
1198 unsigned _len, NSException **_exception,
1199 NSString **_name, NSString **_class, id self)
1201 NSString *elementName = nil;
1202 NSString *componentName = nil;
1203 NSDictionary *config = nil;
1208 if (!_skipComments(_buffer, _idx, _len, _exception))
1212 elementName = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
1213 if (elementName == nil) {
1214 *_exception = _makeException(nil, _buffer, *_idx, _len,
1215 @"expected element name");
1219 if (!_skipComments(_buffer, _idx, _len, _exception))
1222 // Element/Component separator
1223 if (_buffer[*_idx] == ':') {
1224 *_idx += 1; // skip ':'
1227 *_exception = _makeException(*_exception, _buffer, *_idx, _len,
1228 @"expected ':' after element name");
1232 if (!_skipComments(_buffer, _idx, _len, _exception))
1236 componentName = _parseIdentifier(_zone, _buffer, _idx, _len, _exception);
1237 if (componentName == nil) {
1238 *_exception = _makeException(nil, _buffer, *_idx, _len,
1239 @"expected component name");
1243 if (!_skipComments(_buffer, _idx, _len, _exception))
1247 config = _parseWodConfig(_zone, _buffer, _idx, _len, _exception, self);
1251 //NSLog(@"%@ : %@ %@", elementName, componentName, config);
1253 // read trailing ';' if available
1254 if (_skipComments(_buffer, _idx, _len, _exception)) {
1255 if (_buffer[*_idx] == ';') {
1256 *_idx += 1; // skip ';'
1260 *_name = elementName;
1261 *_class = componentName;
1266 NSLog(@"failed at %@:%@ ..", elementName, componentName);
1268 [elementName release]; elementName = nil;
1269 [componentName release]; componentName = nil;
1270 [config release]; config = nil;
1274 @end /* WODParser */