xref: /Universal-ctags/parsers/r-s4class.c (revision cd90567899eac84aa3a28b1598f70cdd1d9ab332)
1 /*
2 *   Copyright (c) 2020, Masatake YAMATO
3 *   Copyright (c) 2020, Red Hat, Inc.
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 S4 Classes and Methods.
9 *   https://www.r-project.org/conferences/useR-2004/Keynotes/Leisch.pdf
10 */
11 
12 
13 /*
14 *   INCLUDE FILES
15 */
16 #include "general.h"	/* must always come first */
17 
18 #include "r.h"
19 #include "kind.h"
20 #include "tokeninfo.h"
21 #include "parse.h"
22 #include "subparser.h"
23 
24 #include <string.h>
25 
26 
27 /*
28 *   DATA DECLARATIONS
29 */
30 
31 struct s4Subparser {
32 	rSubparser r;
33 };
34 
35 
36 /*
37 * FUNCTION PROTOTYPES
38 */
39 
40 static int s4ReadFuncall (rSubparser *s,
41 						  tokenInfo *const func, tokenInfo *const token,
42 						  int parent);
43 static int s4ReadRightSideSymbol (rSubparser *s,
44 								  tokenInfo *const symbol,
45 								  const char *const assignmentOperator,
46 								  int parent,
47 								  tokenInfo *const token);
48 static bool s4AskTagAcceptancy (rSubparser *s, tagEntryInfo *pe);
49 static bool s4HasFunctionAlikeKind (rSubparser *s, tagEntryInfo *e);
50 
51 
52 /*
53 *   DATA DEFINITIONS
54 */
55 
56 typedef enum {
57 	S4_K_CLASS,
58 	S4_K_REPRESENTATION,
59 	S4_K_GENERIC,
60 	S4_K_METHOD,
61 } s4Kind;
62 
63 static kindDefinition S4Kinds[] = {
64 	{ true, 'c', "class",   "classes" },
65 	{ true, 'r', "repr",    "representations" },
66 	{ true, 'g', "generic", "generics" },
67 	{ true, 'm', "method",  "methods" },
68 };
69 
70 static struct s4Subparser s4Subparser = {
71 	.r = {
72 		.subparser = {
73 			.direction = SUBPARSER_BI_DIRECTION,
74 		},
75 		.readFuncall = s4ReadFuncall,
76 		.readRightSideSymbol = s4ReadRightSideSymbol,
77 		.askTagAcceptancy = s4AskTagAcceptancy,
78 		.hasFunctionAlikeKind = s4HasFunctionAlikeKind,
79 	},
80 };
81 
82 
83 /*
84 *   FUNCTION DEFINITIONS
85 */
86 
87 /*
88  * V=================V: parse this area
89  * representation(...)) ...
90  * representation(...), ...
91  */
parseRepresentation(rSubparser * s,tokenInfo * const token,int parent)92 static void parseRepresentation (rSubparser *s, tokenInfo *const token, int parent)
93 {
94 	rTokenReadNoNewline (token);
95 	if (tokenIsTypeVal (token, '('))
96 	{
97 		rTokenReadNoNewline (token);
98 		while (!(tokenIsTypeVal (token, ')') || tokenIsTypeVal (token, ',')))
99 		{
100 			if (!rParseStatement (token, parent, false))
101 				break;
102 			else if (tokenIsTypeVal (token, '\n'))
103 				rTokenReadNoNewline (token);
104 		}
105 	}
106 	else
107 		tokenUnread (token);
108 }
109 
110 /*
111  * V==============V: parse this area
112  * contains = "..."
113  */
parseContains(rSubparser * s,tokenInfo * const token,int parent)114 static void parseContains (rSubparser *s, tokenInfo *const token, int parent)
115 {
116 	rTokenReadNoNewline (token);
117 	if (tokenIsTypeVal (token, '='))
118 	{
119 		rTokenReadNoNewline (token);
120 		if (tokenIsType (token, R_STRING))
121 		{
122 			tagEntryInfo *e = getEntryInCorkQueue (parent);
123 			if (e)
124 			{
125 				vString *n = rExtractNameFromString (token->string);
126 				e->extensionFields.inheritance = vStringDeleteUnwrap (n);
127 			}
128 		}
129 		else
130 			tokenUnread (token);
131 	}
132 	else
133 		tokenUnread (token);
134 }
135 
136 /*
137  *                  V====V: parse this area
138  * setClass ("class", ...)
139  */
parseClassArgs(rSubparser * s,tokenInfo * const token,int parent,tokenInfo * const open_paren CTAGS_ATTR_UNUSED)140 static bool parseClassArgs (rSubparser *s, tokenInfo *const token, int parent,
141 							tokenInfo *const open_paren CTAGS_ATTR_UNUSED)
142 {
143 	do
144 	{
145 		rTokenReadNoNewline (token);
146 
147 		if (tokenIsTypeVal (token, ')'))
148 			break;
149 		else if (tokenIsTypeVal (token, '('))
150 			tokenSkipOverPair (token);
151 		else if (tokenIsTypeVal (token, '{'))
152 			tokenSkipOverPair (token);
153 		else if (tokenIsTypeVal (token, '['))
154 			tokenSkipOverPair (token);
155 		else if (tokenIsType (token, R_SYMBOL))
156 		{
157 			const char *str = tokenString (token);
158 			if (strcmp (str, "representation") == 0)
159 				parseRepresentation (s, token, parent);
160 			else if (strcmp (str, "contains") == 0)
161 				parseContains (s, token, parent);
162 		}
163 	}
164 	while (!tokenIsEOF (token));
165 
166 	return false;
167 }
168 
169 /*
170  *                    V============V: parse this area
171  * setMethod ("method", c(...), ...)
172  *                    V====================V: parse this area
173  * setMethod ("method", signature(...), ...)
174  *
175  * Attaching c(...) or signature(...) to "signature:" field of
176  * the tag for "method" specified by parent.
177  */
parseMethodArgs(rSubparser * s,tokenInfo * const token,int parent,tokenInfo * const open_paren)178 static bool parseMethodArgs (rSubparser *s, tokenInfo *const token, int parent,
179 							 tokenInfo *const open_paren)
180 {
181 	rTokenReadNoNewline (token);
182 	if (tokenIsTypeVal (token, ','))
183 	{
184 		rTokenReadNoNewline (token);
185 		if (tokenIsKeyword (token, R_C)
186 			|| (tokenIsType (token, R_SYMBOL)
187 				&& (strcmp (tokenString (token), "signature") == 0)))
188 		{
189 			rTokenReadNoNewline (token);
190 			if (tokenIsTypeVal (token, '('))
191 			{
192 				vString *signature = vStringNewInit("(");
193 				rSetupCollectingSignature (token, signature);
194 				tokenSkipOverPair (token);
195 				rTeardownCollectingSignature (token);
196 				tagEntryInfo *e = getEntryInCorkQueue (parent);
197 				if (e)
198 				{
199 					e->extensionFields.signature = vStringDeleteUnwrap (signature);
200 					signature = NULL;
201 				}
202 				vStringDelete (signature); /* NULL is acceptable */
203 			}
204 			rTokenReadNoNewline (token);
205 			if (tokenIsTypeVal (token, ','))
206 			{
207 				rTokenReadNoNewline (token);
208 				/* anonymous function for implementing this method may be here.*/
209 				while (!tokenIsTypeVal (token, ')'))
210 				{
211 					if (!rParseStatement (token, parent, true))
212 						break;
213 					else if (tokenIsTypeVal (token, '\n'))
214 						rTokenReadNoNewline (token);
215 				}
216 			}
217 			return false;
218 		}
219 		else
220 			tokenUnread (token);
221 	}
222 	else
223 		tokenUnread (token);
224 	return true;
225 }
226 
227 /*
228  *                      V====V: parse this area
229  * setGeneric ("generic", ...)
230  */
parseGenericArgs(rSubparser * s,tokenInfo * const token,int parent,tokenInfo * const open_paren)231 static bool parseGenericArgs (rSubparser *s, tokenInfo *const token, int parent,
232 							  tokenInfo *const open_paren)
233 {
234 	while (!tokenIsTypeVal (token, ')'))
235 	{
236 		if (!rParseStatement (token, parent, true))
237 			break;
238 		else if (tokenIsTypeVal (token, '\n'))
239 			rTokenReadNoNewline (token);
240 	}
241 	return false;
242 }
243 
244 /* parse
245  * this
246  * area ---\          /--- parseArgs parses this area.
247  *         V====VV----V
248  *   setXXX("...", ...)
249  */
parseSetCommon(rSubparser * s,tokenInfo * const token,int parent,int kind,bool (* parseArgs)(rSubparser *,tokenInfo * const,int,tokenInfo * const))250 static int parseSetCommon (rSubparser *s, tokenInfo *const token, int parent,
251 						   int kind,
252 						   bool (* parseArgs) (rSubparser *, tokenInfo *const, int, tokenInfo *const))
253 {
254 	int q = CORK_NIL;
255 	tokenInfo *const open_paren = newTokenByCopying (token);
256 
257 	rTokenReadNoNewline (token);
258 	if (tokenIsType (token, R_STRING))
259 	{
260 		vString *n = rExtractNameFromString (token->string);
261 		if (n)
262 		{
263 			q = makeSimpleTag (n, kind);
264 			vStringDelete (n);
265 		}
266 	}
267 
268 	bool skip = true;
269 	if (q != CORK_NIL && parseArgs)
270 		skip = (* parseArgs) (s, token, q, open_paren);
271 
272 	if (skip)
273 	{
274 		tokenCopy (token, open_paren);
275 		tokenSkipOverPair (token);
276 	}
277 
278 	tokenDelete (open_paren);
279 	return q;
280 }
281 
parseSetClass(rSubparser * s,tokenInfo * const token,int parent)282 static int parseSetClass (rSubparser *s, tokenInfo *const token, int parent)
283 {
284 	return parseSetCommon (s, token, parent, S4_K_CLASS, parseClassArgs);
285 }
286 
parseSetGeneric(rSubparser * s,tokenInfo * const token,int parent)287 static int parseSetGeneric (rSubparser *s, tokenInfo *const token, int parent)
288 {
289 	return parseSetCommon (s, token, parent, S4_K_GENERIC, parseGenericArgs);
290 }
291 
parseSetMethod(rSubparser * s,tokenInfo * const token,int parent)292 static int parseSetMethod (rSubparser *s, tokenInfo *const token, int parent)
293 {
294 	return parseSetCommon (s, token, parent, S4_K_METHOD, parseMethodArgs);
295 }
296 
s4ReadFuncall(rSubparser * s,tokenInfo * const func,tokenInfo * const token,int parent)297 static int s4ReadFuncall (rSubparser *s,
298 						  tokenInfo *const func, tokenInfo *const token,
299 						  int parent)
300 {
301 	if (strcmp (tokenString (func), "setClass") == 0)
302 		return parseSetClass (s, token, parent);
303 	else if (strcmp (tokenString (func), "setGeneric") == 0)
304 		return parseSetGeneric (s, token, parent);
305 	else if (strcmp (tokenString (func), "setMethod") == 0)
306 		return parseSetMethod (s, token, parent);
307 	else
308 		return CORK_NIL;
309 }
310 
s4AskTagAcceptancy(rSubparser * s,tagEntryInfo * pe)311 static bool s4AskTagAcceptancy (rSubparser *s, tagEntryInfo *pe)
312 {
313 	return (pe->kindIndex == S4_K_CLASS);
314 }
315 
s4HasFunctionAlikeKind(rSubparser * s,tagEntryInfo * e)316 static bool s4HasFunctionAlikeKind (rSubparser *s, tagEntryInfo *e)
317 {
318 	return e->kindIndex == S4_K_METHOD ||
319 		e->kindIndex == S4_K_GENERIC;
320 }
321 
322 /*
323  * setClass("class", representation(r0 = "t0", r1 = "t1", ...
324  *
325  * Attaching t as "typeref:typename:" field of r.
326  *
327  * the tag for "class" -> parent.
328  * r0, r1, ... -> symbol
329  */
s4ReadRightSideSymbol(rSubparser * s,tokenInfo * const symbol,const char * const assignmentOperator,int parent,tokenInfo * const token)330 static int s4ReadRightSideSymbol (rSubparser *s,
331 								  tokenInfo *const symbol,
332 								  const char *const assignmentOperator,
333 								  int parent,
334 								  tokenInfo *const token)
335 {
336 	tagEntryInfo *pe = getEntryInCorkQueue (parent);
337 	if (! (pe
338 		   && pe->langType == s->subparser.slaveParser->id
339 		   && pe->kindIndex == S4_K_CLASS))
340 		return CORK_NIL;
341 
342 	tagEntryInfo e;
343 	int q;
344 	vString *t = NULL;
345 
346 	if (tokenIsType (token, R_STRING))
347 		t = rExtractNameFromString (token->string);
348 
349 	initTagEntry (&e, tokenString (symbol), S4_K_REPRESENTATION);
350 	e.extensionFields.scopeIndex = parent;
351 	if (t)
352 	{
353 		e.extensionFields.typeRef[0] = "typename";
354 		e.extensionFields.typeRef[1] = vStringValue (t);
355 	}
356 	q = makeTagEntry (&e);
357 	vStringDelete (t);			/* NULL is acceptable. */
358 
359 	return q;
360 }
361 
findS4Tags(void)362 static void findS4Tags(void)
363 {
364 	scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
365 }
366 
S4ClassParser(void)367 extern parserDefinition* S4ClassParser (void)
368 {
369 	parserDefinition* const def = parserNew("S4Class");
370 
371 	static parserDependency dependencies [] = {
372 		[0] = { DEPTYPE_SUBPARSER, "R", &s4Subparser },
373 	};
374 
375 	def->dependencies = dependencies;
376 	def->dependencyCount = ARRAY_SIZE (dependencies);
377 
378 	def->kindTable = S4Kinds;
379 	def->kindCount = ARRAY_SIZE(S4Kinds);
380 
381 	def->parser =  findS4Tags;
382 	def->useCork = CORK_QUEUE;
383 
384 	return def;
385 }
386