xref: /Universal-ctags/parsers/autoit.c (revision 6b1a862e526d5017f9f212a321f59d67c859d521)
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