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
;
485 prepareTag (&toCreate
, ident
, kind
);
486 makeTagEntry (&toCreate
);
489 static objcToken waitedToken
, fallBackToken
;
491 /* Ignore everything till waitedToken and jump to comeAfter.
492 * If the "end" keyword is encountered break, doesn't remember
494 static void tillToken (vString
* const UNUSED (ident
), objcToken what
)
496 if (what
== waitedToken
)
497 toDoNext
= comeAfter
;
500 static void tillTokenOrFallBack (vString
* const UNUSED (ident
), objcToken what
)
502 if (what
== waitedToken
)
503 toDoNext
= comeAfter
;
504 else if (what
== fallBackToken
)
510 static int ignoreBalanced_count
= 0;
511 static void ignoreBalanced (vString
* const UNUSED (ident
), objcToken what
)
519 ignoreBalanced_count
++;
525 ignoreBalanced_count
--;
533 if (ignoreBalanced_count
== 0)
534 toDoNext
= comeAfter
;
537 static void parseFields (vString
* const ident
, objcToken what
)
542 toDoNext
= &parseMethods
;
547 toDoNext
= &ignoreBalanced
;
548 comeAfter
= &parseFields
;
551 /* we got an identifier, keep track of it */
553 vStringCopy (tempName
, ident
);
556 /* our last kept identifier must be our variable name =) */
558 addTag (tempName
, K_FIELD
);
559 vStringClear (tempName
);
571 static vString
*fullMethodName
;
572 static vString
*prevIdent
;
574 static void parseMethodsName (vString
* const ident
, objcToken what
)
579 toDoNext
= &tillToken
;
580 comeAfter
= &parseMethodsName
;
581 waitedToken
= Tok_PARR
;
585 vStringCat (fullMethodName
, prevIdent
);
586 vStringCatS (fullMethodName
, ":");
587 vStringClear (prevIdent
);
591 vStringCopy (prevIdent
, ident
);
596 /* method name is not simple */
597 if (vStringLength (fullMethodName
) != '\0')
599 addTag (fullMethodName
, methodKind
);
600 vStringClear (fullMethodName
);
603 addTag (prevIdent
, methodKind
);
605 toDoNext
= &parseMethods
;
606 parseImplemMethods (ident
, what
);
607 vStringClear (prevIdent
);
615 static void parseMethodsImplemName (vString
* const ident
, objcToken what
)
620 toDoNext
= &tillToken
;
621 comeAfter
= &parseMethodsImplemName
;
622 waitedToken
= Tok_PARR
;
626 vStringCat (fullMethodName
, prevIdent
);
627 vStringCatS (fullMethodName
, ":");
628 vStringClear (prevIdent
);
632 vStringCopy (prevIdent
, ident
);
637 /* method name is not simple */
638 if (vStringLength (fullMethodName
) != '\0')
640 addTag (fullMethodName
, methodKind
);
641 vStringClear (fullMethodName
);
644 addTag (prevIdent
, methodKind
);
646 toDoNext
= &parseImplemMethods
;
647 parseImplemMethods (ident
, what
);
648 vStringClear (prevIdent
);
656 static void parseImplemMethods (vString
* const ident
, objcToken what
)
660 case Tok_PLUS
: /* + */
661 toDoNext
= &parseMethodsImplemName
;
662 methodKind
= K_CLASSMETHOD
;
665 case Tok_MINUS
: /* - */
666 toDoNext
= &parseMethodsImplemName
;
667 methodKind
= K_METHOD
;
670 case ObjcEND
: /* @end */
671 popEnclosingContext ();
672 toDoNext
= &globalScope
;
675 case Tok_CurlL
: /* { */
676 toDoNext
= &ignoreBalanced
;
677 ignoreBalanced (ident
, what
);
678 comeAfter
= &parseImplemMethods
;
686 static void parseProperty (vString
* const ident
, objcToken what
)
691 toDoNext
= &tillToken
;
692 comeAfter
= &parseProperty
;
693 waitedToken
= Tok_PARR
;
696 /* we got an identifier, keep track of it */
698 vStringCopy (tempName
, ident
);
701 /* our last kept identifier must be our variable name =) */
703 addTag (tempName
, K_PROPERTY
);
704 vStringClear (tempName
);
712 static void parseMethods (vString
* const UNUSED (ident
), objcToken what
)
716 case Tok_PLUS
: /* + */
717 toDoNext
= &parseMethodsName
;
718 methodKind
= K_CLASSMETHOD
;
721 case Tok_MINUS
: /* - */
722 toDoNext
= &parseMethodsName
;
723 methodKind
= K_METHOD
;
727 toDoNext
= &parseProperty
;
730 case ObjcEND
: /* @end */
731 popEnclosingContext ();
732 toDoNext
= &globalScope
;
735 case Tok_CurlL
: /* { */
736 toDoNext
= &parseFields
;
745 static void parseProtocol (vString
* const ident
, objcToken what
)
747 if (what
== ObjcIDENTIFIER
)
749 pushEnclosingContext (ident
, K_PROTOCOL
);
750 addTag (ident
, K_PROTOCOL
);
752 toDoNext
= &parseMethods
;
755 static void parseImplementation (vString
* const ident
, objcToken what
)
757 if (what
== ObjcIDENTIFIER
)
759 addTag (ident
, K_IMPLEMENTATION
);
760 pushEnclosingContext (ident
, K_IMPLEMENTATION
);
762 toDoNext
= &parseImplemMethods
;
765 static void parseInterface (vString
* const ident
, objcToken what
)
767 if (what
== ObjcIDENTIFIER
)
769 addTag (ident
, K_INTERFACE
);
770 pushEnclosingContext (ident
, K_INTERFACE
);
773 toDoNext
= &parseMethods
;
776 static void parseStructMembers (vString
* const ident
, objcToken what
)
778 static parseNext prev
= NULL
;
789 vStringCopy (tempName
, ident
);
792 case Tok_semi
: /* ';' */
793 addTag (tempName
, K_FIELD
);
794 vStringClear (tempName
);
797 /* some types are complex, the only one
798 * we will loose is the function type.
800 case Tok_CurlL
: /* '{' */
801 case Tok_PARL
: /* '(' */
802 case Tok_SQUAREL
: /* '[' */
803 toDoNext
= &ignoreBalanced
;
805 comeAfter
= &parseStructMembers
;
806 ignoreBalanced (ident
, what
);
810 toDoNext
= comeAfter
;
819 /* Called just after the struct keyword */
820 static boolean parseStruct_gotName
= FALSE
;
821 static void parseStruct (vString
* const ident
, objcToken what
)
826 if (!parseStruct_gotName
)
828 addTag (ident
, K_STRUCT
);
829 pushEnclosingContext (ident
, K_STRUCT
);
830 parseStruct_gotName
= TRUE
;
834 parseStruct_gotName
= FALSE
;
835 popEnclosingContext ();
836 toDoNext
= comeAfter
;
837 comeAfter (ident
, what
);
842 toDoNext
= &parseStructMembers
;
845 /* maybe it was just a forward declaration
846 * in which case, we pop the context */
848 if (parseStruct_gotName
)
849 popEnclosingContext ();
851 toDoNext
= comeAfter
;
852 comeAfter (ident
, what
);
861 /* Parse enumeration members, ignoring potential initialization */
862 static parseNext parseEnumFields_prev
= NULL
;
863 static void parseEnumFields (vString
* const ident
, objcToken what
)
865 if (parseEnumFields_prev
!= NULL
)
867 comeAfter
= parseEnumFields_prev
;
868 parseEnumFields_prev
= NULL
;
874 addTag (ident
, K_ENUM
);
875 parseEnumFields_prev
= comeAfter
;
876 waitedToken
= Tok_COMA
;
877 /* last item might not have a coma */
878 fallBackToken
= Tok_CurlR
;
879 fallback
= comeAfter
;
880 comeAfter
= parseEnumFields
;
881 toDoNext
= &tillTokenOrFallBack
;
885 toDoNext
= comeAfter
;
886 popEnclosingContext ();
895 /* parse enum ... { ... */
896 static boolean parseEnum_named
= FALSE
;
897 static void parseEnum (vString
* const ident
, objcToken what
)
902 if (!parseEnum_named
)
904 addTag (ident
, K_ENUM
);
905 pushEnclosingContext (ident
, K_ENUM
);
906 parseEnum_named
= TRUE
;
910 parseEnum_named
= FALSE
;
911 popEnclosingContext ();
912 toDoNext
= comeAfter
;
913 comeAfter (ident
, what
);
917 case Tok_CurlL
: /* '{' */
918 toDoNext
= &parseEnumFields
;
919 parseEnum_named
= FALSE
;
922 case Tok_semi
: /* ';' */
924 popEnclosingContext ();
925 toDoNext
= comeAfter
;
926 comeAfter (ident
, what
);
935 /* Parse something like
936 * typedef .... ident ;
937 * ignoring the defined type but in the case of struct,
938 * in which case struct are parsed.
940 static void parseTypedef (vString
* const ident
, objcToken what
)
945 toDoNext
= &parseStruct
;
946 comeAfter
= &parseTypedef
;
950 toDoNext
= &parseEnum
;
951 comeAfter
= &parseTypedef
;
955 vStringCopy (tempName
, ident
);
958 case Tok_semi
: /* ';' */
959 addTag (tempName
, K_TYPEDEF
);
960 vStringClear (tempName
);
961 toDoNext
= &globalScope
;
970 static boolean ignorePreprocStuff_escaped
= FALSE
;
971 static void ignorePreprocStuff (vString
* const UNUSED (ident
), objcToken what
)
976 ignorePreprocStuff_escaped
= TRUE
;
980 if (ignorePreprocStuff_escaped
)
982 ignorePreprocStuff_escaped
= FALSE
;
986 toDoNext
= &globalScope
;
991 ignorePreprocStuff_escaped
= FALSE
;
996 static void parseMacroName (vString
* const ident
, objcToken what
)
998 if (what
== ObjcIDENTIFIER
)
999 addTag (ident
, K_MACRO
);
1001 toDoNext
= &ignorePreprocStuff
;
1004 static void parsePreproc (vString
* const ident
, objcToken what
)
1008 case ObjcIDENTIFIER
:
1009 if (strcmp (vStringValue (ident
), "define") == 0)
1010 toDoNext
= &parseMacroName
;
1012 toDoNext
= &ignorePreprocStuff
;
1016 toDoNext
= &ignorePreprocStuff
;
1021 /* Handle the "strong" top levels, all 'big' declarations
1023 static void globalScope (vString
* const ident
, objcToken what
)
1028 toDoNext
= &parsePreproc
;
1032 toDoNext
= &parseStruct
;
1033 comeAfter
= &globalScope
;
1036 case ObjcIDENTIFIER
:
1037 /* we keep track of the identifier if we
1038 * come across a function. */
1039 vStringCopy (tempName
, ident
);
1043 /* if we find an opening parenthesis it means we
1044 * found a function (or a macro...) */
1045 addTag (tempName
, K_FUNCTION
);
1046 vStringClear (tempName
);
1047 comeAfter
= &globalScope
;
1048 toDoNext
= &ignoreBalanced
;
1049 ignoreBalanced (ident
, what
);
1053 toDoNext
= &parseInterface
;
1056 case ObjcIMPLEMENTATION
:
1057 toDoNext
= &parseImplementation
;
1061 toDoNext
= &parseProtocol
;
1065 toDoNext
= parseTypedef
;
1066 comeAfter
= &globalScope
;
1070 comeAfter
= &globalScope
;
1071 toDoNext
= &ignoreBalanced
;
1072 ignoreBalanced (ident
, what
);
1086 /*////////////////////////////////////////////////////////////////
1087 //// Deal with the system */
1089 static void findObjcTags (void)
1091 vString
*name
= vStringNew ();
1095 parentName
= vStringNew ();
1096 tempName
= vStringNew ();
1097 fullMethodName
= vStringNew ();
1098 prevIdent
= vStringNew ();
1100 /* (Re-)initialize state variables, this might be a second file */
1103 parentType
= K_INTERFACE
;
1104 ignoreBalanced_count
= 0;
1106 parseStruct_gotName
= FALSE
;
1107 parseEnumFields_prev
= NULL
;
1108 parseEnum_named
= FALSE
;
1109 ignorePreprocStuff_escaped
= FALSE
;
1111 st
.name
= vStringNew ();
1112 st
.cp
= fileReadLine ();
1113 toDoNext
= &globalScope
;
1115 while (tok
!= Tok_EOF
)
1117 (*toDoNext
) (st
.name
, tok
);
1121 vStringDelete (name
);
1122 vStringDelete (parentName
);
1123 vStringDelete (tempName
);
1124 vStringDelete (fullMethodName
);
1125 vStringDelete (prevIdent
);
1129 fullMethodName
= NULL
;
1132 static void objcInitialize (const langType language
)
1134 Lang_ObjectiveC
= language
;
1139 extern parserDefinition
*ObjcParser (void)
1141 static const char *const extensions
[] = { "m", "h", NULL
};
1142 parserDefinition
*def
= parserNew ("ObjectiveC");
1143 def
->kinds
= ObjcKinds
;
1144 def
->kindCount
= KIND_COUNT (ObjcKinds
);
1145 def
->extensions
= extensions
;
1146 def
->parser
= findObjcTags
;
1147 def
->initialize
= objcInitialize
;