72b7390e6c
git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@3860 ea778897-0a13-0410-b9d1-a72fbfd435f5
357 lines
8.0 KiB
C
357 lines
8.0 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 2001-2002, Darren Hiebert
|
|
*
|
|
* 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 the Pascal language,
|
|
* including some extensions for Object Pascal.
|
|
*/
|
|
|
|
/*
|
|
* INCLUDE FILES
|
|
*/
|
|
#include "general.h" /* must always come first */
|
|
|
|
#include <string.h>
|
|
|
|
#include "entry.h"
|
|
#include "parse.h"
|
|
#include "read.h"
|
|
#include "main.h"
|
|
#include "vstring.h"
|
|
|
|
/*
|
|
* DATA DEFINITIONS
|
|
*/
|
|
typedef enum {
|
|
K_FUNCTION, K_PROCEDURE
|
|
} pascalKind;
|
|
|
|
static kindOption PascalKinds [] = {
|
|
{ TRUE, 'f', "function", "functions"},
|
|
{ TRUE, 'f', "function", "procedures"}
|
|
};
|
|
|
|
/*
|
|
* FUNCTION DEFINITIONS
|
|
*/
|
|
|
|
static void createPascalTag (tagEntryInfo* const tag,
|
|
const vString* const name, const int kind,
|
|
const char *arglist, const char *vartype)
|
|
{
|
|
if (PascalKinds [kind].enabled && name != NULL && vStringLength (name) > 0)
|
|
{
|
|
initTagEntry (tag, vStringValue (name));
|
|
|
|
tag->kindName = PascalKinds [kind].name;
|
|
tag->kind = PascalKinds [kind].letter;
|
|
tag->extensionFields.arglist = arglist;
|
|
tag->extensionFields.varType = vartype;
|
|
}
|
|
else
|
|
initTagEntry (tag, NULL);
|
|
}
|
|
|
|
static void makePascalTag (const tagEntryInfo* const tag)
|
|
{
|
|
if (tag->name != NULL)
|
|
makeTagEntry (tag);
|
|
}
|
|
|
|
static const unsigned char* dbp;
|
|
|
|
#define starttoken(c) (isalpha ((int) c) || (int) c == '_')
|
|
#define intoken(c) (isalnum ((int) c) || (int) c == '_' || (int) c == '.')
|
|
#define endtoken(c) (! intoken (c) && ! isdigit ((int) c))
|
|
|
|
static boolean tail (const char *cp)
|
|
{
|
|
boolean result = FALSE;
|
|
register int len = 0;
|
|
|
|
while (*cp != '\0' && tolower ((int) *cp) == tolower ((int) dbp [len]))
|
|
cp++, len++;
|
|
if (*cp == '\0' && !intoken (dbp [len]))
|
|
{
|
|
dbp += len;
|
|
result = TRUE;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void parseArglist(const char *buf, char **arglist, char **vartype)
|
|
{
|
|
char *c, *start, *end;
|
|
int level;
|
|
|
|
if (NULL == buf || NULL == arglist)
|
|
return;
|
|
|
|
c = strdup(buf);
|
|
/* parse argument list which can be missing like in "function ginit:integer;" */
|
|
if (NULL != (start = strchr(c, '(')))
|
|
{
|
|
for (level = 1, end = start + 1; level > 0; ++end)
|
|
{
|
|
if ('\0' == *end)
|
|
break;
|
|
else if ('(' == *end)
|
|
++ level;
|
|
else if (')' == *end)
|
|
-- level;
|
|
}
|
|
}
|
|
else /* if no argument list was found, continue looking for a return value */
|
|
{
|
|
start = "()";
|
|
end = c;
|
|
}
|
|
|
|
/* parse return type if requested by passing a non-NULL vartype argument */
|
|
if (NULL != vartype)
|
|
{
|
|
char *var, *var_start;
|
|
|
|
*vartype = NULL;
|
|
|
|
if (NULL != (var = strchr(end, ':')))
|
|
{
|
|
var++; /* skip ':' */
|
|
while (isspace((int) *var))
|
|
++var;
|
|
|
|
if (starttoken(*var))
|
|
{
|
|
var_start = var;
|
|
var++;
|
|
while (intoken(*var))
|
|
var++;
|
|
if (endtoken(*var))
|
|
{
|
|
*var = '\0';
|
|
*vartype = strdup(var_start);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*end = '\0';
|
|
*arglist = strdup(start);
|
|
|
|
eFree(c);
|
|
}
|
|
|
|
|
|
/* Algorithm adapted from from GNU etags.
|
|
* Locates tags for procedures & functions. Doesn't do any type- or
|
|
* var-definitions. It does look for the keyword "extern" or "forward"
|
|
* immediately following the procedure statement; if found, the tag is
|
|
* skipped.
|
|
*/
|
|
static void findPascalTags (void)
|
|
{
|
|
vString *name = vStringNew ();
|
|
tagEntryInfo tag;
|
|
char *arglist = NULL;
|
|
char *vartype = NULL;
|
|
pascalKind kind = K_FUNCTION;
|
|
/* each of these flags is TRUE iff: */
|
|
boolean incomment = FALSE; /* point is inside a comment */
|
|
int comment_char = '\0'; /* type of current comment */
|
|
boolean inquote = FALSE; /* point is inside '..' string */
|
|
boolean get_tagname = FALSE;/* point is after PROCEDURE/FUNCTION
|
|
keyword, so next item = potential tag */
|
|
boolean found_tag = FALSE; /* point is after a potential tag */
|
|
boolean inparms = FALSE; /* point is within parameter-list */
|
|
boolean verify_tag = FALSE; /* point has passed the parm-list, so the
|
|
next token will determine whether this
|
|
is a FORWARD/EXTERN to be ignored, or
|
|
whether it is a real tag */
|
|
|
|
dbp = fileReadLine ();
|
|
while (dbp != NULL)
|
|
{
|
|
int c = *dbp++;
|
|
|
|
if (c == '\0') /* if end of line */
|
|
{
|
|
dbp = fileReadLine ();
|
|
if (dbp == NULL || *dbp == '\0')
|
|
continue;
|
|
if (!((found_tag && verify_tag) || get_tagname))
|
|
c = *dbp++; /* only if don't need *dbp pointing
|
|
to the beginning of the name of
|
|
the procedure or function */
|
|
}
|
|
if (incomment)
|
|
{
|
|
if (comment_char == '{' && c == '}')
|
|
incomment = FALSE;
|
|
else if (comment_char == '(' && c == '*' && *dbp == ')')
|
|
{
|
|
dbp++;
|
|
incomment = FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
else if (inquote)
|
|
{
|
|
if (c == '\'')
|
|
inquote = FALSE;
|
|
continue;
|
|
}
|
|
else switch (c)
|
|
{
|
|
case '\'':
|
|
inquote = TRUE; /* found first quote */
|
|
continue;
|
|
case '{': /* found open { comment */
|
|
incomment = TRUE;
|
|
comment_char = c;
|
|
continue;
|
|
case '(':
|
|
if (*dbp == '*') /* found open (* comment */
|
|
{
|
|
incomment = TRUE;
|
|
comment_char = c;
|
|
dbp++;
|
|
}
|
|
else if (found_tag) /* found '(' after tag, i.e., parm-list */
|
|
inparms = TRUE;
|
|
continue;
|
|
case ')': /* end of parms list */
|
|
if (inparms)
|
|
inparms = FALSE;
|
|
continue;
|
|
case ';':
|
|
if (found_tag && !inparms) /* end of proc or fn stmt */
|
|
{
|
|
verify_tag = TRUE;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (found_tag && verify_tag && *dbp != ' ')
|
|
{
|
|
/* check if this is an "extern" declaration */
|
|
if (*dbp == '\0')
|
|
continue;
|
|
if (tolower ((int) *dbp == 'e'))
|
|
{
|
|
if (tail ("extern")) /* superfluous, really! */
|
|
{
|
|
found_tag = FALSE;
|
|
verify_tag = FALSE;
|
|
}
|
|
}
|
|
else if (tolower ((int) *dbp) == 'f')
|
|
{
|
|
if (tail ("forward")) /* check for forward reference */
|
|
{
|
|
found_tag = FALSE;
|
|
verify_tag = FALSE;
|
|
}
|
|
}
|
|
else if (tolower ((int) *dbp) == 't')
|
|
{
|
|
if (tail ("type")) /* check for forward reference */
|
|
{
|
|
found_tag = FALSE;
|
|
verify_tag = FALSE;
|
|
}
|
|
}
|
|
if (found_tag && verify_tag) /* not external proc, so make tag */
|
|
{
|
|
found_tag = FALSE;
|
|
verify_tag = FALSE;
|
|
makePascalTag (&tag);
|
|
continue;
|
|
}
|
|
}
|
|
if (get_tagname) /* grab name of proc or fn */
|
|
{
|
|
const unsigned char *cp;
|
|
|
|
if (*dbp == '\0')
|
|
continue;
|
|
|
|
/* grab block name */
|
|
while (isspace ((int) *dbp))
|
|
++dbp;
|
|
for (cp = dbp ; *cp != '\0' && !endtoken (*cp) ; cp++)
|
|
continue;
|
|
vStringNCopyS (name, (const char*) dbp, cp - dbp);
|
|
eFree(arglist);
|
|
if (kind == K_FUNCTION)
|
|
eFree(vartype);
|
|
parseArglist((const char*) cp, &arglist, (kind == K_FUNCTION) ? &vartype : NULL);
|
|
createPascalTag (&tag, name, kind, arglist, (kind == K_FUNCTION) ? vartype : NULL);
|
|
dbp = cp; /* set dbp to e-o-token */
|
|
get_tagname = FALSE;
|
|
found_tag = TRUE;
|
|
/* and proceed to check for "extern" */
|
|
}
|
|
else if (!incomment && !inquote && !found_tag)
|
|
{
|
|
switch (tolower ((int) c))
|
|
{
|
|
case 'c':
|
|
if (tail ("onstructor"))
|
|
{
|
|
get_tagname = TRUE;
|
|
kind = K_PROCEDURE;
|
|
}
|
|
break;
|
|
case 'd':
|
|
if (tail ("estructor"))
|
|
{
|
|
get_tagname = TRUE;
|
|
kind = K_PROCEDURE;
|
|
}
|
|
break;
|
|
case 'p':
|
|
if (tail ("rocedure"))
|
|
{
|
|
get_tagname = TRUE;
|
|
kind = K_PROCEDURE;
|
|
}
|
|
break;
|
|
case 'f':
|
|
if (tail ("unction"))
|
|
{
|
|
get_tagname = TRUE;
|
|
kind = K_FUNCTION;
|
|
}
|
|
break;
|
|
case 't':
|
|
if (tail ("ype"))
|
|
{
|
|
get_tagname = TRUE;
|
|
kind = K_FUNCTION;
|
|
}
|
|
break;
|
|
}
|
|
} /* while not eof */
|
|
}
|
|
eFree(arglist);
|
|
eFree(vartype);
|
|
vStringDelete(name);
|
|
}
|
|
|
|
extern parserDefinition* PascalParser (void)
|
|
{
|
|
static const char *const extensions [] = { "p", "pas", NULL };
|
|
parserDefinition* def = parserNew ("Pascal");
|
|
def->extensions = extensions;
|
|
def->kinds = PascalKinds;
|
|
def->kindCount = KIND_COUNT (PascalKinds);
|
|
def->parser = findPascalTags;
|
|
return def;
|
|
}
|
|
|
|
/* vi:set tabstop=8 shiftwidth=4: */
|