mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-22 16:33:29 +00:00
parse C decls
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@12094 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
6524175a99
commit
c1d12425e1
3 changed files with 345 additions and 94 deletions
|
@ -1,3 +1,8 @@
|
|||
2002-01-13 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Tools/AGSParser.m: Mostly implemented parsing of C declarations
|
||||
(function, typedef, constant and variable).
|
||||
|
||||
2002-01-11 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* Headers/Foundation/NSArchiver.h: tidyups
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
BOOL commentsRead;
|
||||
BOOL haveSource;
|
||||
BOOL inInstanceVariables;
|
||||
BOOL inArgList;
|
||||
BOOL documentAllInstanceVariables;
|
||||
NSString *declared; /** Where classes were declared. */
|
||||
NSMutableArray *ifStack; /** Track preprocessor conditionals. */
|
||||
|
@ -72,7 +73,7 @@
|
|||
|
||||
- (NSMutableDictionary*) info;
|
||||
- (id) init; /** <init> Simple initialiser */
|
||||
- (NSMutableDictionary*) parseDeclIsSource: (BOOL)isSource;
|
||||
- (NSMutableDictionary*) parseDeclaration;
|
||||
- (NSMutableDictionary*) parseFile: (NSString*)name isSource: (BOOL)isSource;
|
||||
- (NSString*) parseIdentifier;
|
||||
- (NSMutableDictionary*) parseImplementation;
|
||||
|
|
|
@ -122,67 +122,157 @@
|
|||
va_end (ap);
|
||||
}
|
||||
|
||||
- (NSString*) parseDeclaratorInto: (NSMutableArray*)a
|
||||
- (void) parseArgsInto: (NSMutableDictionary*)d
|
||||
{
|
||||
BOOL wasInArgList = inArgList;
|
||||
NSMutableArray *a = [d objectForKey: @"Args"];
|
||||
|
||||
NSAssert([d objectForKey: @"Args"] == nil, NSInternalInconsistencyException);
|
||||
a = [[NSMutableArray alloc] initWithCapacity: 4];
|
||||
[d setObject: a forKey: @"Args"];
|
||||
RELEASE(a);
|
||||
|
||||
inArgList = YES;
|
||||
pos++; // Step past opening '('
|
||||
|
||||
while ([self skipWhiteSpace] < length && buffer[pos] != ')')
|
||||
{
|
||||
if (buffer[pos] == ',')
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
else if (buffer[pos] == '.')
|
||||
{
|
||||
pos += 3; // Skip '...'
|
||||
[d setObject: @"YES" forKey: @"Varargs"];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSMutableDictionary *m;
|
||||
|
||||
m = [self parseDeclaration];
|
||||
if (m == nil)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ([[m objectForKey: @"BaseType"] isEqual: @"void"] == YES
|
||||
&& [m objectForKey: @"Prefix"] == nil)
|
||||
{
|
||||
continue; // C++ style empty arg list. eg. 'int foo(void);'
|
||||
}
|
||||
[a addObject: m];
|
||||
}
|
||||
}
|
||||
if (pos < length)
|
||||
{
|
||||
pos++; // Step past closing ')'
|
||||
}
|
||||
inArgList = wasInArgList;
|
||||
}
|
||||
|
||||
- (void) parseDeclaratorInto: (NSMutableDictionary*)d
|
||||
{
|
||||
NSMutableString *p = nil;
|
||||
NSMutableString *s = nil;
|
||||
|
||||
while ([self skipWhiteSpace] < length)
|
||||
{
|
||||
while (pos < length && buffer[pos] == '*')
|
||||
{
|
||||
[a addObject: @"*"];
|
||||
if (p == nil && (p = [d objectForKey: @"Prefix"]) == nil)
|
||||
{
|
||||
p = [NSMutableString new];
|
||||
[d setObject: p forKey: @"Prefix"];
|
||||
RELEASE(p);
|
||||
}
|
||||
else if ([p hasSuffix: @"("] == NO && [p hasSuffix: @"*"] == NO)
|
||||
{
|
||||
[p appendString: @" "];
|
||||
}
|
||||
[p appendString: @"*"];
|
||||
pos++;
|
||||
}
|
||||
if (buffer[pos] == '(')
|
||||
{
|
||||
NSString *result;
|
||||
|
||||
[a addObject: @"("];
|
||||
if (p == nil && (p = [d objectForKey: @"Prefix"]) == nil)
|
||||
{
|
||||
p = [NSMutableString new];
|
||||
[d setObject: p forKey: @"Prefix"];
|
||||
RELEASE(p);
|
||||
}
|
||||
else if ([p hasSuffix: @"("] == NO && [p hasSuffix: @"*"] == NO)
|
||||
{
|
||||
[p appendString: @" "];
|
||||
}
|
||||
[p appendString: @"("];
|
||||
pos++;
|
||||
result = [self parseDeclaratorInto: a];
|
||||
[self parseDeclaratorInto: d];
|
||||
if ([self skipWhiteSpace] < length && buffer[pos] == '(')
|
||||
{
|
||||
[self parseDeclaratorInto: a]; // parse function args.
|
||||
[self parseArgsInto: d]; // parse function args.
|
||||
}
|
||||
if ([self skipWhiteSpace] < length && buffer[pos] == ')')
|
||||
{
|
||||
[a addObject: @")"];
|
||||
if (s == nil && (s = [d objectForKey: @"Suffix"]) == nil)
|
||||
{
|
||||
s = [NSMutableString new];
|
||||
[d setObject: s forKey: @"Suffix"];
|
||||
RELEASE(s);
|
||||
}
|
||||
[s appendString: @")"];
|
||||
pos++;
|
||||
return result;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self log: @"missing ')' in declarator."];
|
||||
return nil;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *s;
|
||||
NSString *t;
|
||||
|
||||
s = [self parseIdentifier];
|
||||
if ([s isEqualToString: @"const"] || [s isEqualToString: @"volatile"])
|
||||
t = [self parseIdentifier];
|
||||
if (t == nil)
|
||||
{
|
||||
[a addObject: s];
|
||||
return;
|
||||
}
|
||||
if ([t isEqualToString: @"const"] || [t isEqualToString: @"volatile"])
|
||||
{
|
||||
if (p == nil && (p = [d objectForKey: @"Prefix"]) == nil)
|
||||
{
|
||||
p = [NSMutableString new];
|
||||
[d setObject: p forKey: @"Prefix"];
|
||||
RELEASE(p);
|
||||
}
|
||||
else if ([p hasSuffix: @"("] == NO)
|
||||
{
|
||||
[p appendString: @" "];
|
||||
}
|
||||
[p appendString: t];
|
||||
}
|
||||
else
|
||||
{
|
||||
return s; // Parsed all asterisks, consts, and volatiles
|
||||
[d setObject: t forKey: @"Name"];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary*) parseDeclIsSource: (BOOL)isSource
|
||||
- (NSMutableDictionary*) parseDeclaration
|
||||
{
|
||||
NSMutableDictionary *d = [NSMutableDictionary dictionary];
|
||||
CREATE_AUTORELEASE_POOL(arp);
|
||||
static NSSet *qualifiers = nil;
|
||||
static NSSet *keep = nil;
|
||||
NSString *baseType = nil;
|
||||
NSString *declName = nil;
|
||||
NSMutableArray *a1;
|
||||
NSMutableArray *a2;
|
||||
NSMutableString *t = nil;
|
||||
NSMutableArray *a;
|
||||
NSString *s;
|
||||
BOOL isTypedef = NO;
|
||||
BOOL isPointer = NO;
|
||||
BOOL baseConstant = NO;
|
||||
BOOL needScalarType = NO;
|
||||
|
||||
if (qualifiers == nil)
|
||||
|
@ -213,8 +303,7 @@
|
|||
RETAIN(keep);
|
||||
}
|
||||
|
||||
a1 = [NSMutableArray array];
|
||||
a2 = [NSMutableArray array];
|
||||
a = [NSMutableArray array];
|
||||
while ((s = [self parseIdentifier]) != nil)
|
||||
{
|
||||
if ([s isEqualToString: @"static"] == YES)
|
||||
|
@ -241,7 +330,7 @@
|
|||
}
|
||||
if ([keep member: s] != nil)
|
||||
{
|
||||
[a1 addObject: s];
|
||||
[a addObject: s];
|
||||
if ([s isEqual: @"const"] == NO && [s isEqual: @"volatile"] == NO)
|
||||
{
|
||||
needScalarType = YES;
|
||||
|
@ -260,33 +349,40 @@
|
|||
|| [s isEqualToString: @"union"] == YES
|
||||
|| [s isEqualToString: @"enum"] == YES)
|
||||
{
|
||||
baseType = s;
|
||||
NSString *tmp = s;
|
||||
|
||||
s = [self parseIdentifier];
|
||||
if (s == nil)
|
||||
{
|
||||
baseType = [NSString stringWithFormat: @"%@ ...", baseType];
|
||||
s = [NSString stringWithFormat: @"%@ ...", tmp];
|
||||
}
|
||||
else
|
||||
{
|
||||
baseType = [NSString stringWithFormat: @"%@ %@", baseType, s];
|
||||
s = [NSString stringWithFormat: @"%@ %@", tmp, s];
|
||||
/*
|
||||
* It's possible to declare a struct, union, or enum without
|
||||
* giving it a name beyond after the declaration, in this case
|
||||
* we can use something like 'struct foo' as the name.
|
||||
*/
|
||||
[d setObject: s forKey: @"Name"];
|
||||
}
|
||||
if ([self skipWhiteSpace] < length && buffer[pos] == '{')
|
||||
{
|
||||
[self skipBlock];
|
||||
}
|
||||
[a addObject: s];
|
||||
s = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
baseType = s;
|
||||
if (baseType == nil)
|
||||
if (s == nil)
|
||||
{
|
||||
/*
|
||||
* If there is no identifier here, the line must have been
|
||||
* something like 'unsigned *length' so we must set the default
|
||||
* base type of 'int'
|
||||
*/
|
||||
baseType = @"int";
|
||||
[a addObject: @"int"];
|
||||
}
|
||||
else if (needScalarType == YES
|
||||
&& [s isEqualToString: @"char"] == NO
|
||||
|
@ -297,30 +393,157 @@
|
|||
* have a 'char' or an 'int', and if we didn't find one we should
|
||||
* insert one and use what we found as the variable name.
|
||||
*/
|
||||
baseType = @"int";
|
||||
[a addObject: @"int"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[a addObject: s];
|
||||
s = nil; // s used as baseType
|
||||
}
|
||||
}
|
||||
|
||||
if (s == nil)
|
||||
/*
|
||||
* Now build a string containing the base type in a standardised form.
|
||||
*/
|
||||
t = [NSMutableString new];
|
||||
|
||||
if ([a containsObject: @"const"] == YES)
|
||||
{
|
||||
declName = [self parseDeclaratorInto: a2];
|
||||
[t appendString: @"const"];
|
||||
[t appendString: @" "];
|
||||
[a removeObject: @"const"];
|
||||
baseConstant = YES;
|
||||
}
|
||||
else if ([a containsObject: @"volatile"] == YES)
|
||||
{
|
||||
[t appendString: @"volatile"];
|
||||
[t appendString: @" "];
|
||||
[a removeObject: @"volatile"];
|
||||
}
|
||||
|
||||
if ([a containsObject: @"signed"] == YES)
|
||||
{
|
||||
[t appendString: @"signed"];
|
||||
[t appendString: @" "];
|
||||
[a removeObject: @"signed"];
|
||||
}
|
||||
else if ([a containsObject: @"unsigned"] == YES)
|
||||
{
|
||||
[t appendString: @"unsigned"];
|
||||
[t appendString: @" "];
|
||||
[a removeObject: @"unsigned"];
|
||||
}
|
||||
|
||||
if ([a containsObject: @"short"] == YES)
|
||||
{
|
||||
[t appendString: @"short"];
|
||||
[t appendString: @" "];
|
||||
[a removeObject: @"short"];
|
||||
}
|
||||
else if ([a containsObject: @"long"] == YES)
|
||||
{
|
||||
unsigned c = [a count];
|
||||
|
||||
/*
|
||||
* There may be more than one 'long' in a type spec
|
||||
*/
|
||||
while (c-- > 0)
|
||||
{
|
||||
NSString *tmp = [a objectAtIndex: c];
|
||||
|
||||
if ([tmp isEqual: @"long"] == YES)
|
||||
{
|
||||
[t appendString: tmp];
|
||||
[t appendString: @" "];
|
||||
[a removeObjectAtIndex: c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([a count] != 1)
|
||||
{
|
||||
[self log: @"odd values in declaration base type - '%@'", a];
|
||||
[t appendString: [a componentsJoinedByString: @" "]];
|
||||
}
|
||||
else
|
||||
{
|
||||
declName = s;
|
||||
[t appendString: [a objectAtIndex: 0]];
|
||||
}
|
||||
[a removeAllObjects]; // Parsed base type
|
||||
|
||||
[a1 addObject: baseType];
|
||||
[a1 addObjectsFromArray: a2];
|
||||
[d setObject: t forKey: @"BaseType"];
|
||||
RELEASE(t);
|
||||
|
||||
/*
|
||||
* Set the 'Kind' of declaration ... one of 'typedef', 'function',
|
||||
* 'variable', or 'constant'
|
||||
* We may ovrride this later.
|
||||
*/
|
||||
if (isTypedef == YES)
|
||||
{
|
||||
[d setObject: @"typedef" forKey: @"Kind"];
|
||||
}
|
||||
else if (baseConstant == YES)
|
||||
{
|
||||
[d setObject: @"constant" forKey: @"Kind"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[d setObject: @"variable" forKey: @"Kind"];
|
||||
}
|
||||
|
||||
if (s == nil)
|
||||
{
|
||||
[self parseDeclaratorInto: d];
|
||||
/*
|
||||
* There may have been '*' and 'const' applied to the declarator
|
||||
* which will change whether it is a constant or a variable, and
|
||||
* whether it is a pointer to something.
|
||||
* If the last thing to be applied was a '*' it is a variable
|
||||
* which points to a constant. If the last thing was 'const'
|
||||
* then it is a constant (and may be a pointer too).
|
||||
*/
|
||||
s = [d objectForKey: @"Prefix"];
|
||||
if (s != nil)
|
||||
{
|
||||
NSRange r;
|
||||
|
||||
r = [s rangeOfString: @"*"
|
||||
options: NSBackwardsSearch|NSLiteralSearch];
|
||||
if (r.length > 0)
|
||||
{
|
||||
unsigned p = r.location;
|
||||
|
||||
isPointer = YES;
|
||||
if (isTypedef == NO)
|
||||
{
|
||||
r = [s rangeOfString: @"const"
|
||||
options: NSBackwardsSearch|NSLiteralSearch];
|
||||
if (r.length > 0 && r.location >= p)
|
||||
{
|
||||
[d setObject: @"constant" forKey: @"Kind"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[d setObject: s forKey: @"Name"];
|
||||
}
|
||||
|
||||
if ([self skipWhiteSpace] < length)
|
||||
{
|
||||
if (buffer[pos] == '[')
|
||||
{
|
||||
NSMutableString *suffix;
|
||||
|
||||
if ((suffix = [d objectForKey: @"Suffix"]) == nil)
|
||||
{
|
||||
suffix = [NSMutableString new];
|
||||
[d setObject: suffix forKey: @"Suffix"];
|
||||
RELEASE(suffix);
|
||||
}
|
||||
while (buffer[pos] == '[')
|
||||
{
|
||||
unsigned old = pos;
|
||||
|
@ -329,81 +552,103 @@
|
|||
{
|
||||
break;
|
||||
}
|
||||
[a1 addObject: @"[]"];
|
||||
[suffix appendString: @"[]"];
|
||||
}
|
||||
}
|
||||
else if (buffer[pos] == '(')
|
||||
{
|
||||
[self log: @"parse function '%@' of type '%@'",
|
||||
declName, [a1 componentsJoinedByString: @" "]];
|
||||
[self skipStatement];
|
||||
RELEASE(arp);
|
||||
return nil;
|
||||
[self parseArgsInto: d];
|
||||
}
|
||||
}
|
||||
|
||||
if ([d objectForKey: @"Args"] != nil)
|
||||
{
|
||||
/*
|
||||
* If the declaration looked like this int (*foo)() then
|
||||
* 'isPointer' will be YES and 'Suffix' will contain the
|
||||
* bracket after 'foo'. In this case, what we have is a
|
||||
* variable or constant pointer to a function.
|
||||
* Otherwise, we have a function declaration and the
|
||||
* 'Kind' should be set to 'function'.
|
||||
*/
|
||||
if (isPointer == NO || [d objectForKey: @"Suffix"] == nil)
|
||||
{
|
||||
[d setObject: @"function" forKey: @"Kind"];
|
||||
}
|
||||
}
|
||||
|
||||
if ([self skipWhiteSpace] < length)
|
||||
{
|
||||
if (buffer[pos] == ';')
|
||||
if (inArgList == YES)
|
||||
{
|
||||
[self skipStatement];
|
||||
}
|
||||
else if (buffer[pos] == ',')
|
||||
{
|
||||
[self log: @"ignoring multiple comma separated declarations"];
|
||||
[self skipStatement];
|
||||
}
|
||||
else if (buffer[pos] == '=')
|
||||
{
|
||||
[self skipStatement];
|
||||
}
|
||||
else if (buffer[pos] == '{')
|
||||
{
|
||||
[self skipBlock];
|
||||
if (buffer[pos] == ')' || buffer[pos] == ',')
|
||||
{
|
||||
RELEASE(arp);
|
||||
return d;
|
||||
}
|
||||
else
|
||||
{
|
||||
[self log: @"unexpected char (%c) in arg list", buffer[pos]];
|
||||
[self skipStatement];
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self log: @"unexpected char (%c) parsing declaration", buffer[pos]];
|
||||
[self skipStatement];
|
||||
goto fail;
|
||||
}
|
||||
if (buffer[pos] == ';')
|
||||
{
|
||||
[self skipStatement];
|
||||
}
|
||||
else if (buffer[pos] == ',')
|
||||
{
|
||||
[self log: @"ignoring multiple comma separated declarations"];
|
||||
[self skipStatement];
|
||||
}
|
||||
else if (buffer[pos] == '=')
|
||||
{
|
||||
[self skipStatement];
|
||||
}
|
||||
else if (buffer[pos] == '{')
|
||||
{
|
||||
[self skipBlock];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self log: @"unexpected char (%c) in declaration", buffer[pos]];
|
||||
[self skipStatement];
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in any comment on the same line in case it
|
||||
* contains documentation for the declaration.
|
||||
*/
|
||||
if ([self skipSpaces] < length && buffer[pos] == '/')
|
||||
{
|
||||
[self skipComment];
|
||||
}
|
||||
|
||||
if (inInstanceVariables == YES)
|
||||
{
|
||||
NSMutableDictionary *d;
|
||||
NSString *t;
|
||||
|
||||
t = [a1 componentsJoinedByString: @" "];
|
||||
d = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
||||
[d setObject: declName forKey: @"Name"];
|
||||
[d setObject: t forKey: @"Type"];
|
||||
/*
|
||||
* Read in any comment on the same line in case it
|
||||
* contains documentation for the declaration.
|
||||
*/
|
||||
if ([self skipSpaces] < length && buffer[pos] == '/')
|
||||
{
|
||||
[self skipComment];
|
||||
}
|
||||
if (comment != nil)
|
||||
{
|
||||
[d setObject: comment forKey: @"Comment"];
|
||||
DESTROY(comment);
|
||||
}
|
||||
RELEASE(arp);
|
||||
return AUTORELEASE(d);
|
||||
[self log: @"parse '%@'", d];
|
||||
DESTROY(comment);
|
||||
}
|
||||
else if (isTypedef == YES)
|
||||
|
||||
RELEASE(arp);
|
||||
if (inArgList == NO)
|
||||
{
|
||||
[self log: @"parse typedef '%@' of type '%@'",
|
||||
declName, [a1 componentsJoinedByString: @" "]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self log: @"parse variable/constant '%@' of type '%@'",
|
||||
declName, [a1 componentsJoinedByString: @" "]];
|
||||
/*
|
||||
* This is a top-level declaration, so lets tidy up ready for
|
||||
* linking into the documentation tree.
|
||||
*/
|
||||
if ([d objectForKey: @"Name"] == nil)
|
||||
{
|
||||
[self log: @"parse declaration with no name - %@", d];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -500,7 +745,7 @@ fail:
|
|||
* Must be some sort of declaration ...
|
||||
*/
|
||||
pos--;
|
||||
[self parseDeclIsSource: isSource];
|
||||
[self parseDeclaration];
|
||||
// [self skipStatementLine];
|
||||
break;
|
||||
}
|
||||
|
@ -851,7 +1096,7 @@ fail:
|
|||
}
|
||||
else if (shouldDocument == YES)
|
||||
{
|
||||
NSMutableDictionary *iv = [self parseDeclIsSource: NO];
|
||||
NSMutableDictionary *iv = [self parseDeclaration];
|
||||
|
||||
if (iv != nil)
|
||||
{
|
||||
|
@ -1299,7 +1544,7 @@ fail:
|
|||
else
|
||||
{
|
||||
pos--;
|
||||
[self parseDeclIsSource: YES];
|
||||
[self parseDeclaration];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue