xref: /Universal-ctags/parsers/beta.c (revision 16a2541c0698bd8ee03c1be8172ef3191f6e695a)
1 /*
2 *   Copyright (c) 1999-2000, Mjølner Informatics
3 *
4 *   Written by Erik Corry <corry@mjolner.dk>
5 *
6 *   This source code is released for free distribution under the terms of the
7 *   GNU General Public License version 2 or (at your option) any later version.
8 *
9 *   This module contains functions for generating tags for BETA language
10 *   files.
11 */
12 
13 /*
14 *   INCLUDE FILES
15 */
16 #include "general.h"	/* must always come first */
17 
18 #include <string.h>
19 
20 #include "entry.h"
21 #include "parse.h"
22 #include "read.h"
23 #include "routines.h"
24 #include "vstring.h"
25 
26 /*
27 *   MACROS
28 */
29 #define isbident(c) (identarray [(unsigned char) (c)])
30 
31 /*
32 *   DATA DEFINITIONS
33 */
34 typedef enum {
35 	K_FRAGMENT, K_PATTERN, K_SLOT, K_VIRTUAL
36 } betaKind;
37 
38 static kindDefinition BetaKinds [] = {
39 	{ true,  'f', "fragment", "fragment definitions"},
40 	{ false, 'p', "pattern",  "all patterns"},
41 	{ true,  's', "slot",     "slots (fragment uses)"},
42 	{ true,  'v', "virtual",  "patterns (virtual or rebound)"}
43 };
44 
45 /* [A-Z_a-z0-9] */
46 static const char identarray [256] = {
47 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0-15  */
48 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 16-31 */
49 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 32-47    !"#$%&'()*+'-./ */
50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  /* 48-63   0123456789:;<=>? */
51 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 64-79   @ABCDEFGHIJKLMNO */
52 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  /* 80-95   PQRSTUVWXYZ [\]^_ */
53 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 96-111  `abcdefghijklmno */
54 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  /* 112-127  pqrstuvwxyz{|}~ */
55 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 128-  */
56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* -255  */
63 
64 /*
65 *   FUNCTION DEFINITIONS
66 */
67 
makeBetaTag(const char * const name,const betaKind kind)68 static void makeBetaTag (const char* const name, const betaKind kind)
69 {
70 	if (BetaKinds [kind].enabled)
71 	{
72 		tagEntryInfo e;
73 		initTagEntry (&e, name, kind);
74 		makeTagEntry (&e);
75 	}
76 }
77 
findBetaTags(void)78 static void findBetaTags (void)
79 {
80 	vString *line = vStringNew ();
81 	bool incomment = false;
82 	bool inquote = false;
83 	bool dovirtuals = BetaKinds [K_VIRTUAL].enabled;
84 	bool dopatterns = BetaKinds [K_PATTERN].enabled;
85 
86 	int c;
87 	do
88 	{
89 		bool foundfragmenthere = false;
90 		/* find fragment definition (line that starts and ends with --) */
91 		int last;
92 		int first;
93 
94 		vStringClear (line);
95 
96 		while ((c = getcFromInputFile ()) != EOF && c != '\n' && c != '\r')
97 			vStringPut (line, c);
98 
99 		last = vStringLength (line) - 1;
100 		first = 0;
101 		/* skip white space at start and end of line */
102 		while (last > 0 && isspace ((int) vStringChar (line, last))) last--;
103 		while (first < last && isspace ((int) vStringChar (line, first))) first++;
104 		/* if line still has a reasonable length and ... */
105 		if (last - first > 4 &&
106 			(vStringChar (line, first)     == '-' &&
107 			 vStringChar (line, first + 1) == '-' &&
108 			 vStringChar (line, last)      == '-' &&
109 			 vStringChar (line, last - 1)  == '-'))
110 		{
111 			if (!incomment && !inquote)
112 			{
113 				foundfragmenthere = true;
114 				/* skip past -- and whitespace.  Also skip back past 'dopart'
115 				   or 'attributes' to the :.  We have to do this because there
116 				   is no sensible way to include whitespace in a ctags token
117 				   so the conventional space after the ':' would mess us up */
118 				last -= 2;
119 				first += 2;
120 				while (last && vStringChar (line, last) != ':') last--;
121 				while (last && (isspace ((int) vStringChar (line, last-1)))) last--;
122 				while (first < last &&
123 					   (isspace ((int) vStringChar (line, first)) ||
124 						vStringChar (line, first) == '-'))
125 					first++;
126 				/* If there's anything left it is a fragment title */
127 				if (first < last - 1)
128 				{
129 					vStringChar (line, last) = 0;
130 					if (strcasecmp ("LIB", vStringValue (line) + first) &&
131 						strcasecmp ("PROGRAM", vStringValue (line) + first))
132 					{
133 						makeBetaTag (vStringValue (line) + first, K_FRAGMENT);
134 					}
135 				}
136 			}
137 		} else {
138 			int pos = 0;
139 			int len = vStringLength (line);
140 			if (inquote) goto stringtext;
141 			if (incomment) goto commenttext;
142 		programtext:
143 			for ( ; pos < len; pos++)
144 			{
145 				if (vStringChar (line, pos) == '\'')
146 				{
147 					pos++;
148 					inquote = true;
149 					goto stringtext;
150 				}
151 				if (vStringChar (line, pos) == '{')
152 				{
153 					pos++;
154 					incomment = true;
155 					goto commenttext;
156 				}
157 				if (vStringChar (line, pos) == '(' && pos < len - 1 &&
158 					vStringChar (line, pos+1) == '*')
159 				{
160 					pos +=2;
161 					incomment = true;
162 					goto commenttext;
163 				}
164 				/*
165 				 * SLOT definition looks like this:
166 				 * <<SLOT nameofslot: dopart>>
167 				 * or
168 				 * <<SLOT nameofslot: descriptor>>
169 				 */
170 				if (!foundfragmenthere &&
171 					vStringChar (line, pos) == '<' &&
172 					pos+1 < len &&
173 					vStringChar (line, pos+1) == '<' &&
174 					strstr (vStringValue (line) + pos, ">>"))
175 				{
176 					/* Found slot name, get start and end */
177 					int eoname;
178 					char c2;
179 					pos += 2; /* skip past << */
180 					/* skip past space before SLOT */
181 					while (pos < len && isspace ((int) vStringChar (line, pos)))
182 						pos++;
183 					/* skip past SLOT */
184 					if (pos+4 <= len &&
185 						!strncasecmp (vStringValue(line) + pos, "SLOT", (size_t)4))
186 						pos += 4;
187 					/* skip past space after SLOT */
188 					while (pos < len && isspace ((int) vStringChar (line, pos)))
189 						pos++;
190 					eoname = pos;
191 					/* skip to end of name */
192 					while (eoname < len &&
193 							(c2 = vStringChar (line, eoname)) != '>' &&
194 							c2 != ':' &&
195 							!isspace ((int) c2))
196 						eoname++;
197 					if (eoname < len)
198 					{
199 						vStringChar (line, eoname) = 0;
200 						if (strcasecmp ("LIB", vStringValue (line) + pos) &&
201 							strcasecmp ("PROGRAM", vStringValue (line) + pos) &&
202 							strcasecmp ("SLOT", vStringValue (line) + pos))
203 						{
204 							makeBetaTag (vStringValue (line) + pos, K_SLOT);
205 						}
206 					}
207 					if (eoname+1 < len) {
208 						pos = eoname + 1;
209 					} else {
210 						pos = len;
211 						continue;
212 					}
213 				}
214 				/* Only patterns that are virtual, extensions of virtuals or
215 				 * final bindings are normally included so as not to overload
216 	             * totally.
217 				 * That means one of the forms name:: name:< or name::<
218 				 */
219 				if (!foundfragmenthere &&
220 					vStringChar (line, pos) == ':' &&
221 	                (dopatterns ||
222 					 (dovirtuals &&
223 					  (vStringChar (line, pos+1) == ':' ||
224 					   vStringChar (line, pos+1) == '<')
225 					 )
226 					)
227 	               )
228 				{
229 					/* Found pattern name, get start and end */
230 					int eoname = pos;
231 					int soname;
232 					while (eoname && isspace ((int) vStringChar (line, eoname-1)))
233 						eoname--;
234 				foundanothername:
235 					/* terminate right after name */
236 					vStringChar (line, eoname) = 0;
237 					soname = eoname;
238 					while (soname &&
239 						isbident (vStringChar (line, soname-1)))
240 					{
241 						soname--;
242 					}
243 					if (soname != eoname)
244 					{
245 						makeBetaTag (vStringValue (line) + soname, K_PATTERN);
246 						/* scan back past white space */
247 						while (soname &&
248 								isspace ((int) vStringChar (line, soname-1)))
249 							soname--;
250 						if (soname && vStringChar (line, soname-1) == ',')
251 						{
252 							/* we found a new pattern name before comma */
253 							eoname = soname;
254 							goto foundanothername;
255 						}
256 					}
257 				}
258 			}
259 			goto endofline;
260 		commenttext:
261 			for ( ; pos < len; pos++)
262 			{
263 				if (vStringChar (line, pos) == '*' && pos < len - 1 &&
264 					vStringChar (line, pos+1) == ')')
265 				{
266 					pos += 2;
267 					incomment = false;
268 					goto programtext;
269 				}
270 				if (vStringChar (line, pos) == '}')
271 				{
272 					pos++;
273 					incomment = false;
274 					goto programtext;
275 				}
276 			}
277 			goto endofline;
278 		stringtext:
279 			for ( ; pos < len; pos++)
280 			{
281 				if (vStringChar (line, pos) == '\\')
282 				{
283 					if (pos < len - 1) pos++;
284 				}
285 				else if (vStringChar (line, pos) == '\'')
286 				{
287 					pos++;
288 					/* support obsolete '' syntax */
289 					if (pos < len && vStringChar (line, pos) == '\'')
290 					{
291 						continue;
292 					}
293 					inquote = false;
294 					goto programtext;
295 				}
296 			}
297 		}
298 		endofline:
299 		inquote = false;  /* This shouldn't really make a difference */
300 	} while (c != EOF);
301 	vStringDelete (line);
302 }
303 
BetaParser(void)304 extern parserDefinition* BetaParser (void)
305 {
306 	static const char *const extensions [] = { "bet", NULL };
307 	parserDefinition* def = parserNew ("BETA");
308 	def->kindTable      = BetaKinds;
309 	def->kindCount  = ARRAY_SIZE (BetaKinds);
310 	def->extensions = extensions;
311 	def->parser     = findBetaTags;
312 	return def;
313 }
314