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.
8 * This module contains functions for generating tags for Objective C
14 #include "general.h" /* must always come first */
24 /* To get rid of unused parameter warning in
27 #elif defined(__GNUC__)
28 # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
29 #elif defined(__LCLINT__)
30 # define UNUSED(x) /*@unused@*/ x
51 static kindOption ObjcKinds
[] = {
52 {TRUE
, 'i', "interface", "class interface"},
53 {TRUE
, 'I', "implementation", "class implementation"},
54 {TRUE
, 'P', "protocol", "Protocol"},
55 {TRUE
, 'm', "method", "Object's method"},
56 {TRUE
, 'c', "class", "Class' method"},
57 {TRUE
, 'v', "var", "Global variable"},
58 {TRUE
, 'F', "field", "Object field"},
59 {TRUE
, 'f', "function", "A function"},
60 {TRUE
, 'p', "property", "A property"},
61 {TRUE
, 't', "typedef", "A type alias"},
62 {TRUE
, 's', "struct", "A type structure"},
63 {TRUE
, 'e', "enum", "An enumeration"},
64 {TRUE
, 'M', "macro", "A preprocessor macro"},
99 Tok_SQUAREL
, /* '[' */
100 Tok_SQUARER
, /* ']' */
102 Tok_dpoint
, /* ':' */
104 Tok_Backslash
, /* '\\' */
105 Tok_EOL
, /* '\r''\n' */
108 Tok_EOF
/* END of file */
111 typedef objcKeyword objcToken
;
113 typedef struct sOBjcKeywordDesc
{
119 static const objcKeywordDesc objcKeywordTable
[] = {
120 {"typedef", ObjcTYPEDEF
},
121 {"struct", ObjcSTRUCT
},
123 {"@implementation", ObjcIMPLEMENTATION
},
124 {"@interface", ObjcINTERFACE
},
125 {"@protocol", ObjcPROTOCOL
},
126 {"@encode", ObjcENCODE
},
127 {"@property", ObjcPROPERTY
},
128 {"@synchronized", ObjcSYNCHRONIZED
},
129 {"@selector", ObjcSELECTOR
},
132 {"@class", ObjcCLASS
},
133 {"@private", ObjcPRIVATE
},
134 {"@package", ObjcPACKAGE
},
135 {"@public", ObjcPUBLIC
},
136 {"@protected", ObjcPROTECTED
},
137 {"@synthesize", ObjcSYNTHESIZE
},
138 {"@dynamic", ObjcDYNAMIC
},
139 {"@optional", ObjcOPTIONAL
},
140 {"@required", ObjcREQUIRED
},
143 static langType Lang_ObjectiveC
;
145 /*//////////////////////////////////////////////////////////////////
147 typedef struct _lexingState
{
148 vString
*name
; /* current parsed identifier/operator */
149 const unsigned char *cp
; /* position in stream */
152 static void initKeywordHash (void)
154 const size_t count
= sizeof (objcKeywordTable
) / sizeof (objcKeywordDesc
);
157 for (i
= 0; i
< count
; ++i
)
159 addKeyword (objcKeywordTable
[i
].name
, Lang_ObjectiveC
,
160 (int) objcKeywordTable
[i
].id
);
164 /*//////////////////////////////////////////////////////////////////////
166 static boolean
isNum (char c
)
168 return c
>= '0' && c
<= '9';
171 static boolean
isLowerAlpha (char c
)
173 return c
>= 'a' && c
<= 'z';
176 static boolean
isUpperAlpha (char c
)
178 return c
>= 'A' && c
<= 'Z';
181 static boolean
isAlpha (char c
)
183 return isLowerAlpha (c
) || isUpperAlpha (c
);
186 static boolean
isIdent (char c
)
188 return isNum (c
) || isAlpha (c
) || c
== '_';
191 static boolean
isSpace (char c
)
193 return c
== ' ' || c
== '\t';
196 /* return true if it end with an end of line */
197 static void eatWhiteSpace (lexingState
* st
)
199 const unsigned char *cp
= st
->cp
;
200 while (isSpace (*cp
))
206 static void eatString (lexingState
* st
)
208 boolean lastIsBackSlash
= FALSE
;
209 boolean unfinished
= TRUE
;
210 const unsigned char *c
= st
->cp
+ 1;
214 /* end of line should never happen.
216 if (c
== NULL
|| c
[0] == '\0')
218 else if (*c
== '"' && !lastIsBackSlash
)
221 lastIsBackSlash
= *c
== '\\';
229 static void eatComment (lexingState
* st
)
231 boolean unfinished
= TRUE
;
232 boolean lastIsStar
= FALSE
;
233 const unsigned char *c
= st
->cp
+ 2;
237 /* we've reached the end of the line..
238 * so we have to reload a line... */
239 if (c
== NULL
|| *c
== '\0')
241 st
->cp
= fileReadLine ();
242 /* WOOPS... no more input...
243 * we return, next lexing read
244 * will be null and ok */
249 /* we've reached the end of the comment */
250 else if (*c
== '/' && lastIsStar
)
254 lastIsStar
= '*' == *c
;
262 static void readIdentifier (lexingState
* st
)
264 const unsigned char *p
;
265 vStringClear (st
->name
);
267 /* first char is a simple letter */
268 if (isAlpha (*st
->cp
) || *st
->cp
== '_')
269 vStringPut (st
->name
, (int) *st
->cp
);
271 /* Go till you get identifier chars */
272 for (p
= st
->cp
+ 1; isIdent (*p
); p
++)
273 vStringPut (st
->name
, (int) *p
);
277 vStringTerminate (st
->name
);
280 /* read the @something directives */
281 static void readIdentifierObjcDirective (lexingState
* st
)
283 const unsigned char *p
;
284 vStringClear (st
->name
);
286 /* first char is a simple letter */
288 vStringPut (st
->name
, (int) *st
->cp
);
290 /* Go till you get identifier chars */
291 for (p
= st
->cp
+ 1; isIdent (*p
); p
++)
292 vStringPut (st
->name
, (int) *p
);
296 vStringTerminate (st
->name
);
299 /* The lexer is in charge of reading the file.
300 * Some of sub-lexer (like eatComment) also read file.
301 * lexing is finished when the lexer return Tok_EOF */
302 static objcKeyword
lex (lexingState
* st
)
306 /* handling data input here */
307 while (st
->cp
== NULL
|| st
->cp
[0] == '\0')
309 st
->cp
= fileReadLine ();
316 if (isAlpha (*st
->cp
))
319 retType
= lookupKeyword (vStringValue (st
->name
), Lang_ObjectiveC
);
321 if (retType
== -1) /* If it's not a keyword */
323 return ObjcIDENTIFIER
;
330 else if (*st
->cp
== '@')
332 readIdentifierObjcDirective (st
);
333 retType
= lookupKeyword (vStringValue (st
->name
), Lang_ObjectiveC
);
335 if (retType
== -1) /* If it's not a keyword */
344 else if (isSpace (*st
->cp
))
358 return Tok_Backslash
;
365 if (st
->cp
[1] == '*') /* ergl, a comment */
370 else if (st
->cp
[1] == '/')
421 /* default return if nothing is recognized,
422 * shouldn't happen, but at least, it will
423 * be handled without destroying the parsing. */
427 /*//////////////////////////////////////////////////////////////////////
429 typedef void (*parseNext
) (vString
* const ident
, objcToken what
);
431 /********** Helpers */
432 /* This variable hold the 'parser' which is going to
433 * handle the next token */
434 static parseNext toDoNext
;
436 /* Special variable used by parser eater to
437 * determine which action to put after their
438 * job is finished. */
439 static parseNext comeAfter
;
441 /* Used by some parsers detecting certain token
442 * to revert to previous parser. */
443 static parseNext fallback
;
446 /********** Grammar */
447 static void globalScope (vString
* const ident
, objcToken what
);
448 static void parseMethods (vString
* const ident
, objcToken what
);
449 static void parseImplemMethods (vString
* const ident
, objcToken what
);
450 static vString
*tempName
= NULL
;
451 static vString
*parentName
= NULL
;
452 static objcKind parentType
= K_INTERFACE
;
454 /* used to prepare tag for OCaml, just in case their is a need to
455 * add additional information to the tag. */
456 static void prepareTag (tagEntryInfo
* tag
, vString
const *name
, objcKind kind
)
458 initTagEntry (tag
, vStringValue (name
));
459 tag
->kindName
= ObjcKinds
[kind
].name
;
460 tag
->kind
= ObjcKinds
[kind
].letter
;
462 if (parentName
!= NULL
)
464 tag
->extensionFields
.scope
[0] = ObjcKinds
[parentType
].name
;
465 tag
->extensionFields
.scope
[1] = vStringValue (parentName
);
469 static void pushEnclosingContext (const vString
* parent
, objcKind type
)
471 vStringCopy (parentName
, parent
);
475 static void popEnclosingContext (void)
477 vStringClear (parentName
);
480 /* Used to centralise tag creation, and be able to add
481 * more information to it in the future */
482 static void addTag (vString
* const ident
, int kind
)
484 tagEntryInfo toCreate
;
486 if (! ObjcKinds
[kind
].enabled
)
489 prepareTag (&toCreate
, ident
, kind
);
490 makeTagEntry (&toCreate
);
493 static objcToken waitedToken
, fallBackToken
;
495 /* Ignore everything till waitedToken and jump to comeAfter.
496 * If the "end" keyword is encountered break, doesn't remember
498 static void tillToken (vString
* const UNUSED (ident
), objcToken what
)
500 if (what
== waitedToken
)
501 toDoNext
= comeAfter
;
504 static void tillTokenOrFallBack (vString
* const UNUSED (ident
), objcToken what
)
506 if (what
== waitedToken
)
507 toDoNext
= comeAfter
;
508 else if (what
== fallBackToken
)
514 static int ignoreBalanced_count
= 0;
515 static void ignoreBalanced (vString
* const UNUSED (ident
), objcToken what
)
523 ignoreBalanced_count
++;
529 ignoreBalanced_count
--;
537 if (ignoreBalanced_count
== 0)
538 toDoNext
= comeAfter
;
541 static void parseFields (vString
* const ident
, objcToken what
)
546 toDoNext
= &parseMethods
;
551 toDoNext
= &ignoreBalanced
;
552 comeAfter
= &parseFields
;
555 /* we got an identifier, keep track of it */
557 vStringCopy (tempName
, ident
);
560 /* our last kept identifier must be our variable name =) */
562 addTag (tempName
, K_FIELD
);
563 vStringClear (tempName
);
572 static objcKind methodKind
;
575 static vString
*fullMethodName
;
576 static vString
*prevIdent
;
578 static void parseMethodsName (vString
* const ident
, objcToken what
)
583 toDoNext
= &tillToken
;
584 comeAfter
= &parseMethodsName
;
585 waitedToken
= Tok_PARR
;
589 vStringCat (fullMethodName
, prevIdent
);
590 vStringCatS (fullMethodName
, ":");
591 vStringClear (prevIdent
);
595 vStringCopy (prevIdent
, ident
);
600 /* method name is not simple */
601 if (vStringLength (fullMethodName
) != '\0')
603 addTag (fullMethodName
, methodKind
);
604 vStringClear (fullMethodName
);
607 addTag (prevIdent
, methodKind
);
609 toDoNext
= &parseMethods
;
610 parseImplemMethods (ident
, what
);
611 vStringClear (prevIdent
);
619 static void parseMethodsImplemName (vString
* const ident
, objcToken what
)
624 toDoNext
= &tillToken
;
625 comeAfter
= &parseMethodsImplemName
;
626 waitedToken
= Tok_PARR
;
630 vStringCat (fullMethodName
, prevIdent
);
631 vStringCatS (fullMethodName
, ":");
632 vStringClear (prevIdent
);
636 vStringCopy (prevIdent
, ident
);
641 /* method name is not simple */
642 if (vStringLength (fullMethodName
) != '\0')
644 addTag (fullMethodName
, methodKind
);
645 vStringClear (fullMethodName
);
648 addTag (prevIdent
, methodKind
);
650 toDoNext
= &parseImplemMethods
;
651 parseImplemMethods (ident
, what
);
652 vStringClear (prevIdent
);
660 static void parseImplemMethods (vString
* const ident
, objcToken what
)
664 case Tok_PLUS
: /* + */
665 toDoNext
= &parseMethodsImplemName
;
666 methodKind
= K_CLASSMETHOD
;
669 case Tok_MINUS
: /* - */
670 toDoNext
= &parseMethodsImplemName
;
671 methodKind
= K_METHOD
;
674 case ObjcEND
: /* @end */
675 popEnclosingContext ();
676 toDoNext
= &globalScope
;
679 case Tok_CurlL
: /* { */
680 toDoNext
= &ignoreBalanced
;
681 ignoreBalanced (ident
, what
);
682 comeAfter
= &parseImplemMethods
;
690 static void parseProperty (vString
* const ident
, objcToken what
)
695 toDoNext
= &tillToken
;
696 comeAfter
= &parseProperty
;
697 waitedToken
= Tok_PARR
;
700 /* we got an identifier, keep track of it */
702 vStringCopy (tempName
, ident
);
705 /* our last kept identifier must be our variable name =) */
707 addTag (tempName
, K_PROPERTY
);
708 vStringClear (tempName
);
709 toDoNext
= &parseMethods
;
717 static void parseMethods (vString
* const UNUSED (ident
), objcToken what
)
721 case Tok_PLUS
: /* + */
722 toDoNext
= &parseMethodsName
;
723 methodKind
= K_CLASSMETHOD
;
726 case Tok_MINUS
: /* - */
727 toDoNext
= &parseMethodsName
;
728 methodKind
= K_METHOD
;
732 toDoNext
= &parseProperty
;
735 case ObjcEND
: /* @end */
736 popEnclosingContext ();
737 toDoNext
= &globalScope
;
740 case Tok_CurlL
: /* { */
741 toDoNext
= &parseFields
;
750 static void parseProtocol (vString
* const ident
, objcToken what
)
752 if (what
== ObjcIDENTIFIER
)
754 pushEnclosingContext (ident
, K_PROTOCOL
);
755 addTag (ident
, K_PROTOCOL
);
757 toDoNext
= &parseMethods
;
760 static void parseImplementation (vString
* const ident
, objcToken what
)
762 if (what
== ObjcIDENTIFIER
)
764 addTag (ident
, K_IMPLEMENTATION
);
765 pushEnclosingContext (ident
, K_IMPLEMENTATION
);
767 toDoNext
= &parseImplemMethods
;
770 static void parseInterface (vString
* const ident
, objcToken what
)
772 if (what
== ObjcIDENTIFIER
)
774 addTag (ident
, K_INTERFACE
);
775 pushEnclosingContext (ident
, K_INTERFACE
);
778 toDoNext
= &parseMethods
;
781 static void parseStructMembers (vString
* const ident
, objcToken what
)
783 static parseNext prev
= NULL
;
794 vStringCopy (tempName
, ident
);
797 case Tok_semi
: /* ';' */
798 addTag (tempName
, K_FIELD
);
799 vStringClear (tempName
);
802 /* some types are complex, the only one
803 * we will loose is the function type.
805 case Tok_CurlL
: /* '{' */
806 case Tok_PARL
: /* '(' */
807 case Tok_SQUAREL
: /* '[' */
808 toDoNext
= &ignoreBalanced
;
810 comeAfter
= &parseStructMembers
;
811 ignoreBalanced (ident
, what
);
815 toDoNext
= comeAfter
;
824 /* Called just after the struct keyword */
825 static boolean parseStruct_gotName
= FALSE
;
826 static void parseStruct (vString
* const ident
, objcToken what
)
831 if (!parseStruct_gotName
)
833 addTag (ident
, K_STRUCT
);
834 pushEnclosingContext (ident
, K_STRUCT
);
835 parseStruct_gotName
= TRUE
;
839 parseStruct_gotName
= FALSE
;
840 popEnclosingContext ();
841 toDoNext
= comeAfter
;
842 comeAfter (ident
, what
);
847 toDoNext
= &parseStructMembers
;
850 /* maybe it was just a forward declaration
851 * in which case, we pop the context */
853 if (parseStruct_gotName
)
854 popEnclosingContext ();
856 toDoNext
= comeAfter
;
857 comeAfter (ident
, what
);
866 /* Parse enumeration members, ignoring potential initialization */
867 static parseNext parseEnumFields_prev
= NULL
;
868 static void parseEnumFields (vString
* const ident
, objcToken what
)
870 if (parseEnumFields_prev
!= NULL
)
872 comeAfter
= parseEnumFields_prev
;
873 parseEnumFields_prev
= NULL
;
879 addTag (ident
, K_ENUM
);
880 parseEnumFields_prev
= comeAfter
;
881 waitedToken
= Tok_COMA
;
882 /* last item might not have a coma */
883 fallBackToken
= Tok_CurlR
;
884 fallback
= comeAfter
;
885 comeAfter
= parseEnumFields
;
886 toDoNext
= &tillTokenOrFallBack
;
890 toDoNext
= comeAfter
;
891 popEnclosingContext ();
900 /* parse enum ... { ... */
901 static boolean parseEnum_named
= FALSE
;
902 static void parseEnum (vString
* const ident
, objcToken what
)
907 if (!parseEnum_named
)
909 addTag (ident
, K_ENUM
);
910 pushEnclosingContext (ident
, K_ENUM
);
911 parseEnum_named
= TRUE
;
915 parseEnum_named
= FALSE
;
916 popEnclosingContext ();
917 toDoNext
= comeAfter
;
918 comeAfter (ident
, what
);
922 case Tok_CurlL
: /* '{' */
923 toDoNext
= &parseEnumFields
;
924 parseEnum_named
= FALSE
;
927 case Tok_semi
: /* ';' */
929 popEnclosingContext ();
930 toDoNext
= comeAfter
;
931 comeAfter (ident
, what
);
940 /* Parse something like
941 * typedef .... ident ;
942 * ignoring the defined type but in the case of struct,
943 * in which case struct are parsed.
945 static void parseTypedef (vString
* const ident
, objcToken what
)
950 toDoNext
= &parseStruct
;
951 comeAfter
= &parseTypedef
;
955 toDoNext
= &parseEnum
;
956 comeAfter
= &parseTypedef
;
960 vStringCopy (tempName
, ident
);
963 case Tok_semi
: /* ';' */
964 addTag (tempName
, K_TYPEDEF
);
965 vStringClear (tempName
);
966 toDoNext
= &globalScope
;
975 static boolean ignorePreprocStuff_escaped
= FALSE
;
976 static void ignorePreprocStuff (vString
* const UNUSED (ident
), objcToken what
)
981 ignorePreprocStuff_escaped
= TRUE
;
985 if (ignorePreprocStuff_escaped
)
987 ignorePreprocStuff_escaped
= FALSE
;
991 toDoNext
= &globalScope
;
996 ignorePreprocStuff_escaped
= FALSE
;
1001 static void parseMacroName (vString
* const ident
, objcToken what
)
1003 if (what
== ObjcIDENTIFIER
)
1004 addTag (ident
, K_MACRO
);
1006 toDoNext
= &ignorePreprocStuff
;
1009 static void parsePreproc (vString
* const ident
, objcToken what
)
1013 case ObjcIDENTIFIER
:
1014 if (strcmp (vStringValue (ident
), "define") == 0)
1015 toDoNext
= &parseMacroName
;
1017 toDoNext
= &ignorePreprocStuff
;
1021 toDoNext
= &ignorePreprocStuff
;
1026 /* Handle the "strong" top levels, all 'big' declarations
1028 static void globalScope (vString
* const ident
, objcToken what
)
1033 toDoNext
= &parsePreproc
;
1037 toDoNext
= &parseStruct
;
1038 comeAfter
= &globalScope
;
1041 case ObjcIDENTIFIER
:
1042 /* we keep track of the identifier if we
1043 * come across a function. */
1044 vStringCopy (tempName
, ident
);
1048 /* if we find an opening parenthesis it means we
1049 * found a function (or a macro...) */
1050 addTag (tempName
, K_FUNCTION
);
1051 vStringClear (tempName
);
1052 comeAfter
= &globalScope
;
1053 toDoNext
= &ignoreBalanced
;
1054 ignoreBalanced (ident
, what
);
1058 toDoNext
= &parseInterface
;
1061 case ObjcIMPLEMENTATION
:
1062 toDoNext
= &parseImplementation
;
1066 toDoNext
= &parseProtocol
;
1070 toDoNext
= parseTypedef
;
1071 comeAfter
= &globalScope
;
1075 comeAfter
= &globalScope
;
1076 toDoNext
= &ignoreBalanced
;
1077 ignoreBalanced (ident
, what
);
1091 /*////////////////////////////////////////////////////////////////
1092 //// Deal with the system */
1094 static void findObjcTags (void)
1096 vString
*name
= vStringNew ();
1100 parentName
= vStringNew ();
1101 tempName
= vStringNew ();
1102 fullMethodName
= vStringNew ();
1103 prevIdent
= vStringNew ();
1105 /* (Re-)initialize state variables, this might be a second file */
1108 parentType
= K_INTERFACE
;
1109 ignoreBalanced_count
= 0;
1111 parseStruct_gotName
= FALSE
;
1112 parseEnumFields_prev
= NULL
;
1113 parseEnum_named
= FALSE
;
1114 ignorePreprocStuff_escaped
= FALSE
;
1116 st
.name
= vStringNew ();
1117 st
.cp
= fileReadLine ();
1118 toDoNext
= &globalScope
;
1120 while (tok
!= Tok_EOF
)
1122 (*toDoNext
) (st
.name
, tok
);
1125 vStringDelete(st
.name
);
1127 vStringDelete (name
);
1128 vStringDelete (parentName
);
1129 vStringDelete (tempName
);
1130 vStringDelete (fullMethodName
);
1131 vStringDelete (prevIdent
);
1135 fullMethodName
= NULL
;
1138 static void objcInitialize (const langType language
)
1140 Lang_ObjectiveC
= language
;
1145 extern parserDefinition
*ObjcParser (void)
1147 static const char *const extensions
[] = { "m", "h", NULL
};
1148 parserDefinition
*def
= parserNew ("ObjectiveC");
1149 def
->kinds
= ObjcKinds
;
1150 def
->kindCount
= KIND_COUNT (ObjcKinds
);
1151 def
->extensions
= extensions
;
1152 def
->parser
= findObjcTags
;
1153 def
->initialize
= objcInitialize
;