Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

1152 lines (985 sloc) 22.505 kb
/*
* Copyright (c) 2010, Vincent Berthoux
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
* This module contains functions for generating tags for Objective C
* language files.
*/
/*
* INCLUDE FILES
*/
#include "general.h" /* must always come first */
#include <string.h>
#include "keyword.h"
#include "entry.h"
#include "options.h"
#include "read.h"
#include "routines.h"
#include "vstring.h"
/* To get rid of unused parameter warning in
* -Wextra */
#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif
typedef enum {
K_INTERFACE,
K_IMPLEMENTATION,
K_PROTOCOL,
K_METHOD,
K_CLASSMETHOD,
K_VAR,
K_FIELD,
K_FUNCTION,
K_PROPERTY,
K_TYPEDEF,
K_STRUCT,
K_ENUM,
K_MACRO
} objcKind;
static kindOption ObjcKinds[] = {
{TRUE, 'i', "interface", "class interface"},
{TRUE, 'I', "implementation", "class implementation"},
{TRUE, 'p', "protocol", "Protocol"},
{TRUE, 'm', "method", "Object's method"},
{TRUE, 'c', "class", "Class' method"},
{TRUE, 'v', "var", "Global variable"},
{TRUE, 'F', "field", "Object field"},
{TRUE, 'f', "function", "A function"},
{TRUE, 'p', "property", "A property"},
{TRUE, 't', "typedef", "A type alias"},
{TRUE, 's', "struct", "A type structure"},
{TRUE, 'e', "enum", "An enumeration"},
{TRUE, 'M', "macro", "A preprocessor macro"},
};
typedef enum {
ObjcTYPEDEF,
ObjcSTRUCT,
ObjcENUM,
ObjcIMPLEMENTATION,
ObjcINTERFACE,
ObjcPROTOCOL,
ObjcENCODE,
ObjcSYNCHRONIZED,
ObjcSELECTOR,
ObjcPROPERTY,
ObjcEND,
ObjcDEFS,
ObjcCLASS,
ObjcPRIVATE,
ObjcPACKAGE,
ObjcPUBLIC,
ObjcPROTECTED,
ObjcSYNTHESIZE,
ObjcDYNAMIC,
ObjcOPTIONAL,
ObjcREQUIRED,
ObjcSTRING,
ObjcIDENTIFIER,
Tok_COMA, /* ',' */
Tok_PLUS, /* '+' */
Tok_MINUS, /* '-' */
Tok_PARL, /* '(' */
Tok_PARR, /* ')' */
Tok_CurlL, /* '{' */
Tok_CurlR, /* '}' */
Tok_SQUAREL, /* '[' */
Tok_SQUARER, /* ']' */
Tok_semi, /* ';' */
Tok_dpoint, /* ':' */
Tok_Sharp, /* '#' */
Tok_Backslash, /* '\\' */
Tok_EOL, /* '\r''\n' */
Tok_any,
Tok_EOF /* END of file */
} objcKeyword;
typedef objcKeyword objcToken;
typedef struct sOBjcKeywordDesc {
const char *name;
objcKeyword id;
} objcKeywordDesc;
static const objcKeywordDesc objcKeywordTable[] = {
{"typedef", ObjcTYPEDEF},
{"struct", ObjcSTRUCT},
{"enum", ObjcENUM},
{"@implementation", ObjcIMPLEMENTATION},
{"@interface", ObjcINTERFACE},
{"@protocol", ObjcPROTOCOL},
{"@encode", ObjcENCODE},
{"@property", ObjcPROPERTY},
{"@synchronized", ObjcSYNCHRONIZED},
{"@selector", ObjcSELECTOR},
{"@end", ObjcEND},
{"@defs", ObjcDEFS},
{"@class", ObjcCLASS},
{"@private", ObjcPRIVATE},
{"@package", ObjcPACKAGE},
{"@public", ObjcPUBLIC},
{"@protected", ObjcPROTECTED},
{"@synthesize", ObjcSYNTHESIZE},
{"@dynamic", ObjcDYNAMIC},
{"@optional", ObjcOPTIONAL},
{"@required", ObjcREQUIRED},
};
static langType Lang_ObjectiveC;
/*//////////////////////////////////////////////////////////////////
//// lexingInit */
typedef struct _lexingState {
vString *name; /* current parsed identifier/operator */
const unsigned char *cp; /* position in stream */
} lexingState;
static void initKeywordHash (void)
{
const size_t count = sizeof (objcKeywordTable) / sizeof (objcKeywordDesc);
size_t i;
for (i = 0; i < count; ++i)
{
addKeyword (objcKeywordTable[i].name, Lang_ObjectiveC,
(int) objcKeywordTable[i].id);
}
}
/*//////////////////////////////////////////////////////////////////////
//// Lexing */
static boolean isNum (char c)
{
return c >= '0' && c <= '9';
}
static boolean isLowerAlpha (char c)
{
return c >= 'a' && c <= 'z';
}
static boolean isUpperAlpha (char c)
{
return c >= 'A' && c <= 'Z';
}
static boolean isAlpha (char c)
{
return isLowerAlpha (c) || isUpperAlpha (c);
}
static boolean isIdent (char c)
{
return isNum (c) || isAlpha (c) || c == '_';
}
static boolean isSpace (char c)
{
return c == ' ' || c == '\t';
}
/* return true if it end with an end of line */
static void eatWhiteSpace (lexingState * st)
{
const unsigned char *cp = st->cp;
while (isSpace (*cp))
cp++;
st->cp = cp;
}
static void eatString (lexingState * st)
{
boolean lastIsBackSlash = FALSE;
boolean unfinished = TRUE;
const unsigned char *c = st->cp + 1;
while (unfinished)
{
/* end of line should never happen.
* we tolerate it */
if (c == NULL || c[0] == '\0')
break;
else if (*c == '"' && !lastIsBackSlash)
unfinished = FALSE;
else
lastIsBackSlash = *c == '\\';
c++;
}
st->cp = c;
}
static void eatComment (lexingState * st)
{
boolean unfinished = TRUE;
boolean lastIsStar = FALSE;
const unsigned char *c = st->cp + 2;
while (unfinished)
{
/* we've reached the end of the line..
* so we have to reload a line... */
if (c == NULL || *c == '\0')
{
st->cp = fileReadLine ();
/* WOOPS... no more input...
* we return, next lexing read
* will be null and ok */
if (st->cp == NULL)
return;
c = st->cp;
}
/* we've reached the end of the comment */
else if (*c == '/' && lastIsStar)
unfinished = FALSE;
else
{
lastIsStar = '*' == *c;
c++;
}
}
st->cp = c;
}
static void readIdentifier (lexingState * st)
{
const unsigned char *p;
vStringClear (st->name);
/* first char is a simple letter */
if (isAlpha (*st->cp) || *st->cp == '_')
vStringPut (st->name, (int) *st->cp);
/* Go till you get identifier chars */
for (p = st->cp + 1; isIdent (*p); p++)
vStringPut (st->name, (int) *p);
st->cp = p;
vStringTerminate (st->name);
}
/* read the @something directives */
static void readIdentifierObjcDirective (lexingState * st)
{
const unsigned char *p;
vStringClear (st->name);
/* first char is a simple letter */
if (*st->cp == '@')
vStringPut (st->name, (int) *st->cp);
/* Go till you get identifier chars */
for (p = st->cp + 1; isIdent (*p); p++)
vStringPut (st->name, (int) *p);
st->cp = p;
vStringTerminate (st->name);
}
/* The lexer is in charge of reading the file.
* Some of sub-lexer (like eatComment) also read file.
* lexing is finished when the lexer return Tok_EOF */
static objcKeyword lex (lexingState * st)
{
int retType;
/* handling data input here */
while (st->cp == NULL || st->cp[0] == '\0')
{
st->cp = fileReadLine ();
if (st->cp == NULL)
return Tok_EOF;
return Tok_EOL;
}
if (isAlpha (*st->cp))
{
readIdentifier (st);
retType = lookupKeyword (vStringValue (st->name), Lang_ObjectiveC);
if (retType == -1) /* If it's not a keyword */
{
return ObjcIDENTIFIER;
}
else
{
return retType;
}
}
else if (*st->cp == '@')
{
readIdentifierObjcDirective (st);
retType = lookupKeyword (vStringValue (st->name), Lang_ObjectiveC);
if (retType == -1) /* If it's not a keyword */
{
return Tok_any;
}
else
{
return retType;
}
}
else if (isSpace (*st->cp))
{
eatWhiteSpace (st);
return lex (st);
}
else
switch (*st->cp)
{
case '(':
st->cp++;
return Tok_PARL;
case '\\':
st->cp++;
return Tok_Backslash;
case '#':
st->cp++;
return Tok_Sharp;
case '/':
if (st->cp[1] == '*') /* ergl, a comment */
{
eatComment (st);
return lex (st);
}
else if (st->cp[1] == '/')
{
st->cp = NULL;
return lex (st);
}
else
{
st->cp++;
return Tok_any;
}
break;
case ')':
st->cp++;
return Tok_PARR;
case '{':
st->cp++;
return Tok_CurlL;
case '}':
st->cp++;
return Tok_CurlR;
case '[':
st->cp++;
return Tok_SQUAREL;
case ']':
st->cp++;
return Tok_SQUARER;
case ',':
st->cp++;
return Tok_COMA;
case ';':
st->cp++;
return Tok_semi;
case ':':
st->cp++;
return Tok_dpoint;
case '"':
eatString (st);
return Tok_any;
case '+':
st->cp++;
return Tok_PLUS;
case '-':
st->cp++;
return Tok_MINUS;
default:
st->cp++;
break;
}
/* default return if nothing is recognized,
* shouldn't happen, but at least, it will
* be handled without destroying the parsing. */
return Tok_any;
}
/*//////////////////////////////////////////////////////////////////////
//// Parsing */
typedef void (*parseNext) (vString * const ident, objcToken what);
/********** Helpers */
/* This variable hold the 'parser' which is going to
* handle the next token */
static parseNext toDoNext;
/* Special variable used by parser eater to
* determine which action to put after their
* job is finished. */
static parseNext comeAfter;
/* Used by some parsers detecting certain token
* to revert to previous parser. */
static parseNext fallback;
/********** Grammar */
static void globalScope (vString * const ident, objcToken what);
static void parseMethods (vString * const ident, objcToken what);
static void parseImplemMethods (vString * const ident, objcToken what);
static vString *tempName = NULL;
static vString *parentName = NULL;
static objcKind parentType = K_INTERFACE;
/* used to prepare tag for OCaml, just in case their is a need to
* add additional information to the tag. */
static void prepareTag (tagEntryInfo * tag, vString const *name, objcKind kind)
{
initTagEntry (tag, vStringValue (name));
tag->kindName = ObjcKinds[kind].name;
tag->kind = ObjcKinds[kind].letter;
if (parentName != NULL)
{
tag->extensionFields.scope[0] = ObjcKinds[parentType].name;
tag->extensionFields.scope[1] = vStringValue (parentName);
}
}
static void pushEnclosingContext (const vString * parent, objcKind type)
{
vStringCopy (parentName, parent);
parentType = type;
}
static void popEnclosingContext (void)
{
vStringClear (parentName);
}
/* Used to centralise tag creation, and be able to add
* more information to it in the future */
static void addTag (vString * const ident, int kind)
{
tagEntryInfo toCreate;
prepareTag (&toCreate, ident, kind);
makeTagEntry (&toCreate);
}
static objcToken waitedToken, fallBackToken;
/* Ignore everything till waitedToken and jump to comeAfter.
* If the "end" keyword is encountered break, doesn't remember
* why though. */
static void tillToken (vString * const UNUSED (ident), objcToken what)
{
if (what == waitedToken)
toDoNext = comeAfter;
}
static void tillTokenOrFallBack (vString * const UNUSED (ident), objcToken what)
{
if (what == waitedToken)
toDoNext = comeAfter;
else if (what == fallBackToken)
{
toDoNext = fallback;
}
}
static int ignoreBalanced_count = 0;
static void ignoreBalanced (vString * const UNUSED (ident), objcToken what)
{
switch (what)
{
case Tok_PARL:
case Tok_CurlL:
case Tok_SQUAREL:
ignoreBalanced_count++;
break;
case Tok_PARR:
case Tok_CurlR:
case Tok_SQUARER:
ignoreBalanced_count--;
break;
default:
/* don't care */
break;
}
if (ignoreBalanced_count == 0)
toDoNext = comeAfter;
}
static void parseFields (vString * const ident, objcToken what)
{
switch (what)
{
case Tok_CurlR:
toDoNext = &parseMethods;
break;
case Tok_SQUAREL:
case Tok_PARL:
toDoNext = &ignoreBalanced;
comeAfter = &parseFields;
break;
/* we got an identifier, keep track of it */
case ObjcIDENTIFIER:
vStringCopy (tempName, ident);
break;
/* our last kept identifier must be our variable name =) */
case Tok_semi:
addTag (tempName, K_FIELD);
vStringClear (tempName);
break;
default:
/* NOTHING */
break;
}
}
objcKind methodKind;
static vString *fullMethodName;
static vString *prevIdent;
static void parseMethodsName (vString * const ident, objcToken what)
{
switch (what)
{
case Tok_PARL:
toDoNext = &tillToken;
comeAfter = &parseMethodsName;
waitedToken = Tok_PARR;
break;
case Tok_dpoint:
vStringCat (fullMethodName, prevIdent);
vStringCatS (fullMethodName, ":");
vStringClear (prevIdent);
break;
case ObjcIDENTIFIER:
vStringCopy (prevIdent, ident);
break;
case Tok_CurlL:
case Tok_semi:
/* method name is not simple */
if (vStringLength (fullMethodName) != '\0')
{
addTag (fullMethodName, methodKind);
vStringClear (fullMethodName);
}
else
addTag (prevIdent, methodKind);
toDoNext = &parseMethods;
parseImplemMethods (ident, what);
vStringClear (prevIdent);
break;
default:
break;
}
}
static void parseMethodsImplemName (vString * const ident, objcToken what)
{
switch (what)
{
case Tok_PARL:
toDoNext = &tillToken;
comeAfter = &parseMethodsImplemName;
waitedToken = Tok_PARR;
break;
case Tok_dpoint:
vStringCat (fullMethodName, prevIdent);
vStringCatS (fullMethodName, ":");
vStringClear (prevIdent);
break;
case ObjcIDENTIFIER:
vStringCopy (prevIdent, ident);
break;
case Tok_CurlL:
case Tok_semi:
/* method name is not simple */
if (vStringLength (fullMethodName) != '\0')
{
addTag (fullMethodName, methodKind);
vStringClear (fullMethodName);
}
else
addTag (prevIdent, methodKind);
toDoNext = &parseImplemMethods;
parseImplemMethods (ident, what);
vStringClear (prevIdent);
break;
default:
break;
}
}
static void parseImplemMethods (vString * const ident, objcToken what)
{
switch (what)
{
case Tok_PLUS: /* + */
toDoNext = &parseMethodsImplemName;
methodKind = K_CLASSMETHOD;
break;
case Tok_MINUS: /* - */
toDoNext = &parseMethodsImplemName;
methodKind = K_METHOD;
break;
case ObjcEND: /* @end */
popEnclosingContext ();
toDoNext = &globalScope;
break;
case Tok_CurlL: /* { */
toDoNext = &ignoreBalanced;
ignoreBalanced (ident, what);
comeAfter = &parseImplemMethods;
break;
default:
break;
}
}
static void parseProperty (vString * const ident, objcToken what)
{
switch (what)
{
case Tok_PARL:
toDoNext = &tillToken;
comeAfter = &parseProperty;
waitedToken = Tok_PARR;
break;
/* we got an identifier, keep track of it */
case ObjcIDENTIFIER:
vStringCopy (tempName, ident);
break;
/* our last kept identifier must be our variable name =) */
case Tok_semi:
addTag (tempName, K_PROPERTY);
vStringClear (tempName);
break;
default:
break;
}
}
static void parseMethods (vString * const UNUSED (ident), objcToken what)
{
switch (what)
{
case Tok_PLUS: /* + */
toDoNext = &parseMethodsName;
methodKind = K_CLASSMETHOD;
break;
case Tok_MINUS: /* - */
toDoNext = &parseMethodsName;
methodKind = K_METHOD;
break;
case ObjcPROPERTY:
toDoNext = &parseProperty;
break;
case ObjcEND: /* @end */
popEnclosingContext ();
toDoNext = &globalScope;
break;
case Tok_CurlL: /* { */
toDoNext = &parseFields;
break;
default:
break;
}
}
static void parseProtocol (vString * const ident, objcToken what)
{
if (what == ObjcIDENTIFIER)
{
pushEnclosingContext (ident, K_PROTOCOL);
addTag (ident, K_PROTOCOL);
}
toDoNext = &parseMethods;
}
static void parseImplementation (vString * const ident, objcToken what)
{
if (what == ObjcIDENTIFIER)
{
addTag (ident, K_IMPLEMENTATION);
pushEnclosingContext (ident, K_IMPLEMENTATION);
}
toDoNext = &parseImplemMethods;
}
static void parseInterface (vString * const ident, objcToken what)
{
if (what == ObjcIDENTIFIER)
{
addTag (ident, K_INTERFACE);
pushEnclosingContext (ident, K_INTERFACE);
}
toDoNext = &parseMethods;
}
static void parseStructMembers (vString * const ident, objcToken what)
{
static parseNext prev = NULL;
if (prev != NULL)
{
comeAfter = prev;
prev = NULL;
}
switch (what)
{
case ObjcIDENTIFIER:
vStringCopy (tempName, ident);
break;
case Tok_semi: /* ';' */
addTag (tempName, K_FIELD);
vStringClear (tempName);
break;
/* some types are complex, the only one
* we will loose is the function type.
*/
case Tok_CurlL: /* '{' */
case Tok_PARL: /* '(' */
case Tok_SQUAREL: /* '[' */
toDoNext = &ignoreBalanced;
prev = comeAfter;
comeAfter = &parseStructMembers;
ignoreBalanced (ident, what);
break;
case Tok_CurlR:
toDoNext = comeAfter;
break;
default:
/* don't care */
break;
}
}
/* Called just after the struct keyword */
static boolean parseStruct_gotName = FALSE;
static void parseStruct (vString * const ident, objcToken what)
{
switch (what)
{
case ObjcIDENTIFIER:
if (!parseStruct_gotName)
{
addTag (ident, K_STRUCT);
pushEnclosingContext (ident, K_STRUCT);
parseStruct_gotName = TRUE;
}
else
{
parseStruct_gotName = FALSE;
popEnclosingContext ();
toDoNext = comeAfter;
comeAfter (ident, what);
}
break;
case Tok_CurlL:
toDoNext = &parseStructMembers;
break;
/* maybe it was just a forward declaration
* in which case, we pop the context */
case Tok_semi:
if (parseStruct_gotName)
popEnclosingContext ();
toDoNext = comeAfter;
comeAfter (ident, what);
break;
default:
/* we don't care */
break;
}
}
/* Parse enumeration members, ignoring potential initialization */
static parseNext parseEnumFields_prev = NULL;
static void parseEnumFields (vString * const ident, objcToken what)
{
if (parseEnumFields_prev != NULL)
{
comeAfter = parseEnumFields_prev;
parseEnumFields_prev = NULL;
}
switch (what)
{
case ObjcIDENTIFIER:
addTag (ident, K_ENUM);
parseEnumFields_prev = comeAfter;
waitedToken = Tok_COMA;
/* last item might not have a coma */
fallBackToken = Tok_CurlR;
fallback = comeAfter;
comeAfter = parseEnumFields;
toDoNext = &tillTokenOrFallBack;
break;
case Tok_CurlR:
toDoNext = comeAfter;
popEnclosingContext ();
break;
default:
/* don't care */
break;
}
}
/* parse enum ... { ... */
static boolean parseEnum_named = FALSE;
static void parseEnum (vString * const ident, objcToken what)
{
switch (what)
{
case ObjcIDENTIFIER:
if (!parseEnum_named)
{
addTag (ident, K_ENUM);
pushEnclosingContext (ident, K_ENUM);
parseEnum_named = TRUE;
}
else
{
parseEnum_named = FALSE;
popEnclosingContext ();
toDoNext = comeAfter;
comeAfter (ident, what);
}
break;
case Tok_CurlL: /* '{' */
toDoNext = &parseEnumFields;
parseEnum_named = FALSE;
break;
case Tok_semi: /* ';' */
if (parseEnum_named)
popEnclosingContext ();
toDoNext = comeAfter;
comeAfter (ident, what);
break;
default:
/* don't care */
break;
}
}
/* Parse something like
* typedef .... ident ;
* ignoring the defined type but in the case of struct,
* in which case struct are parsed.
*/
static void parseTypedef (vString * const ident, objcToken what)
{
switch (what)
{
case ObjcSTRUCT:
toDoNext = &parseStruct;
comeAfter = &parseTypedef;
break;
case ObjcENUM:
toDoNext = &parseEnum;
comeAfter = &parseTypedef;
break;
case ObjcIDENTIFIER:
vStringCopy (tempName, ident);
break;
case Tok_semi: /* ';' */
addTag (tempName, K_TYPEDEF);
vStringClear (tempName);
toDoNext = &globalScope;
break;
default:
/* we don't care */
break;
}
}
static boolean ignorePreprocStuff_escaped = FALSE;
static void ignorePreprocStuff (vString * const UNUSED (ident), objcToken what)
{
switch (what)
{
case Tok_Backslash:
ignorePreprocStuff_escaped = TRUE;
break;
case Tok_EOL:
if (ignorePreprocStuff_escaped)
{
ignorePreprocStuff_escaped = FALSE;
}
else
{
toDoNext = &globalScope;
}
break;
default:
ignorePreprocStuff_escaped = FALSE;
break;
}
}
static void parseMacroName (vString * const ident, objcToken what)
{
if (what == ObjcIDENTIFIER)
addTag (ident, K_MACRO);
toDoNext = &ignorePreprocStuff;
}
static void parsePreproc (vString * const ident, objcToken what)
{
switch (what)
{
case ObjcIDENTIFIER:
if (strcmp (vStringValue (ident), "define") == 0)
toDoNext = &parseMacroName;
else
toDoNext = &ignorePreprocStuff;
break;
default:
toDoNext = &ignorePreprocStuff;
break;
}
}
/* Handle the "strong" top levels, all 'big' declarations
* happen here */
static void globalScope (vString * const ident, objcToken what)
{
switch (what)
{
case Tok_Sharp:
toDoNext = &parsePreproc;
break;
case ObjcSTRUCT:
toDoNext = &parseStruct;
comeAfter = &globalScope;
break;
case ObjcIDENTIFIER:
/* we keep track of the identifier if we
* come across a function. */
vStringCopy (tempName, ident);
break;
case Tok_PARL:
/* if we find an opening parenthesis it means we
* found a function (or a macro...) */
addTag (tempName, K_FUNCTION);
vStringClear (tempName);
comeAfter = &globalScope;
toDoNext = &ignoreBalanced;
ignoreBalanced (ident, what);
break;
case ObjcINTERFACE:
toDoNext = &parseInterface;
break;
case ObjcIMPLEMENTATION:
toDoNext = &parseImplementation;
break;
case ObjcPROTOCOL:
toDoNext = &parseProtocol;
break;
case ObjcTYPEDEF:
toDoNext = parseTypedef;
comeAfter = &globalScope;
break;
case Tok_CurlL:
comeAfter = &globalScope;
toDoNext = &ignoreBalanced;
ignoreBalanced (ident, what);
break;
case ObjcEND:
case ObjcPUBLIC:
case ObjcPROTECTED:
case ObjcPRIVATE:
default:
/* we don't care */
break;
}
}
/*////////////////////////////////////////////////////////////////
//// Deal with the system */
static void findObjcTags (void)
{
vString *name = vStringNew ();
lexingState st;
objcToken tok;
parentName = vStringNew ();
tempName = vStringNew ();
fullMethodName = vStringNew ();
prevIdent = vStringNew ();
/* (Re-)initialize state variables, this might be a second file */
comeAfter = NULL;
fallback = NULL;
parentType = K_INTERFACE;
ignoreBalanced_count = 0;
methodKind = 0;
parseStruct_gotName = FALSE;
parseEnumFields_prev = NULL;
parseEnum_named = FALSE;
ignorePreprocStuff_escaped = FALSE;
st.name = vStringNew ();
st.cp = fileReadLine ();
toDoNext = &globalScope;
tok = lex (&st);
while (tok != Tok_EOF)
{
(*toDoNext) (st.name, tok);
tok = lex (&st);
}
vStringDelete (name);
vStringDelete (parentName);
vStringDelete (tempName);
vStringDelete (fullMethodName);
vStringDelete (prevIdent);
parentName = NULL;
tempName = NULL;
prevIdent = NULL;
fullMethodName = NULL;
}
static void objcInitialize (const langType language)
{
Lang_ObjectiveC = language;
initKeywordHash ();
}
extern parserDefinition *ObjcParser (void)
{
static const char *const extensions[] = { "m", "h", NULL };
parserDefinition *def = parserNew ("ObjectiveC");
def->kinds = ObjcKinds;
def->kindCount = KIND_COUNT (ObjcKinds);
def->extensions = extensions;
def->parser = findObjcTags;
def->initialize = objcInitialize;
return def;
}
Jump to Line
Something went wrong with that request. Please try again.