fortran.c - ctags-5.8
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include "general.h"
#include <string.h>
#include <limits.h>
#include <ctype.h> #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"
#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))
typedef enum eException {
ExceptionNone, ExceptionEOF, ExceptionFixedFormat, ExceptionLoop
} exception_t;
typedef enum eFortranLineType {
LTYPE_UNDETERMINED,
LTYPE_INVALID,
LTYPE_COMMENT,
LTYPE_CONTINUATION,
LTYPE_EOF,
LTYPE_INITIAL,
LTYPE_SHORT
} lineType;
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;
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 } tagType;
typedef struct sTokenInfo {
tokenType type;
keywordId keyword;
tagType tag;
vString* string;
struct sTokenInfo *secondary;
unsigned long lineNumber;
fpos_t filePosition;
} tokenInfo;
static langType Lang_fortran;
static jmp_buf Exception;
static int Ungetc;
static unsigned int Column;
static boolean FreeSourceForm;
static boolean ParsingString;
static tokenInfo *Parent;
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"}
};
static const keywordDesc FortranKeywordTable [] = {
{ "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 };
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);
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);
}
}
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);
}
}
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)
{
vString *label = vStringNew ();
int column = 0;
lineType type = LTYPE_UNDETERMINED;
do {
int c = fileGetc ();
if (column == 0 && strchr ("*Cc!#$Dd", c) != NULL)
type = LTYPE_COMMENT;
else if (c == '\t') {
column = 8;
type = LTYPE_INITIAL;
}
else if (column == 5)
{
if (c == ' ' || c == '0')
type = LTYPE_INITIAL;
else if (vStringLength (label) == 0)
type = LTYPE_CONTINUATION;
else
type = LTYPE_INVALID;
}
else if (c == ' ')
;
else if (c == EOF)
type = LTYPE_EOF;
else if (c == '\n')
type = LTYPE_SHORT;
else if (isdigit (c))
vStringPut (label, c);
else
type = LTYPE_INVALID;
++column;
} while (column < 6 && type == LTYPE_UNDETERMINED);
Assert (type != LTYPE_UNDETERMINED);
if (vStringLength (label) > 0)
{
vStringTerminate (label);
makeLabelTag (label);
}
vStringDelete (label);
return type;
}
static int getFixedFormChar (void)
{
boolean newline = FALSE;
lineType type;
int c = '\0';
if (Column > 0)
{
#ifdef STRICT_FIXED_FORM
if (Column > 71)
c = skipLine ();
else
#endif
{
c = fileGetc ();
++Column;
}
if (c == '\n')
{
newline = TRUE; Column = 0;
}
else if (c == '!' && ! ParsingString)
{
c = skipLine ();
newline = TRUE; Column = 0;
}
else if (c == '&') {
const int c2 = fileGetc ();
if (c2 == '\n')
longjmp (Exception, (int) ExceptionFixedFormat);
else
fileUngetc (c2);
}
}
while (Column == 0)
{
type = getLineType ();
switch (type)
{
case LTYPE_UNDETERMINED:
case LTYPE_INVALID:
longjmp (Exception, (int) ExceptionFixedFormat);
break;
case LTYPE_SHORT: break;
case LTYPE_COMMENT: skipLine (); break;
case LTYPE_EOF:
Column = 6;
if (newline)
c = '\n';
else
c = EOF;
break;
case LTYPE_INITIAL:
if (newline)
{
c = '\n';
Column = 6;
break;
}
case LTYPE_CONTINUATION:
Column = 5;
do
{
c = fileGetc ();
++Column;
} while (isBlank (c));
if (c == '\n')
Column = 0;
else if (Column > 6)
{
fileUngetc (c);
c = ' ';
}
break;
default:
Assert ("Unexpected line type" == NULL);
}
}
return c;
}
static int skipToNextLine (void)
{
int c = skipLine ();
if (c != EOF)
c = fileGetc ();
return c;
}
static int getFreeFormChar (void)
{
static boolean newline = TRUE;
boolean advanceLine = FALSE;
int c = fileGetc ();
if (c == '&')
{
do
c = fileGetc ();
while (isspace (c) && c != '\n');
if (c == '\n')
{
newline = TRUE;
advanceLine = TRUE;
}
else if (c == '!')
advanceLine = TRUE;
else
{
fileUngetc (c);
c = '&';
}
}
else if (newline && (c == '!' || c == '#'))
advanceLine = TRUE;
while (advanceLine)
{
while (isspace (c))
c = fileGetc ();
if (c == '!' || (newline && c == '#'))
{
c = skipToNextLine ();
newline = TRUE;
continue;
}
if (c == '&')
c = fileGetc ();
else
advanceLine = FALSE;
}
newline = (boolean) (c == '\n');
return c;
}
static int getChar (void)
{
int c;
if (Ungetc != '\0')
{
c = Ungetc;
Ungetc = '\0';
}
else if (FreeSourceForm)
c = getFreeFormChar ();
else
c = getFixedFormChar ();
return c;
}
static void ungetChar (const int c)
{
Ungetc = c;
}
static vString *parseInteger (int c)
{
vString *string = vStringNew ();
if (c == '-')
{
vStringPut (string, c);
c = getChar ();
}
else if (! isdigit (c))
c = getChar ();
while (c != EOF && isdigit (c))
{
vStringPut (string, c);
c = getChar ();
}
vStringTerminate (string);
if (c == '_')
{
do
c = getChar ();
while (c != EOF && isalpha (c));
}
ungetChar (c);
return string;
}
static vString *parseNumeric (int c)
{
vString *string = vStringNew ();
vString *integer = parseInteger (c);
vStringCopy (string, integer);
vStringDelete (integer);
c = getChar ();
if (c == '.')
{
integer = parseInteger ('\0');
vStringPut (string, c);
vStringCat (string, integer);
vStringDelete (integer);
c = getChar ();
}
if (tolower (c) == 'e')
{
integer = parseInteger ('\0');
vStringPut (string, c);
vStringCat (string, integer);
vStringDelete (integer);
}
else
ungetChar (c);
vStringTerminate (string);
return string;
}
static void parseString (vString *const string, const int delimiter)
{
const unsigned long inputLineNumber = getInputLineNumber ();
int c;
ParsingString = TRUE;
c = getChar ();
while (c != delimiter && c != '\n' && c != EOF)
{
vStringPut (string, c);
c = getChar ();
}
if (c == '\n' || c == EOF)
{
verbose ("%s: unterminated character string at line %lu\n",
getInputFileName (), inputLineNumber);
if (c == EOF)
longjmp (Exception, (int) ExceptionEOF);
else if (! FreeSourceForm)
longjmp (Exception, (int) ExceptionFixedFormat);
}
vStringTerminate (string);
ParsingString = FALSE;
}
static void parseIdentifier (vString *const string, const int firstChar)
{
int c = firstChar;
do
{
vStringPut (string, c);
c = getChar ();
} while (isident (c));
vStringTerminate (string);
ungetChar (c); }
static void checkForLabel (void)
{
tokenInfo* token = NULL;
int length;
int c;
do
c = getChar ();
while (isBlank (c));
for (length = 0 ; isdigit (c) && length < 5 ; ++length)
{
if (token == NULL)
{
token = newToken ();
token->type = TOKEN_LABEL;
}
vStringPut (token->string, c);
c = getChar ();
}
if (length > 0 && token != NULL)
{
vStringTerminate (token->string);
makeFortranTag (token, TAG_LABEL);
deleteToken (token);
}
ungetChar (c);
}
static void readIdentifier (tokenInfo *const token, const int c)
{
parseIdentifier (token->string, c);
token->keyword = analyzeToken (token->string, Lang_fortran);
if (! isKeyword (token, KEYWORD_NONE))
token->type = TOKEN_KEYWORD;
else
{
token->type = TOKEN_IDENTIFIER;
if (strncmp (vStringValue (token->string), "end", 3) == 0)
{
vString *const sub = vStringNewInit (vStringValue (token->string) + 3);
const keywordId kw = analyzeToken (sub, Lang_fortran);
vStringDelete (sub);
if (kw != KEYWORD_NONE)
{
token->secondary = newToken ();
token->secondary->type = TOKEN_KEYWORD;
token->secondary->keyword = kw;
token->keyword = KEYWORD_end;
}
}
}
}
static void readToken (tokenInfo *const token)
{
int c;
deleteToken (token->secondary);
token->type = TOKEN_UNDEFINED;
token->tag = TAG_UNDEFINED;
token->keyword = KEYWORD_NONE;
token->secondary = NULL;
vStringClear (token->string);
getNextChar:
c = getChar ();
token->lineNumber = getSourceLineNumber ();
token->filePosition = getInputFilePosition ();
switch (c)
{
case EOF: longjmp (Exception, (int) ExceptionEOF); break;
case ' ': goto getNextChar;
case '\t': goto getNextChar;
case ',': token->type = TOKEN_COMMA; break;
case '(': token->type = TOKEN_PAREN_OPEN; break;
case ')': token->type = TOKEN_PAREN_CLOSE; break;
case '%': token->type = TOKEN_PERCENT; break;
case '*':
case '/':
case '+':
case '-':
case '=':
case '<':
case '>':
{
const char *const operatorChars = "*/+=<>";
do {
vStringPut (token->string, c);
c = getChar ();
} while (strchr (operatorChars, c) != NULL);
ungetChar (c);
vStringTerminate (token->string);
token->type = TOKEN_OPERATOR;
break;
}
case '!':
if (FreeSourceForm)
{
do
c = getChar ();
while (c != '\n' && c != EOF);
}
else
{
skipLine ();
Column = 0;
}
case '\n':
token->type = TOKEN_STATEMENT_END;
if (FreeSourceForm)
checkForLabel ();
break;
case '.':
parseIdentifier (token->string, c);
c = getChar ();
if (c == '.')
{
vStringPut (token->string, c);
vStringTerminate (token->string);
token->type = TOKEN_OPERATOR;
}
else
{
ungetChar (c);
token->type = TOKEN_UNDEFINED;
}
break;
case '"':
case '\'':
parseString (token->string, c);
token->type = TOKEN_STRING;
break;
case ';':
token->type = TOKEN_STATEMENT_END;
break;
case ':':
c = getChar ();
if (c == ':')
token->type = TOKEN_DOUBLE_COLON;
else
{
ungetChar (c);
token->type = TOKEN_UNDEFINED;
}
break;
default:
if (isalpha (c))
readIdentifier (token, c);
else if (isdigit (c))
{
vString *numeric = parseNumeric (c);
vStringCat (token->string, numeric);
vStringDelete (numeric);
token->type = TOKEN_NUMERIC;
}
else
token->type = TOKEN_UNDEFINED;
break;
}
}
static void readSubToken (tokenInfo *const token)
{
if (token->secondary == NULL)
{
token->secondary = newToken ();
readToken (token->secondary);
}
}
static void skipToToken (tokenInfo *const token, tokenType type)
{
while (! isType (token, type) && ! isType (token, TOKEN_STATEMENT_END) &&
!(token->secondary != NULL && isType (token->secondary, TOKEN_STATEMENT_END)))
readToken (token);
}
static void skipPast (tokenInfo *const token, tokenType type)
{
skipToToken (token, type);
if (! isType (token, TOKEN_STATEMENT_END))
readToken (token);
}
static void skipToNextStatement (tokenInfo *const token)
{
do
{
skipToToken (token, TOKEN_STATEMENT_END);
readToken (token);
} while (isType (token, TOKEN_STATEMENT_END));
}
static void skipOverParens (tokenInfo *const token)
{
int level = 0;
do {
if (isType (token, TOKEN_STATEMENT_END))
break;
else if (isType (token, TOKEN_PAREN_OPEN))
++level;
else if (isType (token, TOKEN_PAREN_CLOSE))
--level;
readToken (token);
} while (level > 0);
}
static boolean isTypeSpec (tokenInfo *const token)
{
boolean result;
switch (token->keyword)
{
case KEYWORD_byte:
case KEYWORD_integer:
case KEYWORD_real:
case KEYWORD_double:
case KEYWORD_complex:
case KEYWORD_character:
case KEYWORD_logical:
case KEYWORD_record:
case KEYWORD_type:
result = TRUE;
break;
default:
result = FALSE;
break;
}
return result;
}
static boolean isSubprogramPrefix (tokenInfo *const token)
{
boolean result;
switch (token->keyword)
{
case KEYWORD_elemental:
case KEYWORD_pure:
case KEYWORD_recursive:
case KEYWORD_stdcall:
result = TRUE;
break;
default:
result = FALSE;
break;
}
return result;
}
static void parseTypeSpec (tokenInfo *const token)
{
Assert (isTypeSpec (token));
switch (token->keyword)
{
case KEYWORD_character:
readToken (token);
if (isType (token, TOKEN_OPERATOR) &&
strcmp (vStringValue (token->string), "*") == 0)
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
skipOverParens (token);
else if (isType (token, TOKEN_NUMERIC))
readToken (token);
break;
case KEYWORD_byte:
case KEYWORD_complex:
case KEYWORD_integer:
case KEYWORD_logical:
case KEYWORD_real:
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
skipOverParens (token); if (isType (token, TOKEN_OPERATOR) &&
strcmp (vStringValue (token->string), "*") == 0)
{
readToken (token);
readToken (token);
}
break;
case KEYWORD_double:
readToken (token);
if (isKeyword (token, KEYWORD_complex) ||
isKeyword (token, KEYWORD_precision))
readToken (token);
else
skipToToken (token, TOKEN_STATEMENT_END);
break;
case KEYWORD_record:
readToken (token);
if (isType (token, TOKEN_OPERATOR) &&
strcmp (vStringValue (token->string), "/") == 0)
{
readToken (token); readToken (token); readToken (token); }
break;
case KEYWORD_type:
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
skipOverParens (token); else
parseDerivedTypeDef (token);
break;
default:
skipToToken (token, TOKEN_STATEMENT_END);
break;
}
}
static boolean skipStatementIfKeyword (tokenInfo *const token, keywordId keyword)
{
boolean result = FALSE;
if (isKeyword (token, keyword))
{
result = TRUE;
skipToNextStatement (token);
}
return result;
}
static void parseQualifierSpecList (tokenInfo *const token)
{
do
{
readToken (token); switch (token->keyword)
{
case KEYWORD_parameter:
case KEYWORD_allocatable:
case KEYWORD_external:
case KEYWORD_intrinsic:
case KEYWORD_optional:
case KEYWORD_private:
case KEYWORD_pointer:
case KEYWORD_public:
case KEYWORD_save:
case KEYWORD_target:
readToken (token);
break;
case KEYWORD_dimension:
case KEYWORD_intent:
readToken (token);
skipOverParens (token);
break;
default: skipToToken (token, TOKEN_STATEMENT_END); break;
}
} while (isType (token, TOKEN_COMMA));
if (! isType (token, TOKEN_DOUBLE_COLON))
skipToToken (token, TOKEN_STATEMENT_END);
}
static tagType variableTagType (void)
{
tagType result = TAG_VARIABLE;
if (ancestorCount () > 0)
{
const tokenInfo* const parent = ancestorTop ();
switch (parent->tag)
{
case TAG_MODULE: result = TAG_VARIABLE; break;
case TAG_DERIVED_TYPE: result = TAG_COMPONENT; break;
case TAG_FUNCTION: result = TAG_LOCAL; break;
case TAG_SUBROUTINE: result = TAG_LOCAL; break;
default: result = TAG_VARIABLE; break;
}
}
return result;
}
static void parseEntityDecl (tokenInfo *const token)
{
Assert (isType (token, TOKEN_IDENTIFIER));
makeFortranTag (token, variableTagType ());
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
skipOverParens (token);
if (isType (token, TOKEN_OPERATOR) &&
strcmp (vStringValue (token->string), "*") == 0)
{
readToken (token); if (isType (token, TOKEN_PAREN_OPEN))
skipOverParens (token);
else
readToken (token);
}
if (isType (token, TOKEN_OPERATOR))
{
if (strcmp (vStringValue (token->string), "/") == 0)
{ readToken (token);
skipPast (token, TOKEN_OPERATOR);
}
else if (strcmp (vStringValue (token->string), "=") == 0)
{
while (! isType (token, TOKEN_COMMA) &&
! isType (token, TOKEN_STATEMENT_END))
{
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
skipOverParens (token);
}
}
}
}
static void parseEntityDeclList (tokenInfo *const token)
{
if (isType (token, TOKEN_PERCENT))
skipToNextStatement (token);
else while (isType (token, TOKEN_IDENTIFIER) ||
(isType (token, TOKEN_KEYWORD) &&
!isKeyword (token, KEYWORD_function) &&
!isKeyword (token, KEYWORD_subroutine)))
{
if (isType (token, TOKEN_KEYWORD))
token->type = TOKEN_IDENTIFIER;
parseEntityDecl (token);
if (isType (token, TOKEN_COMMA))
readToken (token);
else if (isType (token, TOKEN_STATEMENT_END))
{
skipToNextStatement (token);
break;
}
}
}
static void parseTypeDeclarationStmt (tokenInfo *const token)
{
Assert (isTypeSpec (token));
parseTypeSpec (token);
if (!isType (token, TOKEN_STATEMENT_END)) {
if (isType (token, TOKEN_COMMA))
parseQualifierSpecList (token);
if (isType (token, TOKEN_DOUBLE_COLON))
readToken (token);
parseEntityDeclList (token);
}
if (isType (token, TOKEN_STATEMENT_END))
skipToNextStatement (token);
}
static void parseCommonNamelistStmt (tokenInfo *const token, tagType type)
{
Assert (isKeyword (token, KEYWORD_common) ||
isKeyword (token, KEYWORD_namelist));
readToken (token);
do
{
if (isType (token, TOKEN_OPERATOR) &&
strcmp (vStringValue (token->string), "/") == 0)
{
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
{
makeFortranTag (token, type);
readToken (token);
}
skipPast (token, TOKEN_OPERATOR);
}
if (isType (token, TOKEN_IDENTIFIER))
makeFortranTag (token, TAG_LOCAL);
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
skipOverParens (token); if (isType (token, TOKEN_COMMA))
readToken (token);
} while (! isType (token, TOKEN_STATEMENT_END));
skipToNextStatement (token);
}
static void parseFieldDefinition (tokenInfo *const token)
{
if (isTypeSpec (token))
parseTypeDeclarationStmt (token);
else if (isKeyword (token, KEYWORD_structure))
parseStructureStmt (token);
else if (isKeyword (token, KEYWORD_union))
parseUnionStmt (token);
else
skipToNextStatement (token);
}
static void parseMap (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_map));
skipToNextStatement (token);
while (! isKeyword (token, KEYWORD_end))
parseFieldDefinition (token);
readSubToken (token);
skipToNextStatement (token);
}
static void parseUnionStmt (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_union));
skipToNextStatement (token);
while (isKeyword (token, KEYWORD_map))
parseMap (token);
readSubToken (token);
skipToNextStatement (token);
}
static void parseStructureStmt (tokenInfo *const token)
{
tokenInfo *name;
Assert (isKeyword (token, KEYWORD_structure));
readToken (token);
if (isType (token, TOKEN_OPERATOR) &&
strcmp (vStringValue (token->string), "/") == 0)
{ readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
makeFortranTag (token, TAG_DERIVED_TYPE);
name = newTokenFrom (token);
skipPast (token, TOKEN_OPERATOR);
}
else
{ name = newToken ();
name->type = TOKEN_IDENTIFIER;
name->tag = TAG_DERIVED_TYPE;
vStringCopyS (name->string, "anonymous");
}
while (isType (token, TOKEN_IDENTIFIER))
{ makeFortranTag (token, TAG_COMPONENT);
readToken (token);
if (isType (token, TOKEN_COMMA))
readToken (token);
}
skipToNextStatement (token);
ancestorPush (name);
while (! isKeyword (token, KEYWORD_end))
parseFieldDefinition (token);
readSubToken (token);
skipToNextStatement (token);
ancestorPop ();
deleteToken (name);
}
static boolean parseSpecificationStmt (tokenInfo *const token)
{
boolean result = TRUE;
switch (token->keyword)
{
case KEYWORD_common:
parseCommonNamelistStmt (token, TAG_COMMON_BLOCK);
break;
case KEYWORD_namelist:
parseCommonNamelistStmt (token, TAG_NAMELIST);
break;
case KEYWORD_structure:
parseStructureStmt (token);
break;
case KEYWORD_allocatable:
case KEYWORD_data:
case KEYWORD_dimension:
case KEYWORD_equivalence:
case KEYWORD_external:
case KEYWORD_intent:
case KEYWORD_intrinsic:
case KEYWORD_optional:
case KEYWORD_pointer:
case KEYWORD_private:
case KEYWORD_public:
case KEYWORD_save:
case KEYWORD_target:
skipToNextStatement (token);
break;
default:
result = FALSE;
break;
}
return result;
}
static void parseComponentDefStmt (tokenInfo *const token)
{
Assert (isTypeSpec (token));
parseTypeSpec (token);
if (isType (token, TOKEN_COMMA))
parseQualifierSpecList (token);
if (isType (token, TOKEN_DOUBLE_COLON))
readToken (token);
parseEntityDeclList (token);
}
static void parseDerivedTypeDef (tokenInfo *const token)
{
if (isType (token, TOKEN_COMMA))
parseQualifierSpecList (token);
if (isType (token, TOKEN_DOUBLE_COLON))
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
makeFortranTag (token, TAG_DERIVED_TYPE);
ancestorPush (token);
skipToNextStatement (token);
if (isKeyword (token, KEYWORD_private) ||
isKeyword (token, KEYWORD_sequence))
{
skipToNextStatement (token);
}
while (! isKeyword (token, KEYWORD_end))
{
if (isTypeSpec (token))
parseComponentDefStmt (token);
else
skipToNextStatement (token);
}
readSubToken (token);
skipToToken (token, TOKEN_STATEMENT_END);
ancestorPop ();
}
static void parseInterfaceBlock (tokenInfo *const token)
{
tokenInfo *name = NULL;
Assert (isKeyword (token, KEYWORD_interface));
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
{
makeFortranTag (token, TAG_INTERFACE);
name = newTokenFrom (token);
}
else if (isKeyword (token, KEYWORD_assignment) ||
isKeyword (token, KEYWORD_operator))
{
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
readToken (token);
if (isType (token, TOKEN_OPERATOR))
{
makeFortranTag (token, TAG_INTERFACE);
name = newTokenFrom (token);
}
}
if (name == NULL)
{
name = newToken ();
name->type = TOKEN_IDENTIFIER;
name->tag = TAG_INTERFACE;
}
ancestorPush (name);
while (! isKeyword (token, KEYWORD_end))
{
switch (token->keyword)
{
case KEYWORD_function: parseFunctionSubprogram (token); break;
case KEYWORD_subroutine: parseSubroutineSubprogram (token); break;
default:
if (isSubprogramPrefix (token))
readToken (token);
else if (isTypeSpec (token))
parseTypeSpec (token);
else
skipToNextStatement (token);
break;
}
}
readSubToken (token);
skipToNextStatement (token);
ancestorPop ();
deleteToken (name);
}
static void parseEntryStmt (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_entry));
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
makeFortranTag (token, TAG_ENTRY_POINT);
skipToNextStatement (token);
}
static boolean parseStmtFunctionStmt (tokenInfo *const token)
{
boolean result = FALSE;
Assert (isType (token, TOKEN_IDENTIFIER));
#if 0#endif
readToken (token);
if (isType (token, TOKEN_PAREN_OPEN))
{
skipOverParens (token);
result = (boolean) (isType (token, TOKEN_OPERATOR) &&
strcmp (vStringValue (token->string), "=") == 0);
}
skipToNextStatement (token);
return result;
}
static boolean isIgnoredDeclaration (tokenInfo *const token)
{
boolean result;
switch (token->keyword)
{
case KEYWORD_cexternal:
case KEYWORD_cglobal:
case KEYWORD_dllexport:
case KEYWORD_dllimport:
case KEYWORD_external:
case KEYWORD_format:
case KEYWORD_include:
case KEYWORD_inline:
case KEYWORD_parameter:
case KEYWORD_pascal:
case KEYWORD_pexternal:
case KEYWORD_pglobal:
case KEYWORD_static:
case KEYWORD_value:
case KEYWORD_virtual:
case KEYWORD_volatile:
result = TRUE;
break;
default:
result = FALSE;
break;
}
return result;
}
static boolean parseDeclarationConstruct (tokenInfo *const token)
{
boolean result = TRUE;
switch (token->keyword)
{
case KEYWORD_entry: parseEntryStmt (token); break;
case KEYWORD_interface: parseInterfaceBlock (token); break;
case KEYWORD_stdcall: readToken (token); break;
case KEYWORD_automatic:
readToken (token);
if (isTypeSpec (token))
parseTypeDeclarationStmt (token);
else
skipToNextStatement (token);
result = TRUE;
break;
default:
if (isIgnoredDeclaration (token))
skipToNextStatement (token);
else if (isTypeSpec (token))
{
parseTypeDeclarationStmt (token);
result = TRUE;
}
else if (isType (token, TOKEN_IDENTIFIER))
result = parseStmtFunctionStmt (token);
else
result = parseSpecificationStmt (token);
break;
}
return result;
}
static boolean parseImplicitPartStmt (tokenInfo *const token)
{
boolean result = TRUE;
switch (token->keyword)
{
case KEYWORD_entry: parseEntryStmt (token); break;
case KEYWORD_implicit:
case KEYWORD_include:
case KEYWORD_parameter:
case KEYWORD_format:
skipToNextStatement (token);
break;
default: result = FALSE; break;
}
return result;
}
static boolean parseSpecificationPart (tokenInfo *const token)
{
boolean result = FALSE;
while (skipStatementIfKeyword (token, KEYWORD_use))
result = TRUE;
while (parseImplicitPartStmt (token))
result = TRUE;
while (parseDeclarationConstruct (token))
result = TRUE;
return result;
}
static void parseBlockData (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_block));
readToken (token);
if (isKeyword (token, KEYWORD_data))
{
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
makeFortranTag (token, TAG_BLOCK_DATA);
}
ancestorPush (token);
skipToNextStatement (token);
parseSpecificationPart (token);
while (! isKeyword (token, KEYWORD_end))
skipToNextStatement (token);
readSubToken (token);
skipToNextStatement (token);
ancestorPop ();
}
static void parseInternalSubprogramPart (tokenInfo *const token)
{
boolean done = FALSE;
if (isKeyword (token, KEYWORD_contains))
skipToNextStatement (token);
do
{
switch (token->keyword)
{
case KEYWORD_function: parseFunctionSubprogram (token); break;
case KEYWORD_subroutine: parseSubroutineSubprogram (token); break;
case KEYWORD_end: done = TRUE; break;
default:
if (isSubprogramPrefix (token))
readToken (token);
else if (isTypeSpec (token))
parseTypeSpec (token);
else
readToken (token);
break;
}
} while (! done);
}
static void parseModule (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_module));
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
makeFortranTag (token, TAG_MODULE);
ancestorPush (token);
skipToNextStatement (token);
parseSpecificationPart (token);
if (isKeyword (token, KEYWORD_contains))
parseInternalSubprogramPart (token);
while (! isKeyword (token, KEYWORD_end))
skipToNextStatement (token);
readSubToken (token);
skipToNextStatement (token);
ancestorPop ();
}
static boolean parseExecutionPart (tokenInfo *const token)
{
boolean result = FALSE;
boolean done = FALSE;
while (! done)
{
switch (token->keyword)
{
default:
if (isSubprogramPrefix (token))
readToken (token);
else
skipToNextStatement (token);
result = TRUE;
break;
case KEYWORD_entry:
parseEntryStmt (token);
result = TRUE;
break;
case KEYWORD_contains:
case KEYWORD_function:
case KEYWORD_subroutine:
done = TRUE;
break;
case KEYWORD_end:
readSubToken (token);
if (isSecondaryKeyword (token, KEYWORD_do) ||
isSecondaryKeyword (token, KEYWORD_if) ||
isSecondaryKeyword (token, KEYWORD_select) ||
isSecondaryKeyword (token, KEYWORD_where))
{
skipToNextStatement (token);
result = TRUE;
}
else
done = TRUE;
break;
}
}
return result;
}
static void parseSubprogram (tokenInfo *const token, const tagType tag)
{
Assert (isKeyword (token, KEYWORD_program) ||
isKeyword (token, KEYWORD_function) ||
isKeyword (token, KEYWORD_subroutine));
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
makeFortranTag (token, tag);
ancestorPush (token);
skipToNextStatement (token);
parseSpecificationPart (token);
parseExecutionPart (token);
if (isKeyword (token, KEYWORD_contains))
parseInternalSubprogramPart (token);
readSubToken (token);
skipToNextStatement (token);
ancestorPop ();
}
static void parseFunctionSubprogram (tokenInfo *const token)
{
parseSubprogram (token, TAG_FUNCTION);
}
static void parseSubroutineSubprogram (tokenInfo *const token)
{
parseSubprogram (token, TAG_SUBROUTINE);
}
static void parseMainProgram (tokenInfo *const token)
{
parseSubprogram (token, TAG_PROGRAM);
}
static void parseProgramUnit (tokenInfo *const token)
{
readToken (token);
do
{
if (isType (token, TOKEN_STATEMENT_END))
readToken (token);
else switch (token->keyword)
{
case KEYWORD_block: parseBlockData (token); break;
case KEYWORD_end: skipToNextStatement (token); break;
case KEYWORD_function: parseFunctionSubprogram (token); break;
case KEYWORD_module: parseModule (token); break;
case KEYWORD_program: parseMainProgram (token); break;
case KEYWORD_subroutine: parseSubroutineSubprogram (token); break;
default:
if (isSubprogramPrefix (token))
readToken (token);
else
{
boolean one = parseSpecificationPart (token);
boolean two = parseExecutionPart (token);
if (! (one || two))
readToken (token);
}
break;
}
} while (TRUE);
}
static boolean findFortranTags (const unsigned int passCount)
{
tokenInfo *token;
exception_t exception;
boolean retry;
Assert (passCount < 3);
Parent = newToken ();
token = newToken ();
FreeSourceForm = (boolean) (passCount > 1);
Column = 0;
exception = (exception_t) setjmp (Exception);
if (exception == ExceptionEOF)
retry = FALSE;
else if (exception == ExceptionFixedFormat && ! FreeSourceForm)
{
verbose ("%s: not fixed source form; retry as free source form\n",
getInputFileName ());
retry = TRUE;
}
else
{
parseProgramUnit (token);
retry = FALSE;
}
ancestorClear ();
deleteToken (token);
deleteToken (Parent);
return retry;
}
static void initialize (const langType language)
{
Lang_fortran = language;
buildFortranKeywordHash ();
}
extern parserDefinition* FortranParser (void)
{
static const char *const extensions [] = {
"f", "for", "ftn", "f77", "f90", "f95",
#ifndef CASE_INSENSITIVE_FILENAMES
"F", "FOR", "FTN", "F77", "F90", "F95",
#endif
NULL
};
parserDefinition* def = parserNew ("Fortran");
def->kinds = FortranKinds;
def->kindCount = KIND_COUNT (FortranKinds);
def->extensions = extensions;
def->parser2 = findFortranTags;
def->initialize = initialize;
return def;
}