xref: /Universal-ctags/parsers/lua.c (revision 93b3d55c8341e112733574a8b5539585d3e7efa0)
1 /*
2 *   Copyright (c) 2000-2001, Max Ischenko <mfi@ukr.net>.
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 *   This module contains functions for generating tags for Lua language.
8 */
9 
10 /*
11 *   INCLUDE FILES
12 */
13 #include "general.h"  /* must always come first */
14 
15 #include <string.h>
16 
17 #include "debug.h"
18 #include "entry.h"
19 #include "parse.h"
20 #include "read.h"
21 #include "routines.h"
22 #include "vstring.h"
23 
24 /*
25 *   DATA DEFINITIONS
26 */
27 typedef enum {
28 	K_FUNCTION,
29 	K_UNKNOWN,
30 } luaKind;
31 
32 typedef enum {
33 	LUA_UNKNOWN_REFERENCED,
34 } luaUnknownRole;
35 
36 static roleDefinition LuaUnknownRoles [] = {
37 	{ false, "referenced", "referenced somehow" },
38 };
39 
40 static kindDefinition LuaKinds [] = {
41 	{ true, 'f', "function", "functions" },
42 
43 	/* `unknown' is a kind just for making FQ tag for functions. */
44 	{ false, 'X', "unknown",  "unknown language object",
45 	  .referenceOnly = true, ATTACH_ROLES(LuaUnknownRoles) },
46 };
47 
48 /*
49 *   FUNCTION DEFINITIONS
50 */
51 
52 /*
53  * Helper function.
54  * Returns 1 if line looks like a line of Lua code.
55  *
56  * TODO: Recognize UNIX bang notation.
57  * (Lua treat first line as a comment if it starts with #!)
58  *
59  */
is_a_code_line(const unsigned char * line)60 static bool is_a_code_line (const unsigned char *line)
61 {
62 	bool result;
63 	const unsigned char *p = line;
64 	while (isspace ((int) *p))
65 		p++;
66 	if (p [0] == '\0')
67 		result = false;
68 	else if (p [0] == '-' && p [1] == '-')
69 		result = false;
70 	else
71 		result = true;
72 	return result;
73 }
74 
isLuaIdentifier(char c)75 static bool isLuaIdentifier (char c)
76 {
77 	return (bool) !(isspace(c)  || c == '(' || c == ')' || c == '=' || c == '.' || c == ':'
78 					|| c == '{' || c == '}');
79 }
80 
set_scope(int child,int parent)81 static void set_scope (int child, int parent)
82 {
83 	if (parent == CORK_NIL || child == CORK_NIL)
84 		return;
85 
86 	tagEntryInfo *e = getEntryInCorkQueue (child);
87 	if (!e)
88 		return;
89 
90 	e->extensionFields.scopeIndex = parent;
91 }
92 
extract_next_token(const char * begin,const char * end_sentinel,vString * name)93 static void extract_next_token (const char *begin, const char *end_sentinel, vString *name)
94 {
95 	if (begin == NULL || end_sentinel == NULL)
96 		return;
97 
98 	Assert (begin <= end_sentinel);
99 
100 	/* Both on '(' */
101 	if (begin == end_sentinel)
102 		return;
103 
104 	/* Trim prefixed white spaces  */
105 	while (isspace ((int) *begin))
106 		begin++;
107 
108 	/* Both on '(' */
109 	if (begin == end_sentinel)
110 		return;
111 
112 	const char *end = end_sentinel - 1;
113 
114 	/* Trim suffixed white spaces  */
115 	while (isspace ((int) *end))
116 		end--;
117 
118 	Assert (begin <= end);
119 
120 	int lastCorkIndx = CORK_NIL;
121 	for (const char *c = begin; c <= end; ++c)
122 	{
123 		if (*c == '.' || *c == ':')
124 		{
125 			int r = makeSimpleRefTag(name,
126 									 K_UNKNOWN, LUA_UNKNOWN_REFERENCED);
127 			set_scope(r, lastCorkIndx);
128 			lastCorkIndx = r;
129 
130 			/* Do not include module names in function name */
131 			vStringClear (name);
132 		}
133 		else if (isLuaIdentifier (*c))
134 			vStringPut (name, (int) *c);
135 		else
136 		{
137 			/* An unexpected character is found
138 			 * between "function" and "(" */
139 			vStringClear (name);
140 			return;
141 		}
142 	}
143 
144 	int d = makeSimpleTag (name, K_FUNCTION);
145 	set_scope(d, lastCorkIndx);
146 	vStringClear (name);
147 }
148 
extract_prev_token(const char * end,const char * begin_sentinel,vString * name)149 static void extract_prev_token (const char *end, const char *begin_sentinel, vString *name)
150 {
151 	const char *begin;
152 
153 	if (end == NULL || begin_sentinel == NULL)
154 		return;
155 
156 	if (! (begin_sentinel <= end))
157 		return;
158 
159 	while (isspace ((int) *end))
160 	{
161 		end--;
162 		if (! (begin_sentinel <= end))
163 			return;
164 	}
165 
166 	begin = end;
167 	while (begin_sentinel <= begin && isLuaIdentifier (*begin))
168 		begin--;
169 
170 	int targetCorkIndex = CORK_NIL;
171 	if (end - begin)
172 	{
173 		vStringNCatS (name, begin + 1, end - begin);
174 		targetCorkIndex = makeSimpleTag (name, K_FUNCTION);
175 		vStringClear (name);
176 	}
177 
178 	if (targetCorkIndex == CORK_NIL || begin_sentinel == begin)
179 		return;
180 
181 	/* Fill the scope field of the function. */
182 	end = begin;
183 	while (begin_sentinel <= (begin + 1))
184 	{
185 		bool on_boundary = false;
186 		if (begin < begin_sentinel || !isLuaIdentifier (*begin))
187 		{
188 			if (end - begin)
189 			{
190 				vStringNCatS (name, begin + 1, end - begin);
191 				int r = makeSimpleRefTag (name,
192 										  K_UNKNOWN, LUA_UNKNOWN_REFERENCED);
193 				set_scope (targetCorkIndex, r);
194 				targetCorkIndex = r;
195 				vStringClear (name);
196 			}
197 			if (begin_sentinel <= begin && ! (*begin == ':' || *begin == '.'))
198 				break;
199 			on_boundary = true;
200 		}
201 		begin--;
202 
203 		if(on_boundary)
204 			end = begin;
205 	}
206 }
207 
findLuaTags(void)208 static void findLuaTags (void)
209 {
210 	vString *name = vStringNew ();
211 	const unsigned char *line;
212 
213 	while ((line = readLineFromInputFile ()) != NULL)
214 	{
215 		const char *p, *q;
216 
217 		if (! is_a_code_line (line))
218 			continue;
219 
220 		p = (const char*) strstr ((const char*) line, "function");
221 		if (p == NULL)
222 			continue;
223 
224 		q = strchr ((const char*) line, '=');
225 
226 		if (q == NULL) {
227 			p = p + 8;  /* skip the `function' word */
228 
229 			/* We expect [ \t(] */
230 			if (! (*p == '(' || isspace ((int)*p)))
231 				continue;
232 			q = strchr ((const char*) p, '(');
233 			if (q)
234 				extract_next_token (p, q, name);
235 		} else if (
236 			   (*(q+1) != '=') /* ignore `if type(v) == "function" then ...' */
237 			   && (q < p)	   /* ignore "function" ~=  */
238 			   ) {
239 			p = (const char*) &line[0];
240 			if (p < q)
241 				extract_prev_token (q - 1, p, name);
242 		}
243 	}
244 	vStringDelete (name);
245 }
246 
LuaParser(void)247 extern parserDefinition* LuaParser (void)
248 {
249 	static const char* const extensions [] = { "lua", NULL };
250 	parserDefinition* def = parserNew ("Lua");
251 	def->kindTable      = LuaKinds;
252 	def->kindCount  = ARRAY_SIZE (LuaKinds);
253 	def->extensions = extensions;
254 	def->parser     = findLuaTags;
255 	def->useCork    = CORK_QUEUE;
256 	def->requestAutomaticFQTag = true;
257 	return def;
258 }
259