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