home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1993 May
/
SIMTEL_0593.ISO
/
msdos
/
eel
/
fortran.e
< prev
next >
Wrap
Text File
|
1988-10-03
|
20KB
|
482 lines
/********** Documentation to be inserted in file "edoc" ***********
for-mode Do automatic indentation of Fortran code.
This command puts the current buffer in Fortran mode and
is invoked automatically for a file with extension .for .
<Enter> runs fortran-indent-line() to insert all necessary tabs
or spaces in the line just typed. tab-size is set to 3. When
the buffer is written, you are asked if it should be untabified.
Matching ('s are shown when typed. fill-mode is set on, and
fill-column is set to 72, so excess space-delimited entities
move to the next line. The mode line says Fortran Fill.
fortran-indent-line Indents previous line of Fortran code.
Determines where whitespace should be adjusted to put statement
numbers in the required columns and to show the block structure of the
code. Does nothing to full-line or on-line comments. If the state-
ment before the one just typed was incomplete, a continuation is made.
if-then-else-elseif-endif blocks are indented to show code structure.
do-continue blocks are also, if there is a continue for each do.
A do-loop without a continue will cause improper indentation of the
following statement, unless the loop is only two lines long.
fortran-indent-region Indent a region of Fortran code.
Indents all lines that terminate between point and mark using
fortran-indent-line().
*********** End of documentation **********/
/* ----- Gary R. Smith (smith#gary@b.mfenet@nmfecc.arpa) */
/****************** Beginning of file "fortran.e" ******************/
#include "eel.h"
/*
Automatic indentation for Fortran code.
Written by Gary R. Smith in Sept. 1986.
Indentation occurs after each Fortran statement is typed, or when
the command fortran-indent-region is given. Entering of Fortran code
is speeded up, because the programmer can let Epsilon insert the spaces
needed to produce clearly indented code.
Indentation involves scanning a statement and the lines preceding it
to determine where whitespace should be adjusted to put statement
numbers in the required columns and to show the block structure of the
code. The following steps occur:
1. If the line begins with a comment character, nothing is done.
2. If the previous statement was incomplete (ends in one of "=+-*,(" ),
the present line is indented as a continuation and the remaining steps
are omitted.
3. If the line begins with a number (string of digits), indentation
and right justification occurs for a statement number.
4. For lines that are neither comments nor continuations, the presence
of if-then-else-elseif-endif or do-continue blocks is detected by
examination of the first strings (after statement numbers, if any) in both
the previous and present statements. An indentation level found from the
previous statement is used for the present line except in these cases:
(a) Presence of "if-then", "else", "elseif-then", or "do" in the previous
statement, increments (by tab_size) the indentation level for the
present line.
(b) Presence of "else", "elseif-then", "endif", or "continue" on the
present line, decrements the indentation level for the present line.
(Note that all "continue"s are treated as if they end do-continue
blocks and may be indented too little if they do not.)
5. If the previous statement begins with a statement number, it ends a
two-line do-loop if the statement before it contains "do" followed by
that statement number. In this case, the indentation level for the
present line is decremented. Only two-line do-loops are detected;
automatic indentation of longer loops occurs only if they end with
"continue".
The previous statement is found by checking the previous line and its
predecessors, skipping blank and empty lines and (both full-line and
on-line) comments, for a line that is not a continuation.
Fill mode is enabled automatically and the fill column set to 73.
Therefore, a space entered past column 72 will cause space-delimited
entities to move to the next line. If the last character remaining
on the long line indicates an incomplete statement, the next line is
made a continuation automatically.
The command fortran-indent-region indents all lines that terminate
between point and mark. The steps described above are performed on all
of the lines. A future improvement would be to minimize time-consuming
searches by remembering the indentation level from one line to the next.
*/
buffer int fortran_mode = 0; /* Are we in fortran mode? */
keytable fortran_tab; /* Key table for fortran mode */
#define FORTRAN_SKIP "(([ \t]*\n)|([ \t]*[!#].*\n))+"
#define FORTRAN_COMMENT "cC!#*"
#define FORTRAN_OP "=+-*,("
#define FORTRAN_CONT '.'
#define MAXWORD 80
#define NBEGWORDS 4
#define NENDWORDS 5
char *beg_word[NBEGWORDS];
char *end_word[NENDWORDS];
command fortran_indent_region() on fortran_tab[ALT(CTRL('\\'))]
{
int temp, *begin;
if (point > mark) { /* If point follows mark, swap */
temp = mark;
mark = point;
point = temp;
}
if (nl_reverse()) /* To begin of line, even if it's first */
point++;
begin = alloc_spot();
*begin = point;
while (point < mark) { /* Indent each line in region */
if (!nl_forward()) break;
fortran_indent_line();
}
mark = *begin; /* Region is just-indented set of lines */
free_spot(begin);
iter = 0;
return;
}
fortran_indent_line()
{
int *orig;
int this_beg, prev_end, prev_indent, prev_cont, prev_stmt;
int stmt_no_len, this_col6;
char key_word[MAXWORD];
int i, cmp_result;
char stmt_no[6], stmt_np[6];
if (point == 0) return; /* Handle first line if indenting region */
this_beg = prev_screen_line(1);
if (index(FORTRAN_COMMENT, character(this_beg))) return;
/* Comment found */
orig = alloc_spot();
*orig = point;
re_search(-1, "[ \t]*\n");
if (point == this_beg) goto exit;
/* Blank or empty line found */
point = this_beg;
prev_end = find_prev_end();
if (prev_end > 0 && stmt_incomp(prev_end)) {
/* Test if stmt is incomplete */
prev_indent = find_indent(prev_end);
prev_cont = check_cont(prev_end); /* 1 if was cont. */
if (check_cont(this_beg)) move_to_column(6);
else make_cont(); /* Make this line a continuation
by replacing cols. 1-6 */
to_column(6 + prev_indent + !prev_cont * tab_size);
/* Replace indentation with same as previous line
+ tab_size if previous stmt not continuation */
goto exit;
}
point = this_beg;
if (stmt_no_len = check_no(stmt_no)) {
to_column(5 - stmt_no_len); /* Right justify stmt no */
point = point + stmt_no_len; /* Point after stmt no */
}
to_column(6); /* Remove whitespace and
put space in col. 6 ( or 1-6 if no stmt no) */
this_col6 = point;
if (prev_end > 0) { /* Test that previous stmt found */
point = prev_end;
prev_indent = find_prev_stmt(stmt_no); /* Find indentation */
if (find_word(1, key_word)) {
/* Word that began previous stmt */
for(i=0; i < NBEGWORDS; ++i) { /* Is it on list? */
cmp_result = strcmp(key_word, beg_word[i]);
if (cmp_result >= 0) break;
}
if (cmp_result == 0 && i == 0) {
/* Is if followed by then? */
point = prev_end;
find_word(-1, key_word);
if (strcmp(key_word, "then"))
cmp_result = 1; /* No */
}
if (!cmp_result) {
prev_indent += tab_size;
goto fin;
}
else {
/* Not beginning of block, check for two-line do-loop */
if (*stmt_no == '\0') goto this;
to_begin_line();
if ((prev_end = find_prev_end()) < 0)
goto this;
find_prev_stmt(stmt_np);
if (find_word(1, key_word)) {
if (strcmp(key_word, "do"))
goto this;
if (check_no(stmt_np) &&
!strcmp(stmt_no, stmt_np)) {
prev_indent -= tab_size;
goto fin;
}
}
else {
key_word_error();
goto exit;
}
}
}
else { /* Apparent error in user type-in */
key_word_error();
goto exit;
}
}
else goto exit; /* Nothing to be done if no previous stmt */
this: point = this_col6;
if (find_word(1, key_word)) { /* Word that begins this stmt */
for(i=0; i < NENDWORDS; ++i) { /* Is it on list? */
cmp_result = strcmp(key_word, end_word[i]);
if (cmp_result >= 0) break;
}
if (!cmp_result) prev_indent -= tab_size;
}
else { /* Apparent error in user type-in */
key_word_error();
goto exit;
}
fin: point = this_col6;
if (prev_indent > 0) insert_to_column(6, 6 + prev_indent);
exit: point = *orig;
free_spot(orig);
return;
}
find_prev_end() /* Move point to last character of previous stmt,
passing comments and whitespace. Return
point if there was a previous statement,
-1 if not */
{
fortran_skip_lines();
re_search(-1, FORTRAN_SKIP); /* Skip to end of stmt */
if (point > 0) return point; /* Last character found */
else return -1; /* No previous stmt */
}
fortran_skip_lines() /* Assumes point is at beginning of a line and
skips back over blank, empty, or comment lines */
{
re_search(-1, "[ \t\n]*"); /* Skip to a line with darkspace */
if (point == 0) return; /* Done if at beginning of file */
while (to_begin_line(), index(FORTRAN_COMMENT, curchar())) {
/* Comment line found */
re_search(-1, "[ \t\n]*");/* Skip to a line with darkspace */
if (point == 0) return;
}
nl_forward(); /* After \n of searchable line */
return;
}
stmt_incomp(loc) /* Check if character before loc indicates an
incomplete stmt. Return nonzero, if incomplete */
int loc;
{
if (loc > 0)
return (index(FORTRAN_OP, character(--loc)) != 0);
else return 0;
}
find_indent(loc) /* Return number of columns of indentation in
the stmt surrounding loc. Point is left
after the end of the indentation */
int loc;
{
point = loc;
move_to_column(6); /* Position point after continuation col. */
re_search(1, "[ \t]*"); /* Move point to first darkspace character */
return current_column() - 6;
}
check_cont(loc) /* Return 1 if the line containing loc has whitespace
in cols. 1-5, then a darkspace character in col. 6.
Otherwise return 0. Leave point after col. 6. */
int loc;
{
point = loc;
to_begin_line();
re_search(1, "[ \t]*"); /* Move point to first darkspace character */
if (current_column() == 5) {
++point;
return 1;
}
else {
move_to_column(6);
return 0;
}
}
make_cont() /* Make line containing point a continuation by
replacing initial whitespace with 5 columns of
whitespace followed by the continuation char.
First darkspace should be the first char
of Fortran on this line */
{
to_begin_line();
to_column(5);
insert(FORTRAN_CONT);
return; /* Point is left after continuation char. */
}
check_no(stmt_no) /* Check if, following point, there is (optional)
whitespace, then a string of 5 or fewer digits
(a Fortran statement number). If so, return
pointer to it in stmt_no */
char *stmt_no;
{
int orig = point;
int begin;
int length;
re_search(1, "[ \t]*"); /* Skip whitespace, if any */
begin = point;
while(isdigit(curchar())) point++;
length = point - begin; /* Length of string of digits */
if (length > 0 && length <= 5) { /* String was found */
point = begin;
parse_string(1, "[0-9]*", stmt_no);
}
else { /* Not found */
length = 0;
*stmt_no = '\0';
}
point = orig;
return length;
}
find_prev_stmt(stmt_no) /* Find start of previous statement, when point is in
it, returning length of statement's indentation
or -1 if there was no previous statement.
Also, return pointer stmt_no to a statement
number, if present */
char *stmt_no;
{
while (point > 0 && check_cont(point)) {
to_begin_line();
fortran_skip_lines();
/* Skip back over empty and blank lines & comments */
--point;
}
if (point >= 0) {
to_begin_line();
check_no(stmt_no);
return find_indent(point);
}
else return -1;
}
find_word(dir, word) /* Searching in direction dir,
find string of alphabetic characters beginning
at point, and copy the string to word,
changing to lower case, and return
the length of the string as function value */
int dir;
char *word;
{
int length;
length = parse_string(dir, "[a-zA-Z]*", word);
while ((*word++ = tolower(*word)) != '\0');
return length;
}
#define MAXERRSTRING 10
key_word_error() /* Report apparent error in user type-in */
{
char err_string[MAXERRSTRING+1];
grab(point, point+MAXERRSTRING, err_string);
say("%s%s", "Expected Fortran keyword but found: ",
err_string);
maybe_ding();
return;
}
command for_mode()
{
char resp[80];
mode_keys = fortran_tab; /* Use these keys */
fortran_tab[')'] = (short) show_matching_delimiter;
indenter = fortran_indent_line;
auto_indent = 1;
fill_mode = 1;
margin_right = 73;
fortran_mode = 1;
major_mode = strsave("Fortran");
make_mode();
beg_word[0] = strsave("if");
beg_word[1] = strsave("elseif");
beg_word[2] = strsave("else");
beg_word[3] = strsave("do");
end_word[0] = strsave("endif");
end_word[1] = strsave("end");
end_word[2] = strsave("elseif");
end_word[3] = strsave("else");
end_word[4] = strsave("continue");
if (search(1, "\t")) {
get_string(resp, "Tab found in file, untabify buffer? [y]");
if (toupper(*resp) != 'N') {
resp[0] = 'a';
while (!isdigit(*resp)) {
resp[0] = '3';
get_string(resp,
"What size tab for untabify? [3]");
}
tab_size = resp[0] - '0';
build_first = 1;
maybe_refresh();
point = 0;
mark = size();
untabify_region();
}
}
point = 0;
mark = 0;
tab_size = 3;
}
/* Make this the default mode for .for files */
suffix_for() { for_mode(); }
/* save_file() from files.e was modified to behave appropriately if
narrow or fortran mode is on, by Gary R. Smith, Sept. 1986 */
buffer int narrow_mode;
command save_file() on cx_tab[CTRL('S')]
{
char backupname[80];
int err;
int mod;
char resp[80];
iter = 0;
if (!*filename)
return write_file();
strcpy(backupname, filename);
strcpy(get_extension(backupname), ".bak");
if (want_backups && strcmp(filename, backupname)) {
delete_file(backupname); /* don't check errors */
rename_file(filename, backupname);
}
if (fortran_mode) { /* Fortran file may need to be untabified */
get_string(resp, "Untabify buffer? [y]");
if (toupper(*resp) != 'N') {
point = 0;
mark = size();
untabify_region();
}
}
mod = modified;
if (err = file_write(filename, strip_returns))
file_error(err,filename,"write error");
else
say("%s written.", filename);
if (narrow_mode)
modified = mod;
return err;
}
/****************** End of file "fortran.e" ******************/