home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / eel / fortran.e < prev    next >
Text File  |  1994-03-04  |  19KB  |  482 lines

  1.  
  2. /********** Documentation to be inserted in file "edoc" ***********
  3.  
  4. for-mode    Do automatic indentation of Fortran code.
  5.         This command puts the current buffer in Fortran mode and
  6.         is invoked automatically for a file with extension .for .
  7.         <Enter> runs fortran-indent-line() to insert all necessary tabs
  8.         or spaces in the line just typed.  tab-size is set to 3.  When 
  9.         the buffer is written, you are asked if it should be untabified.
  10.         Matching ('s are shown when typed.  fill-mode is set on, and
  11.         fill-column is set to 72, so excess space-delimited entities
  12.         move to the next line.  The mode line says Fortran Fill.
  13.  
  14. fortran-indent-line    Indents previous line of Fortran code.
  15.         Determines where whitespace should be adjusted to put statement
  16.         numbers in the required columns and to show the block structure of the
  17.         code.  Does nothing to full-line or on-line comments.  If the state-
  18.         ment before the one just typed was incomplete, a continuation is made.
  19.         if-then-else-elseif-endif blocks are indented to show code structure.
  20.         do-continue blocks are also, if there is a continue for each do.
  21.         A do-loop without a continue will cause improper indentation of the
  22.         following statement, unless the loop is only two lines long.
  23.  
  24. fortran-indent-region    Indent a region of Fortran code.
  25.         Indents all lines that terminate between point and mark using
  26.         fortran-indent-line().
  27.  
  28. ***********             End of documentation            **********/
  29.  
  30. /* -----  Gary R. Smith  (smith#gary@b.mfenet@nmfecc.arpa) */
  31.  
  32. /****************** Beginning of file "fortran.e" ******************/
  33.  
  34. #include "eel.h"
  35.  
  36. /*
  37. Automatic indentation for Fortran code.
  38. Written by Gary R. Smith in Sept. 1986.
  39.  
  40. Indentation occurs after each Fortran statement is typed, or when
  41. the command fortran-indent-region is given.  Entering of Fortran code
  42. is speeded up, because the programmer can let Epsilon insert the spaces
  43. needed to produce clearly indented code.
  44.  
  45. Indentation involves scanning a statement and the lines preceding it
  46. to determine where whitespace should be adjusted to put statement
  47. numbers in the required columns and to show the block structure of the
  48. code.  The following steps occur:
  49. 1. If the line begins with a comment character, nothing is done.
  50. 2. If the previous statement was incomplete (ends in one of "=+-*,(" ),
  51.    the present line is indented as a continuation and the remaining steps
  52.    are omitted.
  53. 3. If the line begins with a number (string of digits), indentation
  54.    and right justification occurs for a statement number.
  55. 4. For lines that are neither comments nor continuations, the presence
  56.    of if-then-else-elseif-endif or do-continue blocks is detected by
  57.    examination of the first strings (after statement numbers, if any) in both
  58.    the previous and present statements.  An indentation level found from the
  59.    previous statement is used for the present line except in these cases:
  60.    (a) Presence of "if-then", "else", "elseif-then", or "do" in the previous
  61.        statement, increments (by tab_size) the indentation level for the
  62.        present line.
  63.    (b) Presence of "else", "elseif-then", "endif", or "continue" on the
  64.        present line, decrements the indentation level for the present line.
  65.        (Note that all "continue"s are treated as if they end do-continue
  66.        blocks and may be indented too little if they do not.)
  67. 5. If the previous statement begins with a statement number, it ends a
  68.    two-line do-loop if the statement before it contains "do" followed by
  69.    that statement number.  In this case, the indentation level for the
  70.    present line is decremented.  Only two-line do-loops are detected;
  71.    automatic indentation of longer loops occurs only if they end with
  72.    "continue".
  73.  
  74. The previous statement is found by checking the previous line and its
  75. predecessors, skipping blank and empty lines and (both full-line and
  76. on-line) comments, for a line that is not a continuation.
  77.  
  78. Fill mode is enabled automatically and the fill column set to 73.
  79. Therefore, a space entered past column 72 will cause space-delimited
  80. entities to move to the next line.  If the last character remaining
  81. on the long line indicates an incomplete statement, the next line is
  82. made a continuation automatically.
  83.  
  84. The command fortran-indent-region indents all lines that terminate
  85. between point and mark.  The steps described above are performed on all
  86. of the lines.  A future improvement would be to minimize time-consuming
  87. searches by remembering the indentation level from one line to the next.
  88. */
  89.  
  90. buffer int fortran_mode = 0;    /* Are we in fortran mode? */
  91.  
  92. keytable fortran_tab;           /* Key table for fortran mode */
  93.  
  94. #define FORTRAN_SKIP "(([ \t]*\n)|([ \t]*[!#].*\n))+"
  95. #define FORTRAN_COMMENT "cC!#*"
  96. #define FORTRAN_OP "=+-*,("
  97. #define FORTRAN_CONT '.'
  98. #define MAXWORD 80
  99. #define NBEGWORDS 4
  100. #define NENDWORDS 5
  101.  
  102. char *beg_word[NBEGWORDS];
  103. char *end_word[NENDWORDS];
  104.  
  105. command fortran_indent_region() on fortran_tab[ALT(CTRL('\\'))]
  106. {
  107.         int temp, *begin;
  108.  
  109.         if (point > mark) {     /* If point follows mark, swap */
  110.                 temp = mark;
  111.                 mark = point;
  112.                 point = temp;
  113.         }
  114.         if (nl_reverse())       /* To begin of line, even if it's first */
  115.                 point++;
  116.         begin = alloc_spot();
  117.         *begin = point;
  118.         while (point < mark) {  /* Indent each line in region */
  119.                 if (!nl_forward()) break;
  120.                 fortran_indent_line();
  121.         }
  122.         mark = *begin;          /* Region is just-indented set of lines */
  123.         free_spot(begin);
  124.         iter = 0;
  125.         return;
  126. }
  127.  
  128. fortran_indent_line()
  129. {
  130.         int *orig;
  131.         int this_beg, prev_end, prev_indent, prev_cont, prev_stmt;
  132.         int stmt_no_len, this_col6;
  133.         char key_word[MAXWORD];
  134.         int i, cmp_result;
  135.         char stmt_no[6], stmt_np[6];
  136.         
  137.         if (point == 0) return; /* Handle first line if indenting region */
  138.         
  139.         this_beg = prev_screen_line(1);
  140.         if (index(FORTRAN_COMMENT, character(this_beg))) return;
  141.                                 /* Comment found */
  142.         orig = alloc_spot();
  143.         *orig = point;
  144.         re_search(-1, "[ \t]*\n");
  145.         if (point == this_beg) goto exit;
  146.                                 /* Blank or empty line found */
  147.         
  148.         point = this_beg;
  149.         prev_end = find_prev_end();
  150.         if (prev_end > 0 && stmt_incomp(prev_end)) {
  151.                                         /* Test if stmt is incomplete */
  152.                 prev_indent = find_indent(prev_end);
  153.                 prev_cont = check_cont(prev_end);       /* 1 if was cont. */
  154.                 if (check_cont(this_beg)) move_to_column(6);
  155.                 else make_cont();       /* Make this line a continuation
  156.                                            by replacing cols. 1-6 */
  157.                 to_column(6 + prev_indent + !prev_cont * tab_size);
  158.                         /* Replace indentation with same as previous line
  159.                            + tab_size if previous stmt not continuation */
  160.                 goto exit;
  161.         }
  162.         
  163.         point = this_beg;
  164.         if (stmt_no_len = check_no(stmt_no)) {
  165.                 to_column(5 - stmt_no_len);     /* Right justify stmt no */
  166.                 point = point + stmt_no_len;    /* Point after stmt no */
  167.         }
  168.         to_column(6);   /* Remove whitespace and
  169.                            put space in col. 6 ( or 1-6 if no stmt no) */
  170.         this_col6 = point;
  171.  
  172.         if (prev_end > 0) {             /* Test that previous stmt found */
  173.                 point = prev_end;
  174.                 prev_indent = find_prev_stmt(stmt_no);  /* Find indentation */
  175.                 if (find_word(1, key_word)) {
  176.                                         /* Word that began previous stmt */
  177.                         for(i=0; i < NBEGWORDS; ++i) {  /* Is it on list? */
  178.                                 cmp_result = strcmp(key_word, beg_word[i]);
  179.                                 if (cmp_result >= 0) break;
  180.                         }
  181.                         if (cmp_result == 0 && i == 0) {
  182.                                         /* Is if followed by then? */
  183.                                 point = prev_end;
  184.                                 find_word(-1, key_word);
  185.                                 if (strcmp(key_word, "then")) 
  186.                                         cmp_result = 1; /* No */
  187.                         }
  188.                         if (!cmp_result) {
  189.                                 prev_indent += tab_size;
  190.                                 goto fin;
  191.                         }
  192.                         else {
  193.                 /* Not beginning of block, check for two-line do-loop */
  194.                                 if (*stmt_no == '\0') goto this;
  195.                                 to_begin_line();
  196.                                 if ((prev_end = find_prev_end()) < 0)
  197.                                         goto this;
  198.                                 find_prev_stmt(stmt_np);
  199.                                 if (find_word(1, key_word)) {
  200.                                         if (strcmp(key_word, "do"))
  201.                                                 goto this;
  202.                                         if (check_no(stmt_np) &&
  203.                                                 !strcmp(stmt_no, stmt_np)) {
  204.                                                 prev_indent -= tab_size;
  205.                                                 goto fin;
  206.                                         }
  207.                                 }
  208.                                 else {
  209.                                         key_word_error();
  210.                                         goto exit;
  211.                                 }
  212.                         }
  213.                 }
  214.                 else {          /* Apparent error in user type-in */
  215.                         key_word_error();
  216.                         goto exit;
  217.                 }
  218.         }
  219.         else goto exit;         /* Nothing to be done if no previous stmt */
  220.  
  221. this:   point = this_col6;
  222.         if (find_word(1, key_word)) {   /* Word that begins this stmt */
  223.                 for(i=0; i < NENDWORDS; ++i) {  /* Is it on list? */
  224.                         cmp_result = strcmp(key_word, end_word[i]);
  225.                         if (cmp_result >= 0) break;
  226.                 }
  227.                 if (!cmp_result) prev_indent -= tab_size;
  228.         }
  229.         else {          /* Apparent error in user type-in */
  230.                 key_word_error();
  231.                 goto exit;
  232.         }
  233.         
  234. fin:    point = this_col6;
  235.         if (prev_indent > 0) insert_to_column(6, 6 + prev_indent);
  236. exit:   point = *orig;
  237.         free_spot(orig);
  238.         return;
  239. }
  240.                 
  241. find_prev_end() /* Move point to last character of previous stmt,
  242.                            passing comments and whitespace.  Return
  243.                            point if there was a previous statement,
  244.                            -1 if not */
  245. {
  246.         fortran_skip_lines();
  247.         re_search(-1, FORTRAN_SKIP);    /* Skip to end of stmt */
  248.         if (point > 0) return point;    /* Last character found */
  249.         else return -1;                 /* No previous stmt */
  250. }
  251.  
  252. fortran_skip_lines()    /* Assumes point is at beginning of a line and
  253.                            skips back over blank, empty, or comment lines */
  254. {
  255.         re_search(-1, "[ \t\n]*");      /* Skip to a line with darkspace */
  256.         if (point == 0) return;         /* Done if at beginning of file */
  257.         while (to_begin_line(), index(FORTRAN_COMMENT, curchar())) {
  258.                                 /* Comment line found */
  259.                 re_search(-1, "[ \t\n]*");/* Skip to a line with darkspace */
  260.                 if (point == 0) return;
  261.         }
  262.         nl_forward();                   /* After \n of searchable line */
  263.         return;
  264. }
  265.  
  266. stmt_incomp(loc)        /* Check if character before loc indicates an
  267.                            incomplete stmt.  Return nonzero, if incomplete */
  268. int loc;
  269. {
  270.         if (loc > 0)
  271.                 return (index(FORTRAN_OP, character(--loc)) != 0);
  272.         else return 0;
  273. }
  274.  
  275. find_indent(loc)        /* Return number of columns of indentation in
  276.                            the stmt surrounding loc.  Point is left
  277.                            after the end of the indentation */
  278. int loc;
  279. {
  280.         point = loc;
  281.         move_to_column(6);      /* Position point after continuation col. */
  282.         re_search(1, "[ \t]*"); /* Move point to first darkspace character */
  283.         return current_column() - 6;
  284. }
  285.  
  286. check_cont(loc) /* Return 1 if the line containing loc has whitespace
  287.                    in cols. 1-5, then a darkspace character in col. 6.
  288.                    Otherwise return 0.  Leave point after col. 6. */
  289. int loc;
  290. {
  291.         point = loc;
  292.         to_begin_line();
  293.         re_search(1, "[ \t]*"); /* Move point to first darkspace character */
  294.         if (current_column() == 5) {
  295.                 ++point;
  296.                 return 1;
  297.         }
  298.         else {
  299.                 move_to_column(6);
  300.                 return 0;
  301.         }
  302. }
  303.  
  304. make_cont()             /* Make line containing point a continuation by
  305.                            replacing initial whitespace with 5 columns of
  306.                            whitespace followed by the continuation char.
  307.                            First darkspace should be the first char
  308.                            of Fortran on this line */
  309. {
  310.         to_begin_line();
  311.         to_column(5);
  312.         insert(FORTRAN_CONT);
  313.         return;         /* Point is left after continuation char. */
  314. }
  315.  
  316. check_no(stmt_no)       /* Check if, following point, there is (optional)
  317.                            whitespace, then a string of 5 or fewer digits
  318.                            (a Fortran statement number).  If so, return
  319.                            pointer to it in stmt_no */
  320. char *stmt_no;
  321. {
  322.         int orig = point;
  323.         int begin;
  324.         int length;
  325.         
  326.         re_search(1, "[ \t]*");         /* Skip whitespace, if any */
  327.         begin = point;
  328.         while(isdigit(curchar())) point++;
  329.         length = point - begin;         /* Length of string of digits */
  330.         if (length > 0 && length <= 5) {        /* String was found */
  331.                 point = begin;  
  332.                 parse_string(1, "[0-9]*", stmt_no);
  333.         }
  334.         else {                                  /* Not found */
  335.                 length = 0;
  336.                 *stmt_no = '\0';
  337.         }
  338.  
  339.         point = orig;
  340.         return length;
  341. }
  342.  
  343. find_prev_stmt(stmt_no) /* Find start of previous statement, when point is in
  344.                            it, returning length of statement's indentation
  345.                            or -1 if there was no previous statement.
  346.                            Also, return pointer stmt_no to a statement
  347.                            number, if present */
  348. char *stmt_no;
  349. {
  350.         while (point > 0 && check_cont(point)) {
  351.                 to_begin_line();
  352.                 fortran_skip_lines();
  353.                         /* Skip back over empty and blank lines & comments */
  354.                 --point;
  355.         }
  356.         if (point >= 0) {
  357.                 to_begin_line();
  358.                 check_no(stmt_no);
  359.                 return find_indent(point);
  360.         }
  361.         else return -1;
  362. }
  363.  
  364. find_word(dir, word)  /* Searching in direction dir, 
  365.                          find string of alphabetic characters beginning
  366.                          at point, and copy the string to word,
  367.                          changing to lower case, and return
  368.                          the length of the string as function value */
  369. int dir;
  370. char *word;
  371. {
  372.         int length;
  373.         
  374.         length = parse_string(dir, "[a-zA-Z]*", word);
  375.         while ((*word++ = tolower(*word)) != '\0');
  376.         return length;
  377. }
  378.  
  379. #define MAXERRSTRING 10
  380. key_word_error()        /* Report apparent error in user type-in */
  381. {
  382.         char err_string[MAXERRSTRING+1];
  383.  
  384.         grab(point, point+MAXERRSTRING, err_string);
  385.         say("%s%s", "Expected Fortran keyword but found: ",
  386.                 err_string);
  387.         maybe_ding();
  388.         return;
  389. }
  390.  
  391. command for_mode()
  392. {
  393.         char resp[80];
  394.  
  395.         mode_keys = fortran_tab;                /* Use these keys */
  396.         fortran_tab[')'] = (short) show_matching_delimiter;
  397.         indenter = fortran_indent_line;
  398.         auto_indent = 1;
  399.         fill_mode = 1;
  400.         margin_right = 73;
  401.         fortran_mode = 1;
  402.         major_mode = strsave("Fortran");
  403.         make_mode();
  404.  
  405.         beg_word[0] = strsave("if");
  406.         beg_word[1] = strsave("elseif");
  407.         beg_word[2] = strsave("else");
  408.         beg_word[3] = strsave("do");
  409.  
  410.         end_word[0] = strsave("endif");
  411.         end_word[1] = strsave("end");
  412.         end_word[2] = strsave("elseif");
  413.         end_word[3] = strsave("else");
  414.         end_word[4] = strsave("continue");
  415.  
  416.         if (search(1, "\t")) {
  417.                 get_string(resp, "Tab found in file, untabify buffer? [y]");
  418.                 if (toupper(*resp) != 'N') {
  419.                         resp[0] = 'a';
  420.                         while (!isdigit(*resp)) {
  421.                                 resp[0] = '3';
  422.                                 get_string(resp,
  423.                                         "What size tab for untabify? [3]");
  424.                         }
  425.                         tab_size = resp[0] - '0';
  426.                         build_first = 1;
  427.                         maybe_refresh();
  428.  
  429.                         point = 0;
  430.                         mark = size();
  431.                         untabify_region();
  432.                 }
  433.         }
  434.         point = 0;
  435.         mark = 0;
  436.         tab_size = 3;
  437. }
  438.  
  439. /* Make this the default mode for .for files */
  440. suffix_for()    { for_mode(); }
  441.  
  442. /* save_file() from files.e was modified to behave appropriately if
  443.    narrow or fortran mode is on, by Gary R. Smith, Sept. 1986 */
  444.  
  445. buffer int narrow_mode;
  446.  
  447. command save_file() on cx_tab[CTRL('S')]
  448. {
  449.         char backupname[80];
  450.         int err;
  451.         int mod;
  452.         char resp[80];
  453.         
  454.         iter = 0;
  455.         if (!*filename)
  456.                 return write_file();
  457.         strcpy(backupname, filename);
  458.         strcpy(get_extension(backupname), ".bak");
  459.         if (want_backups && strcmp(filename, backupname)) {
  460.                 delete_file(backupname);        /* don't check errors */
  461.                 rename_file(filename, backupname);
  462.         }
  463.         if (fortran_mode) {     /* Fortran file may need to be untabified */
  464.                 get_string(resp, "Untabify buffer? [y]");
  465.                 if (toupper(*resp) != 'N') {
  466.                         point = 0;
  467.                         mark = size();
  468.                         untabify_region();
  469.                 }
  470.         }
  471.         mod = modified;
  472.         if (err = file_write(filename, strip_returns))
  473.                 file_error(err,filename,"write error");
  474.         else
  475.                 say("%s written.", filename);
  476.                 if (narrow_mode)
  477.                         modified = mod;
  478.         return err;
  479. }
  480.  
  481. /****************** End of file "fortran.e" ******************/
  482.