2005-11-22 12:26:26 +00:00
|
|
|
/*
|
2009-09-29 11:07:10 +00:00
|
|
|
* Copyright (c) 2000-2003, Darren Hiebert
|
2005-11-22 12:26:26 +00:00
|
|
|
*
|
|
|
|
* This source code is released for free distribution under the terms of the
|
|
|
|
* GNU General Public License.
|
|
|
|
*
|
|
|
|
* This module contains functions for generating tags for PERL language
|
|
|
|
* files.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* INCLUDE FILES
|
|
|
|
*/
|
2009-09-29 11:07:10 +00:00
|
|
|
#include "general.h" /* must always come first */
|
2005-11-22 12:26:26 +00:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
#include "entry.h"
|
|
|
|
#include "options.h"
|
2005-11-22 12:26:26 +00:00
|
|
|
#include "read.h"
|
2009-09-29 11:07:10 +00:00
|
|
|
#include "main.h"
|
2005-11-22 12:26:26 +00:00
|
|
|
#include "vstring.h"
|
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
#define TRACE_PERL_C 0
|
|
|
|
#define TRACE if (TRACE_PERL_C) printf("perl.c:%d: ", __LINE__), printf
|
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
/*
|
|
|
|
* DATA DEFINITIONS
|
|
|
|
*/
|
|
|
|
typedef enum {
|
2009-09-29 11:07:10 +00:00
|
|
|
K_NONE = -1,
|
|
|
|
K_CONSTANT,
|
|
|
|
K_FORMAT,
|
|
|
|
K_LABEL,
|
|
|
|
K_PACKAGE,
|
|
|
|
K_SUBROUTINE,
|
|
|
|
K_SUBROUTINE_DECLARATION
|
2005-11-22 12:26:26 +00:00
|
|
|
} perlKind;
|
|
|
|
|
|
|
|
static kindOption PerlKinds [] = {
|
2009-11-10 17:49:24 +00:00
|
|
|
{ TRUE, 'e', "enum", "constants" },
|
2009-11-10 19:05:51 +00:00
|
|
|
{ TRUE, 'o', "other", "formats" },
|
2009-09-29 11:07:10 +00:00
|
|
|
{ TRUE, 'm', "macro", "labels" },
|
|
|
|
{ TRUE, 'p', "package", "packages" },
|
|
|
|
{ TRUE, 'f', "function", "subroutines" },
|
|
|
|
{ FALSE, 'p', "prototype", "subroutine declarations" },
|
2005-11-22 12:26:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FUNCTION DEFINITIONS
|
|
|
|
*/
|
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
static boolean isIdentifier1 (int c)
|
2006-05-12 16:24:09 +00:00
|
|
|
{
|
2009-09-29 11:07:10 +00:00
|
|
|
return (boolean) (isalpha (c) || c == '_');
|
|
|
|
}
|
|
|
|
|
|
|
|
static boolean isIdentifier (int c)
|
|
|
|
{
|
|
|
|
return (boolean) (isalnum (c) || c == '_');
|
|
|
|
}
|
|
|
|
|
|
|
|
static boolean isPodWord (const char *word)
|
|
|
|
{
|
|
|
|
boolean result = FALSE;
|
|
|
|
if (isalpha (*word))
|
|
|
|
{
|
|
|
|
const char *const pods [] = {
|
|
|
|
"head1", "head2", "head3", "head4", "over", "item", "back",
|
|
|
|
"pod", "begin", "end", "for"
|
|
|
|
};
|
|
|
|
const size_t count = sizeof (pods) / sizeof (pods [0]);
|
|
|
|
const char *white = strpbrk (word, " \t");
|
|
|
|
const size_t len = (white!=NULL) ? (size_t)(white-word) : strlen (word);
|
|
|
|
char *const id = (char*) eMalloc (len + 1);
|
|
|
|
size_t i;
|
|
|
|
strncpy (id, word, len);
|
|
|
|
id [len] = '\0';
|
|
|
|
for (i = 0 ; i < count && ! result ; ++i)
|
|
|
|
{
|
|
|
|
if (strcmp (id, pods [i]) == 0)
|
|
|
|
result = TRUE;
|
|
|
|
}
|
|
|
|
eFree (id);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perl subroutine declaration may look like one of the following:
|
|
|
|
*
|
|
|
|
* sub abc;
|
|
|
|
* sub abc :attr;
|
|
|
|
* sub abc (proto);
|
|
|
|
* sub abc (proto) :attr;
|
|
|
|
*
|
|
|
|
* Note that there may be more than one attribute. Attributes may
|
|
|
|
* have things in parentheses (they look like arguments). Anything
|
|
|
|
* inside of those parentheses goes. Prototypes may contain semi-colons.
|
|
|
|
* The matching end when we encounter (outside of any parentheses) either
|
|
|
|
* a semi-colon (that'd be a declaration) or an left curly brace
|
|
|
|
* (definition).
|
|
|
|
*
|
|
|
|
* This is pretty complicated parsing (plus we all know that only perl can
|
|
|
|
* parse Perl), so we are only promising best effort here.
|
|
|
|
*
|
|
|
|
* If we can't determine what this is (due to a file ending, for example),
|
|
|
|
* we will return FALSE.
|
|
|
|
*/
|
|
|
|
static boolean isSubroutineDeclaration (const unsigned char *cp)
|
|
|
|
{
|
|
|
|
boolean attr = FALSE;
|
|
|
|
int nparens = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
for ( ; *cp; ++cp) {
|
|
|
|
SUB_DECL_SWITCH:
|
|
|
|
switch (*cp) {
|
|
|
|
case ':':
|
|
|
|
if (nparens)
|
|
|
|
break;
|
|
|
|
else if (TRUE == attr)
|
|
|
|
return FALSE; /* Invalid attribute name */
|
|
|
|
else
|
|
|
|
attr = TRUE;
|
|
|
|
break;
|
|
|
|
case '(':
|
|
|
|
++nparens;
|
|
|
|
break;
|
|
|
|
case ')':
|
|
|
|
--nparens;
|
|
|
|
break;
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
break;
|
|
|
|
case ';':
|
|
|
|
if (!nparens)
|
|
|
|
return TRUE;
|
|
|
|
case '{':
|
|
|
|
if (!nparens)
|
|
|
|
return FALSE;
|
|
|
|
default:
|
|
|
|
if (attr) {
|
|
|
|
if (isIdentifier1(*cp)) {
|
|
|
|
cp++;
|
|
|
|
while (isIdentifier (*cp))
|
|
|
|
cp++;
|
|
|
|
attr = FALSE;
|
|
|
|
goto SUB_DECL_SWITCH; /* Instead of --cp; */
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
} else if (nparens) {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (NULL != (cp = fileReadLine ()));
|
|
|
|
|
|
|
|
return FALSE;
|
2006-05-12 16:24:09 +00:00
|
|
|
}
|
2006-05-10 22:35:55 +00:00
|
|
|
|
2005-11-22 12:26:26 +00:00
|
|
|
/* Algorithm adapted from from GNU etags.
|
|
|
|
* Perl support by Bart Robinson <lomew@cs.utah.edu>
|
|
|
|
* Perl sub names: look for /^ [ \t\n]sub [ \t\n]+ [^ \t\n{ (]+/
|
|
|
|
*/
|
|
|
|
static void findPerlTags (void)
|
|
|
|
{
|
2009-09-29 11:07:10 +00:00
|
|
|
vString *name = vStringNew ();
|
|
|
|
vString *package = NULL;
|
|
|
|
boolean skipPodDoc = FALSE;
|
|
|
|
const unsigned char *line;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
while ((line = fileReadLine ()) != NULL)
|
2005-11-22 12:26:26 +00:00
|
|
|
{
|
2009-09-29 11:07:10 +00:00
|
|
|
boolean spaceRequired = FALSE;
|
|
|
|
boolean qualified = FALSE;
|
|
|
|
const unsigned char *cp = line;
|
|
|
|
perlKind kind = K_NONE;
|
|
|
|
tagEntryInfo e;
|
2005-11-22 12:26:26 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
if (skipPodDoc)
|
|
|
|
{
|
|
|
|
if (strncmp ((const char*) line, "=cut", (size_t) 4) == 0)
|
|
|
|
skipPodDoc = FALSE;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (line [0] == '=')
|
|
|
|
{
|
|
|
|
skipPodDoc = isPodWord ((const char*)line + 1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else if (strcmp ((const char*) line, "__DATA__") == 0)
|
|
|
|
break;
|
|
|
|
else if (strcmp ((const char*) line, "__END__") == 0)
|
|
|
|
break;
|
|
|
|
else if (line [0] == '#')
|
|
|
|
continue;
|
2006-05-10 22:35:55 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
while (isspace (*cp))
|
2006-05-12 16:24:09 +00:00
|
|
|
cp++;
|
2009-09-29 11:07:10 +00:00
|
|
|
|
|
|
|
if (strncmp((const char*) cp, "sub", (size_t) 3) == 0)
|
|
|
|
{
|
|
|
|
TRACE("this looks like a sub\n");
|
|
|
|
cp += 3;
|
|
|
|
kind = K_SUBROUTINE;
|
|
|
|
spaceRequired = TRUE;
|
|
|
|
qualified = TRUE;
|
|
|
|
}
|
|
|
|
else if (strncmp((const char*) cp, "use", (size_t) 3) == 0)
|
|
|
|
{
|
|
|
|
cp += 3;
|
|
|
|
if (!isspace(*cp))
|
|
|
|
continue;
|
|
|
|
while (*cp && isspace (*cp))
|
|
|
|
++cp;
|
|
|
|
if (strncmp((const char*) cp, "constant", (size_t) 8) != 0)
|
|
|
|
continue;
|
|
|
|
cp += 8;
|
|
|
|
kind = K_CONSTANT;
|
|
|
|
spaceRequired = TRUE;
|
|
|
|
qualified = TRUE;
|
|
|
|
}
|
|
|
|
else if (strncmp((const char*) cp, "package", (size_t) 7) == 0)
|
|
|
|
{
|
|
|
|
/* This will point to space after 'package' so that a tag
|
|
|
|
can be made */
|
|
|
|
const unsigned char *space = cp += 7;
|
|
|
|
|
|
|
|
if (package == NULL)
|
|
|
|
package = vStringNew ();
|
|
|
|
else
|
|
|
|
vStringClear (package);
|
|
|
|
while (isspace (*cp))
|
|
|
|
cp++;
|
|
|
|
while ((int) *cp != ';' && !isspace ((int) *cp))
|
2006-05-12 16:24:09 +00:00
|
|
|
{
|
2009-09-29 11:07:10 +00:00
|
|
|
vStringPut (package, (int) *cp);
|
|
|
|
cp++;
|
2006-05-12 16:24:09 +00:00
|
|
|
}
|
2009-09-29 11:07:10 +00:00
|
|
|
vStringCatS (package, "::");
|
|
|
|
|
|
|
|
cp = space; /* Rewind */
|
|
|
|
kind = K_PACKAGE;
|
|
|
|
spaceRequired = TRUE;
|
|
|
|
qualified = TRUE;
|
|
|
|
}
|
|
|
|
else if (strncmp((const char*) cp, "format", (size_t) 6) == 0)
|
|
|
|
{
|
|
|
|
cp += 6;
|
|
|
|
kind = K_FORMAT;
|
|
|
|
spaceRequired = TRUE;
|
|
|
|
qualified = TRUE;
|
|
|
|
}
|
2006-05-12 16:24:09 +00:00
|
|
|
else
|
|
|
|
{
|
2009-09-29 11:07:10 +00:00
|
|
|
if (isIdentifier1 (*cp))
|
|
|
|
{
|
|
|
|
const unsigned char *p = cp;
|
|
|
|
while (isIdentifier (*p))
|
|
|
|
++p;
|
|
|
|
while (isspace (*p))
|
|
|
|
++p;
|
|
|
|
if ((int) *p == ':' && (int) *(p + 1) != ':')
|
|
|
|
kind = K_LABEL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (kind != K_NONE)
|
|
|
|
{
|
|
|
|
TRACE("cp0: %s\n", (const char *) cp);
|
|
|
|
if (spaceRequired && *cp && !isspace (*cp))
|
|
|
|
continue;
|
2006-05-10 22:35:55 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
TRACE("cp1: %s\n", (const char *) cp);
|
|
|
|
while (isspace (*cp))
|
|
|
|
cp++;
|
2006-05-10 22:35:55 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
while (!*cp || '#' == *cp) { /* Gobble up empty lines
|
|
|
|
and comments */
|
|
|
|
cp = fileReadLine ();
|
|
|
|
if (!cp)
|
|
|
|
goto END_MAIN_WHILE;
|
|
|
|
while (isspace (*cp))
|
|
|
|
cp++;
|
|
|
|
}
|
2006-05-10 23:00:22 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
while (isIdentifier (*cp) || (K_PACKAGE == kind && ':' == *cp))
|
2006-05-12 16:24:09 +00:00
|
|
|
{
|
2009-09-29 11:07:10 +00:00
|
|
|
vStringPut (name, (int) *cp);
|
|
|
|
cp++;
|
2006-05-12 16:24:09 +00:00
|
|
|
}
|
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
if (K_FORMAT == kind &&
|
|
|
|
vStringLength (name) == 0 && /* cp did not advance */
|
|
|
|
'=' == *cp)
|
|
|
|
{
|
|
|
|
/* format's name is optional. If it's omitted, 'STDOUT'
|
|
|
|
is assumed. */
|
|
|
|
vStringCatS (name, "STDOUT");
|
|
|
|
}
|
2006-05-12 16:24:09 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
vStringTerminate (name);
|
|
|
|
TRACE("name: %s\n", name->buffer);
|
2006-05-10 23:00:22 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
if (0 == vStringLength(name)) {
|
|
|
|
vStringClear(name);
|
|
|
|
continue;
|
2006-05-12 16:24:09 +00:00
|
|
|
}
|
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
if (K_SUBROUTINE == kind)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* isSubroutineDeclaration() may consume several lines. So
|
|
|
|
* we record line positions.
|
|
|
|
*/
|
|
|
|
initTagEntry(&e, vStringValue(name));
|
2006-05-12 16:24:09 +00:00
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
if (TRUE == isSubroutineDeclaration(cp)) {
|
|
|
|
if (TRUE == PerlKinds[K_SUBROUTINE_DECLARATION].enabled) {
|
|
|
|
kind = K_SUBROUTINE_DECLARATION;
|
|
|
|
} else {
|
|
|
|
vStringClear (name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
e.kind = PerlKinds[kind].letter;
|
|
|
|
e.kindName = PerlKinds[kind].name;
|
|
|
|
|
|
|
|
makeTagEntry(&e);
|
|
|
|
|
|
|
|
if (Option.include.qualifiedTags && qualified &&
|
|
|
|
package != NULL && vStringLength (package) > 0)
|
|
|
|
{
|
|
|
|
vString *const qualifiedName = vStringNew ();
|
|
|
|
vStringCopy (qualifiedName, package);
|
|
|
|
vStringCat (qualifiedName, name);
|
|
|
|
e.name = vStringValue(qualifiedName);
|
|
|
|
makeTagEntry(&e);
|
|
|
|
vStringDelete (qualifiedName);
|
|
|
|
}
|
|
|
|
} else if (vStringLength (name) > 0)
|
|
|
|
{
|
|
|
|
makeSimpleTag (name, PerlKinds, kind);
|
|
|
|
if (Option.include.qualifiedTags && qualified &&
|
|
|
|
K_PACKAGE != kind &&
|
|
|
|
package != NULL && vStringLength (package) > 0)
|
|
|
|
{
|
|
|
|
vString *const qualifiedName = vStringNew ();
|
|
|
|
vStringCopy (qualifiedName, package);
|
|
|
|
vStringCat (qualifiedName, name);
|
|
|
|
makeSimpleTag (qualifiedName, PerlKinds, kind);
|
|
|
|
vStringDelete (qualifiedName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vStringClear (name);
|
2006-05-12 16:24:09 +00:00
|
|
|
}
|
2006-05-10 22:35:55 +00:00
|
|
|
}
|
2009-09-29 11:07:10 +00:00
|
|
|
|
|
|
|
END_MAIN_WHILE:
|
|
|
|
vStringDelete (name);
|
|
|
|
if (package != NULL)
|
|
|
|
vStringDelete (package);
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extern parserDefinition* PerlParser (void)
|
|
|
|
{
|
2009-09-29 11:07:10 +00:00
|
|
|
static const char *const extensions [] = { "pl", "pm", "plx", "perl", NULL };
|
|
|
|
parserDefinition* def = parserNew ("Perl");
|
|
|
|
def->kinds = PerlKinds;
|
|
|
|
def->kindCount = KIND_COUNT (PerlKinds);
|
|
|
|
def->extensions = extensions;
|
|
|
|
def->parser = findPerlTags;
|
|
|
|
return def;
|
2005-11-22 12:26:26 +00:00
|
|
|
}
|
|
|
|
|
2009-09-29 11:07:10 +00:00
|
|
|
/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
|