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 "param.h"
12 #include "parse.h"
13 #include "entry.h"
14 #include "tokeninfo.h"
15
16 #include <string.h>
17
18
19 struct tclooSubparser {
20 tclSubparser tcl;
21 bool foundTclOONamespaceImported;
22 };
23
24 static scopeSeparator TclOOGenericSeparators [] = {
25 { KIND_WILDCARD_INDEX, "::" },
26 };
27
28 enum TclOOKind {
29 K_CLASS,
30 K_METHOD,
31 };
32
33 static kindDefinition TclOOKinds[] = {
34 { true, 'c', "class", "classes" },
35 { true, 'm', "method", "methods",
36 ATTACH_SEPARATORS(TclOOGenericSeparators) },
37 };
38
39 static bool tclooForceUse;
40
parseMethod(tokenInfo * token,int owner)41 static void parseMethod (tokenInfo *token, int owner)
42 {
43 tokenRead (token);
44 if (tokenIsType (token, TCL_IDENTIFIER))
45 {
46 tagEntryInfo e;
47
48 initTagEntry(&e, tokenString (token), K_METHOD);
49 e.extensionFields.scopeIndex = owner;
50 makeTagEntry (&e);
51 }
52 skipToEndOfTclCmdline (token);
53 }
54
parseSuperclass(tokenInfo * token,int this_class)55 static void parseSuperclass (tokenInfo *token, int this_class)
56 {
57 tokenRead (token);
58 if (tokenIsType (token, TCL_IDENTIFIER))
59 {
60 tagEntryInfo *e = getEntryInCorkQueue(this_class);
61
62 if (e)
63 {
64 if (e->extensionFields.inheritance)
65 { /* superclass is used twice in a class. */
66 eFree ((void *)e->extensionFields.inheritance);
67 }
68 e->extensionFields.inheritance = eStrdup(tokenString(token));
69 }
70 }
71 skipToEndOfTclCmdline (token);
72 }
73
parseClass(tclSubparser * s CTAGS_ATTR_UNUSED,int parentIndex,void * pstate)74 static int parseClass (tclSubparser *s CTAGS_ATTR_UNUSED, int parentIndex,
75 void *pstate)
76 {
77 tokenInfo *token = newTclToken (pstate);
78 int r = CORK_NIL;
79
80 tokenRead (token);
81 if (tokenIsType (token, TCL_IDENTIFIER)
82 && (strcmp(tokenString(token), "create") == 0))
83 {
84 tokenRead (token);
85 if (tokenIsType (token, TCL_IDENTIFIER))
86 {
87 tagEntryInfo e;
88
89 initTagEntry(&e, tokenString (token), K_CLASS);
90 e.extensionFields.scopeIndex = parentIndex;
91 r = makeTagEntry (&e);
92 }
93
94 if (tokenSkipToType (token, '{'))
95 {
96 do {
97 tokenRead (token);
98 if (tokenIsType (token, TCL_IDENTIFIER)
99 || tokenIsType (token, TCL_KEYWORD))
100 {
101 if (strcmp(tokenString(token), "method") == 0)
102 parseMethod(token, r);
103 else if (strcmp(tokenString(token), "superclass") == 0)
104 parseSuperclass(token, r);
105 else
106 skipToEndOfTclCmdline (token);
107 }
108 else if (token->type == '}')
109 break;
110 } while (!tokenIsEOF(token));
111 }
112 }
113
114 skipToEndOfTclCmdline (token);
115 tokenDelete(token);
116 return r;
117 }
118
commandNotify(tclSubparser * s,char * command,int parentIndex,void * pstate)119 static int commandNotify (tclSubparser *s, char *command,
120 int parentIndex, void *pstate)
121 {
122 struct tclooSubparser *tcloo = (struct tclooSubparser *)s;
123 int r = CORK_NIL;
124
125 if ((tcloo->foundTclOONamespaceImported
126 && (strcmp (command, "class") == 0))
127 || (strcmp (command, "oo::class") == 0))
128 r = parseClass (s, parentIndex, pstate);
129
130 return r;
131 }
132
namespaceImportNotify(tclSubparser * s,char * namespace,void * pstate CTAGS_ATTR_UNUSED)133 static void namespaceImportNotify (tclSubparser *s, char *namespace,
134 void *pstate CTAGS_ATTR_UNUSED)
135 {
136 struct tclooSubparser *tcloo = (struct tclooSubparser *)s;
137
138 if (strcmp(namespace, "oo::*") == 0
139 || strcmp(namespace, "oo::class") == 0)
140 tcloo->foundTclOONamespaceImported = true;
141 }
142
inputStart(subparser * s)143 static void inputStart (subparser *s)
144 {
145 struct tclooSubparser *tcloo = (struct tclooSubparser *)s;
146
147 tcloo->foundTclOONamespaceImported = tclooForceUse;
148 }
149
150 static struct tclooSubparser tclooSubparser = {
151 .tcl = {
152 .subparser = {
153 .direction = SUBPARSER_BI_DIRECTION,
154 .inputStart = inputStart,
155 },
156 .commandNotify = commandNotify,
157 .namespaceImportNotify = namespaceImportNotify,
158 },
159 };
160
findTclOOTags(void)161 static void findTclOOTags(void)
162 {
163 scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
164 }
165
tclooForceUseParamHandler(const langType language CTAGS_ATTR_UNUSED,const char * name,const char * arg)166 static void tclooForceUseParamHandler (const langType language CTAGS_ATTR_UNUSED,
167 const char *name, const char *arg)
168 {
169 tclooForceUse = paramParserBool (arg, tclooForceUse, name, "parameter");
170 }
171
172 static parameterHandlerTable TclOOParameterHandlerTable [] = {
173 { .name = "forceUse",
174 .desc = "enable the parser even when `oo' namespace is not specified in the input (true or [false])" ,
175 .handleParameter = tclooForceUseParamHandler,
176 },
177 };
178
TclOOParser(void)179 extern parserDefinition* TclOOParser (void)
180 {
181 parserDefinition* const def = parserNew("TclOO");
182
183 static parserDependency dependencies [] = {
184 [0] = { DEPTYPE_SUBPARSER, "Tcl", &tclooSubparser },
185 };
186
187 def->dependencies = dependencies;
188 def->dependencyCount = ARRAY_SIZE (dependencies);
189
190 def->kindTable = TclOOKinds;
191 def->kindCount = ARRAY_SIZE(TclOOKinds);
192
193 def->parser = findTclOOTags;
194 def->useCork = CORK_QUEUE;
195 def->requestAutomaticFQTag = true;
196
197 def->parameterHandlerTable = TclOOParameterHandlerTable;
198 def->parameterHandlerCount = ARRAY_SIZE(TclOOParameterHandlerTable);
199
200 return def;
201 }
202