20
20
#include "options.h"
22
24
#include "routines.h"
29
K_CLASS, K_FUNCTION, K_MEMBER
32
static kindOption PythonKinds[] = {
33
{TRUE, 'c', "class", "classes"},
34
{TRUE, 'f', "function", "functions"},
35
{TRUE, 'm', "member", "class members"}
38
30
typedef struct NestingLevel NestingLevel;
39
31
typedef struct NestingLevels NestingLevels;
48
40
struct NestingLevels
50
42
NestingLevel *levels;
43
int n; /* number of levels in use */
48
K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE, K_IMPORT
54
static kindOption PythonKinds[] = {
55
{TRUE, 'c', "class", "classes"},
56
{TRUE, 'f', "function", "functions"},
57
{TRUE, 'm', "member", "class members"},
58
{TRUE, 'v', "variable", "variables"},
59
{TRUE, 'i', "namespace", "imports"}
62
static char const * const singletriple = "'''";
63
static char const * const doubletriple = "\"\"\"";
56
66
* FUNCTION DEFINITIONS
59
#define vStringLast(vs) ((vs)->buffer[(vs)->length - 1])
69
static NestingLevels *nestingLevelsNew (void)
71
NestingLevels *nls = xCalloc (1, NestingLevels);
75
static void nestingLevelsFree (NestingLevels *nls)
78
for (i = 0; i < nls->allocated; i++)
79
vStringDelete(nls->levels[i].name);
80
if (nls->levels) eFree(nls->levels);
84
static void nestingLevelsPush (NestingLevels *nls,
85
const vString *name, int type)
87
NestingLevel *nl = NULL;
89
if (nls->n >= nls->allocated)
92
nls->levels = xRealloc(nls->levels,
93
nls->allocated, NestingLevel);
94
nls->levels[nls->n].name = vStringNew();
96
nl = &nls->levels[nls->n];
99
vStringCopy(nl->name, name);
104
static NestingLevel *nestingLevelsGetCurrent (NestingLevels *nls)
106
Assert (nls != NULL);
111
return &nls->levels[nls->n - 1];
114
static void nestingLevelsPop (NestingLevels *nls)
116
const NestingLevel *nl = nestingLevelsGetCurrent(nls);
119
vStringClear(nl->name);
61
124
static boolean isIdentifierFirstCharacter (int c)
72
135
* extract all relevant information and create a tag.
74
137
static void makeFunctionTag (vString *const function,
75
vString *const parent, int is_class_parent)
138
vString *const parent, int is_class_parent, const char *arglist __unused__)
78
141
initTagEntry (&tag, vStringValue (function));
80
143
tag.kindName = "function";
145
/* tag.extensionFields.arglist = arglist; */
83
147
if (vStringLength (parent) > 0)
243
323
vStringDelete (inheritance);
326
static void parseImports (const char *cp)
329
vString *name, *name_next;
331
cp = skipEverything (cp);
333
if ((pos = strstr (cp, "import")) == NULL)
338
/* continue only if there is some space between the keyword and the identifier */
345
name = vStringNew ();
346
name_next = vStringNew ();
348
cp = skipEverything (cp);
351
cp = parseIdentifier (cp, name);
353
cp = skipEverything (cp);
354
/* we parse the next possible import statement as well to be able to ignore 'foo' in
355
* 'import foo as bar' */
356
parseIdentifier (cp, name_next);
358
/* take the current tag only if the next one is not "as" */
359
if (strcmp (vStringValue (name_next), "as") != 0 &&
360
strcmp (vStringValue (name), "as") != 0)
362
makeSimpleTag (name, PythonKinds, K_IMPORT);
365
vStringDelete (name);
366
vStringDelete (name_next);
369
/* modified from get.c getArglistFromStr().
370
* warning: terminates rest of string past arglist!
371
* note: does not ignore brackets inside strings! */
372
static char *parseArglist(const char *buf)
378
if (NULL == (start = strchr(buf, '(')))
380
for (level = 1, end = start + 1; level > 0; ++end)
384
else if ('(' == *end)
386
else if (')' == *end)
390
return strdup(start);
246
393
static void parseFunction (const char *cp, vString *const def,
247
394
vString *const parent, int is_class_parent)
249
398
cp = parseIdentifier (cp, def);
250
makeFunctionTag (def, parent, is_class_parent);
399
arglist = parseArglist (cp);
400
makeFunctionTag (def, parent, is_class_parent, arglist);
253
404
/* Get the combined name of a nested symbol. Classes are separated with ".",
429
vStringCatS(result, "."); /* make Geany symbol list grouping work properly */
431
if (prev->type == K_CLASS)
279
432
vStringCatS(result, ".");
281
434
vStringCatS(result, "/");
283
437
vStringCat(result, nl->name);
284
is_class = nl->is_class;
438
is_class = (nl->type == K_CLASS);
290
static NestingLevels *newNestingLevels(void)
292
NestingLevels *nls = xCalloc (1, NestingLevels);
296
static void freeNestingLevels(NestingLevels *nls)
444
/* Check whether parent's indentation level is higher than the current level and
447
static void checkParent(NestingLevels *nls, int indent, vString *parent)
299
for (i = 0; i < nls->allocated; i++)
300
vStringDelete(nls->levels[i].name);
301
if (nls->levels) eFree(nls->levels);
452
for (i = 0; i < nls->n; i++)
455
/* is there a better way to compare two vStrings? */
456
if (strcmp(vStringValue(parent), vStringValue(n->name)) == 0)
458
if (n && indent <= n->indentation)
460
/* remove this level by clearing its name */
461
vStringClear(n->name);
305
/* TODO: This is totally out of place in python.c, but strlist.h is not usable.
306
* Maybe should just move these three functions to a separate file, even if no
307
* other parser uses them.
309
468
static void addNestingLevel(NestingLevels *nls, int indentation,
310
vString *name, boolean is_class)
469
const vString *name, boolean is_class)
313
472
NestingLevel *nl = NULL;
322
if (i >= nls->allocated)
325
nls->levels = xRealloc(nls->levels,
326
nls->allocated, NestingLevel);
327
nls->levels[i].name = vStringNew();
481
nestingLevelsPush(nls, name, 0);
329
482
nl = nls->levels + i;
333
vStringCopy(nl->name, name);
485
{ /* reuse existing slot */
487
vStringCopy(nl->name, name);
334
489
nl->indentation = indentation;
335
nl->is_class = is_class;
490
nl->type = is_class ? K_CLASS : !K_CLASS;
493
/* Return a pointer to the start of the next triple string, or NULL. Store
494
* the kind of triple string in "which" if the return is not NULL.
496
static char const *find_triple_start(char const *string, char const **which)
498
char const *cp = string;
502
if (*cp == '"' || *cp == '\'')
504
if (strncmp(cp, doubletriple, 3) == 0)
506
*which = doubletriple;
509
if (strncmp(cp, singletriple, 3) == 0)
511
*which = singletriple;
521
/* Find the end of a triple string as pointed to by "which", and update "which"
522
* with any other triple strings following in the given string.
524
static void find_triple_end(char const *string, char const **which)
526
char const *s = string;
529
/* Check if the string ends in the same line. */
530
s = strstr (s, *which);
534
/* If yes, check if another one starts in the same line. */
535
s = find_triple_start(s, which);
541
static const char *findVariable(const char *line)
543
/* Parse global and class variable names (C.x) from assignment statements.
544
* Object attributes (obj.x) are ignored.
545
* Assignment to a tuple 'x, y = 2, 3' not supported.
546
* TODO: ignore duplicate tags from reassignment statements. */
547
const char *cp, *sp, *eq, *start;
549
cp = strstr(line, "=");
556
return NULL; /* ignore '==' operator and 'x=5,y=6)' function lines */
557
if (*eq == '(' || *eq == '#')
558
break; /* allow 'x = func(b=2,y=2,' lines and comments at the end of line */
562
/* go backwards to the start of the line, checking we have valid chars */
564
while (start >= line && isspace ((int) *start))
566
while (start >= line && isIdentifierCharacter ((int) *start))
568
if (!isIdentifierFirstCharacter(*(start + 1)))
571
while (sp >= line && isspace ((int) *sp))
573
if ((sp + 1) != line) /* the line isn't a simple variable assignment */
575
/* the line is valid, parse the variable name */
580
/* Skip type declaration that optionally follows a cdef/cpdef */
581
static const char *skipTypeDecl (const char *cp, boolean *is_class)
583
const char *lastStart = cp, *ptr = cp;
586
if (!strncmp("extern", ptr, 6)) {
588
ptr = skipSpace(ptr);
589
if (!strncmp("from", ptr, 4)) { return NULL; }
591
if (!strncmp("class", ptr, 5)) {
594
ptr = skipSpace(ptr);
597
/* limit so that we don't pick off "int item=obj()" */
598
while (*ptr && loopCount++ < 2) {
599
while (*ptr && *ptr != '=' && *ptr != '(' && !isspace(*ptr)) ptr++;
600
if (!*ptr || *ptr == '=') return NULL;
602
return lastStart; /* if we stopped on a '(' we are done */
604
ptr = skipSpace(ptr);
606
while (*lastStart == '*') lastStart++; /* cdef int *identifier */
338
611
static void findPythonTags (void)
341
614
vString *const name = vStringNew ();
342
615
vString *const parent = vStringNew();
344
NestingLevels *const nesting_levels = newNestingLevels();
617
NestingLevels *const nesting_levels = nestingLevelsNew();
346
619
const char *line;
347
620
int line_skip = 0;
348
boolean longStringLiteral = FALSE;
621
char const *longStringLiteral = NULL;
350
623
while ((line = (const char *) fileReadLine ()) != NULL)
352
const char *cp = line;
625
const char *cp = line, *candidate;
626
char const *longstring;
627
char const *keyword, *variable;
357
630
cp = skipSpace (cp);
359
if (*cp == '#' || *cp == '\0') /* skip comment or blank line */
632
if (*cp == '\0') /* skip blank line */
635
/* Skip comment if we are not inside a multi-line string. */
636
if (*cp == '#' && !longStringLiteral)
362
639
/* Deal with line continuation. */
375
652
indent = cp - line;
655
checkParent(nesting_levels, indent, parent);
378
657
/* Deal with multiline string ending. */
379
658
if (longStringLiteral)
381
/* Note: We do ignore anything in the same line after a multiline
384
if (strstr (cp, "\"\"\""))
385
longStringLiteral = FALSE;
660
find_triple_end(cp, &longStringLiteral);
389
664
/* Deal with multiline string start. */
390
if ((longstring = strstr (cp, "\"\"\"")) != NULL)
665
longstring = find_triple_start(cp, &longStringLiteral);
392
/* Note: For our purposes, the line just ends at the first long
397
longStringLiteral = TRUE;
398
while ((longstring = strstr (longstring, "\"\"\"")) != NULL)
401
longStringLiteral = !longStringLiteral;
669
find_triple_end(longstring, &longStringLiteral);
670
/* We don't parse for any tags in the rest of the line. */
405
674
/* Deal with def and class keywords. */
435
725
addNestingLevel(nesting_levels, indent, name, is_class);
728
/* Find global and class variables */
729
variable = findVariable(line);
732
const char *start = variable;
733
boolean parent_is_class;
736
while (isIdentifierCharacter ((int) *start))
738
vStringPut (name, (int) *start);
741
vStringTerminate (name);
743
parent_is_class = constructParentString(nesting_levels, indent, parent);
744
/* skip variables in methods */
745
if (! parent_is_class && vStringLength(parent) > 0)
748
makeVariableTag (name, parent);
750
/* Find and parse imports */
439
753
/* Clean up all memory we allocated. */
440
754
vStringDelete (parent);
441
755
vStringDelete (name);
442
756
vStringDelete (continuation);
443
freeNestingLevels (nesting_levels);
757
nestingLevelsFree (nesting_levels);
446
760
extern parserDefinition *PythonParser (void)
448
static const char *const extensions[] = { "py", "pyx", "pxd", "scons", NULL };
762
static const char *const extensions[] = { "py", "pyx", "pxd", "pxi" ,"scons", NULL };
449
763
parserDefinition *def = parserNew ("Python");
450
764
def->kinds = PythonKinds;
451
765
def->kindCount = KIND_COUNT (PythonKinds);