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 typedef struct sOBjcKeywordDesc
{
120 static const objcKeywordDesc objcKeywordTable
[] = {
121 {"typedef", ObjcTYPEDEF
},
122 {"struct", ObjcSTRUCT
},
124 {"@implementation", ObjcIMPLEMENTATION
},
125 {"@interface", ObjcINTERFACE
},
126 {"@protocol", ObjcPROTOCOL
},
127 {"@encode", ObjcENCODE
},
128 {"@property", ObjcPROPERTY
},
129 {"@synchronized", ObjcSYNCHRONIZED
},
130 {"@selector", ObjcSELECTOR
},
133 {"@class", ObjcCLASS
},
134 {"@private", ObjcPRIVATE
},
135 {"@package", ObjcPACKAGE
},
136 {"@public", ObjcPUBLIC
},
137 {"@protected", ObjcPROTECTED
},
138 {"@synthesize", ObjcSYNTHESIZE
},
139 {"@dynamic", ObjcDYNAMIC
},
140 {"@optional", ObjcOPTIONAL
},
141 {"@required", ObjcREQUIRED
},
144 static langType Lang_ObjectiveC
;
146 /*//////////////////////////////////////////////////////////////////
148 typedef struct _lexingState
{
149 vString
*name
; /* current parsed identifier/operator */
150 const unsigned char *cp
; /* position in stream */
153 static void initKeywordHash (void)
155 const size_t count
= sizeof (objcKeywordTable
) / sizeof (objcKeywordDesc
);
158 for (i
= 0; i
< count
; ++i
)
160 addKeyword (objcKeywordTable
[i
].name
, Lang_ObjectiveC
,
161 (int) objcKeywordTable
[i
].id
);
165 /*//////////////////////////////////////////////////////////////////////
167 static boolean
isNum (char c
)
169 return c
>= '0' && c
<= '9';
172 static boolean
isLowerAlpha (char c
)
174 return c
>= 'a' && c
<= 'z';
177 static boolean
isUpperAlpha (char c
)
179 return c
>= 'A' && c
<= 'Z';
182 static boolean
isAlpha (char c
)
184 return isLowerAlpha (c
) || isUpperAlpha (c
);
187 static boolean
isIdent (char c
)
189 return isNum (c
) || isAlpha (c
) || c
== '_';
192 static boolean
isSpace (char c
)
194 return c
== ' ' || c
== '\t';
197 /* return true if it end with an end of line */
198 static void eatWhiteSpace (lexingState
* st
)
200 const unsigned char *cp
= st
->cp
;
201 while (isSpace (*cp
))
207 static void eatString (lexingState
* st
)
209 boolean lastIsBackSlash
= FALSE
;
210 boolean unfinished
= TRUE
;
211 const unsigned char *c
= st
->cp
+ 1;
215 /* end of line should never happen.
217 if (c
== NULL
|| c
[0] == '\0')
219 else if (*c
== '"' && !lastIsBackSlash
)
222 lastIsBackSlash
= *c
== '\\';
230 static void eatComment (lexingState
* st
)
232 boolean unfinished
= TRUE
;
233 boolean lastIsStar
= FALSE
;
234 const unsigned char *c
= st
->cp
+ 2;
238 /* we've reached the end of the line..
239 * so we have to reload a line... */
240 if (c
== NULL
|| *c
== '\0')
242 st
->cp
= fileReadLine ();
243 /* WOOPS... no more input...
244 * we return, next lexing read
245 * will be null and ok */
250 /* we've reached the end of the comment */
251 else if (*c
== '/' && lastIsStar
)
255 lastIsStar
= '*' == *c
;
263 static void readIdentifier (lexingState
* st
)
265 const unsigned char *p
;
266 vStringClear (st
->name
);
268 /* first char is a simple letter */
269 if (isAlpha (*st
->cp
) || *st
->cp
== '_')
270 vStringPut (st
->name
, (int) *st
->cp
);
272 /* Go till you get identifier chars */
273 for (p
= st
->cp
+ 1; isIdent (*p
); p
++)
274 vStringPut (st
->name
, (int) *p
);
278 vStringTerminate (st
->name
);
281 /* read the @something directives */
282 static void readIdentifierObjcDirective (lexingState
* st
)
284 const unsigned char *p
;
285 vStringClear (st
->name
);
287 /* first char is a simple letter */
289 vStringPut (st
->name
, (int) *st
->cp
);
291 /* Go till you get identifier chars */
292 for (p
= st
->cp
+ 1; isIdent (*p
); p
++)
293 vStringPut (st
->name
, (int) *p
);
297 vStringTerminate (st
->name
);
300 /* The lexer is in charge of reading the file.
301 * Some of sub-lexer (like eatComment) also read file.
302 * lexing is finished when the lexer return Tok_EOF */
303 static objcKeyword
lex (lexingState
* st
)
307 /* handling data input here */
308 while (st
->cp
== NULL
|| st
->cp
[0] == '\0')
310 st
->cp
= fileReadLine ();
317 if (isAlpha (*st
->cp
))
320 retType
= lookupKeyword (vStringValue (st
->name
), Lang_ObjectiveC
);
322 if (retType
== -1) /* If it's not a keyword */
324 return ObjcIDENTIFIER
;
331 else if (*st
->cp
== '@')
333 readIdentifierObjcDirective (st
);
334 retType
= lookupKeyword (vStringValue (st
->name
), Lang_ObjectiveC
);
336 if (retType
== -1) /* If it's not a keyword */
345 else if (isSpace (*st
->cp
))
359 return Tok_Backslash
;
366 if (st
->cp
[1] == '*') /* ergl, a comment */
371 else if (st
->cp
[1] == '/')
422 /* default return if nothing is recognized,
423 * shouldn't happen, but at least, it will
424 * be handled without destroying the parsing. */
428 /*//////////////////////////////////////////////////////////////////////
430 typedef void (*parseNext
) (vString
* const ident
, objcToken what
);
432 /********** Helpers */
433 /* This variable hold the 'parser' which is going to
434 * handle the next token */
435 static parseNext toDoNext
;
437 /* Special variable used by parser eater to
438 * determine which action to put after their
439 * job is finished. */
440 static parseNext comeAfter
;
442 /* Used by some parsers detecting certain token
443 * to revert to previous parser. */
444 static parseNext fallback
;
447 /********** Grammar */
448 static void globalScope (vString
* const ident
, objcToken what
);
449 static void parseMethods (vString
* const ident
, objcToken what
);
450 static void parseImplemMethods (vString
* const ident
, objcToken what
);
451 static vString
*tempName
= NULL
;
452 static vString
*parentName
= NULL
;
453 static objcKind parentType
= K_INTERFACE
;
455 /* used to prepare tag for OCaml, just in case their is a need to
456 * add additional information to the tag. */
457 static void prepareTag (tagEntryInfo
* tag
, vString
const *name
, objcKind kind
)
459 initTagEntry (tag
, vStringValue (name
));
460 tag
->kindName
= ObjcKinds
[kind
].name
;
461 tag
->kind
= ObjcKinds
[kind
].letter
;
463 if (parentName
!= NULL
)
465 tag
->extensionFields
.scope
[0] = ObjcKinds
[parentType
].name
;
466 tag
->extensionFields
.scope
[1] = vStringValue (parentName
);
470 static void pushEnclosingContext (const vString
* parent
, objcKind type
)
472 vStringCopy (parentName
, parent
);
476 static void popEnclosingContext (void)
478 vStringClear (parentName
);
481 /* Used to centralise tag creation, and be able to add
482 * more information to it in the future */
483 static void addTag (vString
* const ident
, int kind
)
485 tagEntryInfo toCreate
;
487 if (! ObjcKinds
[kind
].enabled
)
490 prepareTag (&toCreate
, ident
, kind
);
491 makeTagEntry (&toCreate
);
494 static objcToken waitedToken
, fallBackToken
;
496 /* Ignore everything till waitedToken and jump to comeAfter.
497 * If the "end" keyword is encountered break, doesn't remember
499 static void tillToken (vString
* const UNUSED (ident
), objcToken what
)
501 if (what
== waitedToken
)
502 toDoNext
= comeAfter
;
505 static void tillTokenOrFallBack (vString
* const UNUSED (ident
), objcToken what
)
507 if (what
== waitedToken
)
508 toDoNext
= comeAfter
;
509 else if (what
== fallBackToken
)
515 static int ignoreBalanced_count
= 0;
516 static void ignoreBalanced (vString
* const UNUSED (ident
), objcToken what
)
524 ignoreBalanced_count
++;
530 ignoreBalanced_count
--;
538 if (ignoreBalanced_count
== 0)
539 toDoNext
= comeAfter
;
542 static void parseFields (vString
* const ident
, objcToken what
)
547 toDoNext
= &parseMethods
;
552 toDoNext
= &ignoreBalanced
;
553 comeAfter
= &parseFields
;
556 /* we got an identifier, keep track of it */
558 vStringCopy (tempName
, ident
);
561 /* our last kept identifier must be our variable name =) */
563 addTag (tempName
, K_FIELD
);
564 vStringClear (tempName
);
573 static objcKind methodKind
;
576 static vString
*fullMethodName
;
577 static vString
*prevIdent
;
579 static void parseMethodsName (vString
* const ident
, objcToken what
)
584 toDoNext
= &tillToken
;
585 comeAfter
= &parseMethodsName
;
586 waitedToken
= Tok_PARR
;
590 vStringCat (fullMethodName
, prevIdent
);
591 vStringCatS (fullMethodName
, ":");
592 vStringClear (prevIdent
);
596 vStringCopy (prevIdent
, ident
);
601 /* method name is not simple */
602 if (vStringLength (fullMethodName
) != '\0')
604 addTag (fullMethodName
, methodKind
);
605 vStringClear (fullMethodName
);
608 addTag (prevIdent
, methodKind
);
610 toDoNext
= &parseMethods
;
611 parseImplemMethods (ident
, what
);
612 vStringClear (prevIdent
);
620 static void parseMethodsImplemName (vString
* const ident
, objcToken what
)
625 toDoNext
= &tillToken
;
626 comeAfter
= &parseMethodsImplemName
;
627 waitedToken
= Tok_PARR
;
631 vStringCat (fullMethodName
, prevIdent
);
632 vStringCatS (fullMethodName
, ":");
633 vStringClear (prevIdent
);
637 vStringCopy (prevIdent
, ident
);
642 /* method name is not simple */
643 if (vStringLength (fullMethodName
) != '\0')
645 addTag (fullMethodName
, methodKind
);
646 vStringClear (fullMethodName
);
649 addTag (prevIdent
, methodKind
);
651 toDoNext
= &parseImplemMethods
;
652 parseImplemMethods (ident
, what
);
653 vStringClear (prevIdent
);
661 static void parseImplemMethods (vString
* const ident
, objcToken what
)
665 case Tok_PLUS
: /* + */
666 toDoNext
= &parseMethodsImplemName
;
667 methodKind
= K_CLASSMETHOD
;
670 case Tok_MINUS
: /* - */
671 toDoNext
= &parseMethodsImplemName
;
672 methodKind
= K_METHOD
;
675 case ObjcEND
: /* @end */
676 popEnclosingContext ();
677 toDoNext
= &globalScope
;
680 case Tok_CurlL
: /* { */
681 toDoNext
= &ignoreBalanced
;
682 ignoreBalanced (ident
, what
);
683 comeAfter
= &parseImplemMethods
;
691 static void parseProperty (vString
* const ident
, objcToken what
)
696 toDoNext
= &tillToken
;
697 comeAfter
= &parseProperty
;
698 waitedToken
= Tok_PARR
;
701 /* we got an identifier, keep track of it */
703 vStringCopy (tempName
, ident
);
706 /* our last kept identifier must be our variable name =) */
708 addTag (tempName
, K_PROPERTY
);
709 vStringClear (tempName
);
710 toDoNext
= &parseMethods
;
718 static void parseMethods (vString
* const UNUSED (ident
), objcToken what
)
722 case Tok_PLUS
: /* + */
723 toDoNext
= &parseMethodsName
;
724 methodKind
= K_CLASSMETHOD
;
727 case Tok_MINUS
: /* - */
728 toDoNext
= &parseMethodsName
;
729 methodKind
= K_METHOD
;
733 toDoNext
= &parseProperty
;
736 case ObjcEND
: /* @end */
737 popEnclosingContext ();
738 toDoNext
= &globalScope
;
741 case Tok_CurlL
: /* { */
742 toDoNext
= &parseFields
;
751 static void parseProtocol (vString
* const ident
, objcToken what
)
753 if (what
== ObjcIDENTIFIER
)
755 pushEnclosingContext (ident
, K_PROTOCOL
);
756 addTag (ident
, K_PROTOCOL
);
758 toDoNext
= &parseMethods
;
761 static void parseImplementation (vString
* const ident
, objcToken what
)
763 if (what
== ObjcIDENTIFIER
)
765 addTag (ident
, K_IMPLEMENTATION
);
766 pushEnclosingContext (ident
, K_IMPLEMENTATION
);
768 toDoNext
= &parseImplemMethods
;
771 static void parseInterface (vString
* const ident
, objcToken what
)
773 if (what
== ObjcIDENTIFIER
)
775 addTag (ident
, K_INTERFACE
);
776 pushEnclosingContext (ident
, K_INTERFACE
);
779 toDoNext
= &parseMethods
;
782 static void parseStructMembers (vString
* const ident
, objcToken what
)
784 static parseNext prev
= NULL
;
795 vStringCopy (tempName
, ident
);
798 case Tok_semi
: /* ';' */
799 addTag (tempName
, K_FIELD
);
800 vStringClear (tempName
);
803 /* some types are complex, the only one
804 * we will loose is the function type.
806 case Tok_CurlL
: /* '{' */
807 case Tok_PARL
: /* '(' */
808 case Tok_SQUAREL
: /* '[' */
809 toDoNext
= &ignoreBalanced
;
811 comeAfter
= &parseStructMembers
;
812 ignoreBalanced (ident
, what
);
816 toDoNext
= comeAfter
;
825 /* Called just after the struct keyword */
826 static boolean parseStruct_gotName
= FALSE
;
827 static void parseStruct (vString
* const ident
, objcToken what
)
832 if (!parseStruct_gotName
)
834 addTag (ident
, K_STRUCT
);
835 pushEnclosingContext (ident
, K_STRUCT
);
836 parseStruct_gotName
= TRUE
;
840 parseStruct_gotName
= FALSE
;
841 popEnclosingContext ();
842 toDoNext
= comeAfter
;
843 comeAfter (ident
, what
);
848 toDoNext
= &parseStructMembers
;
851 /* maybe it was just a forward declaration
852 * in which case, we pop the context */
854 if (parseStruct_gotName
)
855 popEnclosingContext ();
857 toDoNext
= comeAfter
;
858 comeAfter (ident
, what
);
867 /* Parse enumeration members, ignoring potential initialization */
868 static parseNext parseEnumFields_prev
= NULL
;
869 static void parseEnumFields (vString
* const ident
, objcToken what
)
871 if (parseEnumFields_prev
!= NULL
)
873 comeAfter
= parseEnumFields_prev
;
874 parseEnumFields_prev
= NULL
;
880 addTag (ident
, K_ENUM
);
881 parseEnumFields_prev
= comeAfter
;
882 waitedToken
= Tok_COMA
;
883 /* last item might not have a coma */
884 fallBackToken
= Tok_CurlR
;
885 fallback
= comeAfter
;
886 comeAfter
= parseEnumFields
;
887 toDoNext
= &tillTokenOrFallBack
;
891 toDoNext
= comeAfter
;
892 popEnclosingContext ();
901 /* parse enum ... { ... */
902 static boolean parseEnum_named
= FALSE
;
903 static void parseEnum (vString
* const ident
, objcToken what
)
908 if (!parseEnum_named
)
910 addTag (ident
, K_ENUM
);
911 pushEnclosingContext (ident
, K_ENUM
);
912 parseEnum_named
= TRUE
;
916 parseEnum_named
= FALSE
;
917 popEnclosingContext ();
918 toDoNext
= comeAfter
;
919 comeAfter (ident
, what
);
923 case Tok_CurlL
: /* '{' */
924 toDoNext
= &parseEnumFields
;
925 parseEnum_named
= FALSE
;
928 case Tok_semi
: /* ';' */
930 popEnclosingContext ();
931 toDoNext
= comeAfter
;
932 comeAfter (ident
, what
);
941 /* Parse something like
942 * typedef .... ident ;
943 * ignoring the defined type but in the case of struct,
944 * in which case struct are parsed.
946 static void parseTypedef (vString
* const ident
, objcToken what
)
951 toDoNext
= &parseStruct
;
952 comeAfter
= &parseTypedef
;
956 toDoNext
= &parseEnum
;
957 comeAfter
= &parseTypedef
;
961 vStringCopy (tempName
, ident
);
964 case Tok_semi
: /* ';' */
965 addTag (tempName
, K_TYPEDEF
);
966 vStringClear (tempName
);
967 toDoNext
= &globalScope
;
976 static boolean ignorePreprocStuff_escaped
= FALSE
;
977 static void ignorePreprocStuff (vString
* const UNUSED (ident
), objcToken what
)
982 ignorePreprocStuff_escaped
= TRUE
;
986 if (ignorePreprocStuff_escaped
)
988 ignorePreprocStuff_escaped
= FALSE
;
992 toDoNext
= &globalScope
;
997 ignorePreprocStuff_escaped
= FALSE
;
1002 static void parseMacroName (vString
* const ident
, objcToken what
)
1004 if (what
== ObjcIDENTIFIER
)
1005 addTag (ident
, K_MACRO
);
1007 toDoNext
= &ignorePreprocStuff
;
1010 static void parsePreproc (vString
* const ident
, objcToken what
)
1014 case ObjcIDENTIFIER
:
1015 if (strcmp (vStringValue (ident
), "define") == 0)
1016 toDoNext
= &parseMacroName
;
1018 toDoNext
= &ignorePreprocStuff
;
1022 toDoNext
= &ignorePreprocStuff
;
1027 /* Handle the "strong" top levels, all 'big' declarations
1029 static void globalScope (vString
* const ident
, objcToken what
)
1034 toDoNext
= &parsePreproc
;
1038 toDoNext
= &parseStruct
;
1039 comeAfter
= &globalScope
;
1042 case ObjcIDENTIFIER
:
1043 /* we keep track of the identifier if we
1044 * come across a function. */
1045 vStringCopy (tempName
, ident
);
1049 /* if we find an opening parenthesis it means we
1050 * found a function (or a macro...) */
1051 addTag (tempName
, K_FUNCTION
);
1052 vStringClear (tempName
);
1053 comeAfter
= &globalScope
;
1054 toDoNext
= &ignoreBalanced
;
1055 ignoreBalanced (ident
, what
);
1059 toDoNext
= &parseInterface
;
1062 case ObjcIMPLEMENTATION
:
1063 toDoNext
= &parseImplementation
;
1067 toDoNext
= &parseProtocol
;
1071 toDoNext
= parseTypedef
;
1072 comeAfter
= &globalScope
;
1076 comeAfter
= &globalScope
;
1077 toDoNext
= &ignoreBalanced
;
1078 ignoreBalanced (ident
, what
);
1092 /*////////////////////////////////////////////////////////////////
1093 //// Deal with the system */
1095 static void findObjcTags (void)
1097 vString
*name
= vStringNew ();
1101 parentName
= vStringNew ();
1102 tempName
= vStringNew ();
1103 fullMethodName
= vStringNew ();
1104 prevIdent
= vStringNew ();
1106 /* (Re-)initialize state variables, this might be a second file */
1109 parentType
= K_INTERFACE
;
1110 ignoreBalanced_count
= 0;
1112 parseStruct_gotName
= FALSE
;
1113 parseEnumFields_prev
= NULL
;
1114 parseEnum_named
= FALSE
;
1115 ignorePreprocStuff_escaped
= FALSE
;
1117 st
.name
= vStringNew ();
1118 st
.cp
= fileReadLine ();
1119 toDoNext
= &globalScope
;
1121 while (tok
!= Tok_EOF
)
1123 (*toDoNext
) (st
.name
, tok
);
1126 vStringDelete(st
.name
);
1128 vStringDelete (name
);
1129 vStringDelete (parentName
);
1130 vStringDelete (tempName
);
1131 vStringDelete (fullMethodName
);
1132 vStringDelete (prevIdent
);
1136 fullMethodName
= NULL
;
1139 static void objcInitialize (const langType language
)
1141 Lang_ObjectiveC
= language
;
1146 extern parserDefinition
*ObjcParser (void)
1148 static const char *const extensions
[] = { "m", "h", NULL
};
1149 parserDefinition
*def
= parserNew ("ObjectiveC");
1150 def
->kinds
= ObjcKinds
;
1151 def
->kindCount
= KIND_COUNT (ObjcKinds
);
1152 def
->extensions
= extensions
;
1153 def
->parser
= findObjcTags
;
1154 def
->initialize
= objcInitialize
;