cosmopolitan/third_party/ctags/pascal.c
2022-11-13 13:26:28 -08:00

270 lines
6.2 KiB
C

// clang-format off
/*
* $Id: pascal.c 536 2007-06-02 06:09:00Z elliotth $
*
* 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 "third_party/ctags/general.h" /* must always come first */
#include "libc/mem/alg.h"
#include "libc/str/str.h"
#include "third_party/ctags/entry.h"
#include "third_party/ctags/parse.h"
#include "third_party/ctags/read.h"
#include "third_party/ctags/vstring.h"
/*
* DATA DEFINITIONS
*/
typedef enum {
K_FUNCTION, K_PROCEDURE
} pascalKind;
static kindOption PascalKinds [] = {
{ TRUE, 'f', "function", "functions"},
{ TRUE, 'p', "procedure", "procedures"}
};
/*
* FUNCTION DEFINITIONS
*/
static void createPascalTag (
tagEntryInfo* const tag, const vString* const name, const int kind)
{
if (PascalKinds [kind].enabled && name != NULL && vStringLength (name) > 0)
{
initTagEntry (tag, vStringValue (name));
tag->kindName = PascalKinds [kind].name;
tag->kind = PascalKinds [kind].letter;
}
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;
}
/* 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;
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;
}
}
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);
createPascalTag (&tag, name, kind);
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;
}
} /* while not eof */
}
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=4 shiftwidth=4: */