53
72
return (boolean) (isalnum (c) || c == '_');
57
/* remove all previous classes with more indent than the current one */
58
static GList *clean_class_list(GList *list, gint indent)
62
tmp = g_list_first(list);
65
if (((lastClass*)tmp->data)->indent >= indent)
67
g_free(((lastClass*)tmp->data)->name);
71
list = g_list_remove(list, tmp->data);
75
/* Given a string with the contents of a line directly after the "def" keyword,
76
* extract all relevant information and create a tag.
78
static void makeFunctionTag (vString *const function,
79
vString *const parent, int is_class_parent)
82
initTagEntry (&tag, vStringValue (function));
84
tag.kindName = "function";
87
if (vStringLength (parent) > 0)
91
tag.kindName = "member";
93
tag.extensionFields.scope [0] = "class";
94
tag.extensionFields.scope [1] = vStringValue (parent);
98
tag.extensionFields.scope [0] = "function";
99
tag.extensionFields.scope [1] = vStringValue (parent);
103
/* If a function starts with __, we mark it as file scope.
104
* FIXME: What is the proper way to signal such attributes?
105
* TODO: What does functions/classes starting with _ and __ mean in python?
107
if (strncmp (vStringValue (function), "__", 2) == 0 &&
108
strcmp (vStringValue (function), "__init__") != 0)
110
tag.extensionFields.access = "private";
111
tag.isFileScope = TRUE;
115
tag.extensionFields.access = "public";
120
/* Given a string with the contents of the line directly after the "class"
121
* keyword, extract all necessary information and create a tag.
123
static void makeClassTag (vString *const class, vString *const inheritance,
124
vString *const parent, int is_class_parent)
127
initTagEntry (&tag, vStringValue (class));
128
tag.kindName = "class";
130
if (vStringLength (parent) > 0)
134
tag.extensionFields.scope [0] = "class";
135
tag.extensionFields.scope [1] = vStringValue (parent);
139
tag.extensionFields.scope [0] = "function";
140
tag.extensionFields.scope [1] = vStringValue (parent);
143
tag.extensionFields.inheritance = vStringValue (inheritance);
147
static void makeVariableTag (vString *const var, vString *const parent)
150
initTagEntry (&tag, vStringValue (var));
151
tag.kindName = "variable";
153
if (vStringLength (parent) > 0)
155
tag.extensionFields.scope [0] = "class";
156
tag.extensionFields.scope [1] = vStringValue (parent);
161
/* Skip a single or double quoted string. */
162
static const char *skipString (const char *cp)
164
const char *start = cp;
166
for (cp++; *cp; cp++)
170
else if (*cp == '\\')
172
else if (*cp == *start)
178
/* Skip everything up to an identifier start. */
179
static const char *skipEverything (const char *cp)
183
if (*cp == '"' || *cp == '\'')
188
if (isIdentifierFirstCharacter ((int) *cp))
194
/* Skip an identifier. */
195
static const char *skipIdentifier (const char *cp)
197
while (isIdentifierCharacter ((int) *cp))
202
static const char *findDefinitionOrClass (const char *cp)
206
cp = skipEverything (cp);
207
if (!strncmp(cp, "def", 3) || !strncmp(cp, "class", 5))
211
cp = skipIdentifier (cp);
216
static const char *skipSpace (const char *cp)
218
while (isspace ((int) *cp))
223
/* Starting at ''cp'', parse an identifier into ''identifier''. */
224
static const char *parseIdentifier (const char *cp, vString *const identifier)
226
vStringClear (identifier);
227
while (isIdentifierCharacter ((int) *cp))
229
vStringPut (identifier, (int) *cp);
232
vStringTerminate (identifier);
236
static void parseClass (const char *cp, vString *const class,
237
vString *const parent, int is_class_parent)
239
vString *const inheritance = vStringNew ();
240
vStringClear (inheritance);
241
cp = parseIdentifier (cp, class);
250
/* Closing parenthesis can be in follow up line. */
251
cp = (const char *) fileReadLine ();
253
vStringPut (inheritance, ' ');
256
vStringPut (inheritance, *cp);
259
vStringTerminate (inheritance);
261
makeClassTag (class, inheritance, parent, is_class_parent);
262
vStringDelete (inheritance);
265
static void parseFunction (const char *cp, vString *const def,
266
vString *const parent, int is_class_parent)
268
cp = parseIdentifier (cp, def);
269
makeFunctionTag (def, parent, is_class_parent);
272
/* Get the combined name of a nested symbol. Classes are separated with ".",
273
* functions with "/". For example this code:
280
* Would produce this string:
281
* MyClass.MyFunction/SubFunction/SubClass.Method
283
static boolean constructParentString(NestingLevels *nls, int indent,
287
NestingLevel *prev = NULL;
288
int is_class = FALSE;
289
vStringClear (result);
290
for (i = 0; i < nls->n; i++)
292
NestingLevel *nl = nls->levels + i;
293
if (indent <= nl->indentation)
298
vStringCatS(result, ".");
300
vStringCatS(result, "/");
302
vStringCat(result, nl->name);
303
is_class = nl->is_class;
309
/* Check whether parent's indentation level is higher than the current level and
312
static void checkParent(NestingLevels *nls, int indent, vString *parent)
317
for (i = 0; i < nls->n; i++)
320
/* is there a better way to compare two vStrings? */
321
if (strcmp(vStringValue(parent), vStringValue(n->name)) == 0)
323
if (n && indent <= n->indentation)
325
/* remove this level by clearing its name */
326
vStringClear(n->name);
333
static NestingLevels *newNestingLevels(void)
335
NestingLevels *nls = xCalloc (1, NestingLevels);
339
static void freeNestingLevels(NestingLevels *nls)
342
for (i = 0; i < nls->allocated; i++)
343
vStringDelete(nls->levels[i].name);
344
if (nls->levels) eFree(nls->levels);
348
/* TODO: This is totally out of place in python.c, but strlist.h is not usable.
349
* Maybe should just move these three functions to a separate file, even if no
350
* other parser uses them.
352
static void addNestingLevel(NestingLevels *nls, int indentation,
353
vString *name, boolean is_class)
356
NestingLevel *nl = NULL;
358
for (i = 0; i < nls->n; i++)
360
nl = nls->levels + i;
361
if (indentation <= nl->indentation) break;
365
if (i >= nls->allocated)
368
nls->levels = xRealloc(nls->levels,
369
nls->allocated, NestingLevel);
370
nls->levels[i].name = vStringNew();
372
nl = nls->levels + i;
376
vStringCopy(nl->name, name);
377
nl->indentation = indentation;
378
nl->is_class = is_class;
381
/* Return a pointer to the start of the next triple string, or NULL. Store
382
* the kind of triple string in "which" if the return is not NULL.
384
static char const *find_triple_start(char const *string, char const **which)
386
char const *cp = string;
390
if (*cp == '"' || *cp == '\'')
392
if (strncmp(cp, doubletriple, 3) == 0)
394
*which = doubletriple;
397
if (strncmp(cp, singletriple, 3) == 0)
399
*which = singletriple;
409
/* Find the end of a triple string as pointed to by "which", and update "which"
410
* with any other triple strings following in the given string.
412
static void find_triple_end(char const *string, char const **which)
414
char const *s = string;
417
/* Check if the string ends in the same line. */
418
s = strstr (s, *which);
422
/* If yes, check if another one starts in the same line. */
423
s = find_triple_start(s, which);
429
static const char *findVariable(const char *line)
431
/* Parse global and class variable names (C.x) from assignment statements.
432
* Object attributes (obj.x) are ignored.
433
* Assignment to a tuple 'x, y = 2, 3' not supported.
434
* TODO: ignore duplicate tags from reassignment statements. */
435
const char *cp, *sp, *eq, *start;
437
cp = strstr(line, "=");
444
return NULL; /* ignore '==' operator and 'x=5,y=6)' function lines */
446
break; /* allow 'x = func(b=2,y=2,' lines */
450
/* go backwards to the start of the line, checking we have valid chars */
452
while (start >= line && isspace ((int) *start))
454
while (start >= line && isIdentifierCharacter ((int) *start))
456
if (!isIdentifierFirstCharacter(*(start + 1)))
459
while (sp >= line && isspace ((int) *sp))
461
if ((sp + 1) != line) /* the line isn't a simple variable assignment */
463
/* the line is valid, parse the variable name */
84
468
static void findPythonTags (void)
86
GList *parents = NULL, *tmp; /* list of classes which are around the token */
87
vString *name = vStringNew ();
89
const unsigned char *line;
90
boolean inMultilineString = FALSE;
91
boolean wasInMultilineString = FALSE;
92
lastClass *lastclass = NULL;
93
boolean inFunction = FALSE;
96
while ((line = fileReadLine ()) != NULL)
98
const unsigned char *cp = line;
470
vString *const continuation = vStringNew ();
471
vString *const name = vStringNew ();
472
vString *const parent = vStringNew();
474
NestingLevels *const nesting_levels = newNestingLevels();
478
char const *longStringLiteral = NULL;
480
while ((line = (const char *) fileReadLine ()) != NULL)
103
strncmp ((const char*) cp, "\"\"\"", (size_t) 3) == 0)
105
inMultilineString = (boolean) !inMultilineString;
106
if (! inMultilineString)
107
wasInMultilineString = TRUE;
111
strncmp ((const char*) cp, "'''", (size_t) 3) == 0)
113
inMultilineString = (boolean) !inMultilineString;
114
if (! inMultilineString)
115
wasInMultilineString = TRUE;
119
if (*cp == '\0' || wasInMultilineString)
121
wasInMultilineString = FALSE;
122
break; /* at end of multiline string */
125
/* update indent-sensitive things */
126
if (!inMultilineString && !isspace(*cp))
130
if (indent < fn_indent)
133
if (lastclass != NULL)
135
if (indent <= lastclass->indent)
139
parents = clean_class_list(parents, indent);
140
last = g_list_last(parents);
142
lastclass = last->data;
149
if (inMultilineString)
151
else if (isspace ((int) *cp))
153
/* count indentation amount of current line
154
* the indentation has to be made with tabs only _or_ spaces only, if they are mixed
155
* the code below gets confused */
162
} while (isspace(*cp));
165
cp++; /* non-indent whitespace */
169
else if (strncmp ((const char*) cp, "class", (size_t) 5) == 0)
172
if (isspace ((int) *cp))
174
lastClass *newclass = g_new(lastClass, 1);
176
while (isspace ((int) *cp))
178
while (isalnum ((int) *cp) || *cp == '_')
180
vStringPut (name, (int) *cp);
183
vStringTerminate (name);
185
newclass->name = g_strdup(vStringValue(name));
186
newclass->indent = indent;
187
parents = g_list_append(parents, newclass);
188
if (lastclass == NULL)
189
makeSimpleTag (name, PythonKinds, K_CLASS);
482
const char *cp = line;
483
char const *longstring;
484
char const *keyword, *variable;
489
if (*cp == '\0') /* skip blank line */
492
/* Skip comment if we are not inside a multi-line string. */
493
if (*cp == '#' && !longStringLiteral)
496
/* Deal with line continuation. */
497
if (!line_skip) vStringClear(continuation);
498
vStringCatS(continuation, line);
499
vStringStripTrailing(continuation);
500
if (vStringLast(continuation) == '\\')
502
vStringChop(continuation);
503
vStringCatS(continuation, " ");
507
cp = line = vStringValue(continuation);
512
checkParent(nesting_levels, indent, parent);
514
/* Deal with multiline string ending. */
515
if (longStringLiteral)
517
find_triple_end(cp, &longStringLiteral);
521
/* Deal with multiline string start. */
522
longstring = find_triple_start(cp, &longStringLiteral);
526
find_triple_end(longstring, &longStringLiteral);
527
/* We don't parse for any tags in the rest of the line. */
531
/* Deal with def and class keywords. */
532
keyword = findDefinitionOrClass (cp);
535
boolean found = FALSE;
536
boolean is_class = FALSE;
537
if (!strncmp (keyword, "def ", 4))
539
cp = skipSpace (keyword + 3);
542
else if (!strncmp (keyword, "class ", 6))
544
cp = skipSpace (keyword + 5);
551
boolean is_parent_class;
554
constructParentString(nesting_levels, indent, parent);
557
parseClass (cp, name, parent, is_parent_class);
191
makeSimpleScopedTag (name, PythonKinds, K_CLASS,
192
PythonKinds[K_CLASS].name, lastclass->name, "public");
559
parseFunction(cp, name, parent, is_parent_class);
195
lastclass = newclass;
196
break; /* ignore rest of line so that lastclass is not reset immediately */
561
addNestingLevel(nesting_levels, indent, name, is_class);
199
else if (strncmp ((const char*) cp, "def", (size_t) 3) == 0)
202
if (isspace ((int) *cp))
204
while (isspace ((int) *cp))
206
while (isalnum ((int) *cp) || *cp == '_')
208
vStringPut (name, (int) *cp);
211
vStringTerminate (name);
212
if (!isspace(*line) || lastclass == NULL || strlen(lastclass->name) <= 0)
213
makeSimpleTag (name, PythonKinds, K_FUNCTION);
215
makeSimpleScopedTag (name, PythonKinds, K_METHOD,
216
PythonKinds[K_CLASS].name, lastclass->name, "public");
220
fn_indent = indent + 1;
221
break; /* ignore rest of line so inFunction is not cancelled immediately */
224
else if (!inFunction && *(const char*)cp == '=')
564
/* Find global and class variables */
565
variable = findVariable(line);
226
/* Parse global and class variable names (C.x) from assignment statements.
227
* Object attributes (obj.x) are ignored.
228
* Assignment to a tuple 'x, y = 2, 3' not supported.
229
* TODO: ignore duplicate tags from reassignment statements. */
230
const guchar *sp, *eq, *start;
568
const char *start = variable;
569
boolean parent_is_class;
236
goto skipvar; /* ignore '==' operator and 'x=5,y=6)' function lines */
238
break; /* allow 'x = func(b=2,y=2,' lines */
241
/* go backwards to the start of the line, checking we have valid chars */
243
while (start >= line && isspace ((int) *start))
245
while (start >= line && isIdentifierCharacter ((int) *start))
247
if (!isIdentifierFirstCharacter(*(start + 1)))
250
while (sp >= line && isspace ((int) *sp))
252
if ((sp + 1) != line) /* the line isn't a simple variable assignment */
254
/* the line is valid, parse the variable name */
256
572
while (isIdentifierCharacter ((int) *start))
258
574
vStringPut (name, (int) *start);