1 /*
2 *   Copyright (c) 2017, Masatake YAMATO
3 *
4 *   This source code is released for free distribution under the terms of the
5 *   GNU General Public License version 2 or (at your option) any later version.
6 *
7 */
8 
9 #include "general.h"  /* must always come first */
10 #include "tcl.h"
11 #include "entry.h"
12 #include "param.h"
13 #include "parse.h"
14 #include "read.h"
15 #include "keyword.h"
16 
17 #include <string.h>
18 
19 
20 struct itclSubparser {
21 	tclSubparser tcl;
22 	bool foundITclNamespaceImported;
23 };
24 
25 static scopeSeparator ITclGenericSeparators [] = {
26 	{ KIND_WILDCARD_INDEX, "::" },
27 };
28 
29 enum ITclKind {
30 	K_CLASS,
31 	K_METHOD,
32 	K_VARIABLE,
33 	K_COMMON,
34 	K_PROC,
35 };
36 
37 static kindDefinition ITclKinds[] = {
38 	{ true, 'c', "class", "classes" },
39 	{ true, 'm', "method", "methods",
40 	  ATTACH_SEPARATORS(ITclGenericSeparators)},
41 	{ true, 'v', "variable", "object-specific variables",
42 	  ATTACH_SEPARATORS(ITclGenericSeparators)},
43 	{ true, 'C', "common", "common variables",
44 	  ATTACH_SEPARATORS(ITclGenericSeparators)},
45 	{ true, 'p', "procedure", "procedures within the  class  namespace",
46 	  ATTACH_SEPARATORS(ITclGenericSeparators)},
47 };
48 
49 enum {
50 	KEYWORD_INHERIT,
51 	KEYWORD_METHOD,
52 	KEYWORD_PRIVATE,
53 	KEYWORD_PROTECTED,
54 	KEYWORD_PUBLIC,
55 	KEYWORD_VARIABLE,
56 	KEYWORD_COMMON,
57 	KEYWORD_PROC,
58 };
59 
60 typedef int keywordId; /* to allow KEYWORD_NONE */
61 
62 static const keywordTable ITclKeywordTable[] = {
63 	/* keyword			keyword ID */
64 	{ "inherit",		KEYWORD_INHERIT		},
65 	{ "method",			KEYWORD_METHOD		},
66 	{ "private",		KEYWORD_PRIVATE		},
67 	{ "protected",		KEYWORD_PROTECTED	},
68 	{ "public",			KEYWORD_PUBLIC		},
69 	{ "variable",		KEYWORD_VARIABLE	},
70 	{ "common",			KEYWORD_COMMON		},
71 	{ "proc",			KEYWORD_PROC		},
72 };
73 
74 static bool itclForceUse;
75 
resolveKeyword(vString * string)76 static keywordId resolveKeyword (vString *string)
77 {
78 	char *s = vStringValue (string);
79 	static langType lang = LANG_AUTO;
80 
81 	if (lang == LANG_AUTO)
82 		lang = getInputLanguage ();
83 
84 	return lookupKeyword (s, lang);
85 }
86 
parseInherit(tokenInfo * token,int r)87 static void parseInherit (tokenInfo *token, int r)
88 {
89 	vString *inherits = vStringNew ();
90 
91 	do {
92 		tokenRead (token);
93 		if (tokenIsType (token, TCL_IDENTIFIER))
94 		{
95 			if (vStringLength(inherits) != 0)
96 				vStringPut (inherits, ',');
97 			vStringCat(inherits, token->string);
98 		}
99 		else if (tokenIsType(token, TCL_EOL))
100 			break;
101 		else
102 		{
103 			skipToEndOfTclCmdline (token);
104 			break;
105 		}
106 	} while (1);
107 
108 	if (vStringLength(inherits) > 0)
109 	{
110 		tagEntryInfo *e = getEntryInCorkQueue (r);
111 		if (e)
112 		{
113 			e->extensionFields.inheritance = vStringDeleteUnwrap (inherits);
114 			return;
115 		}
116 	}
117 
118 	vStringDelete (inherits);
119 }
120 
attachProtectionMaybe(tagEntryInfo * e,keywordId protection)121 static void attachProtectionMaybe(tagEntryInfo *e, keywordId protection)
122 {
123 		switch (protection)
124 		{
125 		case KEYWORD_PROTECTED:
126 			e->extensionFields.access = "protected";
127 			break;
128 		case KEYWORD_PRIVATE:
129 			e->extensionFields.access = "private";
130 			break;
131 		case KEYWORD_PUBLIC:
132 			e->extensionFields.access = "public";
133 			break;
134 		}
135 }
136 
parseSubobject(tokenInfo * token,int parent,enum ITclKind kind,keywordId protection)137 static void parseSubobject (tokenInfo *token, int parent, enum ITclKind kind, keywordId protection)
138 {
139 	int r = CORK_NIL;
140 
141 	tokenRead (token);
142 	if (tokenIsType (token, TCL_IDENTIFIER))
143 	{
144 		tagEntryInfo e;
145 
146 		initTagEntry(&e, vStringValue (token->string), kind);
147 		e.extensionFields.scopeIndex = parent;
148 		attachProtectionMaybe (&e, protection);
149 		r = makeTagEntry (&e);
150 	}
151 
152 	skipToEndOfTclCmdline (token);
153 	tagEntryInfo *e = getEntryInCorkQueue (r);
154 	if (e)
155 		e->extensionFields.endLine = token->lineNumber;
156 }
157 
158 
parseVariable(tokenInfo * token,int r,keywordId protection)159 static void parseVariable (tokenInfo *token, int r, keywordId protection)
160 {
161 	parseSubobject(token, r, K_VARIABLE, protection);
162 }
163 
parseMethod(tokenInfo * token,int r,keywordId protection)164 static void parseMethod (tokenInfo *token, int r, keywordId protection)
165 {
166 	parseSubobject(token, r, K_METHOD, protection);
167 }
168 
parseProc(tokenInfo * token,int r,keywordId protection)169 static void parseProc (tokenInfo *token, int r, keywordId protection)
170 {
171 	parseSubobject(token, r, K_PROC, protection);
172 }
173 
parseCommon(tokenInfo * token,int r,keywordId protection)174 static void parseCommon (tokenInfo *token, int r, keywordId protection)
175 {
176 	parseSubobject(token, r, K_COMMON, protection);
177 }
178 
parseClass(tclSubparser * s CTAGS_ATTR_UNUSED,int parentIndex,void * pstate)179 static int parseClass (tclSubparser *s CTAGS_ATTR_UNUSED, int parentIndex,
180 					   void *pstate)
181 {
182 	tokenInfo *token = newTclToken (pstate);
183 	int r = CORK_NIL;
184 
185 	tokenRead (token);
186 	if (tokenIsType (token, TCL_IDENTIFIER))
187 	{
188 		tagEntryInfo e;
189 
190 		initTagEntry(&e, vStringValue (token->string), K_CLASS);
191 		e.extensionFields.scopeIndex = parentIndex;
192 		r = makeTagEntry (&e);
193 	}
194 
195 	if (tokenSkipToType (token, '{'))
196 	{
197 		keywordId protection = KEYWORD_NONE;
198 
199 		do {
200 			tokenRead (token);
201 			if (tokenIsType (token, TCL_IDENTIFIER)
202 				|| tokenIsType (token, TCL_KEYWORD))
203 			{
204 				keywordId k = resolveKeyword (token->string);
205 				switch (k)
206 				{
207 				case KEYWORD_INHERIT:
208 					parseInherit(token, r);
209 					protection = KEYWORD_NONE;
210 					break;
211 				case KEYWORD_VARIABLE:
212 					parseVariable(token, r, protection);
213 					protection = KEYWORD_NONE;
214 					break;
215 				case KEYWORD_METHOD:
216 					parseMethod(token, r, protection);
217 					protection = KEYWORD_NONE;
218 					break;
219 				case KEYWORD_COMMON:
220 					parseCommon(token, r, protection);
221 					protection = KEYWORD_NONE;
222 					break;
223 				case KEYWORD_PUBLIC:
224 				case KEYWORD_PROTECTED:
225 				case KEYWORD_PRIVATE:
226 					protection = k;
227 					continue;
228 				case KEYWORD_PROC:
229 					parseProc(token, r, protection);
230 					protection = KEYWORD_NONE;
231 					break;
232 				default:
233 					protection = KEYWORD_NONE;
234 					skipToEndOfTclCmdline (token);
235 					break;
236 				}
237 			}
238 			else if (token->type == '}')
239 			{
240 				protection = KEYWORD_NONE;
241 				break;
242 			}
243 			else
244 			{
245 				protection = KEYWORD_NONE;
246 				skipToEndOfTclCmdline (token);
247 			}
248 		} while (!tokenIsEOF(token));
249 	}
250 
251 	tokenDelete(token);
252 	return r;
253 }
254 
commandNotify(tclSubparser * s,char * command,int parentIndex,void * pstate)255 static int commandNotify (tclSubparser *s, char *command,
256 						  int parentIndex, void *pstate)
257 {
258 	struct itclSubparser *itcl = (struct itclSubparser *)s;
259 	int r = CORK_NIL;
260 
261 	if ((itcl->foundITclNamespaceImported
262 		 && (strcmp (command, "class") == 0))
263 		|| (strcmp (command, "itcl::class") == 0))
264 		r = parseClass (s, parentIndex, pstate);
265 
266 	return r;
267 }
268 
namespaceImportNotify(tclSubparser * s,char * namespace,void * pstate CTAGS_ATTR_UNUSED)269 static void namespaceImportNotify (tclSubparser *s, char *namespace,
270 								   void *pstate CTAGS_ATTR_UNUSED)
271 {
272 	struct itclSubparser *itcl = (struct itclSubparser *)s;
273 
274 	if (strcmp(namespace, "itcl::*") == 0
275 		|| strcmp(namespace, "itcl::class") == 0)
276 		itcl->foundITclNamespaceImported = true;
277 }
278 
inputStart(subparser * s)279 static void inputStart (subparser *s)
280 {
281 	struct itclSubparser *itcl = (struct itclSubparser *)s;
282 
283 	itcl->foundITclNamespaceImported = itclForceUse;
284 }
285 
286 struct itclSubparser itclSubparser = {
287 	.tcl = {
288 		.subparser = {
289 			.direction = SUBPARSER_BI_DIRECTION,
290 			.inputStart = inputStart,
291 		},
292 		.commandNotify = commandNotify,
293 		.namespaceImportNotify = namespaceImportNotify,
294 	},
295 };
296 
findITclTags(void)297 static void findITclTags(void)
298 {
299 	scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
300 }
301 
itclForceUseParamHandler(const langType language CTAGS_ATTR_UNUSED,const char * name,const char * arg)302 static void itclForceUseParamHandler (const langType language CTAGS_ATTR_UNUSED,
303 									  const char *name, const char *arg)
304 {
305 	itclForceUse = paramParserBool (arg, itclForceUse, name, "parameter");
306 }
307 
308 static parameterHandlerTable ItclParameterHandlerTable [] = {
309 	{ .name = "forceUse",
310 	  .desc = "enable the parser even when `itcl' namespace is not specified in the input (true or [false])" ,
311 	  .handleParameter = itclForceUseParamHandler,
312 	},
313 };
314 
ITclParser(void)315 extern parserDefinition* ITclParser (void)
316 {
317 	static const char *const extensions [] = { "itcl", NULL };
318 	parserDefinition* const def = parserNew("ITcl");
319 
320 	static parserDependency dependencies [] = {
321 		[0] = { DEPTYPE_SUBPARSER, "Tcl", &itclSubparser },
322 	};
323 
324 	def->dependencies = dependencies;
325 	def->dependencyCount = ARRAY_SIZE (dependencies);
326 
327 	def->kindTable = ITclKinds;
328 	def->kindCount = ARRAY_SIZE(ITclKinds);
329 
330 	def->extensions = extensions;
331 	def->parser = findITclTags;
332 	def->useCork = CORK_QUEUE;
333 	def->requestAutomaticFQTag = true;
334 
335 	def->keywordTable = ITclKeywordTable;
336 	def->keywordCount = ARRAY_SIZE (ITclKeywordTable);
337 
338 	def->parameterHandlerTable = ItclParameterHandlerTable;
339 	def->parameterHandlerCount = ARRAY_SIZE(ItclParameterHandlerTable);
340 
341 	return def;
342 }
343