~ubuntu-branches/ubuntu/raring/geany/raring-proposed

« back to all changes in this revision

Viewing changes to tagmanager/python.c

  • Committer: Bazaar Package Importer
  • Author(s): Damián Viano
  • Date: 2009-05-29 21:22:54 UTC
  • mfrom: (1.3.1 upstream) (3.1.2 experimental)
  • mto: This revision was merged to the branch mainline in revision 20.
  • Revision ID: james.westby@ubuntu.com-20090529212254-cx6t2a7itgii3p5f
Tags: 0.17-1
* New upstream release
* 20_add_debdiff_as_diff_type.dpatch renamed as
  20_add_debian_specific_filetypes.dpatch, 
  - add .dpatch as a diff filetype
  - add control as a conf filetype
* 20_change_gpl_location.dpatch freshen
* add 20_debian_control_tags.dpatch better handling of debian/control files.
  Thanks to Enrico Tröger. (Closes: #520776)
* debian/copyright Add Frank Lanitz, update years

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
 
2
*   $Id: python.c 2694 2008-06-15 17:50:07Z eht16 $
2
3
*
3
 
*   Copyright (c) 2000-2001, Darren Hiebert
 
4
*   Copyright (c) 2000-2003, Darren Hiebert
4
5
*
5
6
*   This source code is released for free distribution under the terms of the
6
7
*   GNU General Public License.
8
9
*   This module contains functions for generating tags for Python language
9
10
*   files.
10
11
*/
11
 
 
12
12
/*
13
13
*   INCLUDE FILES
14
14
*/
15
 
#include "general.h"    /* must always come first */
16
 
#include <glib.h>
 
15
#include "general.h"  /* must always come first */
 
16
 
17
17
#include <string.h>
18
18
 
19
 
#include "parse.h"
 
19
#include "entry.h"
 
20
#include "options.h"
20
21
#include "read.h"
 
22
#include "main.h"
21
23
#include "vstring.h"
22
24
 
23
25
/*
24
26
*   DATA DEFINITIONS
25
27
*/
26
28
typedef enum {
27
 
    K_CLASS, K_FUNCTION, K_METHOD, K_VARIABLE
 
29
        K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE
28
30
} pythonKind;
29
31
 
30
 
static kindOption PythonKinds [] = {
31
 
    { TRUE, 'c', "class",    "classes" },
32
 
    { TRUE, 'f', "function", "functions" },
33
 
    { TRUE, 'm', "member", "methods" },
34
 
    { TRUE, 'v', "variable", "variables" }
35
 
};
36
 
 
37
 
typedef struct _lastClass {
38
 
        gchar *name;
39
 
        gint indent;
40
 
} lastClass;
 
32
static kindOption PythonKinds[] = {
 
33
        {TRUE, 'c', "class",    "classes"},
 
34
        {TRUE, 'f', "function", "functions"},
 
35
        {TRUE, 'm', "member",   "class members"},
 
36
    {TRUE, 'v', "variable", "variables"}
 
37
};
 
38
 
 
39
typedef struct NestingLevel NestingLevel;
 
40
typedef struct NestingLevels NestingLevels;
 
41
 
 
42
struct NestingLevel
 
43
{
 
44
        int indentation;
 
45
        vString *name;
 
46
        boolean is_class;
 
47
};
 
48
 
 
49
struct NestingLevels
 
50
{
 
51
        NestingLevel *levels;
 
52
        int n;
 
53
        int allocated;
 
54
};
 
55
 
 
56
static char const * const singletriple = "'''";
 
57
static char const * const doubletriple = "\"\"\"";
41
58
 
42
59
/*
43
60
*   FUNCTION DEFINITIONS
44
61
*/
45
62
 
 
63
#define vStringLast(vs) ((vs)->buffer[(vs)->length - 1])
 
64
 
46
65
static boolean isIdentifierFirstCharacter (int c)
47
66
{
48
67
        return (boolean) (isalpha (c) || c == '_');
53
72
        return (boolean) (isalnum (c) || c == '_');
54
73
}
55
74
 
56
 
 
57
 
/* remove all previous classes with more indent than the current one */
58
 
static GList *clean_class_list(GList *list, gint indent)
59
 
{
60
 
        GList *tmp, *tmp2;
61
 
 
62
 
        tmp = g_list_first(list);
63
 
        while (tmp != NULL)
64
 
        {
65
 
                if (((lastClass*)tmp->data)->indent >= indent)
66
 
                {
67
 
                        g_free(((lastClass*)tmp->data)->name);
68
 
                        g_free(tmp->data);
69
 
                        tmp2 = tmp->next;
70
 
 
71
 
                        list = g_list_remove(list, tmp->data);
72
 
                        tmp = tmp2;
73
 
                }
74
 
                else
75
 
                {
76
 
                        tmp = tmp->next;
77
 
                }
78
 
        }
79
 
 
80
 
        return list;
81
 
}
82
 
 
 
75
/* Given a string with the contents of a line directly after the "def" keyword,
 
76
 * extract all relevant information and create a tag.
 
77
 */
 
78
static void makeFunctionTag (vString *const function,
 
79
        vString *const parent, int is_class_parent)
 
80
{
 
81
        tagEntryInfo tag;
 
82
        initTagEntry (&tag, vStringValue (function));
 
83
 
 
84
        tag.kindName = "function";
 
85
        tag.kind = 'f';
 
86
 
 
87
        if (vStringLength (parent) > 0)
 
88
        {
 
89
                if (is_class_parent)
 
90
                {
 
91
                        tag.kindName = "member";
 
92
                        tag.kind = 'm';
 
93
                        tag.extensionFields.scope [0] = "class";
 
94
                        tag.extensionFields.scope [1] = vStringValue (parent);
 
95
                }
 
96
                else
 
97
                {
 
98
                        tag.extensionFields.scope [0] = "function";
 
99
                        tag.extensionFields.scope [1] = vStringValue (parent);
 
100
                }
 
101
        }
 
102
 
 
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?
 
106
         */
 
107
        if (strncmp (vStringValue (function), "__", 2) == 0 &&
 
108
                strcmp (vStringValue (function), "__init__") != 0)
 
109
        {
 
110
                tag.extensionFields.access = "private";
 
111
                tag.isFileScope = TRUE;
 
112
        }
 
113
        else
 
114
        {
 
115
                tag.extensionFields.access = "public";
 
116
        }
 
117
        makeTagEntry (&tag);
 
118
}
 
119
 
 
120
/* Given a string with the contents of the line directly after the "class"
 
121
 * keyword, extract all necessary information and create a tag.
 
122
 */
 
123
static void makeClassTag (vString *const class, vString *const inheritance,
 
124
        vString *const parent, int is_class_parent)
 
125
{
 
126
        tagEntryInfo tag;
 
127
        initTagEntry (&tag, vStringValue (class));
 
128
        tag.kindName = "class";
 
129
        tag.kind = 'c';
 
130
        if (vStringLength (parent) > 0)
 
131
        {
 
132
                if (is_class_parent)
 
133
                {
 
134
                        tag.extensionFields.scope [0] = "class";
 
135
                        tag.extensionFields.scope [1] = vStringValue (parent);
 
136
                }
 
137
                else
 
138
                {
 
139
                        tag.extensionFields.scope [0] = "function";
 
140
                        tag.extensionFields.scope [1] = vStringValue (parent);
 
141
                }
 
142
        }
 
143
        tag.extensionFields.inheritance = vStringValue (inheritance);
 
144
        makeTagEntry (&tag);
 
145
}
 
146
 
 
147
static void makeVariableTag (vString *const var, vString *const parent)
 
148
{
 
149
        tagEntryInfo tag;
 
150
        initTagEntry (&tag, vStringValue (var));
 
151
        tag.kindName = "variable";
 
152
        tag.kind = 'v';
 
153
        if (vStringLength (parent) > 0)
 
154
        {
 
155
                tag.extensionFields.scope [0] = "class";
 
156
                tag.extensionFields.scope [1] = vStringValue (parent);
 
157
        }
 
158
        makeTagEntry (&tag);
 
159
}
 
160
 
 
161
/* Skip a single or double quoted string. */
 
162
static const char *skipString (const char *cp)
 
163
{
 
164
        const char *start = cp;
 
165
        int escaped = 0;
 
166
        for (cp++; *cp; cp++)
 
167
        {
 
168
                if (escaped)
 
169
                        escaped--;
 
170
                else if (*cp == '\\')
 
171
                        escaped++;
 
172
                else if (*cp == *start)
 
173
                        return cp + 1;
 
174
        }
 
175
        return cp;
 
176
}
 
177
 
 
178
/* Skip everything up to an identifier start. */
 
179
static const char *skipEverything (const char *cp)
 
180
{
 
181
        for (; *cp; cp++)
 
182
        {
 
183
                if (*cp == '"' || *cp == '\'')
 
184
                {
 
185
                        cp = skipString(cp);
 
186
                        if (!*cp) break;
 
187
                }
 
188
                if (isIdentifierFirstCharacter ((int) *cp))
 
189
                        return cp;
 
190
        }
 
191
        return cp;
 
192
}
 
193
 
 
194
/* Skip an identifier. */
 
195
static const char *skipIdentifier (const char *cp)
 
196
{
 
197
        while (isIdentifierCharacter ((int) *cp))
 
198
                cp++;
 
199
        return cp;
 
200
}
 
201
 
 
202
static const char *findDefinitionOrClass (const char *cp)
 
203
{
 
204
        while (*cp)
 
205
        {
 
206
                cp = skipEverything (cp);
 
207
                if (!strncmp(cp, "def", 3) || !strncmp(cp, "class", 5))
 
208
                {
 
209
                        return cp;
 
210
                }
 
211
                cp = skipIdentifier (cp);
 
212
        }
 
213
        return NULL;
 
214
}
 
215
 
 
216
static const char *skipSpace (const char *cp)
 
217
{
 
218
        while (isspace ((int) *cp))
 
219
                ++cp;
 
220
        return cp;
 
221
}
 
222
 
 
223
/* Starting at ''cp'', parse an identifier into ''identifier''. */
 
224
static const char *parseIdentifier (const char *cp, vString *const identifier)
 
225
{
 
226
        vStringClear (identifier);
 
227
        while (isIdentifierCharacter ((int) *cp))
 
228
        {
 
229
                vStringPut (identifier, (int) *cp);
 
230
                ++cp;
 
231
        }
 
232
        vStringTerminate (identifier);
 
233
        return cp;
 
234
}
 
235
 
 
236
static void parseClass (const char *cp, vString *const class,
 
237
        vString *const parent, int is_class_parent)
 
238
{
 
239
        vString *const inheritance = vStringNew ();
 
240
        vStringClear (inheritance);
 
241
        cp = parseIdentifier (cp, class);
 
242
        cp = skipSpace (cp);
 
243
        if (*cp == '(')
 
244
        {
 
245
                ++cp;
 
246
                while (*cp != ')')
 
247
                {
 
248
                        if (*cp == '\0')
 
249
                        {
 
250
                                /* Closing parenthesis can be in follow up line. */
 
251
                                cp = (const char *) fileReadLine ();
 
252
                                if (!cp) break;
 
253
                                vStringPut (inheritance, ' ');
 
254
                                continue;
 
255
                        }
 
256
                        vStringPut (inheritance, *cp);
 
257
                        ++cp;
 
258
                }
 
259
                vStringTerminate (inheritance);
 
260
        }
 
261
        makeClassTag (class, inheritance, parent, is_class_parent);
 
262
        vStringDelete (inheritance);
 
263
}
 
264
 
 
265
static void parseFunction (const char *cp, vString *const def,
 
266
        vString *const parent, int is_class_parent)
 
267
{
 
268
        cp = parseIdentifier (cp, def);
 
269
        makeFunctionTag (def, parent, is_class_parent);
 
270
}
 
271
 
 
272
/* Get the combined name of a nested symbol. Classes are separated with ".",
 
273
 * functions with "/". For example this code:
 
274
 * class MyClass:
 
275
 *     def myFunction:
 
276
 *         def SubFunction:
 
277
 *             class SubClass:
 
278
 *                 def Method:
 
279
 *                     pass
 
280
 * Would produce this string:
 
281
 * MyClass.MyFunction/SubFunction/SubClass.Method
 
282
 */
 
283
static boolean constructParentString(NestingLevels *nls, int indent,
 
284
        vString *result)
 
285
{
 
286
        int i;
 
287
        NestingLevel *prev = NULL;
 
288
        int is_class = FALSE;
 
289
        vStringClear (result);
 
290
        for (i = 0; i < nls->n; i++)
 
291
        {
 
292
                NestingLevel *nl = nls->levels + i;
 
293
                if (indent <= nl->indentation)
 
294
                        break;
 
295
                if (prev)
 
296
                {
 
297
                        if (prev->is_class)
 
298
                                vStringCatS(result, ".");
 
299
                        else
 
300
                                vStringCatS(result, "/");
 
301
                }
 
302
                vStringCat(result, nl->name);
 
303
                is_class = nl->is_class;
 
304
                prev = nl;
 
305
        }
 
306
        return is_class;
 
307
}
 
308
 
 
309
/* Check whether parent's indentation level is higher than the current level and
 
310
 * if so, remove it.
 
311
 */
 
312
static void checkParent(NestingLevels *nls, int indent, vString *parent)
 
313
{
 
314
        int i;
 
315
        NestingLevel *n;
 
316
 
 
317
        for (i = 0; i < nls->n; i++)
 
318
        {
 
319
                n = nls->levels + i;
 
320
                /* is there a better way to compare two vStrings? */
 
321
                if (strcmp(vStringValue(parent), vStringValue(n->name)) == 0)
 
322
                {
 
323
                        if (n && indent <= n->indentation)
 
324
                        {
 
325
                                /* remove this level by clearing its name */
 
326
                                vStringClear(n->name);
 
327
                        }
 
328
                        break;
 
329
                }
 
330
        }
 
331
}
 
332
 
 
333
static NestingLevels *newNestingLevels(void)
 
334
{
 
335
        NestingLevels *nls = xCalloc (1, NestingLevels);
 
336
        return nls;
 
337
}
 
338
 
 
339
static void freeNestingLevels(NestingLevels *nls)
 
340
{
 
341
        int i;
 
342
        for (i = 0; i < nls->allocated; i++)
 
343
                vStringDelete(nls->levels[i].name);
 
344
        if (nls->levels) eFree(nls->levels);
 
345
        eFree(nls);
 
346
}
 
347
 
 
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.
 
351
 */
 
352
static void addNestingLevel(NestingLevels *nls, int indentation,
 
353
        vString *name, boolean is_class)
 
354
{
 
355
        int i;
 
356
        NestingLevel *nl = NULL;
 
357
 
 
358
        for (i = 0; i < nls->n; i++)
 
359
        {
 
360
                nl = nls->levels + i;
 
361
                if (indentation <= nl->indentation) break;
 
362
        }
 
363
        if (i == nls->n)
 
364
        {
 
365
                if (i >= nls->allocated)
 
366
                {
 
367
                        nls->allocated++;
 
368
                        nls->levels = xRealloc(nls->levels,
 
369
                                nls->allocated, NestingLevel);
 
370
                        nls->levels[i].name = vStringNew();
 
371
                }
 
372
                nl = nls->levels + i;
 
373
        }
 
374
        nls->n = i + 1;
 
375
 
 
376
        vStringCopy(nl->name, name);
 
377
        nl->indentation = indentation;
 
378
        nl->is_class = is_class;
 
379
}
 
380
 
 
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.
 
383
 */
 
384
static char const *find_triple_start(char const *string, char const **which)
 
385
{
 
386
        char const *cp = string;
 
387
 
 
388
        for (; *cp; cp++)
 
389
        {
 
390
                if (*cp == '"' || *cp == '\'')
 
391
                {
 
392
                        if (strncmp(cp, doubletriple, 3) == 0)
 
393
                        {
 
394
                                *which = doubletriple;
 
395
                                return cp;
 
396
                        }
 
397
                        if (strncmp(cp, singletriple, 3) == 0)
 
398
                        {
 
399
                                *which = singletriple;
 
400
                                return cp;
 
401
                        }
 
402
                        cp = skipString(cp);
 
403
                        if (!*cp) break;
 
404
                }
 
405
        }
 
406
        return NULL;
 
407
}
 
408
 
 
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.
 
411
 */
 
412
static void find_triple_end(char const *string, char const **which)
 
413
{
 
414
        char const *s = string;
 
415
        while (1)
 
416
        {
 
417
                /* Check if the string ends in the same line. */
 
418
                s = strstr (s, *which);
 
419
                if (!s) break;
 
420
                s += 3;
 
421
                *which = NULL;
 
422
                /* If yes, check if another one starts in the same line. */
 
423
                s = find_triple_start(s, which);
 
424
                if (!s) break;
 
425
                s += 3;
 
426
        }
 
427
}
 
428
 
 
429
static const char *findVariable(const char *line)
 
430
{
 
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;
 
436
 
 
437
        cp = strstr(line, "=");
 
438
        if (!cp)
 
439
                return NULL;
 
440
        eq = cp + 1;
 
441
        while (*eq)
 
442
        {
 
443
                if (*eq == '=')
 
444
                        return NULL;    /* ignore '==' operator and 'x=5,y=6)' function lines */
 
445
                if (*eq == '(')
 
446
                        break;  /* allow 'x = func(b=2,y=2,' lines */
 
447
                eq++;
 
448
        }
 
449
 
 
450
        /* go backwards to the start of the line, checking we have valid chars */
 
451
        start = cp - 1;
 
452
        while (start >= line && isspace ((int) *start))
 
453
                --start;
 
454
        while (start >= line && isIdentifierCharacter ((int) *start))
 
455
                --start;
 
456
        if (!isIdentifierFirstCharacter(*(start + 1)))
 
457
                return NULL;
 
458
        sp = start;
 
459
        while (sp >= line && isspace ((int) *sp))
 
460
                --sp;
 
461
        if ((sp + 1) != line)   /* the line isn't a simple variable assignment */
 
462
                return NULL;
 
463
        /* the line is valid, parse the variable name */
 
464
        ++start;
 
465
        return start;
 
466
}
83
467
 
84
468
static void findPythonTags (void)
85
469
{
86
 
    GList *parents = NULL, *tmp; /* list of classes which are around the token */
87
 
    vString *name = vStringNew ();
88
 
    gint indent;
89
 
    const unsigned char *line;
90
 
    boolean inMultilineString = FALSE;
91
 
    boolean wasInMultilineString = FALSE;
92
 
        lastClass *lastclass = NULL;
93
 
    boolean inFunction = FALSE;
94
 
    gint fn_indent = 0;
95
 
 
96
 
    while ((line = fileReadLine ()) != NULL)
97
 
    {
98
 
        const unsigned char *cp = line;
99
 
        indent = 0;
100
 
        while (*cp != '\0')
 
470
        vString *const continuation = vStringNew ();
 
471
        vString *const name = vStringNew ();
 
472
        vString *const parent = vStringNew();
 
473
 
 
474
        NestingLevels *const nesting_levels = newNestingLevels();
 
475
 
 
476
        const char *line;
 
477
        int line_skip = 0;
 
478
        char const *longStringLiteral = NULL;
 
479
 
 
480
        while ((line = (const char *) fileReadLine ()) != NULL)
101
481
        {
102
 
            if (*cp=='"' &&
103
 
                strncmp ((const char*) cp, "\"\"\"", (size_t) 3) == 0)
104
 
            {
105
 
                inMultilineString = (boolean) !inMultilineString;
106
 
                if (! inMultilineString)
107
 
                        wasInMultilineString = TRUE;
108
 
                cp += 3;
109
 
            }
110
 
            if (*cp=='\'' &&
111
 
                strncmp ((const char*) cp, "'''", (size_t) 3) == 0)
112
 
            {
113
 
                inMultilineString = (boolean) !inMultilineString;
114
 
                if (! inMultilineString)
115
 
                        wasInMultilineString = TRUE;
116
 
                cp += 3;
117
 
            }
118
 
 
119
 
                if (*cp == '\0' || wasInMultilineString)
120
 
                {
121
 
                        wasInMultilineString = FALSE;
122
 
                        break;  /* at end of multiline string */
123
 
                }
124
 
 
125
 
                /* update indent-sensitive things */
126
 
                if (!inMultilineString && !isspace(*cp))
127
 
                {
128
 
                        if (inFunction)
129
 
                        {
130
 
                                if (indent < fn_indent)
131
 
                                        inFunction = FALSE;
132
 
                        }
133
 
                    if (lastclass != NULL)
134
 
                    {
135
 
                                if (indent <= lastclass->indent)
136
 
                                {
137
 
                                        GList *last;
138
 
 
139
 
                                        parents = clean_class_list(parents, indent);
140
 
                                        last = g_list_last(parents);
141
 
                                        if (last != NULL)
142
 
                                                lastclass = last->data;
143
 
                                        else
144
 
                                                lastclass = NULL;
145
 
                                }
146
 
                    }
147
 
                }
148
 
 
149
 
            if (inMultilineString)
150
 
                ++cp;
151
 
                else if (isspace ((int) *cp))
152
 
                {
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 */
156
 
                        if (cp == line)
157
 
                        {
158
 
                                do
159
 
                                {
160
 
                                        indent++;
161
 
                                        cp++;
162
 
                                } while (isspace(*cp));
163
 
                        }
164
 
                        else
165
 
                                cp++;   /* non-indent whitespace */
166
 
                }
167
 
            else if (*cp == '#')
168
 
                break;
169
 
            else if (strncmp ((const char*) cp, "class", (size_t) 5) == 0)
170
 
            {
171
 
                        cp += 5;
172
 
                        if (isspace ((int) *cp))
173
 
                        {
174
 
                                lastClass *newclass = g_new(lastClass, 1);
175
 
 
176
 
                                while (isspace ((int) *cp))
177
 
                                ++cp;
178
 
                                while (isalnum ((int) *cp)  ||  *cp == '_')
179
 
                                {
180
 
                                vStringPut (name, (int) *cp);
181
 
                                ++cp;
182
 
                                }
183
 
                                vStringTerminate (name);
184
 
 
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;
 
485
                int indent;
 
486
 
 
487
                cp = skipSpace (cp);
 
488
 
 
489
                if (*cp == '\0')  /* skip blank line */
 
490
                        continue;
 
491
 
 
492
                /* Skip comment if we are not inside a multi-line string. */
 
493
                if (*cp == '#' && !longStringLiteral)
 
494
                        continue;
 
495
 
 
496
                /* Deal with line continuation. */
 
497
                if (!line_skip) vStringClear(continuation);
 
498
                vStringCatS(continuation, line);
 
499
                vStringStripTrailing(continuation);
 
500
                if (vStringLast(continuation) == '\\')
 
501
                {
 
502
                        vStringChop(continuation);
 
503
                        vStringCatS(continuation, " ");
 
504
                        line_skip = 1;
 
505
                        continue;
 
506
                }
 
507
                cp = line = vStringValue(continuation);
 
508
                cp = skipSpace (cp);
 
509
                indent = cp - line;
 
510
                line_skip = 0;
 
511
 
 
512
                checkParent(nesting_levels, indent, parent);
 
513
 
 
514
                /* Deal with multiline string ending. */
 
515
                if (longStringLiteral)
 
516
                {
 
517
                        find_triple_end(cp, &longStringLiteral);
 
518
                        continue;
 
519
                }
 
520
 
 
521
                /* Deal with multiline string start. */
 
522
                longstring = find_triple_start(cp, &longStringLiteral);
 
523
                if (longstring)
 
524
                {
 
525
                        longstring += 3;
 
526
                        find_triple_end(longstring, &longStringLiteral);
 
527
                        /* We don't parse for any tags in the rest of the line. */
 
528
                        continue;
 
529
                }
 
530
 
 
531
                /* Deal with def and class keywords. */
 
532
                keyword = findDefinitionOrClass (cp);
 
533
                if (keyword)
 
534
                {
 
535
                        boolean found = FALSE;
 
536
                        boolean is_class = FALSE;
 
537
                        if (!strncmp (keyword, "def ", 4))
 
538
                        {
 
539
                                cp = skipSpace (keyword + 3);
 
540
                                found = TRUE;
 
541
                        }
 
542
                        else if (!strncmp (keyword, "class ", 6))
 
543
                        {
 
544
                                cp = skipSpace (keyword + 5);
 
545
                                found = TRUE;
 
546
                                is_class = TRUE;
 
547
                        }
 
548
 
 
549
                        if (found)
 
550
                        {
 
551
                                boolean is_parent_class;
 
552
 
 
553
                                is_parent_class =
 
554
                                        constructParentString(nesting_levels, indent, parent);
 
555
 
 
556
                                if (is_class)
 
557
                                        parseClass (cp, name, parent, is_parent_class);
190
558
                                else
191
 
                                        makeSimpleScopedTag (name, PythonKinds, K_CLASS,
192
 
                                                PythonKinds[K_CLASS].name, lastclass->name, "public");
193
 
                                vStringClear (name);
 
559
                                        parseFunction(cp, name, parent, is_parent_class);
194
560
 
195
 
                                lastclass = newclass;
196
 
                                break;  /* ignore rest of line so that lastclass is not reset immediately */
 
561
                                addNestingLevel(nesting_levels, indent, name, is_class);
197
562
                        }
198
 
            }
199
 
            else if (strncmp ((const char*) cp, "def", (size_t) 3) == 0)
200
 
            {
201
 
                cp += 3;
202
 
                if (isspace ((int) *cp))
203
 
                {
204
 
                    while (isspace ((int) *cp))
205
 
                        ++cp;
206
 
                    while (isalnum ((int) *cp)  ||  *cp == '_')
207
 
                    {
208
 
                        vStringPut (name, (int) *cp);
209
 
                        ++cp;
210
 
                    }
211
 
                    vStringTerminate (name);
212
 
                    if (!isspace(*line) || lastclass == NULL || strlen(lastclass->name) <= 0)
213
 
                        makeSimpleTag (name, PythonKinds, K_FUNCTION);
214
 
                    else
215
 
                        makeSimpleScopedTag (name, PythonKinds, K_METHOD,
216
 
                                             PythonKinds[K_CLASS].name, lastclass->name, "public");
217
 
                    vStringClear (name);
218
 
 
219
 
                    inFunction = TRUE;
220
 
                    fn_indent = indent + 1;
221
 
                    break;      /* ignore rest of line so inFunction is not cancelled immediately */
222
563
                }
223
 
            }
224
 
                else if (!inFunction && *(const char*)cp == '=')
 
564
                /* Find global and class variables */
 
565
                variable = findVariable(line);
 
566
                if (variable)
225
567
                {
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;
231
570
 
232
 
                        eq = cp + 1;
233
 
                        while (*eq)
234
 
                        {
235
 
                                if (*eq == '=')
236
 
                                        goto skipvar;   /* ignore '==' operator and 'x=5,y=6)' function lines */
237
 
                                if (*eq == '(')
238
 
                                        break;  /* allow 'x = func(b=2,y=2,' lines */
239
 
                                eq++;
240
 
                        }
241
 
                        /* go backwards to the start of the line, checking we have valid chars */
242
 
                        start = cp - 1;
243
 
                        while (start >= line && isspace ((int) *start))
244
 
                                --start;
245
 
                        while (start >= line && isIdentifierCharacter ((int) *start))
246
 
                                --start;
247
 
                        if (!isIdentifierFirstCharacter(*(start + 1)))
248
 
                                goto skipvar;
249
 
                        sp = start;
250
 
                        while (sp >= line && isspace ((int) *sp))
251
 
                                --sp;
252
 
                        if ((sp + 1) != line)   /* the line isn't a simple variable assignment */
253
 
                                goto skipvar;
254
 
                        /* the line is valid, parse the variable name */
255
 
                        ++start;
 
571
                        vStringClear (name);
256
572
                        while (isIdentifierCharacter ((int) *start))
257
573
                        {
258
574
                                vStringPut (name, (int) *start);
260
576
                        }
261
577
                        vStringTerminate (name);
262
578
 
263
 
                        if (lastclass == NULL)
264
 
                                makeSimpleTag (name, PythonKinds, K_VARIABLE);
265
 
                        else
266
 
                                makeSimpleScopedTag (name, PythonKinds, K_VARIABLE,
267
 
                                        PythonKinds[K_CLASS].name, lastclass->name, "public");  /* class member variables */
268
 
 
269
 
                        vStringClear (name);
270
 
 
271
 
                        skipvar:
272
 
                        ++cp;
 
579
                        parent_is_class = constructParentString(nesting_levels, indent, parent);
 
580
                        /* skip variables in methods */
 
581
                        if (! parent_is_class && vStringLength(parent) > 0)
 
582
                                continue;
 
583
 
 
584
                        makeVariableTag (name, parent);
273
585
                }
274
 
            else if (*cp != '\0')
275
 
            {
276
 
                do
277
 
                    ++cp;
278
 
                while (isalnum ((int) *cp)  ||  *cp == '_');
279
 
            }
280
586
        }
281
 
    }
282
 
    vStringDelete (name);
283
 
 
284
 
    /* clear the remaining elements in the list */
285
 
    tmp = g_list_first(parents);
286
 
    while (tmp != NULL)
287
 
    {
288
 
        if (tmp->data)
289
 
        {
290
 
                        g_free(((lastClass*)tmp->data)->name);
291
 
                        g_free(tmp->data);
292
 
        }
293
 
        tmp = tmp->next;
294
 
    }
295
 
    g_list_free(parents);
 
587
        /* Clean up all memory we allocated. */
 
588
        vStringDelete (parent);
 
589
        vStringDelete (name);
 
590
        vStringDelete (continuation);
 
591
        freeNestingLevels (nesting_levels);
296
592
}
297
593
 
298
 
extern parserDefinition* PythonParser (void)
 
594
extern parserDefinition *PythonParser (void)
299
595
{
300
 
    static const char *const extensions [] = { "py", "python", NULL };
301
 
    parserDefinition* def = parserNew ("Python");
302
 
    def->kinds      = PythonKinds;
303
 
    def->kindCount  = KIND_COUNT (PythonKinds);
304
 
    def->extensions = extensions;
305
 
    def->parser     = findPythonTags;
306
 
    return def;
 
596
        static const char *const extensions[] = { "py", "pyx", "pxd", "scons", "python", NULL };
 
597
        parserDefinition *def = parserNew ("Python");
 
598
        def->kinds = PythonKinds;
 
599
        def->kindCount = KIND_COUNT (PythonKinds);
 
600
        def->extensions = extensions;
 
601
        def->parser = findPythonTags;
 
602
        return def;
307
603
}
308
604
 
309
 
/* vi:set tabstop=8 shiftwidth=4: */
 
605
/* vi:set tabstop=4 shiftwidth=4: */