1 /*
2 * Copyright (c) 2019, Masatake YAMATO
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 Moose perl extension.
8 * https://metacpan.org/pod/Moose
9 *
10 * This module can gather tags for Moo perl extension, too.
11 * https://metacpan.org/pod/Moo
12 *
13 */
14
15 /* NOTE about kind/role design:
16 *
17 * sub foo { ... }
18 * after 'foo' => sub { ...}
19 *
20 * There were two ideas to capture 'foo':
21 *
22 * A: capturing 'foo' as a reference tag with 'method' kind and 'wrapped' role, and
23 * B: capturing 'foo' as a definition tag with 'wrapper' kind.
24 *
25 * This implementation takes the idea B. */
26
27 /*
28 * INCLUDE FILES
29 */
30 #include "general.h" /* must always come first */
31
32 #include "debug.h"
33 #include "entry.h"
34 #include "kind.h"
35 #include "parse.h"
36 #include "perl.h"
37 #include "read.h"
38 #include "routines.h"
39 #include "trace.h"
40
41 #include <string.h>
42
43 /*
44 * DATA DECLARATIONS
45 */
46
47 enum MooseKind {
48 K_CLASS,
49 K_METHOD,
50 K_ATTR,
51 K_WRAPPER,
52 K_ROLE,
53 };
54
55 static kindDefinition MooseKinds[] = {
56 { true, 'c', "class", "classes" },
57 { true, 'm', "method", "methods" },
58 { true, 'a', "attribute", "attributes" },
59 { true, 'w', "wrapper", "wrappers" },
60 { true, 'r', "role", "roles" },
61 };
62
63 typedef enum {
64 F_WRAPPING,
65 } MooseField;
66
67 static fieldDefinition MooseFields [] = {
68 {
69 .name = "wrapping",
70 .description = "how a wrapper wrapping the method (around, after, or before)",
71 .enabled = true,
72 },
73 };
74
75 enum Wrapping {
76 W_UNKNOWN,
77 W_AROUND,
78 W_BEFORE,
79 W_AFTER,
80 };
81
82 static char * WrappingStrings[] = {
83 "unknown",
84 "around",
85 "before",
86 "after",
87 };
88
89 struct mooseSubparser {
90 perlSubparser perl;
91 bool notInMoose;
92 bool inPod;
93 int packageCork;
94 int classCork;
95 bool notContinuousExtendsLines;
96
97 int indexForFunctionParameters;
98
99 /* functionParametersModifiersStateCounter is for tracking the conditions
100 * that both "use Moose;" and "use Functions::Parameters qw/:modifiers/"
101 * are specified.
102 * functionParametersModifiersStateCounter is initialized to 2.
103 * Decrement functionParametersModifiersStateCounter when finding one of two.
104 * When functionParametersModifiersStateCounter is 0, the state is
105 * propagated to notInFunctionParametersModifiers. */
106 bool notInFunctionParametersModifiers;
107 int functionParametersModifiersStateCounter;
108 #define RESET_FunctionParameters_STATE(MOOSE) \
109 do { \
110 MOOSE->notInFunctionParametersModifiers = true; \
111 MOOSE->functionParametersModifiersStateCounter = 2; \
112 } while (0)
113 #define DEC_FunctionParameters_STATE(MOOSE) \
114 do { \
115 MOOSE->functionParametersModifiersStateCounter--; \
116 if (MOOSE->functionParametersModifiersStateCounter == 0) \
117 MOOSE->notInFunctionParametersModifiers = false; \
118 if (MOOSE->functionParametersModifiersStateCounter < 0) \
119 MOOSE->functionParametersModifiersStateCounter = 0; \
120 } while (0)
121 #define INC_FunctionParameters_STATE(MOOSE) \
122 do { \
123 MOOSE->functionParametersModifiersStateCounter++; \
124 if (MOOSE->functionParametersModifiersStateCounter > 0) \
125 MOOSE->notInFunctionParametersModifiers = true; \
126 if (MOOSE->functionParametersModifiersStateCounter > 2) \
127 MOOSE->functionParametersModifiersStateCounter = 2; \
128 } while (0)
129
130 vString *supersOrRoles;
131 };
132
133 /*
134 * FUNCTION PROTOTYPES
135 */
136 static void inputStart (subparser *s);
137 static void inputEnd (subparser *s);
138 static void makeTagEntryNotify (subparser *s, const tagEntryInfo *tag, int corkIndex);
139 static void enterMoose (struct mooseSubparser *moose, bool role);
140 static void leaveMoose (struct mooseSubparser *moose);
141 static void enteringPodNotify (perlSubparser *perl);
142 static void leavingPodNotify (perlSubparser *perl);
143 static void findingQuotedWordNotify (perlSubparser *perl, int moduleIndex, const char *qwd);
144
145 /*
146 * DATA DEFINITIONS
147 */
148
149 static struct mooseSubparser mooseSubparser = {
150 .perl = {
151 .subparser = {
152 .direction = SUBPARSER_BI_DIRECTION,
153 .inputStart = inputStart,
154 .inputEnd = inputEnd,
155 .makeTagEntryNotify = makeTagEntryNotify,
156 },
157 .enteringPodNotify = enteringPodNotify,
158 .leavingPodNotify = leavingPodNotify,
159 .findingQuotedWordNotify = findingQuotedWordNotify,
160 },
161 };
162
163
164 /*
165 * FUNCTION DEFINITIONS
166 */
167
inputStart(subparser * s)168 static void inputStart (subparser *s)
169 {
170 struct mooseSubparser *moose = (struct mooseSubparser *)s;
171
172 moose->notInMoose = true;
173 moose->packageCork = CORK_NIL;
174 moose->classCork = CORK_NIL;
175 moose->inPod = false;
176 moose->supersOrRoles = vStringNew ();
177 moose->notContinuousExtendsLines = true;
178 moose->indexForFunctionParameters = CORK_NIL;
179 RESET_FunctionParameters_STATE (moose);
180 }
181
inputEnd(subparser * s)182 static void inputEnd (subparser *s)
183 {
184 struct mooseSubparser *moose = (struct mooseSubparser *)s;
185 tagEntryInfo *e = getEntryInCorkQueue (moose->classCork);
186 if (e)
187 e->extensionFields.endLine = getInputLineNumber ();
188
189 vStringDelete (moose->supersOrRoles);
190 moose->supersOrRoles = NULL;
191 }
192
makeTagEntryNotify(subparser * s,const tagEntryInfo * tag,int corkIndex)193 static void makeTagEntryNotify (subparser *s, const tagEntryInfo *tag, int corkIndex)
194 {
195 perlSubparser *perl = (perlSubparser *)s;
196 struct mooseSubparser *moose = (struct mooseSubparser *)perl;
197
198 if (isTagExtraBitMarked(tag, XTAG_QUALIFIED_TAGS))
199 return;
200
201 if (tag->kindIndex == KIND_PERL_PACKAGE)
202 moose->packageCork = corkIndex;
203 else if ((!moose->notInMoose) && tag->kindIndex == KIND_PERL_SUBROUTINE)
204 {
205 tagEntryInfo moose_e;
206 initTagEntry (&moose_e, tag->name, K_METHOD);
207 setTagPositionFromTag (&moose_e, tag);
208 moose_e.extensionFields.scopeIndex = moose->classCork;
209 makeTagEntry (&moose_e);
210 }
211 else if (tag->kindIndex == KIND_PERL_MODULE)
212 {
213 if (isRoleAssigned(tag, ROLE_PERL_MODULE_USED))
214 {
215 if (strcmp (tag->name, "Moose") == 0
216 || strcmp (tag->name, "Moo") == 0)
217 enterMoose (moose, false);
218 else if (strcmp (tag->name, "Moose::Role") == 0)
219 enterMoose (moose, true);
220 else if (strcmp (tag->name, "Function::Parameters") == 0)
221 moose->indexForFunctionParameters = corkIndex;
222 }
223 else if (isRoleAssigned(tag, ROLE_PERL_MODULE_UNUSED))
224 {
225 if (strcmp (tag->name, "Moose") == 0
226 || strcmp (tag->name, "Moo") == 0)
227 leaveMoose (moose);
228 else if (strcmp (tag->name, "Function::Parameters") == 0)
229 {
230 moose->indexForFunctionParameters = CORK_NIL;
231 INC_FunctionParameters_STATE(moose);
232 }
233 }
234 }
235 }
236
enteringPodNotify(perlSubparser * perl)237 static void enteringPodNotify (perlSubparser *perl)
238 {
239 struct mooseSubparser *moose = (struct mooseSubparser *)perl;
240 moose->inPod = true;
241 }
242
leavingPodNotify(perlSubparser * perl)243 static void leavingPodNotify (perlSubparser *perl)
244 {
245 struct mooseSubparser *moose = (struct mooseSubparser *)perl;
246 moose->inPod = false;
247 }
248
findingQuotedWordNotify(perlSubparser * perl,int moduleIndex,const char * qwd)249 static void findingQuotedWordNotify (perlSubparser *perl,
250 int moduleIndex, const char *qwd)
251 {
252 struct mooseSubparser *moose = (struct mooseSubparser *)perl;
253 if (moose->indexForFunctionParameters != moduleIndex)
254 return;
255
256 if (strcmp (qwd, ":modifiers") == 0)
257 DEC_FunctionParameters_STATE(moose);
258 }
259
leaveMoose(struct mooseSubparser * moose)260 static void leaveMoose (struct mooseSubparser *moose)
261 {
262 moose->notContinuousExtendsLines = true;
263
264 tagEntryInfo *e = getEntryInCorkQueue (moose->classCork);
265 if (!e)
266 return;
267
268 e->extensionFields.endLine = getInputLineNumber ();
269
270 moose->classCork = CORK_NIL;
271 moose->notInMoose = true;
272 moose->packageCork = CORK_NIL;
273 INC_FunctionParameters_STATE(moose);
274 }
275
enterMoose(struct mooseSubparser * moose,bool role)276 static void enterMoose (struct mooseSubparser *moose, bool role)
277 {
278 moose->notContinuousExtendsLines = true;
279
280 tagEntryInfo *perl_e = getEntryInCorkQueue (moose->packageCork);
281 if (!perl_e)
282 return;
283
284 moose->notInMoose = false;
285
286 tagEntryInfo moose_e;
287 initTagEntry (&moose_e, perl_e->name, role? K_ROLE: K_CLASS);
288 moose_e.lineNumber = perl_e->lineNumber;
289 moose_e.filePosition = perl_e->filePosition;
290 moose->classCork = makeTagEntry (&moose_e);
291 vStringClear (moose->supersOrRoles);
292
293 DEC_FunctionParameters_STATE(moose);
294
295 return;
296 }
297
parseExtendsClass(const char * input,size_t input_len,vString * inherits,bool * notContinuousLine)298 static void parseExtendsClass (const char *input,
299 size_t input_len,
300 vString *inherits,
301 bool *notContinuousLine)
302 {
303 int i = 0;
304 do
305 {
306 if (input [i] == ',')
307 i++;
308
309 for (; (i < input_len) && input[i] != '\n' && input[i] != '\0'; i++)
310 {
311 if (input[i] == '\'' || input[i] == ' ' || input[i] == '\t')
312 continue;
313 else if (input[i] == ',')
314 {
315 vStringPut (inherits, ',');
316 *notContinuousLine = false;
317 break;
318 }
319 else if (input[i] == ';')
320 {
321 *notContinuousLine = true;
322 break;
323 }
324 else
325 vStringPut (inherits, input[i]);
326 }
327 }
328 while (input[i] == ',');
329 }
330
findExtendsClass(const char * line,const regexMatch * matches,unsigned int count,void * data)331 static bool findExtendsClass (const char *line,
332 const regexMatch *matches,
333 unsigned int count,
334 void *data)
335 {
336 struct mooseSubparser *moose = data;
337 moose->notContinuousExtendsLines = true;
338
339 if (moose->inPod)
340 return true;
341
342 tagEntryInfo *e = getEntryInCorkQueue (moose->classCork);
343 if (!e)
344 return true;
345
346 const char *input = line + matches[2].start;
347 vString *str = moose->supersOrRoles;
348
349 if (vStringLength (str) > 0)
350 vStringPut (str, ',');
351 parseExtendsClass (input, matches[2].length, str,
352 &moose->notContinuousExtendsLines);
353
354 if (moose->notContinuousExtendsLines == true
355 && vStringLength (str) > 0)
356 {
357 if (e->extensionFields.inheritance)
358 eFree ((void *)e->extensionFields.inheritance);
359 e->extensionFields.inheritance = vStringStrdup (str);
360 }
361
362 return true;
363 }
364
findExtendsClassContinuation(const char * line,const regexMatch * matches,unsigned int count,void * data)365 static bool findExtendsClassContinuation (const char *line,
366 const regexMatch *matches,
367 unsigned int count,
368 void *data)
369 {
370 struct mooseSubparser *moose = data;
371 moose->notContinuousExtendsLines = true;
372
373 tagEntryInfo *e = getEntryInCorkQueue (moose->classCork);
374 if (!e)
375 return true;
376
377 const char *input = line + matches[1].start;
378 vString *str = moose->supersOrRoles;
379
380 parseExtendsClass (input, matches[1].length, str,
381 &moose->notContinuousExtendsLines);
382
383 if (moose->notContinuousExtendsLines == true
384 && vStringLength (str) > 0)
385 {
386 if (e->extensionFields.inheritance)
387 eFree ((void *)e->extensionFields.inheritance);
388 e->extensionFields.inheritance = vStringStrdup (str);
389 }
390
391 return true;
392 }
393
parseAttributeOrWrapper(const char * str,int parentCorkIndex,int extraTerminator,int kind,enum Wrapping wrapping)394 static const char *parseAttributeOrWrapper (const char *str, int parentCorkIndex, int extraTerminator,
395 int kind, enum Wrapping wrapping)
396 {
397 int i;
398
399 for (i = 0;
400 str[i]
401 && str[i] != extraTerminator
402 && (isalnum ((unsigned char)str[i]) || str[i] == '_');
403 i++)
404 ; /* Just advancing `i' */
405
406 if (i == 0)
407 return NULL;
408
409 tagEntryInfo e;
410 char *buf = eStrndup (str, i);
411
412 initTagEntry (&e, buf, kind);
413 if (parentCorkIndex != CORK_NIL)
414 e.extensionFields.scopeIndex = parentCorkIndex;
415
416 int corkIndex = makeTagEntry (&e);
417 if (kind == K_WRAPPER)
418 attachParserFieldToCorkEntry (corkIndex, MooseFields[F_WRAPPING].ftype,
419 WrappingStrings [wrapping]);
420 eFree (buf);
421
422 return str[i] == '\0'? NULL: str + i;
423 }
424
solveKindAndWrapping(const char * str,int * kind,enum Wrapping * wrapping)425 static void solveKindAndWrapping (const char *str, int *kind, enum Wrapping *wrapping)
426 {
427 *kind = K_WRAPPER;
428 *wrapping = W_UNKNOWN;
429 switch (str[0])
430 {
431 case 'h': /* has */
432 *kind = K_ATTR;
433 break;
434 case 'a': /* around or after */
435 if (str[1] == 'r')
436 *wrapping = W_AROUND;
437 else if (str[1] == 'f')
438 *wrapping = W_AFTER;
439 break;
440 case 'b': /* before */
441 *wrapping = W_BEFORE;
442 break;
443 case 'o': /* override */
444 *kind = K_METHOD;
445 }
446 }
447
findAttributeOrWrapperOne(const char * line,const regexMatch * matches,unsigned int count,void * data)448 static bool findAttributeOrWrapperOne (const char *line,
449 const regexMatch *matches,
450 unsigned int count,
451 void *data)
452 {
453 struct mooseSubparser *moose = data;
454 int kind;
455 enum Wrapping wrapping;
456
457 if (moose->inPod)
458 return true;
459
460 moose->notContinuousExtendsLines = true;
461
462 if (count < 3)
463 return true;
464
465 solveKindAndWrapping (line + matches[1].start, &kind, &wrapping);
466 parseAttributeOrWrapper (line + matches[2].start, moose->classCork, -1,
467 kind, wrapping);
468 return true;
469 }
470
findAttributeOrWrapperMulti(const char * line,const regexMatch * matches,unsigned int count,void * data)471 static bool findAttributeOrWrapperMulti (const char *line,
472 const regexMatch *matches,
473 unsigned int count,
474 void *data)
475 {
476
477 struct mooseSubparser *moose = data;
478 int kind;
479 enum Wrapping wrapping;
480 int terminator;
481
482 if (moose->inPod)
483 return true;
484
485 moose->notContinuousExtendsLines = true;
486
487 if (count < 4)
488 return true;
489
490 solveKindAndWrapping (line + matches[1].start, &kind, &wrapping);
491 terminator = line[matches[2].start];
492
493
494 const char *str = line + matches[3].start;
495 while ((str = parseAttributeOrWrapper (str, moose->classCork, terminator,
496 kind, wrapping)))
497 {
498 int i;
499 for (i = 0; (str[i] == ' ') || (str[i] == '\t'); i++)
500 ;
501 if (str[i] == '\0' || str[i] == terminator)
502 break;
503 str = str + i;
504 }
505
506 return true;
507 }
508
findMooseTags(void)509 static void findMooseTags (void)
510 {
511 scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
512 }
513
initializeMooseParser(langType language)514 static void initializeMooseParser (langType language)
515 {
516 addLanguageCallbackRegex (language, "^[ \t]*(extends|with) *(.+)",
517 "{exclusive}",
518 findExtendsClass, &mooseSubparser.notInMoose,
519 &mooseSubparser);
520 addLanguageCallbackRegex (language, "^[ \t]*(has|after|before|around|override) +"
521 /* foo => ()
522 * 'foo' => ()
523 * "foo" => ()
524 * ( foo => ())
525 * ( "foo" => ())
526 * ( 'foo' => ()) */
527 "\\(?[ \t]*[\"']?"
528 ""
529 "([a-zA-Z_][a-zA-Z0-9_]*([ \t][a-zA-Z_][a-zA-Z0-9_]*)*).*=>",
530 "{exclusive}",
531 findAttributeOrWrapperOne, &mooseSubparser.notInMoose,
532 &mooseSubparser);
533 addLanguageCallbackRegex (language, "^[ \t]*(has|after|before|around) +\\(?\\[qw([^ \t])[ \t]*"
534 /* [qw/foo bar/] => ()
535 * ([qw/foo bar/] => ()) */
536 "([a-zA-Z_][a-zA-Z0-9_]*([ \t][a-zA-Z_][a-zA-Z0-9_]*)*).*=>",
537 "{exclusive}",
538 findAttributeOrWrapperMulti, &mooseSubparser.notInMoose,
539 &mooseSubparser);
540 addLanguageCallbackRegex (language, "^[ \t]*(has|after|before|around|override) +"
541 "([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*\\(",
542 "{exclusive}",
543 findAttributeOrWrapperOne, &mooseSubparser.notInFunctionParametersModifiers,
544 &mooseSubparser);
545 addLanguageCallbackRegex (language, "^[ \t]*(.+)",
546 "{exclusive}",
547 findExtendsClassContinuation, &mooseSubparser.notContinuousExtendsLines,
548 &mooseSubparser);
549 }
550
MooseParser(void)551 extern parserDefinition* MooseParser (void)
552 {
553 parserDefinition* const def = parserNew("Moose");
554
555 static parserDependency dependencies [] = {
556 [0] = { DEPTYPE_SUBPARSER, "Perl", &mooseSubparser },
557 };
558
559 def->dependencies = dependencies;
560 def->dependencyCount = ARRAY_SIZE (dependencies);
561
562 def->kindTable = MooseKinds;
563 def->kindCount = ARRAY_SIZE(MooseKinds);
564
565 def->fieldTable = MooseFields;
566 def->fieldCount = ARRAY_SIZE (MooseFields);
567
568 def->initialize = initializeMooseParser;
569 def->parser = findMooseTags;
570 def->useCork = CORK_QUEUE;
571
572 return def;
573 }
574