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