3 * Copyright (c) 2010, Vincent Berthoux
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License version 2 or (at your option) any later version.
8 * This module contains functions for generating tags for Objective C
14 #include "general.h" /* must always come first */
25 /* To get rid of unused parameter warning in
28 #elif defined(__GNUC__)
29 # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
30 #elif defined(__LCLINT__)
31 # define UNUSED(x) /*@unused@*/ x
52 static kindOption ObjcKinds
[] = {
53 {TRUE
, 'i', "interface", "class interface"},
54 {TRUE
, 'I', "implementation", "class implementation"},
55 {TRUE
, 'P', "protocol", "Protocol"},
56 {TRUE
, 'm', "method", "Object's method"},
57 {TRUE
, 'c', "class", "Class' method"},
58 {TRUE
, 'v', "var", "Global variable"},
59 {TRUE
, 'F', "field", "Object field"},
60 {TRUE
, 'f', "function", "A function"},
61 {TRUE
, 'p', "property", "A property"},
62 {TRUE
, 't', "typedef", "A type alias"},
63 {TRUE
, 's', "struct", "A type structure"},
64 {TRUE
, 'e', "enum", "An enumeration"},
65 {TRUE
, 'M', "macro", "A preprocessor macro"},
100 Tok_SQUAREL
, /* '[' */
101 Tok_SQUARER
, /* ']' */
103 Tok_dpoint
, /* ':' */
105 Tok_Backslash
, /* '\\' */
106 Tok_EOL
, /* '\r''\n' */
109 Tok_EOF
/* END of file */
112 typedef objcKeyword objcToken
;
114 static const keywordTable objcKeywordTable
[] = {
115 {"typedef", ObjcTYPEDEF
},
116 {"struct", ObjcSTRUCT
},
118 {"@implementation", ObjcIMPLEMENTATION
},
119 {"@interface", ObjcINTERFACE
},
120 {"@protocol", ObjcPROTOCOL
},
121 {"@encode", ObjcENCODE
},
122 {"@property", ObjcPROPERTY
},
123 {"@synchronized", ObjcSYNCHRONIZED
},
124 {"@selector", ObjcSELECTOR
},
127 {"@class", ObjcCLASS
},
128 {"@private", ObjcPRIVATE
},
129 {"@package", ObjcPACKAGE
},
130 {"@public", ObjcPUBLIC
},
131 {"@protected", ObjcPROTECTED
},
132 {"@synthesize", ObjcSYNTHESIZE
},
133 {"@dynamic", ObjcDYNAMIC
},
134 {"@optional", ObjcOPTIONAL
},
135 {"@required", ObjcREQUIRED
},
138 static langType Lang_ObjectiveC
;
140 /*//////////////////////////////////////////////////////////////////
142 typedef struct _lexingState
{
143 vString
*name
; /* current parsed identifier/operator */
144 const unsigned char *cp
; /* position in stream */
147 /*//////////////////////////////////////////////////////////////////////
149 static boolean
isNum (char c
)
151 return c
>= '0' && c
<= '9';
154 static boolean
isLowerAlpha (char c
)
156 return c
>= 'a' && c
<= 'z';
159 static boolean
isUpperAlpha (char c
)
161 return c
>= 'A' && c
<= 'Z';
164 static boolean
isAlpha (char c
)
166 return isLowerAlpha (c
) || isUpperAlpha (c
);
169 static boolean
isIdent (char c
)
171 return isNum (c
) || isAlpha (c
) || c
== '_';
174 static boolean
isSpace (char c
)
176 return c
== ' ' || c
== '\t';
179 /* return true if it end with an end of line */
180 static void eatWhiteSpace (lexingState
* st
)
182 const unsigned char *cp
= st
->cp
;
183 while (isSpace (*cp
))
189 static void eatString (lexingState
* st
)
191 boolean lastIsBackSlash
= FALSE
;
192 boolean unfinished
= TRUE
;
193 const unsigned char *c
= st
->cp
+ 1;
197 /* end of line should never happen.
199 if (c
== NULL
|| c
[0] == '\0')
201 else if (*c
== '"' && !lastIsBackSlash
)
204 lastIsBackSlash
= *c
== '\\';
212 static void eatComment (lexingState
* st
)
214 boolean unfinished
= TRUE
;
215 boolean lastIsStar
= FALSE
;
216 const unsigned char *c
= st
->cp
+ 2;
220 /* we've reached the end of the line..
221 * so we have to reload a line... */
222 if (c
== NULL
|| *c
== '\0')
224 st
->cp
= readLineFromInputFile ();
225 /* WOOPS... no more input...
226 * we return, next lexing read
227 * will be null and ok */
232 /* we've reached the end of the comment */
233 else if (*c
== '/' && lastIsStar
)
237 lastIsStar
= '*' == *c
;
245 static void readIdentifier (lexingState
* st
)
247 const unsigned char *p
;
248 vStringClear (st
->name
);
250 /* first char is a simple letter */
251 if (isAlpha (*st
->cp
) || *st
->cp
== '_')
252 vStringPut (st
->name
, (int) *st
->cp
);
254 /* Go till you get identifier chars */
255 for (p
= st
->cp
+ 1; isIdent (*p
); p
++)
256 vStringPut (st
->name
, (int) *p
);
260 vStringTerminate (st
->name
);
263 /* read the @something directives */
264 static void readIdentifierObjcDirective (lexingState
* st
)
266 const unsigned char *p
;
267 vStringClear (st
->name
);
269 /* first char is a simple letter */
271 vStringPut (st
->name
, (int) *st
->cp
);
273 /* Go till you get identifier chars */
274 for (p
= st
->cp
+ 1; isIdent (*p
); p
++)
275 vStringPut (st
->name
, (int) *p
);
279 vStringTerminate (st
->name
);
282 /* The lexer is in charge of reading the file.
283 * Some of sub-lexer (like eatComment) also read file.
284 * lexing is finished when the lexer return Tok_EOF */
285 static objcKeyword
lex (lexingState
* st
)
289 /* handling data input here */
290 while (st
->cp
== NULL
|| st
->cp
[0] == '\0')
292 st
->cp
= readLineFromInputFile ();
299 if (isAlpha (*st
->cp
))
302 retType
= lookupKeyword (vStringValue (st
->name
), Lang_ObjectiveC
);
304 if (retType
== -1) /* If it's not a keyword */
306 return ObjcIDENTIFIER
;
313 else if (*st
->cp
== '@')
315 readIdentifierObjcDirective (st
);
316 retType
= lookupKeyword (vStringValue (st
->name
), Lang_ObjectiveC
);
318 if (retType
== -1) /* If it's not a keyword */
327 else if (isSpace (*st
->cp
))
341 return Tok_Backslash
;
348 if (st
->cp
[1] == '*') /* ergl, a comment */
353 else if (st
->cp
[1] == '/')
404 /* default return if nothing is recognized,
405 * shouldn't happen, but at least, it will
406 * be handled without destroying the parsing. */
410 /*//////////////////////////////////////////////////////////////////////
412 typedef void (*parseNext
) (vString
* const ident
, objcToken what
);
414 /********** Helpers */
415 /* This variable hold the 'parser' which is going to
416 * handle the next token */
417 static parseNext toDoNext
;
419 /* Special variable used by parser eater to
420 * determine which action to put after their
421 * job is finished. */
422 static parseNext comeAfter
;
424 /* Used by some parsers detecting certain token
425 * to revert to previous parser. */
426 static parseNext fallback
;
429 /********** Grammar */
430 static void globalScope (vString
* const ident
, objcToken what
);
431 static void parseMethods (vString
* const ident
, objcToken what
);
432 static void parseImplemMethods (vString
* const ident
, objcToken what
);
433 static vString
*tempName
= NULL
;
434 static vString
*parentName
= NULL
;
435 static objcKind parentType
= K_INTERFACE
;
437 /* used to prepare tag for OCaml, just in case their is a need to
438 * add additional information to the tag. */
439 static void prepareTag (tagEntryInfo
* tag
, vString
const *name
, objcKind kind
)
441 initTagEntry (tag
, vStringValue (name
), &(ObjcKinds
[kind
]));
443 if (parentName
!= NULL
)
445 tag
->extensionFields
.scopeKind
= &(ObjcKinds
[parentType
]);
446 tag
->extensionFields
.scopeName
= vStringValue (parentName
);
450 static void pushEnclosingContext (const vString
* parent
, objcKind type
)
452 vStringCopy (parentName
, parent
);
456 static void popEnclosingContext (void)
458 vStringClear (parentName
);
461 /* Used to centralise tag creation, and be able to add
462 * more information to it in the future */
463 static void addTag (vString
* const ident
, int kind
)
465 tagEntryInfo toCreate
;
467 if (! ObjcKinds
[kind
].enabled
)
470 prepareTag (&toCreate
, ident
, kind
);
471 makeTagEntry (&toCreate
);
474 static objcToken waitedToken
, fallBackToken
;
476 /* Ignore everything till waitedToken and jump to comeAfter.
477 * If the "end" keyword is encountered break, doesn't remember
479 static void tillToken (vString
* const UNUSED (ident
), objcToken what
)
481 if (what
== waitedToken
)
482 toDoNext
= comeAfter
;
485 static void tillTokenOrFallBack (vString
* const UNUSED (ident
), objcToken what
)
487 if (what
== waitedToken
)
488 toDoNext
= comeAfter
;
489 else if (what
== fallBackToken
)
495 static int ignoreBalanced_count
= 0;
496 static void ignoreBalanced (vString
* const UNUSED (ident
), objcToken what
)
504 ignoreBalanced_count
++;
510 ignoreBalanced_count
--;
518 if (ignoreBalanced_count
== 0)
519 toDoNext
= comeAfter
;
522 static void parseFields (vString
* const ident
, objcToken what
)
527 toDoNext
= &parseMethods
;
532 toDoNext
= &ignoreBalanced
;
533 comeAfter
= &parseFields
;
536 /* we got an identifier, keep track of it */
538 vStringCopy (tempName
, ident
);
541 /* our last kept identifier must be our variable name =) */
543 addTag (tempName
, K_FIELD
);
544 vStringClear (tempName
);
553 static objcKind methodKind
;
556 static vString
*fullMethodName
;
557 static vString
*prevIdent
;
559 static void parseMethodsName (vString
* const ident
, objcToken what
)
564 toDoNext
= &tillToken
;
565 comeAfter
= &parseMethodsName
;
566 waitedToken
= Tok_PARR
;
570 vStringCat (fullMethodName
, prevIdent
);
571 vStringCatS (fullMethodName
, ":");
572 vStringClear (prevIdent
);
576 vStringCopy (prevIdent
, ident
);
581 /* method name is not simple */
582 if (vStringLength (fullMethodName
) != '\0')
584 addTag (fullMethodName
, methodKind
);
585 vStringClear (fullMethodName
);
588 addTag (prevIdent
, methodKind
);
590 toDoNext
= &parseMethods
;
591 parseImplemMethods (ident
, what
);
592 vStringClear (prevIdent
);
600 static void parseMethodsImplemName (vString
* const ident
, objcToken what
)
605 toDoNext
= &tillToken
;
606 comeAfter
= &parseMethodsImplemName
;
607 waitedToken
= Tok_PARR
;
611 vStringCat (fullMethodName
, prevIdent
);
612 vStringCatS (fullMethodName
, ":");
613 vStringClear (prevIdent
);
617 vStringCopy (prevIdent
, ident
);
622 /* method name is not simple */
623 if (vStringLength (fullMethodName
) != '\0')
625 addTag (fullMethodName
, methodKind
);
626 vStringClear (fullMethodName
);
629 addTag (prevIdent
, methodKind
);
631 toDoNext
= &parseImplemMethods
;
632 parseImplemMethods (ident
, what
);
633 vStringClear (prevIdent
);
641 static void parseImplemMethods (vString
* const ident
, objcToken what
)
645 case Tok_PLUS
: /* + */
646 toDoNext
= &parseMethodsImplemName
;
647 methodKind
= K_CLASSMETHOD
;
650 case Tok_MINUS
: /* - */
651 toDoNext
= &parseMethodsImplemName
;
652 methodKind
= K_METHOD
;
655 case ObjcEND
: /* @end */
656 popEnclosingContext ();
657 toDoNext
= &globalScope
;
660 case Tok_CurlL
: /* { */
661 toDoNext
= &ignoreBalanced
;
662 ignoreBalanced (ident
, what
);
663 comeAfter
= &parseImplemMethods
;
671 static void parseProperty (vString
* const ident
, objcToken what
)
676 toDoNext
= &tillToken
;
677 comeAfter
= &parseProperty
;
678 waitedToken
= Tok_PARR
;
681 /* we got an identifier, keep track of it */
683 vStringCopy (tempName
, ident
);
686 /* our last kept identifier must be our variable name =) */
688 addTag (tempName
, K_PROPERTY
);
689 vStringClear (tempName
);
690 toDoNext
= &parseMethods
;
698 static void parseMethods (vString
* const UNUSED (ident
), objcToken what
)
702 case Tok_PLUS
: /* + */
703 toDoNext
= &parseMethodsName
;
704 methodKind
= K_CLASSMETHOD
;
707 case Tok_MINUS
: /* - */
708 toDoNext
= &parseMethodsName
;
709 methodKind
= K_METHOD
;
713 toDoNext
= &parseProperty
;
716 case ObjcEND
: /* @end */
717 popEnclosingContext ();
718 toDoNext
= &globalScope
;
721 case Tok_CurlL
: /* { */
722 toDoNext
= &parseFields
;
731 static void parseProtocol (vString
* const ident
, objcToken what
)
733 if (what
== ObjcIDENTIFIER
)
735 pushEnclosingContext (ident
, K_PROTOCOL
);
736 addTag (ident
, K_PROTOCOL
);
738 toDoNext
= &parseMethods
;
741 static void parseImplementation (vString
* const ident
, objcToken what
)
743 if (what
== ObjcIDENTIFIER
)
745 addTag (ident
, K_IMPLEMENTATION
);
746 pushEnclosingContext (ident
, K_IMPLEMENTATION
);
748 toDoNext
= &parseImplemMethods
;
751 static void parseInterface (vString
* const ident
, objcToken what
)
753 if (what
== ObjcIDENTIFIER
)
755 addTag (ident
, K_INTERFACE
);
756 pushEnclosingContext (ident
, K_INTERFACE
);
759 toDoNext
= &parseMethods
;
762 static void parseStructMembers (vString
* const ident
, objcToken what
)
764 static parseNext prev
= NULL
;
775 vStringCopy (tempName
, ident
);
778 case Tok_semi
: /* ';' */
779 addTag (tempName
, K_FIELD
);
780 vStringClear (tempName
);
783 /* some types are complex, the only one
784 * we will loose is the function type.
786 case Tok_CurlL
: /* '{' */
787 case Tok_PARL
: /* '(' */
788 case Tok_SQUAREL
: /* '[' */
789 toDoNext
= &ignoreBalanced
;
791 comeAfter
= &parseStructMembers
;
792 ignoreBalanced (ident
, what
);
796 toDoNext
= comeAfter
;
805 /* Called just after the struct keyword */
806 static boolean parseStruct_gotName
= FALSE
;
807 static void parseStruct (vString
* const ident
, objcToken what
)
812 if (!parseStruct_gotName
)
814 addTag (ident
, K_STRUCT
);
815 pushEnclosingContext (ident
, K_STRUCT
);
816 parseStruct_gotName
= TRUE
;
820 parseStruct_gotName
= FALSE
;
821 popEnclosingContext ();
822 toDoNext
= comeAfter
;
823 comeAfter (ident
, what
);
828 toDoNext
= &parseStructMembers
;
831 /* maybe it was just a forward declaration
832 * in which case, we pop the context */
834 if (parseStruct_gotName
)
835 popEnclosingContext ();
837 toDoNext
= comeAfter
;
838 comeAfter (ident
, what
);
847 /* Parse enumeration members, ignoring potential initialization */
848 static parseNext parseEnumFields_prev
= NULL
;
849 static void parseEnumFields (vString
* const ident
, objcToken what
)
851 if (parseEnumFields_prev
!= NULL
)
853 comeAfter
= parseEnumFields_prev
;
854 parseEnumFields_prev
= NULL
;
860 addTag (ident
, K_ENUM
);
861 parseEnumFields_prev
= comeAfter
;
862 waitedToken
= Tok_COMA
;
863 /* last item might not have a coma */
864 fallBackToken
= Tok_CurlR
;
865 fallback
= comeAfter
;
866 comeAfter
= parseEnumFields
;
867 toDoNext
= &tillTokenOrFallBack
;
871 toDoNext
= comeAfter
;
872 popEnclosingContext ();
881 /* parse enum ... { ... */
882 static boolean parseEnum_named
= FALSE
;
883 static void parseEnum (vString
* const ident
, objcToken what
)
888 if (!parseEnum_named
)
890 addTag (ident
, K_ENUM
);
891 pushEnclosingContext (ident
, K_ENUM
);
892 parseEnum_named
= TRUE
;
896 parseEnum_named
= FALSE
;
897 popEnclosingContext ();
898 toDoNext
= comeAfter
;
899 comeAfter (ident
, what
);
903 case Tok_CurlL
: /* '{' */
904 toDoNext
= &parseEnumFields
;
905 parseEnum_named
= FALSE
;
908 case Tok_semi
: /* ';' */
910 popEnclosingContext ();
911 toDoNext
= comeAfter
;
912 comeAfter (ident
, what
);
921 /* Parse something like
922 * typedef .... ident ;
923 * ignoring the defined type but in the case of struct,
924 * in which case struct are parsed.
926 static void parseTypedef (vString
* const ident
, objcToken what
)
931 toDoNext
= &parseStruct
;
932 comeAfter
= &parseTypedef
;
936 toDoNext
= &parseEnum
;
937 comeAfter
= &parseTypedef
;
941 vStringCopy (tempName
, ident
);
944 case Tok_semi
: /* ';' */
945 addTag (tempName
, K_TYPEDEF
);
946 vStringClear (tempName
);
947 toDoNext
= &globalScope
;
956 static boolean ignorePreprocStuff_escaped
= FALSE
;
957 static void ignorePreprocStuff (vString
* const UNUSED (ident
), objcToken what
)
962 ignorePreprocStuff_escaped
= TRUE
;
966 if (ignorePreprocStuff_escaped
)
968 ignorePreprocStuff_escaped
= FALSE
;
972 toDoNext
= &globalScope
;
977 ignorePreprocStuff_escaped
= FALSE
;
982 static void parseMacroName (vString
* const ident
, objcToken what
)
984 if (what
== ObjcIDENTIFIER
)
985 addTag (ident
, K_MACRO
);
987 toDoNext
= &ignorePreprocStuff
;
990 static void parsePreproc (vString
* const ident
, objcToken what
)
995 if (strcmp (vStringValue (ident
), "define") == 0)
996 toDoNext
= &parseMacroName
;
998 toDoNext
= &ignorePreprocStuff
;
1002 toDoNext
= &ignorePreprocStuff
;
1007 /* Handle the "strong" top levels, all 'big' declarations
1009 static void globalScope (vString
* const ident
, objcToken what
)
1014 toDoNext
= &parsePreproc
;
1018 toDoNext
= &parseStruct
;
1019 comeAfter
= &globalScope
;
1022 case ObjcIDENTIFIER
:
1023 /* we keep track of the identifier if we
1024 * come across a function. */
1025 vStringCopy (tempName
, ident
);
1029 /* if we find an opening parenthesis it means we
1030 * found a function (or a macro...) */
1031 addTag (tempName
, K_FUNCTION
);
1032 vStringClear (tempName
);
1033 comeAfter
= &globalScope
;
1034 toDoNext
= &ignoreBalanced
;
1035 ignoreBalanced (ident
, what
);
1039 toDoNext
= &parseInterface
;
1042 case ObjcIMPLEMENTATION
:
1043 toDoNext
= &parseImplementation
;
1047 toDoNext
= &parseProtocol
;
1051 toDoNext
= parseTypedef
;
1052 comeAfter
= &globalScope
;
1056 comeAfter
= &globalScope
;
1057 toDoNext
= &ignoreBalanced
;
1058 ignoreBalanced (ident
, what
);
1072 /*////////////////////////////////////////////////////////////////
1073 //// Deal with the system */
1075 static void findObjcTags (void)
1077 vString
*name
= vStringNew ();
1081 parentName
= vStringNew ();
1082 tempName
= vStringNew ();
1083 fullMethodName
= vStringNew ();
1084 prevIdent
= vStringNew ();
1086 /* (Re-)initialize state variables, this might be a second file */
1089 parentType
= K_INTERFACE
;
1090 ignoreBalanced_count
= 0;
1092 parseStruct_gotName
= FALSE
;
1093 parseEnumFields_prev
= NULL
;
1094 parseEnum_named
= FALSE
;
1095 ignorePreprocStuff_escaped
= FALSE
;
1097 st
.name
= vStringNew ();
1098 st
.cp
= readLineFromInputFile ();
1099 toDoNext
= &globalScope
;
1101 while (tok
!= Tok_EOF
)
1103 (*toDoNext
) (st
.name
, tok
);
1106 vStringDelete(st
.name
);
1108 vStringDelete (name
);
1109 vStringDelete (parentName
);
1110 vStringDelete (tempName
);
1111 vStringDelete (fullMethodName
);
1112 vStringDelete (prevIdent
);
1116 fullMethodName
= NULL
;
1119 static void objcInitialize (const langType language
)
1121 Lang_ObjectiveC
= language
;
1124 extern parserDefinition
*ObjcParser (void)
1126 static const char *const extensions
[] = { "m", "h", NULL
};
1127 parserDefinition
*def
= parserNewFull ("ObjectiveC", KIND_FILE_ALT
);
1128 def
->kinds
= ObjcKinds
;
1129 def
->kindCount
= ARRAY_SIZE (ObjcKinds
);
1130 def
->extensions
= extensions
;
1131 def
->parser
= findObjcTags
;
1132 def
->initialize
= objcInitialize
;
1133 def
->keywordTable
= objcKeywordTable
;
1134 def
->keywordCount
= ARRAY_SIZE (objcKeywordTable
);