⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fortran.c

📁 ultraEdit的Ctag标签工具的实现源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
*   $Id: fortran.c,v 1.41 2004/03/15 04:17:36 darren Exp $
*
*   Copyright (c) 1998-2003, 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 Fortran language
*   files.
*/

/*
*   INCLUDE FILES
*/
#include "general.h"	/* must always come first */

#include <string.h>
#include <limits.h>
#include <ctype.h>	/* to define tolower () */
#include <setjmp.h>

#include "debug.h"
#include "entry.h"
#include "keyword.h"
#include "options.h"
#include "parse.h"
#include "read.h"
#include "routines.h"
#include "vstring.h"

/*
*   MACROS
*/
#define isident(c)		(isalnum(c) || (c) == '_')
#define isBlank(c)		(boolean) (c == ' ' || c == '\t')
#define isType(token,t)		(boolean) ((token)->type == (t))
#define isKeyword(token,k)	(boolean) ((token)->keyword == (k))
#define isSecondaryKeyword(token,k)  (boolean) ((token)->secondary == NULL ? \
    FALSE : (token)->secondary->keyword == (k))

/*
*   DATA DECLARATIONS
*/

typedef enum eException {
    ExceptionNone, ExceptionEOF, ExceptionFixedFormat, ExceptionLoop
} exception_t;

/*  Used to designate type of line read in fixed source form.
 */
typedef enum eFortranLineType {
    LTYPE_UNDETERMINED,
    LTYPE_INVALID,
    LTYPE_COMMENT,
    LTYPE_CONTINUATION,
    LTYPE_EOF,
    LTYPE_INITIAL,
    LTYPE_SHORT
} lineType;

/*  Used to specify type of keyword.
 */
typedef enum eKeywordId {
    KEYWORD_NONE = -1,
    KEYWORD_allocatable,
    KEYWORD_assignment,
    KEYWORD_automatic,
    KEYWORD_block,
    KEYWORD_byte,
    KEYWORD_cexternal,
    KEYWORD_cglobal,
    KEYWORD_character,
    KEYWORD_common,
    KEYWORD_complex,
    KEYWORD_contains,
    KEYWORD_data,
    KEYWORD_dimension,
    KEYWORD_dllexport,
    KEYWORD_dllimport,
    KEYWORD_do,
    KEYWORD_double,
    KEYWORD_elemental,
    KEYWORD_end,
    KEYWORD_entry,
    KEYWORD_equivalence,
    KEYWORD_external,
    KEYWORD_format,
    KEYWORD_function,
    KEYWORD_if,
    KEYWORD_implicit,
    KEYWORD_include,
    KEYWORD_inline,
    KEYWORD_integer,
    KEYWORD_intent,
    KEYWORD_interface,
    KEYWORD_intrinsic,
    KEYWORD_logical,
    KEYWORD_map,
    KEYWORD_module,
    KEYWORD_namelist,
    KEYWORD_operator,
    KEYWORD_optional,
    KEYWORD_parameter,
    KEYWORD_pascal,
    KEYWORD_pexternal,
    KEYWORD_pglobal,
    KEYWORD_pointer,
    KEYWORD_precision,
    KEYWORD_private,
    KEYWORD_program,
    KEYWORD_public,
    KEYWORD_pure,
    KEYWORD_real,
    KEYWORD_record,
    KEYWORD_recursive,
    KEYWORD_save,
    KEYWORD_select,
    KEYWORD_sequence,
    KEYWORD_static,
    KEYWORD_stdcall,
    KEYWORD_structure,
    KEYWORD_subroutine,
    KEYWORD_target,
    KEYWORD_then,
    KEYWORD_type,
    KEYWORD_union,
    KEYWORD_use,
    KEYWORD_value,
    KEYWORD_virtual,
    KEYWORD_volatile,
    KEYWORD_where,
    KEYWORD_while
} keywordId;

/*  Used to determine whether keyword is valid for the token language and
 *  what its ID is.
 */
typedef struct sKeywordDesc {
    const char *name;
    keywordId id;
} keywordDesc;

typedef enum eTokenType {
    TOKEN_UNDEFINED,
    TOKEN_COMMA,
    TOKEN_DOUBLE_COLON,
    TOKEN_IDENTIFIER,
    TOKEN_KEYWORD,
    TOKEN_LABEL,
    TOKEN_NUMERIC,
    TOKEN_OPERATOR,
    TOKEN_PAREN_CLOSE,
    TOKEN_PAREN_OPEN,
    TOKEN_PERCENT,
    TOKEN_STATEMENT_END,
    TOKEN_STRING
} tokenType;

typedef enum eTagType {
    TAG_UNDEFINED = -1,
    TAG_BLOCK_DATA,
    TAG_COMMON_BLOCK,
    TAG_ENTRY_POINT,
    TAG_FUNCTION,
    TAG_INTERFACE,
    TAG_COMPONENT,
    TAG_LABEL,
    TAG_LOCAL,
    TAG_MODULE,
    TAG_NAMELIST,
    TAG_PROGRAM,
    TAG_SUBROUTINE,
    TAG_DERIVED_TYPE,
    TAG_VARIABLE,
    TAG_COUNT		/* must be last */
} tagType;

typedef struct sTokenInfo {
    tokenType type;
    keywordId keyword;
    tagType tag;
    vString* string;
    struct sTokenInfo *secondary;
    unsigned long lineNumber;
    fpos_t filePosition;
} tokenInfo;

/*
*   DATA DEFINITIONS
*/

static langType Lang_fortran;
static jmp_buf Exception;
static int Ungetc;
static unsigned int Column;
static boolean FreeSourceForm;
static boolean ParsingString;
static tokenInfo *Parent;

/* indexed by tagType */
static kindOption FortranKinds [] = {
    { TRUE,  'b', "block data", "block data"},
    { TRUE,  'c', "common",     "common blocks"},
    { TRUE,  'e', "entry",      "entry points"},
    { TRUE,  'f', "function",   "functions"},
    { FALSE, 'i', "interface",  "interface contents, generic names, and operators"},
    { TRUE,  'k', "component",  "type and structure components"},
    { TRUE,  'l', "label",      "labels"},
    { FALSE, 'L', "local",      "local, common block, and namelist variables"},
    { TRUE,  'm', "module",     "modules"},
    { TRUE,  'n', "namelist",   "namelists"},
    { TRUE,  'p', "program",    "programs"},
    { TRUE,  's', "subroutine", "subroutines"},
    { TRUE,  't', "type",       "derived types and structures"},
    { TRUE,  'v', "variable",   "program (global) and module variables"}
};

/* For a definition of Fortran 77 with extensions:
 * http://scienide.uwaterloo.ca/MIPSpro7/007-2362-004/sgi_html/index.html
 *
 * For the Compaq Fortran Reference Manual:
 * http://h18009.www1.hp.com/fortran/docs/lrm/dflrm.htm
 */

static const keywordDesc FortranKeywordTable [] = {
    /* keyword		keyword ID */
    { "allocatable",	KEYWORD_allocatable	},
    { "assignment",	KEYWORD_assignment	},
    { "automatic",	KEYWORD_automatic	},
    { "block",		KEYWORD_block		},
    { "byte",		KEYWORD_byte		},
    { "cexternal",	KEYWORD_cexternal	},
    { "cglobal",	KEYWORD_cglobal		},
    { "character",	KEYWORD_character	},
    { "common",		KEYWORD_common		},
    { "complex",	KEYWORD_complex		},
    { "contains",	KEYWORD_contains	},
    { "data",		KEYWORD_data		},
    { "dimension",	KEYWORD_dimension	},
    { "dll_export",	KEYWORD_dllexport	},
    { "dll_import",	KEYWORD_dllimport	},
    { "do",		KEYWORD_do		},
    { "double",		KEYWORD_double		},
    { "elemental",	KEYWORD_elemental	},
    { "end",		KEYWORD_end		},
    { "entry",		KEYWORD_entry		},
    { "equivalence",	KEYWORD_equivalence	},
    { "external",	KEYWORD_external	},
    { "format",		KEYWORD_format		},
    { "function",	KEYWORD_function	},
    { "if",		KEYWORD_if		},
    { "implicit",	KEYWORD_implicit	},
    { "include",	KEYWORD_include		},
    { "inline",		KEYWORD_inline		},
    { "integer",	KEYWORD_integer		},
    { "intent",		KEYWORD_intent		},
    { "interface",	KEYWORD_interface	},
    { "intrinsic",	KEYWORD_intrinsic	},
    { "logical",	KEYWORD_logical		},
    { "map",		KEYWORD_map		},
    { "module",		KEYWORD_module		},
    { "namelist",	KEYWORD_namelist	},
    { "operator",	KEYWORD_operator	},
    { "optional",	KEYWORD_optional	},
    { "parameter",	KEYWORD_parameter	},
    { "pascal",		KEYWORD_pascal		},
    { "pexternal",	KEYWORD_pexternal	},
    { "pglobal",	KEYWORD_pglobal		},
    { "pointer",	KEYWORD_pointer		},
    { "precision",	KEYWORD_precision	},
    { "private",	KEYWORD_private		},
    { "program",	KEYWORD_program		},
    { "public",		KEYWORD_public		},
    { "pure",		KEYWORD_pure		},
    { "real",		KEYWORD_real		},
    { "record",		KEYWORD_record		},
    { "recursive",	KEYWORD_recursive	},
    { "save",		KEYWORD_save		},
    { "select",		KEYWORD_select		},
    { "sequence",	KEYWORD_sequence	},
    { "static",		KEYWORD_static		},
    { "stdcall",	KEYWORD_stdcall		},
    { "structure",	KEYWORD_structure	},
    { "subroutine",	KEYWORD_subroutine	},
    { "target",		KEYWORD_target		},
    { "then",		KEYWORD_then		},
    { "type",		KEYWORD_type		},
    { "union",		KEYWORD_union		},
    { "use",		KEYWORD_use		},
    { "value",		KEYWORD_value		},
    { "virtual",	KEYWORD_virtual		},
    { "volatile",	KEYWORD_volatile	},
    { "where",		KEYWORD_where		},
    { "while",		KEYWORD_while		}
};

static struct {
    unsigned int count;
    unsigned int max;
    tokenInfo* list;
} Ancestors = { 0, 0, NULL };

/*
*   FUNCTION PROTOTYPES
*/
static void parseStructureStmt (tokenInfo *const token);
static void parseUnionStmt (tokenInfo *const token);
static void parseDerivedTypeDef (tokenInfo *const token);
static void parseFunctionSubprogram (tokenInfo *const token);
static void parseSubroutineSubprogram (tokenInfo *const token);

/*
*   FUNCTION DEFINITIONS
*/

static void ancestorPush (tokenInfo *const token)
{
    enum { incrementalIncrease = 10 };
    if (Ancestors.list == NULL)
    {
	Assert (Ancestors.max == 0);
	Ancestors.count = 0;
	Ancestors.max   = incrementalIncrease;
	Ancestors.list  = xMalloc (Ancestors.max, tokenInfo);
    }
    else if (Ancestors.count == Ancestors.max)
    {
	Ancestors.max += incrementalIncrease;
	Ancestors.list = xRealloc (Ancestors.list, Ancestors.max, tokenInfo);
    }
    Ancestors.list [Ancestors.count] = *token;
    Ancestors.list [Ancestors.count].string = vStringNewCopy (token->string);
    Ancestors.count++;
}

static void ancestorPop (void)
{
    Assert (Ancestors.count > 0);
    --Ancestors.count;
    vStringDelete (Ancestors.list [Ancestors.count].string);

    Ancestors.list [Ancestors.count].type       = TOKEN_UNDEFINED;
    Ancestors.list [Ancestors.count].keyword    = KEYWORD_NONE;
    Ancestors.list [Ancestors.count].secondary  = NULL;
    Ancestors.list [Ancestors.count].tag        = TAG_UNDEFINED;
    Ancestors.list [Ancestors.count].string     = NULL;
    Ancestors.list [Ancestors.count].lineNumber = 0L;
}

static const tokenInfo* ancestorScope (void)
{
    tokenInfo *result = NULL;
    unsigned int i;
    for (i = Ancestors.count  ;  i > 0  &&  result == NULL ;  --i)
    {
	tokenInfo *const token = Ancestors.list + i - 1;
	if (token->type == TOKEN_IDENTIFIER &&
	    token->tag != TAG_UNDEFINED  && token->tag != TAG_INTERFACE)
	    result = token;
    }
    return result;
}

static const tokenInfo* ancestorTop (void)
{
    Assert (Ancestors.count > 0);
    return &Ancestors.list [Ancestors.count - 1];
}

#define ancestorCount() (Ancestors.count)

static void ancestorClear (void)
{
    while (Ancestors.count > 0)
	ancestorPop ();
    if (Ancestors.list != NULL)
	eFree (Ancestors.list);
    Ancestors.list = NULL;
    Ancestors.count = 0;
    Ancestors.max = 0;
}

static boolean insideInterface (void)
{
    boolean result = FALSE;
    unsigned int i;
    for (i = 0  ;  i < Ancestors.count && !result ;  ++i)
    {
	if (Ancestors.list [i].tag == TAG_INTERFACE)
	    result = TRUE;
    }
    return result;
}

static void buildFortranKeywordHash (void)
{
    const size_t count = sizeof (FortranKeywordTable) /
			 sizeof (FortranKeywordTable [0]);
    size_t i;
    for (i = 0  ;  i < count  ;  ++i)
    {
	const keywordDesc* const p = &FortranKeywordTable [i];
	addKeyword (p->name, Lang_fortran, (int) p->id);
    }
}

/*
*   Tag generation functions
*/

static tokenInfo *newToken (void)
{
    tokenInfo *const token = xMalloc (1, tokenInfo);

    token->type	        = TOKEN_UNDEFINED;
    token->keyword      = KEYWORD_NONE;
    token->tag	        = TAG_UNDEFINED;
    token->string       = vStringNew ();
    token->secondary    = NULL;
    token->lineNumber   = getSourceLineNumber ();
    token->filePosition	= getInputFilePosition ();

    return token;
}

static tokenInfo *newTokenFrom (tokenInfo *const token)
{
    tokenInfo *result = newToken ();
    *result = *token;
    result->string = vStringNewCopy (token->string);
    token->secondary = NULL;
    return result;
}

static void deleteToken (tokenInfo *const token)
{
    if (token != NULL)
    {
	vStringDelete (token->string);
	deleteToken (token->secondary);
	token->secondary = NULL;
	eFree (token);
    }
}

static boolean isFileScope (const tagType type)
{
    return (boolean) (type == TAG_LABEL || type == TAG_LOCAL);
}

static boolean includeTag (const tagType type)
{
    boolean include;
    Assert (type != TAG_UNDEFINED);
    include = FortranKinds [(int) type].enabled;
    if (include && isFileScope (type))
	include = Option.include.fileScope;
    return include;
}

static void makeFortranTag (tokenInfo *const token, tagType tag)
{
    token->tag = tag;
    if (includeTag (token->tag))
    {
	const char *const name = vStringValue (token->string);
	tagEntryInfo e;

	initTagEntry (&e, name);

	if (token->tag == TAG_COMMON_BLOCK)
	    e.lineNumberEntry = (boolean) (Option.locate != EX_PATTERN);

	e.lineNumber	= token->lineNumber;
	e.filePosition	= token->filePosition;
	e.isFileScope	= isFileScope (token->tag);
	e.kindName	= FortranKinds [token->tag].name;
	e.kind		= FortranKinds [token->tag].letter;
	e.truncateLine	= (boolean) (token->tag != TAG_LABEL);

	if (ancestorCount () > 0)
	{
	    const tokenInfo* const scope = ancestorScope ();
	    if (scope != NULL)
	    {
		e.extensionFields.scope [0] = FortranKinds [scope->tag].name;
		e.extensionFields.scope [1] = vStringValue (scope->string);
	    }
	}
	if (! insideInterface () || includeTag (TAG_INTERFACE))
	    makeTagEntry (&e);
    }
}

/*
*   Parsing functions
*/

static int skipLine (void)
{
    int c;

    do
	c = fileGetc ();
    while (c != EOF  &&  c != '\n');

    return c;
}

static void makeLabelTag (vString *const label)
{
    tokenInfo *token = newToken ();
    token->type  = TOKEN_LABEL;
    vStringCopy (token->string, label);
    makeFortranTag (token, TAG_LABEL);
    deleteToken (token);
}

static lineType getLineType (void)
{
    static vString *label = NULL;
    int column = 0;
    lineType type = LTYPE_UNDETERMINED;

    if (label == NULL)
	label = vStringNew ();

    do		/* read in first 6 "margin" characters */
    {
	int c = fileGetc ();

	/* 3.2.1  Comment_Line.  A comment line is any line that contains
	 * a C or an asterisk in column 1, or contains only blank characters
	 * in  columns 1 through 72.  A comment line that contains a C or
	 * an asterisk in column 1 may contain any character capable  of
	 * representation in the processor in columns 2 through 72.
	 */
	/*  EXCEPTION! Some compilers permit '!' as a commment character here.
	 *
	 *  Treat # and $ in column 1 as comment to permit preprocessor directives.
	 *  Treat D and d in column 1 as comment for HP debug statements.
	 */
	if (column == 0  &&  strchr ("*Cc!#$Dd", c) != NULL)
	    type = LTYPE_COMMENT;
	else if (c == '\t')  /* EXCEPTION! Some compilers permit a tab here */
	{
	    column = 8;
	    type = LTYPE_INITIAL;
	}
	else if (column == 5)
	{
	    /* 3.2.2  Initial_Line.  An initial line is any line that is not
	     * a comment line and contains the character blank or the digit 0
	     * in column 6.  Columns 1 through 5 may contain a statement label
	     * (3.4), or each of the columns 1 through 5 must contain the

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -