1 /*
2 * Copyright (c) 2017, Alexey Olenich
3 * Copyright (c) 2018, Colomban Wendling <colomban@geany.org>
4 *
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.
7 *
8 * This module contains functions for generating tags for AutoIt functions.
9 * Homepage https://www.autoitscript.com/site/autoit/
10 * Online Documentation https://www.autoitscript.com/autoit3/docs/
11 */
12
13 /*
14 * INCLUDE FILES
15 */
16 #include "general.h" /* must always come first */
17
18 #include <string.h>
19
20 #include "parse.h"
21 #include "entry.h"
22 #include "nestlevel.h"
23 #include "read.h"
24 #include "routines.h"
25 #include "vstring.h"
26
27 /*
28 * DATA DEFINITIONS
29 */
30 typedef enum {
31 K_FUNCTION,
32 K_REGION,
33 K_GLOBALVAR,
34 K_LOCALVAR,
35 K_SCRIPT,
36 } AutoItKind;
37
38 typedef enum {
39 R_INCLUDE_SYSTEM,
40 R_INCLUDE_LOCAL,
41 } AutoItIncludeRole;
42
43 typedef enum {
44 F_PROPERTIES,
45 } AutoItField;
46
47 static roleDefinition AutoItIncludeRoles [] = {
48 { true, "system", "system include" },
49 { true, "local", "local include" },
50 };
51
52 static kindDefinition AutoItKinds [] = {
53 { true, 'f', "func", "functions" },
54 { true, 'r', "region", "regions" },
55 { true, 'g', "global", "global variables" },
56 { true, 'l', "local", "local variables" },
57 { true, 'S', "script", "included scripts",
58 .referenceOnly = true, ATTACH_ROLES (AutoItIncludeRoles) },
59 };
60
61 static fieldDefinition AutoItFields [] = {
62 {
63 .name = "properties",
64 .description = "properties (static, volatile, ...)",
65 .enabled = false,
66 },
67 };
68
69 /*
70 * FUNCTION DEFINITIONS
71 */
72
73 /* it's unclear what *is* an identifier character, so maybe we're too strict */
isIdentChar(int c)74 static bool isIdentChar (int c)
75 {
76 return isalnum (c) || c == '_';
77 }
78
match(const unsigned char * line,const char * word,const unsigned char ** positionAfter)79 static bool match (const unsigned char *line, const char *word,
80 const unsigned char **positionAfter)
81 {
82 size_t len = strlen (word);
83 bool matched = (strncasecmp ((const char*) line, word, len) == 0 &&
84 ! isIdentChar (line[len]));
85
86 if (matched && positionAfter)
87 *positionAfter = line + len;
88
89 return matched;
90 }
91
makeAutoItTag(const NestingLevels * const nls,const vString * const name,const int kindIndex,const vString * const signature)92 static int makeAutoItTag (const NestingLevels *const nls,
93 const vString* const name,
94 const int kindIndex,
95 const vString *const signature)
96 {
97 int r = CORK_NIL;
98
99 if (isInputLanguageKindEnabled(kindIndex) && name != NULL && vStringLength (name) > 0)
100 {
101 NestingLevel *nl = nestingLevelsGetCurrent (nls);
102 tagEntryInfo e;
103
104 initTagEntry (&e, vStringValue (name), kindIndex);
105
106 if (nl)
107 e.extensionFields.scopeIndex = nl->corkIndex;
108 if (signature)
109 e.extensionFields.signature = vStringValue (signature);
110
111 r = makeTagEntry (&e);
112 }
113
114 return r;
115 }
116
makeSimpleAutoItTag(const NestingLevels * const nls,const vString * const name,const int kindIndex)117 static int makeSimpleAutoItTag (const NestingLevels *const nls,
118 const vString* const name,
119 const int kindIndex)
120 {
121 return makeAutoItTag (nls, name, kindIndex, NULL);
122 }
123
setEndLine(const NestingLevels * const nls)124 static void setEndLine (const NestingLevels *const nls)
125 {
126 NestingLevel *nl = nestingLevelsGetCurrent (nls);
127 tagEntryInfo *entry;
128
129 if (nl && (entry = getEntryInCorkQueue (nl->corkIndex)) != NULL)
130 entry->extensionFields.endLine = getInputLineNumber ();
131 }
132
skipSpaces(const unsigned char ** p)133 static void skipSpaces (const unsigned char **p)
134 {
135 while (isspace ((int) **p))
136 ++(*p);
137 }
138
139 /* parses after a "func" keyword */
parseFunc(const unsigned char * p,NestingLevels * nls)140 static int parseFunc (const unsigned char *p, NestingLevels *nls)
141 {
142 int k = CORK_NIL;
143 vString *name = vStringNew ();
144
145 skipSpaces (&p);
146 while (isIdentChar ((int) *p))
147 {
148 vStringPut (name, (int) *p);
149 ++p;
150 }
151 skipSpaces (&p);
152 if (*p == '(' && (vStringLength (name) > 0))
153 {
154 vString *signature = vStringNew ();
155
156 do
157 vStringPut (signature, (int) *p);
158 while (*p != ')' && *p++);
159
160 k = makeAutoItTag (nls, name, K_FUNCTION, signature);
161 nestingLevelsPush (nls, k);
162 vStringDelete (signature);
163 }
164
165 vStringDelete (name);
166
167 return k;
168 }
169
findAutoItTags(void)170 static void findAutoItTags (void)
171 {
172 vString *name = vStringNew ();
173 const unsigned char *line;
174 NestingLevels *nls = nestingLevelsNew (0);
175 unsigned int commentDepth = 0;
176
177 while ((line = readLineFromInputFile ()) != NULL)
178 {
179 const unsigned char* p = line;
180 if (p [0] == '#')
181 {
182 p++;
183 if (match (p, "cs", NULL) || match (p, "comments-start", NULL))
184 commentDepth++;
185 else if (commentDepth > 0)
186 {
187 if (match (p, "ce", NULL) || match (p, "comments-end", NULL))
188 commentDepth--;
189 }
190 else if (match (p, "region", &p))
191 {
192 skipSpaces (&p);
193 while (*p != '\0')
194 {
195 vStringPut (name, (int) *p);
196 ++p;
197 }
198
199 if (vStringLength(name) > 0)
200 {
201 int k = makeSimpleAutoItTag (nls, name, K_REGION);
202 nestingLevelsPush (nls, k);
203 vStringClear (name);
204 }
205 }
206 else if (nls->n > 0 && match (p, "endregion", NULL))
207 {
208 setEndLine (nls);
209 nestingLevelsPop (nls);
210 }
211 else if (match (p, "include", &p))
212 {
213 skipSpaces (&p);
214 if (*p == '<' || *p == '"')
215 {
216 const AutoItIncludeRole role = (*p == '<')
217 ? R_INCLUDE_SYSTEM
218 : R_INCLUDE_LOCAL;
219
220 ++p;
221 while (*p != '\0' && *p != '>' && *p != '"')
222 {
223 vStringPut (name, (int) *p);
224 ++p;
225 }
226 if (vStringLength(name) > 0)
227 {
228 makeSimpleRefTag (name, K_SCRIPT, role);
229 vStringClear (name);
230 }
231 }
232 }
233 }
234 else if (commentDepth == 0)
235 {
236 bool isGlobal = false;
237 bool isStatic = false;
238
239 /* skip white space */
240 skipSpaces (&p);
241 if (*p == ';')
242 /* ignore single-line comments */;
243 else if (match (p, "volatile", &p))
244 {
245 skipSpaces (&p);
246 if (match (p, "func", &p))
247 {
248 int k = parseFunc (p, nls);
249 if (k != CORK_NIL)
250 attachParserFieldToCorkEntry (k, AutoItFields[F_PROPERTIES].ftype, "volatile");
251 }
252 }
253 else if (match (p, "func", &p))
254 parseFunc (p, nls);
255 else if (nls->n > 0 && match (p, "endfunc", NULL))
256 {
257 setEndLine (nls);
258 nestingLevelsPop (nls);
259 }
260 else if ((isStatic = match (p, "static", &p)) ||
261 (isGlobal = match (p, "global", &p)) ||
262 match (p, "local", &p))
263 {
264 /*
265 * variable-identifier ::= "$[a-zA-Z_0-9]+"
266 * scope-modifier ::= "Local" | "Global"
267 * variable-declaration ::= "Static" [scope-modifier] variable-identifier
268 * scope-modifier ["Static" | "Const"] variable-identifier
269 */
270 skipSpaces (&p);
271 if (isStatic)
272 {
273 /* skip optional modifiers */
274 if ((isGlobal = match (p, "global", &p)) ||
275 match (p, "local", &p))
276 {
277 skipSpaces (&p);
278 }
279 }
280 else if ((isStatic = match (p, "static", &p)))
281 skipSpaces (&p);
282 else if (match (p, "const", &p))
283 skipSpaces (&p);
284 if (*p == '$')
285 {
286 vStringPut (name, (int) *p++);
287 while (isIdentChar ((int) *p))
288 {
289 vStringPut (name, (int) *p);
290 ++p;
291 }
292 if (vStringLength(name) > 0)
293 {
294 int k = makeSimpleAutoItTag (nls, name, isGlobal ? K_GLOBALVAR : K_LOCALVAR);
295 if (k != CORK_NIL && isStatic)
296 attachParserFieldToCorkEntry (k, AutoItFields[F_PROPERTIES].ftype, "static");
297 }
298 vStringClear (name);
299 }
300 }
301 }
302 }
303 vStringDelete (name);
304 nestingLevelsFree (nls);
305 }
306
AutoItParser(void)307 parserDefinition *AutoItParser (void)
308 {
309 static char const *extensions[] = { "au3", "AU3", "aU3", "Au3", NULL };
310 parserDefinition* def = parserNew ("AutoIt");
311 def->kindTable = AutoItKinds;
312 def->kindCount = ARRAY_SIZE (AutoItKinds);
313 def->fieldTable = AutoItFields;
314 def->fieldCount = ARRAY_SIZE (AutoItFields);
315 def->extensions = extensions;
316 def->parser = findAutoItTags;
317 def->useCork = CORK_QUEUE;
318 return def;
319 }
320