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 void ignoreBalanced (vString
* const UNUSED (ident
), objcToken what
)
512 static int 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 void parseStruct (vString
* const ident
, objcToken what
)
822 static boolean gotName
= FALSE
;
829 addTag (ident
, K_STRUCT
);
830 pushEnclosingContext (ident
, K_STRUCT
);
836 popEnclosingContext ();
837 toDoNext
= comeAfter
;
838 comeAfter (ident
, what
);
843 toDoNext
= &parseStructMembers
;
846 /* maybe it was just a forward declaration
847 * in which case, we pop the context */
850 popEnclosingContext ();
852 toDoNext
= comeAfter
;
853 comeAfter (ident
, what
);
862 /* Parse enumeration members, ignoring potential initialization */
863 static void parseEnumFields (vString
* const ident
, objcToken what
)
865 static parseNext prev
= NULL
;
876 addTag (ident
, K_ENUM
);
878 waitedToken
= Tok_COMA
;
879 /* last item might not have a coma */
880 fallBackToken
= Tok_CurlR
;
881 fallback
= comeAfter
;
882 comeAfter
= parseEnumFields
;
883 toDoNext
= &tillTokenOrFallBack
;
887 toDoNext
= comeAfter
;
888 popEnclosingContext ();
897 /* parse enum ... { ... */
898 static void parseEnum (vString
* const ident
, objcToken what
)
900 static boolean named
= FALSE
;
907 addTag (ident
, K_ENUM
);
908 pushEnclosingContext (ident
, K_ENUM
);
914 popEnclosingContext ();
915 toDoNext
= comeAfter
;
916 comeAfter (ident
, what
);
920 case Tok_CurlL
: /* '{' */
921 toDoNext
= &parseEnumFields
;
925 case Tok_semi
: /* ';' */
927 popEnclosingContext ();
928 toDoNext
= comeAfter
;
929 comeAfter (ident
, what
);
938 /* Parse something like
939 * typedef .... ident ;
940 * ignoring the defined type but in the case of struct,
941 * in which case struct are parsed.
943 static void parseTypedef (vString
* const ident
, objcToken what
)
948 toDoNext
= &parseStruct
;
949 comeAfter
= &parseTypedef
;
953 toDoNext
= &parseEnum
;
954 comeAfter
= &parseTypedef
;
958 vStringCopy (tempName
, ident
);
961 case Tok_semi
: /* ';' */
962 addTag (tempName
, K_TYPEDEF
);
963 vStringClear (tempName
);
964 toDoNext
= &globalScope
;
973 static void ignorePreprocStuff (vString
* const UNUSED (ident
), objcToken what
)
975 static boolean escaped
= FALSE
;
990 toDoNext
= &globalScope
;
1000 static void parseMacroName (vString
* const ident
, objcToken what
)
1002 if (what
== ObjcIDENTIFIER
)
1003 addTag (ident
, K_MACRO
);
1005 toDoNext
= &ignorePreprocStuff
;
1008 static void parsePreproc (vString
* const ident
, objcToken what
)
1012 case ObjcIDENTIFIER
:
1013 if (strcmp (vStringValue (ident
), "define") == 0)
1014 toDoNext
= &parseMacroName
;
1016 toDoNext
= &ignorePreprocStuff
;
1020 toDoNext
= &ignorePreprocStuff
;
1025 /* Handle the "strong" top levels, all 'big' declarations
1027 static void globalScope (vString
* const ident
, objcToken what
)
1032 toDoNext
= &parsePreproc
;
1036 toDoNext
= &parseStruct
;
1037 comeAfter
= &globalScope
;
1040 case ObjcIDENTIFIER
:
1041 /* we keep track of the identifier if we
1042 * come across a function. */
1043 vStringCopy (tempName
, ident
);
1047 /* if we find an opening parenthesis it means we
1048 * found a function (or a macro...) */
1049 addTag (tempName
, K_FUNCTION
);
1050 vStringClear (tempName
);
1051 comeAfter
= &globalScope
;
1052 toDoNext
= &ignoreBalanced
;
1053 ignoreBalanced (ident
, what
);
1057 toDoNext
= &parseInterface
;
1060 case ObjcIMPLEMENTATION
:
1061 toDoNext
= &parseImplementation
;
1065 toDoNext
= &parseProtocol
;
1069 toDoNext
= parseTypedef
;
1070 comeAfter
= &globalScope
;
1074 comeAfter
= &globalScope
;
1075 toDoNext
= &ignoreBalanced
;
1076 ignoreBalanced (ident
, what
);
1090 /*////////////////////////////////////////////////////////////////
1091 //// Deal with the system */
1093 static void findObjcTags (void)
1095 vString
*name
= vStringNew ();
1099 parentName
= vStringNew ();
1100 tempName
= vStringNew ();
1101 fullMethodName
= vStringNew ();
1102 prevIdent
= vStringNew ();
1104 st
.name
= vStringNew ();
1105 st
.cp
= fileReadLine ();
1106 toDoNext
= &globalScope
;
1108 while (tok
!= Tok_EOF
)
1110 (*toDoNext
) (st
.name
, tok
);
1114 vStringDelete (name
);
1115 vStringDelete (parentName
);
1116 vStringDelete (tempName
);
1117 vStringDelete (fullMethodName
);
1118 vStringDelete (prevIdent
);
1122 fullMethodName
= NULL
;
1125 static void objcInitialize (const langType language
)
1127 Lang_ObjectiveC
= language
;
1132 extern parserDefinition
*ObjcParser (void)
1134 static const char *const extensions
[] = { "m", "h", NULL
};
1135 parserDefinition
*def
= parserNew ("ObjectiveC");
1136 def
->kinds
= ObjcKinds
;
1137 def
->kindCount
= KIND_COUNT (ObjcKinds
);
1138 def
->extensions
= extensions
;
1139 def
->parser
= findObjcTags
;
1140 def
->initialize
= objcInitialize
;