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 static 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