~ubuntu-branches/ubuntu/utopic/exuberant-ctags/utopic

« back to all changes in this revision

Viewing changes to python.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2009-07-14 15:05:23 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20090714150523-rytn1psh622nb709
Tags: 1:5.8-1
* New upstream release. Debian bugs fixed:
  - Add support for ASP classes (closes: #529215).
* Compress files using gzip -n (cf. debhelper 6.0.6).
* Add a watch file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
*   $Id: python.c 567 2007-06-24 01:20:20Z elliotth $
 
2
*   $Id: python.c 720 2009-07-07 03:55:23Z dhiebert $
3
3
*
4
4
*   Copyright (c) 2000-2003, Darren Hiebert
5
5
*
19
19
#include "entry.h"
20
20
#include "options.h"
21
21
#include "read.h"
 
22
#include "main.h"
 
23
#include "vstring.h"
22
24
#include "routines.h"
23
 
#include "vstring.h"
 
25
#include "debug.h"
24
26
 
25
27
/*
26
 
*   DATA DEFINITIONS
 
28
*   DATA DECLARATIONS
27
29
*/
28
 
typedef enum {
29
 
        K_CLASS, K_FUNCTION, K_MEMBER
30
 
} pythonKind;
31
 
 
32
 
static kindOption PythonKinds[] = {
33
 
        {TRUE, 'c', "class",    "classes"},
34
 
        {TRUE, 'f', "function", "functions"},
35
 
        {TRUE, 'm', "member",   "class members"}
36
 
};
37
 
 
38
30
typedef struct NestingLevel NestingLevel;
39
31
typedef struct NestingLevels NestingLevels;
40
32
 
42
34
{
43
35
        int indentation;
44
36
        vString *name;
45
 
        boolean is_class;
 
37
        int type;
46
38
};
47
39
 
48
40
struct NestingLevels
49
41
{
50
42
        NestingLevel *levels;
51
 
        int n;
 
43
        int n;                                  /* number of levels in use */
52
44
        int allocated;
53
45
};
54
46
 
 
47
typedef enum {
 
48
        K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE, K_IMPORT
 
49
} pythonKind;
 
50
 
 
51
/*
 
52
*   DATA DEFINITIONS
 
53
*/
 
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"}
 
60
};
 
61
 
 
62
static char const * const singletriple = "'''";
 
63
static char const * const doubletriple = "\"\"\"";
 
64
 
55
65
/*
56
66
*   FUNCTION DEFINITIONS
57
67
*/
58
68
 
59
 
#define vStringLast(vs) ((vs)->buffer[(vs)->length - 1])
 
69
static NestingLevels *nestingLevelsNew (void)
 
70
{
 
71
        NestingLevels *nls = xCalloc (1, NestingLevels);
 
72
        return nls;
 
73
}
 
74
 
 
75
static void nestingLevelsFree (NestingLevels *nls)
 
76
{
 
77
        int i;
 
78
        for (i = 0; i < nls->allocated; i++)
 
79
                vStringDelete(nls->levels[i].name);
 
80
        if (nls->levels) eFree(nls->levels);
 
81
        eFree(nls);
 
82
}
 
83
 
 
84
static void nestingLevelsPush (NestingLevels *nls,
 
85
        const vString *name, int type)
 
86
{
 
87
        NestingLevel *nl = NULL;
 
88
 
 
89
        if (nls->n >= nls->allocated)
 
90
        {
 
91
                nls->allocated++;
 
92
                nls->levels = xRealloc(nls->levels,
 
93
                        nls->allocated, NestingLevel);
 
94
                nls->levels[nls->n].name = vStringNew();
 
95
        }
 
96
        nl = &nls->levels[nls->n];
 
97
        nls->n++;
 
98
 
 
99
        vStringCopy(nl->name, name);
 
100
        nl->type = type;
 
101
}
 
102
 
 
103
#if 0
 
104
static NestingLevel *nestingLevelsGetCurrent (NestingLevels *nls)
 
105
{
 
106
        Assert (nls != NULL);
 
107
 
 
108
        if (nls->n < 1)
 
109
                return NULL;
 
110
 
 
111
        return &nls->levels[nls->n - 1];
 
112
}
 
113
 
 
114
static void nestingLevelsPop (NestingLevels *nls)
 
115
{
 
116
        const NestingLevel *nl = nestingLevelsGetCurrent(nls);
 
117
 
 
118
        Assert (nl != NULL);
 
119
        vStringClear(nl->name);
 
120
        nls->n--;
 
121
}
 
122
#endif
60
123
 
61
124
static boolean isIdentifierFirstCharacter (int c)
62
125
{
72
135
 * extract all relevant information and create a tag.
73
136
 */
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__)
76
139
{
77
140
        tagEntryInfo tag;
78
141
        initTagEntry (&tag, vStringValue (function));
79
 
        
 
142
 
80
143
        tag.kindName = "function";
81
144
        tag.kind = 'f';
 
145
        /* tag.extensionFields.arglist = arglist; */
82
146
 
83
147
        if (vStringLength (parent) > 0)
84
148
        {
140
204
        makeTagEntry (&tag);
141
205
}
142
206
 
 
207
static void makeVariableTag (vString *const var, vString *const parent)
 
208
{
 
209
        tagEntryInfo tag;
 
210
        initTagEntry (&tag, vStringValue (var));
 
211
        tag.kindName = "variable";
 
212
        tag.kind = 'v';
 
213
        if (vStringLength (parent) > 0)
 
214
        {
 
215
                tag.extensionFields.scope [0] = "class";
 
216
                tag.extensionFields.scope [1] = vStringValue (parent);
 
217
        }
 
218
        makeTagEntry (&tag);
 
219
}
 
220
 
143
221
/* Skip a single or double quoted string. */
144
222
static const char *skipString (const char *cp)
145
223
{
162
240
{
163
241
        for (; *cp; cp++)
164
242
        {
 
243
                if (*cp == '"' || *cp == '\'')
 
244
                {
 
245
                        cp = skipString(cp);
 
246
                        if (!*cp) break;
 
247
                }
165
248
                if (isIdentifierFirstCharacter ((int) *cp))
166
249
                        return cp;
167
 
                if (*cp == '"' || *cp == '\'')
168
 
                {
169
 
                        cp = skipString(cp);
170
 
                }
171
 
    }
172
 
    return cp;
 
250
        }
 
251
        return cp;
173
252
}
174
253
 
175
254
/* Skip an identifier. */
177
256
{
178
257
        while (isIdentifierCharacter ((int) *cp))
179
258
                cp++;
180
 
    return cp;
 
259
        return cp;
181
260
}
182
261
 
183
262
static const char *findDefinitionOrClass (const char *cp)
185
264
        while (*cp)
186
265
        {
187
266
                cp = skipEverything (cp);
188
 
                if (!strncmp(cp, "def", 3) || !strncmp(cp, "class", 5))
 
267
                if (!strncmp(cp, "def", 3) || !strncmp(cp, "class", 5) ||
 
268
                        !strncmp(cp, "cdef", 4) || !strncmp(cp, "cpdef", 5))
189
269
                {
190
270
                        return cp;
191
271
                }
243
323
        vStringDelete (inheritance);
244
324
}
245
325
 
 
326
static void parseImports (const char *cp)
 
327
{
 
328
        const char *pos;
 
329
        vString *name, *name_next;
 
330
 
 
331
        cp = skipEverything (cp);
 
332
 
 
333
        if ((pos = strstr (cp, "import")) == NULL)
 
334
                return;
 
335
 
 
336
        cp = pos + 6;
 
337
 
 
338
        /* continue only if there is some space between the keyword and the identifier */
 
339
        if (! isspace (*cp))
 
340
                return;
 
341
 
 
342
        cp++;
 
343
        cp = skipSpace (cp);
 
344
 
 
345
        name = vStringNew ();
 
346
        name_next = vStringNew ();
 
347
 
 
348
        cp = skipEverything (cp);
 
349
        while (*cp)
 
350
        {
 
351
                cp = parseIdentifier (cp, name);
 
352
 
 
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);
 
357
 
 
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)
 
361
                {
 
362
                        makeSimpleTag (name, PythonKinds, K_IMPORT);
 
363
                }
 
364
        }
 
365
        vStringDelete (name);
 
366
        vStringDelete (name_next);
 
367
}
 
368
 
 
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)
 
373
{
 
374
        char *start, *end;
 
375
        int level;
 
376
        if (NULL == buf)
 
377
                return NULL;
 
378
        if (NULL == (start = strchr(buf, '(')))
 
379
                return NULL;
 
380
        for (level = 1, end = start + 1; level > 0; ++end)
 
381
        {
 
382
                if ('\0' == *end)
 
383
                        break;
 
384
                else if ('(' == *end)
 
385
                        ++ level;
 
386
                else if (')' == *end)
 
387
                        -- level;
 
388
        }
 
389
        *end = '\0';
 
390
        return strdup(start);
 
391
}
 
392
 
246
393
static void parseFunction (const char *cp, vString *const def,
247
394
        vString *const parent, int is_class_parent)
248
395
{
 
396
        char *arglist;
 
397
 
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);
 
401
        eFree (arglist);
251
402
}
252
403
 
253
404
/* Get the combined name of a nested symbol. Classes are separated with ".",
275
426
                        break;
276
427
                if (prev)
277
428
                {
278
 
                        if (prev->is_class)
 
429
                        vStringCatS(result, ".");       /* make Geany symbol list grouping work properly */
 
430
/*
 
431
                        if (prev->type == K_CLASS)
279
432
                                vStringCatS(result, ".");
280
433
                        else
281
434
                                vStringCatS(result, "/");
 
435
*/
282
436
                }
283
437
                vStringCat(result, nl->name);
284
 
                is_class = nl->is_class;
 
438
                is_class = (nl->type == K_CLASS);
285
439
                prev = nl;
286
440
        }
287
441
        return is_class;
288
442
}
289
443
 
290
 
static NestingLevels *newNestingLevels(void)
291
 
{
292
 
        NestingLevels *nls = xCalloc (1, NestingLevels);
293
 
        return nls;
294
 
}
295
 
 
296
 
static void freeNestingLevels(NestingLevels *nls)
 
444
/* Check whether parent's indentation level is higher than the current level and
 
445
 * if so, remove it.
 
446
 */
 
447
static void checkParent(NestingLevels *nls, int indent, vString *parent)
297
448
{
298
449
        int i;
299
 
        for (i = 0; i < nls->allocated; i++)
300
 
                vStringDelete(nls->levels[i].name);
301
 
        if (nls->levels) eFree(nls->levels);
302
 
        eFree(nls);
 
450
        NestingLevel *n;
 
451
 
 
452
        for (i = 0; i < nls->n; i++)
 
453
        {
 
454
                n = nls->levels + i;
 
455
                /* is there a better way to compare two vStrings? */
 
456
                if (strcmp(vStringValue(parent), vStringValue(n->name)) == 0)
 
457
                {
 
458
                        if (n && indent <= n->indentation)
 
459
                        {
 
460
                                /* remove this level by clearing its name */
 
461
                                vStringClear(n->name);
 
462
                        }
 
463
                        break;
 
464
                }
 
465
        }
303
466
}
304
467
 
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.
308
 
 */
309
468
static void addNestingLevel(NestingLevels *nls, int indentation,
310
 
        vString *name, boolean is_class)
 
469
        const vString *name, boolean is_class)
311
470
{
312
471
        int i;
313
472
        NestingLevel *nl = NULL;
319
478
        }
320
479
        if (i == nls->n)
321
480
        {
322
 
                if (i >= nls->allocated)
323
 
                {
324
 
                        nls->allocated++;
325
 
                        nls->levels = xRealloc(nls->levels,
326
 
                                nls->allocated, NestingLevel);
327
 
                        nls->levels[i].name = vStringNew();
328
 
                }
 
481
                nestingLevelsPush(nls, name, 0);
329
482
                nl = nls->levels + i;
330
483
        }
331
 
        nls->n = i + 1;
332
 
 
333
 
        vStringCopy(nl->name, name);
 
484
        else
 
485
        {       /* reuse existing slot */
 
486
                nls->n = i + 1;
 
487
                vStringCopy(nl->name, name);
 
488
        }
334
489
        nl->indentation = indentation;
335
 
        nl->is_class = is_class;
 
490
        nl->type = is_class ? K_CLASS : !K_CLASS;
 
491
}
 
492
 
 
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.
 
495
 */
 
496
static char const *find_triple_start(char const *string, char const **which)
 
497
{
 
498
        char const *cp = string;
 
499
 
 
500
        for (; *cp; cp++)
 
501
        {
 
502
                if (*cp == '"' || *cp == '\'')
 
503
                {
 
504
                        if (strncmp(cp, doubletriple, 3) == 0)
 
505
                        {
 
506
                                *which = doubletriple;
 
507
                                return cp;
 
508
                        }
 
509
                        if (strncmp(cp, singletriple, 3) == 0)
 
510
                        {
 
511
                                *which = singletriple;
 
512
                                return cp;
 
513
                        }
 
514
                        cp = skipString(cp);
 
515
                        if (!*cp) break;
 
516
                }
 
517
        }
 
518
        return NULL;
 
519
}
 
520
 
 
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.
 
523
 */
 
524
static void find_triple_end(char const *string, char const **which)
 
525
{
 
526
        char const *s = string;
 
527
        while (1)
 
528
        {
 
529
                /* Check if the string ends in the same line. */
 
530
                s = strstr (s, *which);
 
531
                if (!s) break;
 
532
                s += 3;
 
533
                *which = NULL;
 
534
                /* If yes, check if another one starts in the same line. */
 
535
                s = find_triple_start(s, which);
 
536
                if (!s) break;
 
537
                s += 3;
 
538
        }
 
539
}
 
540
 
 
541
static const char *findVariable(const char *line)
 
542
{
 
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;
 
548
 
 
549
        cp = strstr(line, "=");
 
550
        if (!cp)
 
551
                return NULL;
 
552
        eq = cp + 1;
 
553
        while (*eq)
 
554
        {
 
555
                if (*eq == '=')
 
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 */
 
559
                eq++;
 
560
        }
 
561
 
 
562
        /* go backwards to the start of the line, checking we have valid chars */
 
563
        start = cp - 1;
 
564
        while (start >= line && isspace ((int) *start))
 
565
                --start;
 
566
        while (start >= line && isIdentifierCharacter ((int) *start))
 
567
                --start;
 
568
        if (!isIdentifierFirstCharacter(*(start + 1)))
 
569
                return NULL;
 
570
        sp = start;
 
571
        while (sp >= line && isspace ((int) *sp))
 
572
                --sp;
 
573
        if ((sp + 1) != line)   /* the line isn't a simple variable assignment */
 
574
                return NULL;
 
575
        /* the line is valid, parse the variable name */
 
576
        ++start;
 
577
        return start;
 
578
}
 
579
 
 
580
/* Skip type declaration that optionally follows a cdef/cpdef */
 
581
static const char *skipTypeDecl (const char *cp, boolean *is_class)
 
582
{
 
583
        const char *lastStart = cp, *ptr = cp;
 
584
        int loopCount = 0;
 
585
        ptr = skipSpace(cp);
 
586
        if (!strncmp("extern", ptr, 6)) {
 
587
                ptr += 6;
 
588
                ptr = skipSpace(ptr);
 
589
                if (!strncmp("from", ptr, 4)) { return NULL; }
 
590
        }
 
591
        if (!strncmp("class", ptr, 5)) {
 
592
                ptr += 5 ;
 
593
                *is_class = TRUE;
 
594
                ptr = skipSpace(ptr);
 
595
                return ptr;
 
596
        }
 
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;
 
601
                if (*ptr == '(') {
 
602
                    return lastStart; /* if we stopped on a '(' we are done */
 
603
                }
 
604
                ptr = skipSpace(ptr);
 
605
                lastStart = ptr;
 
606
                while (*lastStart == '*') lastStart++;  /* cdef int *identifier */
 
607
        }
 
608
        return NULL;
336
609
}
337
610
 
338
611
static void findPythonTags (void)
341
614
        vString *const name = vStringNew ();
342
615
        vString *const parent = vStringNew();
343
616
 
344
 
        NestingLevels *const nesting_levels = newNestingLevels();
 
617
        NestingLevels *const nesting_levels = nestingLevelsNew();
345
618
 
346
619
        const char *line;
347
620
        int line_skip = 0;
348
 
        boolean longStringLiteral = FALSE;
 
621
        char const *longStringLiteral = NULL;
349
622
 
350
623
        while ((line = (const char *) fileReadLine ()) != NULL)
351
624
        {
352
 
                const char *cp = line;
353
 
                char *longstring;
354
 
                const char *keyword;
 
625
                const char *cp = line, *candidate;
 
626
                char const *longstring;
 
627
                char const *keyword, *variable;
355
628
                int indent;
356
629
 
357
630
                cp = skipSpace (cp);
358
631
 
359
 
                if (*cp == '#' || *cp == '\0')  /* skip comment or blank line */
 
632
                if (*cp == '\0')  /* skip blank line */
 
633
                        continue;
 
634
 
 
635
                /* Skip comment if we are not inside a multi-line string. */
 
636
                if (*cp == '#' && !longStringLiteral)
360
637
                        continue;
361
638
 
362
639
                /* Deal with line continuation. */
375
652
                indent = cp - line;
376
653
                line_skip = 0;
377
654
 
 
655
                checkParent(nesting_levels, indent, parent);
 
656
 
378
657
                /* Deal with multiline string ending. */
379
658
                if (longStringLiteral)
380
659
                {
381
 
                        /* Note: We do ignore anything in the same line after a multiline
382
 
                         * string for now.
383
 
                         */
384
 
                        if (strstr (cp, "\"\"\""))
385
 
                                longStringLiteral = FALSE;
 
660
                        find_triple_end(cp, &longStringLiteral);
386
661
                        continue;
387
662
                }
388
 
                
 
663
 
389
664
                /* Deal with multiline string start. */
390
 
                if ((longstring = strstr (cp, "\"\"\"")) != NULL)
 
665
                longstring = find_triple_start(cp, &longStringLiteral);
 
666
                if (longstring)
391
667
                {
392
 
                        /* Note: For our purposes, the line just ends at the first long
393
 
                         * string.
394
 
                         */
395
 
                        *longstring = '\0';
396
668
                        longstring += 3;
397
 
                        longStringLiteral = TRUE;
398
 
                        while ((longstring = strstr (longstring, "\"\"\"")) != NULL)
399
 
                        {
400
 
                                longstring += 3;
401
 
                                longStringLiteral = !longStringLiteral;
402
 
                        }
 
669
                        find_triple_end(longstring, &longStringLiteral);
 
670
                        /* We don't parse for any tags in the rest of the line. */
 
671
                        continue;
403
672
                }
404
673
 
405
674
                /* Deal with def and class keywords. */
419
688
                                found = TRUE;
420
689
                                is_class = TRUE;
421
690
                        }
 
691
                        else if (!strncmp (keyword, "cdef ", 5))
 
692
                    {
 
693
                        cp = skipSpace(keyword + 4);
 
694
                        candidate = skipTypeDecl (cp, &is_class);
 
695
                        if (candidate)
 
696
                        {
 
697
                                found = TRUE;
 
698
                                cp = candidate;
 
699
                        }
 
700
 
 
701
                    }
 
702
                else if (!strncmp (keyword, "cpdef ", 6))
 
703
                    {
 
704
                        cp = skipSpace(keyword + 5);
 
705
                        candidate = skipTypeDecl (cp, &is_class);
 
706
                        if (candidate)
 
707
                        {
 
708
                                found = TRUE;
 
709
                                cp = candidate;
 
710
                        }
 
711
                    }
422
712
 
423
713
                        if (found)
424
714
                        {
435
725
                                addNestingLevel(nesting_levels, indent, name, is_class);
436
726
                        }
437
727
                }
 
728
                /* Find global and class variables */
 
729
                variable = findVariable(line);
 
730
                if (variable)
 
731
                {
 
732
                        const char *start = variable;
 
733
                        boolean parent_is_class;
 
734
 
 
735
                        vStringClear (name);
 
736
                        while (isIdentifierCharacter ((int) *start))
 
737
                        {
 
738
                                vStringPut (name, (int) *start);
 
739
                                ++start;
 
740
                        }
 
741
                        vStringTerminate (name);
 
742
 
 
743
                        parent_is_class = constructParentString(nesting_levels, indent, parent);
 
744
                        /* skip variables in methods */
 
745
                        if (! parent_is_class && vStringLength(parent) > 0)
 
746
                                continue;
 
747
 
 
748
                        makeVariableTag (name, parent);
 
749
                }
 
750
                /* Find and parse imports */
 
751
                parseImports(line);
438
752
        }
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);
444
758
}
445
759
 
446
760
extern parserDefinition *PythonParser (void)
447
761
{
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);