/* Implementation of Fortran lexer Copyright (C) 1995-1997 Free Software Foundation, Inc. Contributed by James Craig Burley (burley@gnu.ai.mit.edu). This file is part of GNU Fortran. GNU Fortran is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Fortran is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Fortran; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "proj.h" #include #include "top.h" #include "bad.h" #include "com.h" #include "lex.h" #include "malloc.h" #include "src.h" #if FFECOM_targetCURRENT == FFECOM_targetGCC #include "config.j" #include "flags.j" #include "input.j" #include "tree.j" #endif #ifdef DWARF_DEBUGGING_INFO void dwarfout_resume_previous_source_file (register unsigned); void dwarfout_start_new_source_file (register char *); void dwarfout_define (register unsigned, register char *); void dwarfout_undef (register unsigned, register char *); #endif DWARF_DEBUGGING_INFO static void ffelex_append_to_token_ (char c); static int ffelex_backslash_ (int c, ffewhereColumnNumber col); static void ffelex_bad_1_ (ffebad errnum, ffewhereLineNumber ln0, ffewhereColumnNumber cn0); static void ffelex_bad_2_ (ffebad errnum, ffewhereLineNumber ln0, ffewhereColumnNumber cn0, ffewhereLineNumber ln1, ffewhereColumnNumber cn1); static void ffelex_bad_here_ (int num, ffewhereLineNumber ln0, ffewhereColumnNumber cn0); static void ffelex_finish_statement_ (void); #if FFECOM_targetCURRENT == FFECOM_targetGCC static int ffelex_get_directive_line_ (char **text, FILE *finput); static int ffelex_hash_ (FILE *f); #endif static ffewhereColumnNumber ffelex_image_char_ (int c, ffewhereColumnNumber col); static void ffelex_include_ (void); static bool ffelex_is_free_char_ctx_contin_ (ffewhereColumnNumber col); static bool ffelex_is_free_nonc_ctx_contin_ (ffewhereColumnNumber col); static void ffelex_next_line_ (void); static void ffelex_prepare_eos_ (void); static void ffelex_send_token_ (void); static ffelexHandler ffelex_swallow_tokens_ (ffelexToken t); static ffelexToken ffelex_token_new_ (void); /* Pertaining to the geometry of the input file. */ /* Initial size for card image to be allocated. */ #define FFELEX_columnINITIAL_SIZE_ 255 /* The card image itself, which grows as source lines get longer. It has room for ffelex_card_size_ + 8 characters, and the length of the current image is ffelex_card_length_. (The + 8 characters are made available for easy handling of tabs and such.) */ static char *ffelex_card_image_; static ffewhereColumnNumber ffelex_card_size_; static ffewhereColumnNumber ffelex_card_length_; /* Max width for free-form lines (ISO F90). */ #define FFELEX_FREE_MAX_COLUMNS_ 132 /* True if we saw a tab on the current line, as this (currently) means the line is therefore treated as though final_nontab_column_ were infinite. */ static bool ffelex_saw_tab_; /* TRUE if current line is known to be erroneous, so don't bother expanding room for it just to display it. */ static bool ffelex_bad_line_ = FALSE; /* Last column for vanilla, i.e. non-tabbed, line. Usually 72 or 132. */ static ffewhereColumnNumber ffelex_final_nontab_column_; /* Array for quickly deciding what kind of line the current card has, based on its first character. */ static ffelexType ffelex_first_char_[256]; /* Pertaining to file management. */ /* The wf argument of the most recent active ffelex_file_(fixed,free) function. */ static ffewhereFile ffelex_current_wf_; /* TRUE if an INCLUDE statement can be processed (ffelex_set_include can be called). */ static bool ffelex_permit_include_; /* TRUE if an INCLUDE statement is pending (ffelex_set_include has been called). */ static bool ffelex_set_include_; /* Information on the pending INCLUDE file. */ static FILE *ffelex_include_file_; static bool ffelex_include_free_form_; static ffewhereFile ffelex_include_wherefile_; /* Current master line count. */ static ffewhereLineNumber ffelex_linecount_current_; /* Next master line count. */ static ffewhereLineNumber ffelex_linecount_next_; /* ffewhere info on the latest (currently active) line read from the active source file. */ static ffewhereLine ffelex_current_wl_; static ffewhereColumn ffelex_current_wc_; /* Pertaining to tokens in general. */ /* Initial capacity for text in a CHARACTER/HOLLERITH/NAME/NAMES/NUMBER token. */ #define FFELEX_columnTOKEN_SIZE_ 63 #if FFELEX_columnTOKEN_SIZE_ < FFEWHERE_indexMAX #error "token size too small!" #endif /* Current token being lexed. */ static ffelexToken ffelex_token_; /* Handler for current token. */ static ffelexHandler ffelex_handler_; /* TRUE if fixed-form lexer is to generate NAMES instead of NAME tokens. */ static bool ffelex_names_; /* TRUE if both lexers are to generate NAMES instead of NAME tokens. */ static bool ffelex_names_pure_; /* TRUE if 0-9 starts a NAME token instead of NUMBER, for parsing hex numbers. */ static bool ffelex_hexnum_; /* For ffelex_swallow_tokens(). */ static ffelexHandler ffelex_eos_handler_; /* Number of tokens sent since last EOS or beginning of input file (include INCLUDEd files). */ static unsigned long int ffelex_number_of_tokens_; /* Number of labels sent (as NUMBER tokens) since last reset of ffelex_number_of_tokens_ to 0, should be 0 or 1 in most cases. (Fixed-form source only.) */ static unsigned long int ffelex_label_tokens_; /* Metering for token management, to catch token-memory leaks. */ static long int ffelex_total_tokens_ = 0; static long int ffelex_old_total_tokens_ = 1; static long int ffelex_token_nextid_ = 0; /* Pertaining to lexing CHARACTER and HOLLERITH tokens. */ /* >0 if a Hollerith constant of that length might be in mid-lex, used when the next character seen is 'H' or 'h' to enter HOLLERITH lexing mode (see ffelex_raw_mode_). */ static long int ffelex_expecting_hollerith_; /* -3: Backslash (escape) sequence being lexed in CHARACTER. -2: Possible closing apostrophe/quote seen in CHARACTER. -1: Lexing CHARACTER. 0: Not lexing CHARACTER or HOLLERITH. >0: Lexing HOLLERITH, value is # chars remaining to expect. */ static long int ffelex_raw_mode_; /* When lexing CHARACTER, open quote/apostrophe (either ' or "). */ static char ffelex_raw_char_; /* TRUE when backslash processing had to use most recent character to finish its state engine, but that character is not part of the backslash sequence, so must be reconsidered as a "normal" character in CHARACTER/HOLLERITH lexing. */ static bool ffelex_backslash_reconsider_ = FALSE; /* Characters preread before lexing happened (might include EOF). */ static int *ffelex_kludge_chars_ = NULL; /* Doing the kludge processing, so not initialized yet. */ static bool ffelex_kludge_flag_ = FALSE; /* The beginning of a (possible) CHARACTER/HOLLERITH token. */ static ffewhereLine ffelex_raw_where_line_; static ffewhereColumn ffelex_raw_where_col_; /* Call this to append another character to the current token. If it isn't currently big enough for it, it will be enlarged. The current token must be a CHARACTER, HOLLERITH, NAME, NAMES, or NUMBER. */ static void ffelex_append_to_token_ (char c) { if (ffelex_token_->text == NULL) { ffelex_token_->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", FFELEX_columnTOKEN_SIZE_ + 1); ffelex_token_->size = FFELEX_columnTOKEN_SIZE_; ffelex_token_->length = 0; } else if (ffelex_token_->length >= ffelex_token_->size) { ffelex_token_->text = malloc_resize_ksr (malloc_pool_image (), ffelex_token_->text, (ffelex_token_->size << 1) + 1, ffelex_token_->size + 1); ffelex_token_->size <<= 1; assert (ffelex_token_->length < ffelex_token_->size); } #ifdef MAP_CHARACTER Sorry, MAP_CHARACTER is not going to work as expected in GNU Fortran, please contact fortran@gnu.ai.mit.edu if you wish to fund work to port g77 to non-ASCII machines. #endif ffelex_token_->text[ffelex_token_->length++] = c; } /* Do backslash (escape) processing for a CHARACTER/HOLLERITH token being lexed. */ static int ffelex_backslash_ (int c, ffewhereColumnNumber col) { static int state = 0; static unsigned int count; static int code; static unsigned int firstdig = 0; static int nonnull; static ffewhereLineNumber line; static ffewhereColumnNumber column; /* See gcc/c-lex.c readescape() for a straightforward version of this state engine for handling backslashes in character/ hollerith constants. */ #define wide_flag 0 #define warn_traditional 0 #define flag_traditional 0 switch (state) { case 0: if ((c == '\\') && (ffelex_raw_mode_ != 0) && ffe_is_backslash ()) { state = 1; column = col + 1; line = ffelex_linecount_current_; return EOF; } return c; case 1: state = 0; /* Assume simple case. */ switch (c) { case 'x': if (warn_traditional) { ffebad_start_msg_lex ("The meaning of `\\x' (at %0) varies with -traditional", FFEBAD_severityWARNING); ffelex_bad_here_ (0, line, column); ffebad_finish (); } if (flag_traditional) return c; code = 0; count = 0; nonnull = 0; state = 2; return EOF; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': code = c - '0'; count = 1; state = 3; return EOF; case '\\': case '\'': case '"': return c; #if 0 /* Inappropriate for Fortran. */ case '\n': ffelex_next_line_ (); *ignore_ptr = 1; return 0; #endif case 'n': return TARGET_NEWLINE; case 't': return TARGET_TAB; case 'r': return TARGET_CR; case 'f': return TARGET_FF; case 'b': return TARGET_BS; case 'a': if (warn_traditional) { ffebad_start_msg_lex ("The meaning of `\\a' (at %0) varies with -traditional", FFEBAD_severityWARNING); ffelex_bad_here_ (0, line, column); ffebad_finish (); } if (flag_traditional) return c; return TARGET_BELL; case 'v': #if 0 /* Vertical tab is present in common usage compilers. */ if (flag_traditional) return c; #endif return TARGET_VT; case 'e': case 'E': case '(': case '{': case '[': case '%': if (pedantic) { char m[2]; m[0] = c; m[1] = '\0'; ffebad_start_msg_lex ("Non-ANSI-C-standard escape sequence `\\%A' at %0", FFEBAD_severityPEDANTIC); ffelex_bad_here_ (0, line, column); ffebad_string (m); ffebad_finish (); } return (c == 'E' || c == 'e') ? 033 : c; case '?': return c; default: if (c >= 040 && c < 0177) { char m[2]; m[0] = c; m[1] = '\0'; ffebad_start_msg_lex ("Unknown escape sequence `\\%A' at %0", FFEBAD_severityPEDANTIC); ffelex_bad_here_ (0, line, column); ffebad_string (m); ffebad_finish (); } else if (c == EOF) { ffebad_start_msg_lex ("Unterminated escape sequence `\\' at %0", FFEBAD_severityPEDANTIC); ffelex_bad_here_ (0, line, column); ffebad_finish (); } else { char m[20]; sprintf (&m[0], "%x", c); ffebad_start_msg_lex ("Unknown escape sequence `\\' followed by char code 0x%A at %0", FFEBAD_severityPEDANTIC); ffelex_bad_here_ (0, line, column); ffebad_string (m); ffebad_finish (); } } return c; case 2: if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')) { code *= 16; if (c >= 'a' && c <= 'f') code += c - 'a' + 10; if (c >= 'A' && c <= 'F') code += c - 'A' + 10; if (c >= '0' && c <= '9') code += c - '0'; if (code != 0 || count != 0) { if (count == 0) firstdig = code; count++; } nonnull = 1; return EOF; } state = 0; if (! nonnull) { ffebad_start_msg_lex ("\\x used at %0 with no following hex digits", FFEBAD_severityFATAL); ffelex_bad_here_ (0, line, column); ffebad_finish (); } else if (count == 0) /* Digits are all 0's. Ok. */ ; else if ((count - 1) * 4 >= TYPE_PRECISION (integer_type_node) || (count > 1 && ((1 << (TYPE_PRECISION (integer_type_node) - (count - 1) * 4)) <= (int) firstdig))) { ffebad_start_msg_lex ("Hex escape at %0 out of range", FFEBAD_severityPEDANTIC); ffelex_bad_here_ (0, line, column); ffebad_finish (); } break; case 3: if ((c <= '7') && (c >= '0') && (count++ < 3)) { code = (code * 8) + (c - '0'); return EOF; } state = 0; break; default: assert ("bad backslash state" == NULL); abort (); } /* Come here when code has a built character, and c is the next character that might (or might not) be the next one in the constant. */ /* Don't bother doing this check for each character going into CHARACTER or HOLLERITH constants, just the escaped-value ones. gcc apparently checks every single character, which seems like it'd be kinda slow and not worth doing anyway. */ if (!wide_flag && TYPE_PRECISION (char_type_node) < HOST_BITS_PER_INT && code >= (1 << TYPE_PRECISION (char_type_node))) { ffebad_start_msg_lex ("Escape sequence at %0 out of range for character", FFEBAD_severityFATAL); ffelex_bad_here_ (0, line, column); ffebad_finish (); } if (c == EOF) { /* Known end of constant, just append this character. */ ffelex_append_to_token_ (code); if (ffelex_raw_mode_ > 0) --ffelex_raw_mode_; return EOF; } /* Have two characters to handle. Do the first, then leave it to the caller to detect anything special about the second. */ ffelex_append_to_token_ (code); if (ffelex_raw_mode_ > 0) --ffelex_raw_mode_; ffelex_backslash_reconsider_ = TRUE; return c; } /* ffelex_bad_1_ -- Issue diagnostic with one source point ffelex_bad_1_(FFEBAD_SOME_ERROR,ffelex_linecount_current_,column + 1); Creates ffewhere line and column objects for the source point, sends them along with the error code to ffebad, then kills the line and column objects before returning. */ static void ffelex_bad_1_ (ffebad errnum, ffewhereLineNumber ln0, ffewhereColumnNumber cn0) { ffewhereLine wl0; ffewhereColumn wc0; wl0 = ffewhere_line_new (ln0); wc0 = ffewhere_column_new (cn0); ffebad_start_lex (errnum); ffebad_here (0, wl0, wc0); ffebad_finish (); ffewhere_line_kill (wl0); ffewhere_column_kill (wc0); } /* ffelex_bad_2_ -- Issue diagnostic with two source points ffelex_bad_2_(FFEBAD_SOME_ERROR,ffelex_linecount_current_,column + 1, otherline,othercolumn); Creates ffewhere line and column objects for the source points, sends them along with the error code to ffebad, then kills the line and column objects before returning. */ static void ffelex_bad_2_ (ffebad errnum, ffewhereLineNumber ln0, ffewhereColumnNumber cn0, ffewhereLineNumber ln1, ffewhereColumnNumber cn1) { ffewhereLine wl0, wl1; ffewhereColumn wc0, wc1; wl0 = ffewhere_line_new (ln0); wc0 = ffewhere_column_new (cn0); wl1 = ffewhere_line_new (ln1); wc1 = ffewhere_column_new (cn1); ffebad_start_lex (errnum); ffebad_here (0, wl0, wc0); ffebad_here (1, wl1, wc1); ffebad_finish (); ffewhere_line_kill (wl0); ffewhere_column_kill (wc0); ffewhere_line_kill (wl1); ffewhere_column_kill (wc1); } static void ffelex_bad_here_ (int n, ffewhereLineNumber ln0, ffewhereColumnNumber cn0) { ffewhereLine wl0; ffewhereColumn wc0; wl0 = ffewhere_line_new (ln0); wc0 = ffewhere_column_new (cn0); ffebad_here (n, wl0, wc0); ffewhere_line_kill (wl0); ffewhere_column_kill (wc0); } #if FFECOM_targetCURRENT == FFECOM_targetGCC static int ffelex_getc_ (FILE *finput) { int c; if (ffelex_kludge_chars_ == NULL) return getc (finput); c = *ffelex_kludge_chars_++; if (c != 0) return c; ffelex_kludge_chars_ = NULL; return getc (finput); } #endif #if FFECOM_targetCURRENT == FFECOM_targetGCC static int ffelex_cfebackslash_ (int *use_d, int *d, FILE *finput) { register int c = getc (finput); register int code; register unsigned count; unsigned firstdig = 0; int nonnull; *use_d = 0; switch (c) { case 'x': if (warn_traditional) warning ("the meaning of `\\x' varies with -traditional"); if (flag_traditional) return c; code = 0; count = 0; nonnull = 0; while (1) { c = getc (finput); if (!(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F') && !(c >= '0' && c <= '9')) { *use_d = 1; *d = c; break; } code *= 16; if (c >= 'a' && c <= 'f') code += c - 'a' + 10; if (c >= 'A' && c <= 'F') code += c - 'A' + 10; if (c >= '0' && c <= '9') code += c - '0'; if (code != 0 || count != 0) { if (count == 0) firstdig = code; count++; } nonnull = 1; } if (! nonnull) error ("\\x used with no following hex digits"); else if (count == 0) /* Digits are all 0's. Ok. */ ; else if ((count - 1) * 4 >= TYPE_PRECISION (integer_type_node) || (count > 1 && (((unsigned) 1 << (TYPE_PRECISION (integer_type_node) - (count - 1) * 4)) <= firstdig))) pedwarn ("hex escape out of range"); return code; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': code = 0; count = 0; while ((c <= '7') && (c >= '0') && (count++ < 3)) { code = (code * 8) + (c - '0'); c = getc (finput); } *use_d = 1; *d = c; return code; case '\\': case '\'': case '"': return c; case '\n': ffelex_next_line_ (); *use_d = 2; return 0; case EOF: *use_d = 1; *d = EOF; return EOF; case 'n': return TARGET_NEWLINE; case 't': return TARGET_TAB; case 'r': return TARGET_CR; case 'f': return TARGET_FF; case 'b': return TARGET_BS; case 'a': if (warn_traditional) warning ("the meaning of `\\a' varies with -traditional"); if (flag_traditional) return c; return TARGET_BELL; case 'v': #if 0 /* Vertical tab is present in common usage compilers. */ if (flag_traditional) return c; #endif return TARGET_VT; case 'e': case 'E': if (pedantic) pedwarn ("non-ANSI-standard escape sequence, `\\%c'", c); return 033; case '?': return c; /* `\(', etc, are used at beginning of line to avoid confusing Emacs. */ case '(': case '{': case '[': /* `\%' is used to prevent SCCS from getting confused. */ case '%': if (pedantic) pedwarn ("non-ANSI escape sequence `\\%c'", c); return c; } if (c >= 040 && c < 0177) pedwarn ("unknown escape sequence `\\%c'", c); else pedwarn ("unknown escape sequence: `\\' followed by char code 0x%x", c); return c; } #endif /* A miniature version of the C front-end lexer. */ #if FFECOM_targetCURRENT == FFECOM_targetGCC static int ffelex_cfelex_ (ffelexToken *xtoken, FILE *finput, int c) { ffelexToken token; char buff[129]; char *p; char *q; char *r; register unsigned buffer_length; if ((*xtoken != NULL) && !ffelex_kludge_flag_) ffelex_token_kill (*xtoken); switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': buffer_length = ARRAY_SIZE (buff); p = &buff[0]; q = p; r = &buff[buffer_length]; for (;;) { *p++ = c; if (p >= r) { register unsigned bytes_used = (p - q); buffer_length *= 2; q = (char *)xrealloc (q, buffer_length); p = &q[bytes_used]; r = &q[buffer_length]; } c = ffelex_getc_ (finput); if (!isdigit (c)) break; } *p = '\0'; token = ffelex_token_new_number (q, ffewhere_line_unknown (), ffewhere_column_unknown ()); if (q != &buff[0]) free (q); break; case '\"': buffer_length = ARRAY_SIZE (buff); p = &buff[0]; q = p; r = &buff[buffer_length]; c = ffelex_getc_ (finput); for (;;) { bool done = FALSE; int use_d = 0; int d; switch (c) { case '\"': c = getc (finput); done = TRUE; break; case '\\': /* ~~~~~ */ c = ffelex_cfebackslash_ (&use_d, &d, finput); break; case EOF: case '\n': fatal ("Badly formed directive -- no closing quote"); done = TRUE; break; default: break; } if (done) break; if (use_d != 2) /* 0=>c, 1=>cd, 2=>nil. */ { *p++ = c; if (p >= r) { register unsigned bytes_used = (p - q); buffer_length = bytes_used * 2; q = (char *)xrealloc (q, buffer_length); p = &q[bytes_used]; r = &q[buffer_length]; } } if (use_d == 1) c = d; else c = getc (finput); } *p = '\0'; token = ffelex_token_new_character (q, ffewhere_line_unknown (), ffewhere_column_unknown ()); if (q != &buff[0]) free (q); break; default: token = NULL; break; } *xtoken = token; return c; } #endif #if FFECOM_targetCURRENT == FFECOM_targetGCC static void ffelex_file_pop_ (char *input_filename) { if (input_file_stack->next) { struct file_stack *p = input_file_stack; input_file_stack = p->next; free (p); input_file_stack_tick++; #ifdef DWARF_DEBUGGING_INFO if (debug_info_level == DINFO_LEVEL_VERBOSE && write_symbols == DWARF_DEBUG) dwarfout_resume_previous_source_file (input_file_stack->line); #endif /* DWARF_DEBUGGING_INFO */ } else error ("#-lines for entering and leaving files don't match"); /* Now that we've pushed or popped the input stack, update the name in the top element. */ if (input_file_stack) input_file_stack->name = input_filename; } #endif #if FFECOM_targetCURRENT == FFECOM_targetGCC static void ffelex_file_push_ (int old_lineno, char *input_filename) { struct file_stack *p = (struct file_stack *) xmalloc (sizeof (struct file_stack)); input_file_stack->line = old_lineno; p->next = input_file_stack; p->name = input_filename; input_file_stack = p; input_file_stack_tick++; #ifdef DWARF_DEBUGGING_INFO if (debug_info_level == DINFO_LEVEL_VERBOSE && write_symbols == DWARF_DEBUG) dwarfout_start_new_source_file (input_filename); #endif /* DWARF_DEBUGGING_INFO */ /* Now that we've pushed or popped the input stack, update the name in the top element. */ if (input_file_stack) input_file_stack->name = input_filename; } #endif /* Prepare to finish a statement-in-progress by sending the current token, if any, then setting up EOS as the current token with the appropriate current pointer. The caller can then move the current pointer before actually sending EOS, if desired, as it is in typical fixed-form cases. */ static void ffelex_prepare_eos_ () { if (ffelex_token_->type != FFELEX_typeNONE) { ffelex_backslash_ (EOF, 0); switch (ffelex_raw_mode_) { case -2: break; case -1: ffebad_start_lex ((ffelex_raw_char_ == '\'') ? FFEBAD_NO_CLOSING_APOSTROPHE : FFEBAD_NO_CLOSING_QUOTE); ffebad_here (0, ffelex_token_->where_line, ffelex_token_->where_col); ffebad_here (1, ffelex_current_wl_, ffelex_current_wc_); ffebad_finish (); break; case 0: break; default: { char num[20]; ffebad_start_lex (FFEBAD_NOT_ENOUGH_HOLLERITH_CHARS); ffebad_here (0, ffelex_token_->where_line, ffelex_token_->where_col); ffebad_here (1, ffelex_current_wl_, ffelex_current_wc_); sprintf (num, "%lu", (unsigned long) ffelex_raw_mode_); ffebad_string (num); ffebad_finish (); break; } } ffelex_raw_mode_ = 0; ffelex_send_token_ (); } ffelex_token_->type = FFELEX_typeEOS; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_use (ffelex_current_wc_); } static void ffelex_finish_statement_ () { if ((ffelex_number_of_tokens_ == 0) && (ffelex_token_->type == FFELEX_typeNONE)) return; /* Don't have a statement pending. */ if (ffelex_token_->type != FFELEX_typeEOS) ffelex_prepare_eos_ (); ffelex_permit_include_ = TRUE; ffelex_send_token_ (); ffelex_permit_include_ = FALSE; ffelex_number_of_tokens_ = 0; ffelex_label_tokens_ = 0; ffelex_names_ = TRUE; ffelex_names_pure_ = FALSE; /* Probably not necessary. */ ffelex_hexnum_ = FALSE; if (!ffe_is_ffedebug ()) return; /* For debugging purposes only. */ if (ffelex_total_tokens_ != ffelex_old_total_tokens_) { fprintf (dmpout, "; token_track had %ld tokens, now have %ld.\n", ffelex_old_total_tokens_, ffelex_total_tokens_); ffelex_old_total_tokens_ = ffelex_total_tokens_; } } /* Copied from gcc/c-common.c get_directive_line. */ #if FFECOM_targetCURRENT == FFECOM_targetGCC static int ffelex_get_directive_line_ (char **text, FILE *finput) { static char *directive_buffer = NULL; static unsigned buffer_length = 0; register char *p; register char *buffer_limit; register int looking_for = 0; register int char_escaped = 0; if (buffer_length == 0) { directive_buffer = (char *)xmalloc (128); buffer_length = 128; } buffer_limit = &directive_buffer[buffer_length]; for (p = directive_buffer; ; ) { int c; /* Make buffer bigger if it is full. */ if (p >= buffer_limit) { register unsigned bytes_used = (p - directive_buffer); buffer_length *= 2; directive_buffer = (char *)xrealloc (directive_buffer, buffer_length); p = &directive_buffer[bytes_used]; buffer_limit = &directive_buffer[buffer_length]; } c = getc (finput); /* Discard initial whitespace. */ if ((c == ' ' || c == '\t') && p == directive_buffer) continue; /* Detect the end of the directive. */ if ((c == '\n' && looking_for == 0) || c == EOF) { if (looking_for != 0) fatal ("Bad directive -- missing close-quote"); *p++ = '\0'; *text = directive_buffer; return c; } *p++ = c; if (c == '\n') ffelex_next_line_ (); /* Handle string and character constant syntax. */ if (looking_for) { if (looking_for == c && !char_escaped) looking_for = 0; /* Found terminator... stop looking. */ } else if (c == '\'' || c == '"') looking_for = c; /* Don't stop buffering until we see another another one of these (or an EOF). */ /* Handle backslash. */ char_escaped = (c == '\\' && ! char_escaped); } } #endif /* Handle # directives that make it through (or are generated by) the preprocessor. As much as reasonably possible, emulate the behavior of the gcc compiler phase cc1, though interactions between #include and INCLUDE might possibly produce bizarre results in terms of error reporting and the generation of debugging info vis-a-vis the locations of some things. Returns the next character unhandled, which is always newline or EOF. */ #if FFECOM_targetCURRENT == FFECOM_targetGCC static int ffelex_hash_ (FILE *finput) { register int c; ffelexToken token = NULL; /* Read first nonwhite char after the `#'. */ c = ffelex_getc_ (finput); while (c == ' ' || c == '\t') c = ffelex_getc_ (finput); /* If a letter follows, then if the word here is `line', skip it and ignore it; otherwise, ignore the line, with an error if the word isn't `pragma', `ident', `define', or `undef'. */ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { if (c == 'p') { if (getc (finput) == 'r' && getc (finput) == 'a' && getc (finput) == 'g' && getc (finput) == 'm' && getc (finput) == 'a' && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n' || c == EOF)) { goto skipline; #if 0 /* g77 doesn't handle pragmas, so ignores them FOR NOW. */ #ifdef HANDLE_SYSV_PRAGMA return handle_sysv_pragma (finput, c); #else /* !HANDLE_SYSV_PRAGMA */ #ifdef HANDLE_PRAGMA HANDLE_PRAGMA (finput); #endif /* HANDLE_PRAGMA */ goto skipline; #endif /* !HANDLE_SYSV_PRAGMA */ #endif /* 0 */ } } else if (c == 'd') { if (getc (finput) == 'e' && getc (finput) == 'f' && getc (finput) == 'i' && getc (finput) == 'n' && getc (finput) == 'e' && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n' || c == EOF)) { char *text; c = ffelex_get_directive_line_ (&text, finput); #ifdef DWARF_DEBUGGING_INFO if ((debug_info_level == DINFO_LEVEL_VERBOSE) && (write_symbols == DWARF_DEBUG)) dwarfout_define (lineno, text); #endif /* DWARF_DEBUGGING_INFO */ goto skipline; } } else if (c == 'u') { if (getc (finput) == 'n' && getc (finput) == 'd' && getc (finput) == 'e' && getc (finput) == 'f' && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n' || c == EOF)) { char *text; c = ffelex_get_directive_line_ (&text, finput); #ifdef DWARF_DEBUGGING_INFO if ((debug_info_level == DINFO_LEVEL_VERBOSE) && (write_symbols == DWARF_DEBUG)) dwarfout_undef (lineno, text); #endif /* DWARF_DEBUGGING_INFO */ goto skipline; } } else if (c == 'l') { if (getc (finput) == 'i' && getc (finput) == 'n' && getc (finput) == 'e' && ((c = getc (finput)) == ' ' || c == '\t')) goto linenum; } else if (c == 'i') { if (getc (finput) == 'd' && getc (finput) == 'e' && getc (finput) == 'n' && getc (finput) == 't' && ((c = getc (finput)) == ' ' || c == '\t')) { /* #ident. The pedantic warning is now in cccp.c. */ /* Here we have just seen `#ident '. A string constant should follow. */ while (c == ' ' || c == '\t') c = getc (finput); /* If no argument, ignore the line. */ if (c == '\n' || c == EOF) return c; c = ffelex_cfelex_ (&token, finput, c); if ((token == NULL) || (ffelex_token_type (token) != FFELEX_typeCHARACTER)) { error ("invalid #ident"); goto skipline; } if (ffe_is_ident ()) { #ifdef ASM_OUTPUT_IDENT ASM_OUTPUT_IDENT (asm_out_file, ffelex_token_text (token)); #endif } /* Skip the rest of this line. */ goto skipline; } } error ("undefined or invalid # directive"); goto skipline; } linenum: /* Here we have either `#line' or `# '. In either case, it should be a line number; a digit should follow. */ while (c == ' ' || c == '\t') c = ffelex_getc_ (finput); /* If the # is the only nonwhite char on the line, just ignore it. Check the new newline. */ if (c == '\n' || c == EOF) return c; /* Something follows the #; read a token. */ c = ffelex_cfelex_ (&token, finput, c); if ((token != NULL) && (ffelex_token_type (token) == FFELEX_typeNUMBER)) { int old_lineno = lineno; char *old_input_filename = input_filename; ffewhereFile wf; /* subtract one, because it is the following line that gets the specified number */ int l = atoi (ffelex_token_text (token)) - 1; /* Is this the last nonwhite stuff on the line? */ while (c == ' ' || c == '\t') c = ffelex_getc_ (finput); if (c == '\n' || c == EOF) { /* No more: store the line number and check following line. */ lineno = l; if (!ffelex_kludge_flag_) { ffewhere_file_set (NULL, TRUE, (ffewhereLineNumber) l); if (token != NULL) ffelex_token_kill (token); } return c; } /* More follows: it must be a string constant (filename). */ /* Read the string constant. */ c = ffelex_cfelex_ (&token, finput, c); if ((token == NULL) || (ffelex_token_type (token) != FFELEX_typeCHARACTER)) { error ("invalid #line"); goto skipline; } lineno = l; if (ffelex_kludge_flag_) input_filename = ffelex_token_text (token); else { wf = ffewhere_file_new (ffelex_token_text (token), ffelex_token_length (token)); input_filename = ffewhere_file_name (wf); ffewhere_file_set (wf, TRUE, (ffewhereLineNumber) l); } #if 0 /* Not sure what g77 should do with this yet. */ /* Each change of file name reinitializes whether we are now in a system header. */ in_system_header = 0; #endif if (main_input_filename == 0) main_input_filename = input_filename; /* Is this the last nonwhite stuff on the line? */ while (c == ' ' || c == '\t') c = getc (finput); if (c == '\n' || c == EOF) { if (!ffelex_kludge_flag_) { /* Update the name in the top element of input_file_stack. */ if (input_file_stack) input_file_stack->name = input_filename; if (token != NULL) ffelex_token_kill (token); } return c; } c = ffelex_cfelex_ (&token, finput, c); /* `1' after file name means entering new file. `2' after file name means just left a file. */ if ((token != NULL) && (ffelex_token_type (token) == FFELEX_typeNUMBER)) { int num = atoi (ffelex_token_text (token)); if (ffelex_kludge_flag_) { lineno = 1; input_filename = old_input_filename; fatal ("Use `#line ...' instead of `# ...' in first line"); } if (num == 1) { /* Pushing to a new file. */ ffelex_file_push_ (old_lineno, input_filename); } else if (num == 2) { /* Popping out of a file. */ ffelex_file_pop_ (input_filename); } /* Is this the last nonwhite stuff on the line? */ while (c == ' ' || c == '\t') c = getc (finput); if (c == '\n' || c == EOF) { if (token != NULL) ffelex_token_kill (token); return c; } c = ffelex_cfelex_ (&token, finput, c); } /* `3' after file name means this is a system header file. */ #if 0 /* Not sure what g77 should do with this yet. */ if ((token != NULL) && (ffelex_token_type (token) == FFELEX_typeNUMBER) && (atoi (ffelex_token_text (token)) == 3)) in_system_header = 1; #endif while (c == ' ' || c == '\t') c = getc (finput); if (((token != NULL) || (c != '\n' && c != EOF)) && ffelex_kludge_flag_) { lineno = 1; input_filename = old_input_filename; fatal ("Use `#line ...' instead of `# ...' in first line"); } } else error ("invalid #-line"); /* skip the rest of this line. */ skipline: if ((token != NULL) && !ffelex_kludge_flag_) ffelex_token_kill (token); while ((c = getc (finput)) != EOF && c != '\n') ; return c; } #endif /* FFECOM_targetCURRENT == FFECOM_targetGCC */ /* "Image" a character onto the card image, return incremented column number. Normally invoking this function as in column = ffelex_image_char_ (c, column); is the same as doing: ffelex_card_image_[column++] = c; However, tabs and carriage returns are handled specially, to preserve the visual "image" of the input line (in most editors) in the card image. Carriage returns are ignored, as they are assumed to be followed by newlines. A tab is handled by first doing: ffelex_card_image_[column++] = ' '; That is, it translates to at least one space. Then, as many spaces are imaged as necessary to bring the column number to the next tab position, where tab positions start in the ninth column and each eighth column afterwards. ALSO, a static var named ffelex_saw_tab_ is set to TRUE to notify the lexer that a tab was seen. Columns are numbered and tab stops set as illustrated below: 012345670123456701234567... x y z xx yy zz ... xxxxxxx yyyyyyy zzzzzzz xxxxxxxx yyyyyyyy... */ static ffewhereColumnNumber ffelex_image_char_ (int c, ffewhereColumnNumber column) { if (column >= ffelex_card_size_) { ffewhereColumnNumber newmax = ffelex_card_size_ << 1; if (ffelex_bad_line_) return column; if ((newmax >> 1) != ffelex_card_size_) { /* Overflowed column number. */ ffelex_bad_line_ = TRUE; strcpy (&ffelex_card_image_[column], "..."); ffelex_card_length_ = column + 3; ffelex_bad_1_ (FFEBAD_LINE_TOO_LONG, ffelex_linecount_current_, column + 1); column += 3; return column; } ffelex_card_image_ = malloc_resize_ksr (malloc_pool_image (), ffelex_card_image_, newmax + 9, ffelex_card_size_ + 9); ffelex_card_size_ = newmax; } switch (c) { case '\r': break; case '\t': ffelex_saw_tab_ = TRUE; ffelex_card_image_[column++] = ' '; while ((column & 7) != 0) ffelex_card_image_[column++] = ' '; break; case '\0': if (!ffelex_bad_line_) { ffelex_bad_line_ = TRUE; strcpy (&ffelex_card_image_[column], "[\\0]"); ffelex_card_length_ = column + 4; ffebad_start_msg_lex ("Null character at %0 -- line ignored", FFEBAD_severityFATAL); ffelex_bad_here_ (0, ffelex_linecount_current_, column + 1); ffebad_finish (); column += 4; } break; default: ffelex_card_image_[column++] = c; break; } return column; } static void ffelex_include_ () { ffewhereFile include_wherefile = ffelex_include_wherefile_; FILE *include_file = ffelex_include_file_; /* The rest of this is to push, and after the INCLUDE file is processed, pop, the static lexer state info that pertains to each particular input file. */ char *card_image; ffewhereColumnNumber card_size = ffelex_card_size_; ffewhereColumnNumber card_length = ffelex_card_length_; ffewhereLine current_wl = ffelex_current_wl_; ffewhereColumn current_wc = ffelex_current_wc_; bool saw_tab = ffelex_saw_tab_; ffewhereColumnNumber final_nontab_column = ffelex_final_nontab_column_; ffewhereFile current_wf = ffelex_current_wf_; ffewhereLineNumber linecount_current = ffelex_linecount_current_; ffewhereLineNumber linecount_offset = ffewhere_line_filelinenum (current_wl); #if FFECOM_targetCURRENT == FFECOM_targetGCC int old_lineno = lineno; char *old_input_filename = input_filename; #endif if (card_length != 0) { card_image = malloc_new_ks (malloc_pool_image (), "FFELEX saved card image", card_length); memcpy (card_image, ffelex_card_image_, card_length); } else card_image = NULL; ffelex_set_include_ = FALSE; ffelex_next_line_ (); ffewhere_file_set (include_wherefile, TRUE, 0); #if FFECOM_targetCURRENT == FFECOM_targetGCC ffelex_file_push_ (old_lineno, ffewhere_file_name (include_wherefile)); #endif /* FFECOM_targetCURRENT == FFECOM_targetGCC */ if (ffelex_include_free_form_) ffelex_file_free (include_wherefile, include_file); else ffelex_file_fixed (include_wherefile, include_file); #if FFECOM_targetCURRENT == FFECOM_targetGCC ffelex_file_pop_ (ffewhere_file_name (current_wf)); #endif /* FFECOM_targetCURRENT == FFECOM_targetGCC */ ffewhere_file_set (current_wf, TRUE, linecount_offset); ffecom_close_include (include_file); if (card_length != 0) { #ifdef REDUCE_CARD_SIZE_AFTER_BIGGY /* Define if occasional large lines. */ #error "need to handle possible reduction of card size here!!" #endif assert (ffelex_card_size_ >= card_length); /* It shrunk?? */ memcpy (ffelex_card_image_, card_image, card_length); } ffelex_card_image_[card_length] = '\0'; #if FFECOM_targetCURRENT == FFECOM_targetGCC input_filename = old_input_filename; lineno = old_lineno; #endif ffelex_linecount_current_ = linecount_current; ffelex_current_wf_ = current_wf; ffelex_final_nontab_column_ = final_nontab_column; ffelex_saw_tab_ = saw_tab; ffelex_current_wc_ = current_wc; ffelex_current_wl_ = current_wl; ffelex_card_length_ = card_length; ffelex_card_size_ = card_size; } /* ffelex_is_free_char_ctx_contin_ -- Character Context Continuation? ffewhereColumnNumber col; int c; // Char at col. if ((c == '&') && ffelex_is_free_char_ctx_contin_(col + 1)) // We have a continuation indicator. If there are spaces starting at ffelex_card_image_[col] up through the null character, where is 0 or greater, returns TRUE. */ static bool ffelex_is_free_char_ctx_contin_ (ffewhereColumnNumber col) { while (ffelex_card_image_[col] != '\0') { if (ffelex_card_image_[col++] != ' ') return FALSE; } return TRUE; } /* ffelex_is_free_nonc_ctx_contin_ -- Noncharacter Context Continuation? ffewhereColumnNumber col; int c; // Char at col. if ((c == '&') && ffelex_is_free_nonc_ctx_contin_(col + 1)) // We have a continuation indicator. If there are spaces starting at ffelex_card_image_[col] up through the null character or '!', where is 0 or greater, returns TRUE. */ static bool ffelex_is_free_nonc_ctx_contin_ (ffewhereColumnNumber col) { while ((ffelex_card_image_[col] != '\0') && (ffelex_card_image_[col] != '!')) { if (ffelex_card_image_[col++] != ' ') return FALSE; } return TRUE; } static void ffelex_next_line_ () { ffelex_linecount_current_ = ffelex_linecount_next_; ++ffelex_linecount_next_; #if FFECOM_targetCURRENT == FFECOM_targetGCC ++lineno; #endif } static void ffelex_send_token_ () { ++ffelex_number_of_tokens_; ffelex_backslash_ (EOF, 0); if (ffelex_token_->text == NULL) { if (ffelex_token_->type == FFELEX_typeCHARACTER) { ffelex_append_to_token_ ('\0'); ffelex_token_->length = 0; } } else ffelex_token_->text[ffelex_token_->length] = '\0'; assert (ffelex_raw_mode_ == 0); if (ffelex_token_->type == FFELEX_typeNAMES) { ffewhere_line_kill (ffelex_token_->currentnames_line); ffewhere_column_kill (ffelex_token_->currentnames_col); } assert (ffelex_handler_ != NULL); ffelex_handler_ = (ffelexHandler) (*ffelex_handler_) (ffelex_token_); assert (ffelex_handler_ != NULL); ffelex_token_kill (ffelex_token_); ffelex_token_ = ffelex_token_new_ (); ffelex_token_->uses = 1; ffelex_token_->text = NULL; if (ffelex_raw_mode_ < 0) { ffelex_token_->type = FFELEX_typeCHARACTER; ffelex_token_->where_line = ffelex_raw_where_line_; ffelex_token_->where_col = ffelex_raw_where_col_; ffelex_raw_where_line_ = ffewhere_line_unknown (); ffelex_raw_where_col_ = ffewhere_column_unknown (); } else { ffelex_token_->type = FFELEX_typeNONE; ffelex_token_->where_line = ffewhere_line_unknown (); ffelex_token_->where_col = ffewhere_column_unknown (); } if (ffelex_set_include_) ffelex_include_ (); } /* ffelex_swallow_tokens_ -- Eat all tokens delivered to me return ffelex_swallow_tokens_; Return this handler when you don't want to look at any more tokens in the statement because you've encountered an unrecoverable error in the statement. */ static ffelexHandler ffelex_swallow_tokens_ (ffelexToken t) { assert (ffelex_eos_handler_ != NULL); if ((ffelex_token_type (t) == FFELEX_typeEOS) || (ffelex_token_type (t) == FFELEX_typeSEMICOLON)) return (ffelexHandler) (*ffelex_eos_handler_) (t); return (ffelexHandler) ffelex_swallow_tokens_; } static ffelexToken ffelex_token_new_ () { ffelexToken t; ++ffelex_total_tokens_; t = (ffelexToken) malloc_new_ks (malloc_pool_image (), "FFELEX token", sizeof (*t)); t->id_ = ffelex_token_nextid_++; return t; } static char * ffelex_type_string_ (ffelexType type) { static char *types[] = { "FFELEX_typeNONE", "FFELEX_typeCOMMENT", "FFELEX_typeEOS", "FFELEX_typeEOF", "FFELEX_typeERROR", "FFELEX_typeRAW", "FFELEX_typeQUOTE", "FFELEX_typeDOLLAR", "FFELEX_typeHASH", "FFELEX_typePERCENT", "FFELEX_typeAMPERSAND", "FFELEX_typeAPOSTROPHE", "FFELEX_typeOPEN_PAREN", "FFELEX_typeCLOSE_PAREN", "FFELEX_typeASTERISK", "FFELEX_typePLUS", "FFELEX_typeMINUS", "FFELEX_typePERIOD", "FFELEX_typeSLASH", "FFELEX_typeNUMBER", "FFELEX_typeOPEN_ANGLE", "FFELEX_typeEQUALS", "FFELEX_typeCLOSE_ANGLE", "FFELEX_typeNAME", "FFELEX_typeCOMMA", "FFELEX_typePOWER", "FFELEX_typeCONCAT", "FFELEX_typeDEBUG", "FFELEX_typeNAMES", "FFELEX_typeHOLLERITH", "FFELEX_typeCHARACTER", "FFELEX_typeCOLON", "FFELEX_typeSEMICOLON", "FFELEX_typeUNDERSCORE", "FFELEX_typeQUESTION", "FFELEX_typeOPEN_ARRAY", "FFELEX_typeCLOSE_ARRAY", "FFELEX_typeCOLONCOLON", "FFELEX_typeREL_LE", "FFELEX_typeREL_NE", "FFELEX_typeREL_EQ", "FFELEX_typePOINTS", "FFELEX_typeREL_GE" }; if (type >= ARRAY_SIZE (types)) return "???"; return types[type]; } void ffelex_display_token (ffelexToken t) { if (t == NULL) t = ffelex_token_; fprintf (dmpout, "; Token #%lu is %s (line %" ffewhereLineNumber_f "u, col %" ffewhereColumnNumber_f "u)", t->id_, ffelex_type_string_ (t->type), ffewhere_line_number (t->where_line), ffewhere_column_number (t->where_col)); if (t->text != NULL) fprintf (dmpout, ": \"%.*s\"\n", (int) t->length, t->text); else fprintf (dmpout, ".\n"); } /* ffelex_expecting_character -- Tells if next token expected to be CHARACTER if (ffelex_expecting_character()) // next token delivered by lexer will be CHARACTER. If the most recent call to ffelex_set_expecting_hollerith since the last token was delivered by the lexer passed a length of -1, then we return TRUE, because the next token we deliver will be typeCHARACTER, else we return FALSE. */ bool ffelex_expecting_character () { return (ffelex_raw_mode_ != 0); } /* ffelex_file_fixed -- Lex a given file in fixed source form ffewhere wf; FILE *f; ffelex_file_fixed(wf,f); Lexes the file according to Fortran 90 ANSI + VXT specifications. */ ffelexHandler ffelex_file_fixed (ffewhereFile wf, FILE *f) { register int c; /* Character currently under consideration. */ register ffewhereColumnNumber column; /* Not really; 0 means column 1... */ bool disallow_continuation_line; bool ignore_disallowed_continuation; int latest_char_in_file = 0; /* For getting back into comment-skipping code. */ ffelexType lextype; ffewhereColumnNumber first_label_char; /* First char of label -- column number. */ char label_string[6]; /* Text of label. */ int labi; /* Length of label text. */ bool finish_statement; /* Previous statement finished? */ bool have_content; /* This line have content? */ bool just_do_label; /* Nothing but label (and continuation?) on line. */ /* Lex is called for a particular file, not for a particular program unit. Yet the two events do share common characteristics. The first line in a file or in a program unit cannot be a continuation line. No token can be in mid-formation. No current label for the statement exists, since there is no current statement. */ assert (ffelex_handler_ != NULL); #if FFECOM_targetCURRENT == FFECOM_targetGCC lineno = 0; input_filename = ffewhere_file_name (wf); #endif ffelex_current_wf_ = wf; disallow_continuation_line = TRUE; ignore_disallowed_continuation = FALSE; ffelex_token_->type = FFELEX_typeNONE; ffelex_number_of_tokens_ = 0; ffelex_label_tokens_ = 0; ffelex_current_wl_ = ffewhere_line_unknown (); ffelex_current_wc_ = ffewhere_column_unknown (); latest_char_in_file = '\n'; goto first_line; /* :::::::::::::::::::: */ /* Come here to get a new line. */ beginning_of_line: /* :::::::::::::::::::: */ disallow_continuation_line = FALSE; /* Come here directly when last line didn't clarify the continuation issue. */ beginning_of_line_again: /* :::::::::::::::::::: */ #ifdef REDUCE_CARD_SIZE_AFTER_BIGGY /* Define if occasional large lines. */ if (ffelex_card_size_ != FFELEX_columnINITIAL_SIZE_) { ffelex_card_image_ = malloc_resize_ks (malloc_pool_image (), ffelex_card_image_, FFELEX_columnINITIAL_SIZE_ + 9, ffelex_card_size_ + 9); ffelex_card_size_ = FFELEX_columnINITIAL_SIZE_; } #endif first_line: /* :::::::::::::::::::: */ c = latest_char_in_file; if ((c == EOF) || ((c = ffelex_getc_ (f)) == EOF)) { end_of_file: /* :::::::::::::::::::: */ /* Line ending in EOF instead of \n still counts as a whole line. */ ffelex_finish_statement_ (); ffewhere_line_kill (ffelex_current_wl_); ffewhere_column_kill (ffelex_current_wc_); return (ffelexHandler) ffelex_handler_; } ffelex_next_line_ (); ffelex_bad_line_ = FALSE; /* Skip over comment (and otherwise ignored) lines as quickly as possible! */ while (((lextype = ffelex_first_char_[c]) == FFELEX_typeCOMMENT) || (lextype == FFELEX_typeERROR) || (lextype == FFELEX_typeSLASH) || (lextype == FFELEX_typeHASH)) { /* Test most frequent type of line first, etc. */ if ((lextype == FFELEX_typeCOMMENT) || ((lextype == FFELEX_typeSLASH) && ((c = getc (f)) == '*'))) /* NOTE SIDE-EFFECT. */ { /* Typical case (straight comment), just ignore rest of line. */ comment_line: /* :::::::::::::::::::: */ while ((c != '\n') && (c != EOF)) c = getc (f); } #if FFECOM_targetCURRENT == FFECOM_targetGCC else if (lextype == FFELEX_typeHASH) c = ffelex_hash_ (f); #endif else if (lextype == FFELEX_typeSLASH) { /* SIDE-EFFECT ABOVE HAS HAPPENED. */ ffelex_card_image_[0] = '/'; ffelex_card_image_[1] = c; column = 2; goto bad_first_character; /* :::::::::::::::::::: */ } else /* typeERROR or unsupported typeHASH. */ { /* Bad first character, get line and display it with message. */ column = ffelex_image_char_ (c, 0); bad_first_character: /* :::::::::::::::::::: */ ffelex_bad_line_ = TRUE; while (((c = getc (f)) != '\n') && (c != EOF)) column = ffelex_image_char_ (c, column); ffelex_card_image_[column] = '\0'; ffelex_card_length_ = column; ffelex_bad_1_ (FFEBAD_FIRST_CHAR_INVALID, ffelex_linecount_current_, 1); } /* Read past last char in line. */ if (c == EOF) { ffelex_next_line_ (); goto end_of_file; /* :::::::::::::::::::: */ } c = getc (f); ffelex_next_line_ (); if (c == EOF) goto end_of_file; /* :::::::::::::::::::: */ ffelex_bad_line_ = FALSE; } /* while [c, first char, means comment] */ ffelex_saw_tab_ = (c == '&') || (ffelex_final_nontab_column_ == 0); if (lextype == FFELEX_typeDEBUG) c = ' '; /* A 'D' or 'd' in column 1 with the debug-lines option on. */ column = ffelex_image_char_ (c, 0); /* Read the entire line in as is (with whitespace processing). */ while (((c = getc (f)) != '\n') && (c != EOF)) column = ffelex_image_char_ (c, column); if (ffelex_bad_line_) { ffelex_card_image_[column] = '\0'; ffelex_card_length_ = column; goto comment_line; /* :::::::::::::::::::: */ } /* If no tab, cut off line after column 72/132. */ if (!ffelex_saw_tab_ && (column > ffelex_final_nontab_column_)) { /* Technically, we should now fill ffelex_card_image_ up thru column 72/132 with spaces, since character/hollerith constants must count them in that manner. To save CPU time in several ways (avoid a loop here that would be used only when we actually end a line in character-constant mode; avoid writing memory unnecessarily; avoid a loop later checking spaces when not scanning for character-constant characters), we don't do this, and we do the appropriate thing when we encounter end-of-line while actually processing a character constant. */ column = ffelex_final_nontab_column_; } ffelex_card_image_[column] = '\0'; ffelex_card_length_ = column; /* Save next char in file so we can use register-based c while analyzing line we just read. */ latest_char_in_file = c; /* Should be either '\n' or EOF. */ have_content = FALSE; /* Handle label, if any. */ labi = 0; first_label_char = FFEWHERE_columnUNKNOWN; for (column = 0; column < 5; ++column) { switch (c = ffelex_card_image_[column]) { case '\0': case '!': goto stop_looking; /* :::::::::::::::::::: */ case ' ': break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': label_string[labi++] = c; if (first_label_char == FFEWHERE_columnUNKNOWN) first_label_char = column + 1; break; case '&': if (column != 0) { ffelex_bad_1_ (FFEBAD_LABEL_FIELD_NOT_NUMERIC, ffelex_linecount_current_, column + 1); goto beginning_of_line_again; /* :::::::::::::::::::: */ } if (ffe_is_pedantic ()) ffelex_bad_1_ (FFEBAD_AMPERSAND, ffelex_linecount_current_, 1); finish_statement = FALSE; just_do_label = FALSE; goto got_a_continuation; /* :::::::::::::::::::: */ case '/': if (ffelex_card_image_[column + 1] == '*') goto stop_looking; /* :::::::::::::::::::: */ /* Fall through. */ default: ffelex_bad_1_ (FFEBAD_LABEL_FIELD_NOT_NUMERIC, ffelex_linecount_current_, column + 1); goto beginning_of_line_again; /* :::::::::::::::::::: */ } } stop_looking: /* :::::::::::::::::::: */ label_string[labi] = '\0'; /* Find first nonblank char starting with continuation column. */ if (column == 5) /* In which case we didn't see end of line in label field. */ while ((c = ffelex_card_image_[column]) == ' ') ++column; /* Now we're trying to figure out whether this is a continuation line and whether there's anything else of substance on the line. The cases are as follows: 1. If a line has an explicit continuation character (other than the digit zero), then if it also has a label, the label is ignored and an error message is printed. Any remaining text on the line is passed to the parser tasks, thus even an all-blank line (possibly with an ignored label) aside from a positive continuation character might have meaning in the midst of a character or hollerith constant. 2. If a line has no explicit continuation character (that is, it has a space in column 6 and the first non-space character past column 6 is not a digit 0-9), then there are two possibilities: A. A label is present and/or a non-space (and non-comment) character appears somewhere after column 6. Terminate processing of the previous statement, if any, send the new label for the next statement, if any, and start processing a new statement with this non-blank character, if any. B. The line is essentially blank, except for a possible comment character. Don't terminate processing of the previous statement and don't pass any characters to the parser tasks, since the line is not flagged as a continuation line. We treat it just like a completely blank line. 3. If a line has a continuation character of zero (0), then we terminate processing of the previous statement, if any, send the new label for the next statement, if any, and start processing a new statement, if any non-blank characters are present. If, when checking to see if we should terminate the previous statement, it is found that there is no previous statement but that there is an outstanding label, substitute CONTINUE as the statement for the label and display an error message. */ finish_statement = FALSE; just_do_label = FALSE; switch (c) { case '!': /* ANSI Fortran 90 says ! in column 6 is continuation. */ /* VXT Fortran says ! anywhere is comment, even column 6. */ if (ffe_is_vxt () || (column != 5)) goto no_tokens_on_line; /* :::::::::::::::::::: */ goto got_a_continuation; /* :::::::::::::::::::: */ case '/': if (ffelex_card_image_[column + 1] != '*') goto some_other_character; /* :::::::::::::::::::: */ /* Fall through. */ if (column == 5) { /* This seems right to do. But it is close to call, since / * starting in column 6 will thus be interpreted as a continuation line beginning with '*'. */ goto got_a_continuation;/* :::::::::::::::::::: */ } /* Fall through. */ case '\0': /* End of line. Therefore may be continued-through line, so handle pending label as possible to-be-continued and drive end-of-statement for any previous statement, else treat as blank line. */ no_tokens_on_line: /* :::::::::::::::::::: */ if (ffe_is_pedantic () && (c == '/')) ffelex_bad_1_ (FFEBAD_NON_ANSI_COMMENT, ffelex_linecount_current_, column + 1); if (first_label_char != FFEWHERE_columnUNKNOWN) { /* Can't be a continued-through line if it has a label. */ finish_statement = TRUE; have_content = TRUE; just_do_label = TRUE; break; } goto beginning_of_line_again; /* :::::::::::::::::::: */ case '0': if (ffe_is_pedantic () && (column != 5)) ffelex_bad_1_ (FFEBAD_NON_ANSI_CONTINUATION_COLUMN, ffelex_linecount_current_, column + 1); finish_statement = TRUE; goto check_for_content; /* :::::::::::::::::::: */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* NOTE: This label can be reached directly from the code that lexes the label field in columns 1-5. */ got_a_continuation: /* :::::::::::::::::::: */ if (first_label_char != FFEWHERE_columnUNKNOWN) { ffelex_bad_2_ (FFEBAD_LABEL_ON_CONTINUATION, ffelex_linecount_current_, first_label_char, ffelex_linecount_current_, column + 1); first_label_char = FFEWHERE_columnUNKNOWN; } if (disallow_continuation_line) { if (!ignore_disallowed_continuation) ffelex_bad_1_ (FFEBAD_INVALID_CONTINUATION, ffelex_linecount_current_, column + 1); goto beginning_of_line_again; /* :::::::::::::::::::: */ } if (ffe_is_pedantic () && (column != 5)) ffelex_bad_1_ (FFEBAD_NON_ANSI_CONTINUATION_COLUMN, ffelex_linecount_current_, column + 1); if ((ffelex_raw_mode_ != 0) && (((c = ffelex_card_image_[column + 1]) != '\0') || !ffelex_saw_tab_)) { ++column; have_content = TRUE; break; } check_for_content: /* :::::::::::::::::::: */ while ((c = ffelex_card_image_[++column]) == ' ') ; if ((c == '\0') || (c == '!') || ((c == '/') && (ffelex_card_image_[column + 1] == '*'))) { if (ffe_is_pedantic () && (c == '/')) ffelex_bad_1_ (FFEBAD_NON_ANSI_COMMENT, ffelex_linecount_current_, column + 1); just_do_label = TRUE; } else have_content = TRUE; break; default: some_other_character: /* :::::::::::::::::::: */ if (column == 5) goto got_a_continuation;/* :::::::::::::::::::: */ /* Here is the very normal case of a regular character starting in column 7 or beyond with a blank in column 6. */ finish_statement = TRUE; have_content = TRUE; break; } if (have_content || (first_label_char != FFEWHERE_columnUNKNOWN)) { /* The line has content of some kind, install new end-statement point for error messages. Note that "content" includes cases where there's little apparent content but enough to finish a statement. That's because finishing a statement can trigger an impending INCLUDE, and that requires accurate line info being maintained by the lexer. */ if (finish_statement) ffelex_prepare_eos_ (); /* Prepare EOS before we move current pointer. */ ffewhere_line_kill (ffelex_current_wl_); ffewhere_column_kill (ffelex_current_wc_); ffelex_current_wl_ = ffewhere_line_new (ffelex_linecount_current_); ffelex_current_wc_ = ffewhere_column_new (ffelex_card_length_ + 1); } /* We delay this for a combination of reasons. Mainly, it can start INCLUDE processing, and we want to delay that until the lexer's info on the line is coherent. And we want to delay that until we're sure there's a reason to make that info coherent, to avoid saving lots of useless lines. */ if (finish_statement) ffelex_finish_statement_ (); /* If label is present, enclose it in a NUMBER token and send it along. */ if (first_label_char != FFEWHERE_columnUNKNOWN) { assert (ffelex_token_->type == FFELEX_typeNONE); ffelex_token_->type = FFELEX_typeNUMBER; ffelex_append_to_token_ ('\0'); /* Make room for label text. */ strcpy (ffelex_token_->text, label_string); ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (first_label_char); ffelex_token_->length = labi; ffelex_send_token_ (); ++ffelex_label_tokens_; } if (just_do_label) goto beginning_of_line; /* :::::::::::::::::::: */ /* Here is the main engine for parsing. c holds the character at column. It is already known that c is not a blank, end of line, or shriek, unless ffelex_raw_mode_ is not 0 (indicating we are in a character/hollerith constant). A partially filled token may already exist in ffelex_token_. One special case: if, when the end of the line is reached, continuation_line is FALSE and the only token on the line is END, then it is indeed the last statement. We don't look for continuation lines during this program unit in that case. This is according to ANSI. */ if (ffelex_raw_mode_ != 0) { parse_raw_character: /* :::::::::::::::::::: */ if (c == '\0') { ffewhereColumnNumber i; if (ffelex_saw_tab_ || (column >= ffelex_final_nontab_column_)) goto beginning_of_line; /* :::::::::::::::::::: */ /* Pad out line with "virtual" spaces. */ for (i = column; i < ffelex_final_nontab_column_; ++i) ffelex_card_image_[i] = ' '; ffelex_card_image_[i] = '\0'; ffelex_card_length_ = i; c = ' '; } switch (ffelex_raw_mode_) { case -3: c = ffelex_backslash_ (c, column); if (c == EOF) break; if (!ffelex_backslash_reconsider_) ffelex_append_to_token_ (c); ffelex_raw_mode_ = -1; break; case -2: if (c == ffelex_raw_char_) { ffelex_raw_mode_ = -1; ffelex_append_to_token_ (c); } else { ffelex_raw_mode_ = 0; ffelex_backslash_reconsider_ = TRUE; } break; case -1: if (c == ffelex_raw_char_) ffelex_raw_mode_ = -2; else { c = ffelex_backslash_ (c, column); if (c == EOF) { ffelex_raw_mode_ = -3; break; } ffelex_append_to_token_ (c); } break; default: c = ffelex_backslash_ (c, column); if (c == EOF) break; if (!ffelex_backslash_reconsider_) { ffelex_append_to_token_ (c); --ffelex_raw_mode_; } break; } if (ffelex_backslash_reconsider_) ffelex_backslash_reconsider_ = FALSE; else c = ffelex_card_image_[++column]; if (ffelex_raw_mode_ == 0) { ffelex_send_token_ (); assert (ffelex_raw_mode_ == 0); while (c == ' ') c = ffelex_card_image_[++column]; if ((c == '\0') || (c == '!') || ((c == '/') && (ffelex_card_image_[column + 1] == '*'))) goto beginning_of_line; /* :::::::::::::::::::: */ goto parse_nonraw_character; /* :::::::::::::::::::: */ } goto parse_raw_character; /* :::::::::::::::::::: */ } parse_nonraw_character: /* :::::::::::::::::::: */ switch (ffelex_token_->type) { case FFELEX_typeNONE: switch (c) { case '\"': ffelex_token_->type = FFELEX_typeQUOTE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '$': ffelex_token_->type = FFELEX_typeDOLLAR; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '%': ffelex_token_->type = FFELEX_typePERCENT; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '&': ffelex_token_->type = FFELEX_typeAMPERSAND; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '\'': ffelex_token_->type = FFELEX_typeAPOSTROPHE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '(': ffelex_token_->type = FFELEX_typeOPEN_PAREN; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case ')': ffelex_token_->type = FFELEX_typeCLOSE_PAREN; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '*': ffelex_token_->type = FFELEX_typeASTERISK; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '+': ffelex_token_->type = FFELEX_typePLUS; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case ',': ffelex_token_->type = FFELEX_typeCOMMA; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '-': ffelex_token_->type = FFELEX_typeMINUS; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '.': ffelex_token_->type = FFELEX_typePERIOD; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '/': ffelex_token_->type = FFELEX_typeSLASH; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ffelex_token_->type = ffelex_hexnum_ ? FFELEX_typeNAME : FFELEX_typeNUMBER; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_append_to_token_ (c); break; case ':': ffelex_token_->type = FFELEX_typeCOLON; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case ';': ffelex_token_->type = FFELEX_typeSEMICOLON; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_permit_include_ = TRUE; ffelex_send_token_ (); ffelex_permit_include_ = FALSE; break; case '<': ffelex_token_->type = FFELEX_typeOPEN_ANGLE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '=': ffelex_token_->type = FFELEX_typeEQUALS; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '>': ffelex_token_->type = FFELEX_typeCLOSE_ANGLE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '?': ffelex_token_->type = FFELEX_typeQUESTION; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '_': if (1 || ffe_is_90 ()) { ffelex_token_->type = FFELEX_typeUNDERSCORE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; } /* Fall through. */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': c = ffesrc_char_source (c); if (ffesrc_char_match_init (c, 'H', 'h') && ffelex_expecting_hollerith_ != 0) { ffelex_raw_mode_ = ffelex_expecting_hollerith_; ffelex_token_->type = FFELEX_typeHOLLERITH; ffelex_token_->where_line = ffelex_raw_where_line_; ffelex_token_->where_col = ffelex_raw_where_col_; ffelex_raw_where_line_ = ffewhere_line_unknown (); ffelex_raw_where_col_ = ffewhere_column_unknown (); c = ffelex_card_image_[++column]; goto parse_raw_character; /* :::::::::::::::::::: */ } if (ffelex_names_) { ffelex_token_->where_line = ffewhere_line_use (ffelex_token_->currentnames_line = ffewhere_line_use (ffelex_current_wl_)); ffelex_token_->where_col = ffewhere_column_use (ffelex_token_->currentnames_col = ffewhere_column_new (column + 1)); ffelex_token_->type = FFELEX_typeNAMES; } else { ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_token_->type = FFELEX_typeNAME; } ffelex_append_to_token_ (c); break; default: ffelex_bad_1_ (FFEBAD_UNRECOGNIZED_CHARACTER, ffelex_linecount_current_, column + 1); ffelex_finish_statement_ (); disallow_continuation_line = TRUE; ignore_disallowed_continuation = TRUE; goto beginning_of_line_again; /* :::::::::::::::::::: */ } break; case FFELEX_typeNAME: switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': c = ffesrc_char_source (c); /* Fall through. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '_': case '$': if ((c == '$') && !ffe_is_dollar_ok ()) { ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } ffelex_append_to_token_ (c); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeNAMES: switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': c = ffesrc_char_source (c); /* Fall through. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '_': case '$': if ((c == '$') && !ffe_is_dollar_ok ()) { ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } if (ffelex_token_->length < FFEWHERE_indexMAX) { ffewhere_track (&ffelex_token_->currentnames_line, &ffelex_token_->currentnames_col, ffelex_token_->wheretrack, ffelex_token_->length, ffelex_linecount_current_, column + 1); } ffelex_append_to_token_ (c); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeNUMBER: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ffelex_append_to_token_ (c); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeASTERISK: switch (c) { case '*': /* ** */ ffelex_token_->type = FFELEX_typePOWER; ffelex_send_token_ (); break; default: /* * not followed by another *. */ ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeCOLON: switch (c) { case ':': /* :: */ ffelex_token_->type = FFELEX_typeCOLONCOLON; ffelex_send_token_ (); break; default: /* : not followed by another :. */ ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeSLASH: switch (c) { case '/': /* // */ ffelex_token_->type = FFELEX_typeCONCAT; ffelex_send_token_ (); break; case ')': /* /) */ ffelex_token_->type = FFELEX_typeCLOSE_ARRAY; ffelex_send_token_ (); break; case '=': /* /= */ ffelex_token_->type = FFELEX_typeREL_NE; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeOPEN_PAREN: switch (c) { case '/': /* (/ */ ffelex_token_->type = FFELEX_typeOPEN_ARRAY; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeOPEN_ANGLE: switch (c) { case '=': /* <= */ ffelex_token_->type = FFELEX_typeREL_LE; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeEQUALS: switch (c) { case '=': /* == */ ffelex_token_->type = FFELEX_typeREL_EQ; ffelex_send_token_ (); break; case '>': /* => */ ffelex_token_->type = FFELEX_typePOINTS; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeCLOSE_ANGLE: switch (c) { case '=': /* >= */ ffelex_token_->type = FFELEX_typeREL_GE; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; default: assert ("Serious error!!" == NULL); abort (); break; } c = ffelex_card_image_[++column]; parse_next_character: /* :::::::::::::::::::: */ if (ffelex_raw_mode_ != 0) goto parse_raw_character; /* :::::::::::::::::::: */ while (c == ' ') c = ffelex_card_image_[++column]; if ((c == '\0') || (c == '!') || ((c == '/') && (ffelex_card_image_[column + 1] == '*'))) { if ((ffelex_number_of_tokens_ == ffelex_label_tokens_) && (ffelex_token_->type == FFELEX_typeNAMES) && (ffelex_token_->length == 3) && (ffesrc_strncmp_2c (ffe_case_match (), ffelex_token_->text, "END", "end", "End", 3) == 0)) { ffelex_finish_statement_ (); disallow_continuation_line = TRUE; ignore_disallowed_continuation = FALSE; goto beginning_of_line_again; /* :::::::::::::::::::: */ } goto beginning_of_line; /* :::::::::::::::::::: */ } goto parse_nonraw_character; /* :::::::::::::::::::: */ } /* ffelex_file_free -- Lex a given file in free source form ffewhere wf; FILE *f; ffelex_file_free(wf,f); Lexes the file according to Fortran 90 ANSI + VXT specifications. */ ffelexHandler ffelex_file_free (ffewhereFile wf, FILE *f) { register int c; /* Character currently under consideration. */ register ffewhereColumnNumber column; /* Not really; 0 means column 1... */ bool continuation_line; ffewhereColumnNumber continuation_column; int latest_char_in_file; /* For getting back into comment-skipping code. */ /* Lex is called for a particular file, not for a particular program unit. Yet the two events do share common characteristics. The first line in a file or in a program unit cannot be a continuation line. No token can be in mid-formation. No current label for the statement exists, since there is no current statement. */ assert (ffelex_handler_ != NULL); #if FFECOM_targetCURRENT == FFECOM_targetGCC lineno = 0; input_filename = ffewhere_file_name (wf); #endif ffelex_current_wf_ = wf; continuation_line = FALSE; ffelex_token_->type = FFELEX_typeNONE; ffelex_number_of_tokens_ = 0; ffelex_current_wl_ = ffewhere_line_unknown (); ffelex_current_wc_ = ffewhere_column_unknown (); latest_char_in_file = '\n'; /* Come here to get a new line. */ beginning_of_line: /* :::::::::::::::::::: */ c = latest_char_in_file; if ((c == EOF) || ((c = ffelex_getc_ (f)) == EOF)) { end_of_file: /* :::::::::::::::::::: */ /* Line ending in EOF instead of \n still counts as a whole line. */ ffelex_finish_statement_ (); ffewhere_line_kill (ffelex_current_wl_); ffewhere_column_kill (ffelex_current_wc_); return (ffelexHandler) ffelex_handler_; } ffelex_next_line_ (); ffelex_bad_line_ = FALSE; /* Skip over initial-comment and empty lines as quickly as possible! */ while ((c == '\n') || (c == '!') || (c == '#')) { if (c == '#') { #if FFECOM_targetCURRENT == FFECOM_targetGCC c = ffelex_hash_ (f); #else /* Don't skip over # line after all. */ break; #endif } comment_line: /* :::::::::::::::::::: */ while ((c != '\n') && (c != EOF)) c = getc (f); if (c == EOF) { ffelex_next_line_ (); goto end_of_file; /* :::::::::::::::::::: */ } c = getc (f); ffelex_next_line_ (); if (c == EOF) goto end_of_file; /* :::::::::::::::::::: */ } ffelex_saw_tab_ = FALSE; column = ffelex_image_char_ (c, 0); /* Read the entire line in as is (with whitespace processing). */ while (((c = getc (f)) != '\n') && (c != EOF)) column = ffelex_image_char_ (c, column); if (ffelex_bad_line_) { ffelex_card_image_[column] = '\0'; ffelex_card_length_ = column; goto comment_line; /* :::::::::::::::::::: */ } /* If no tab, cut off line after column 132. */ if (!ffelex_saw_tab_ && (column > FFELEX_FREE_MAX_COLUMNS_)) column = FFELEX_FREE_MAX_COLUMNS_; ffelex_card_image_[column] = '\0'; ffelex_card_length_ = column; /* Save next char in file so we can use register-based c while analyzing line we just read. */ latest_char_in_file = c; /* Should be either '\n' or EOF. */ column = 0; continuation_column = 0; /* Skip over initial spaces to see if the first nonblank character is exclamation point, newline, or EOF (line is therefore a comment) or ampersand (line is therefore a continuation line). */ while ((c = ffelex_card_image_[column]) == ' ') ++column; switch (c) { case '!': case '\0': goto beginning_of_line; /* :::::::::::::::::::: */ case '&': continuation_column = column + 1; break; default: break; } /* The line definitely has content of some kind, install new end-statement point for error messages. */ ffewhere_line_kill (ffelex_current_wl_); ffewhere_column_kill (ffelex_current_wc_); ffelex_current_wl_ = ffewhere_line_new (ffelex_linecount_current_); ffelex_current_wc_ = ffewhere_column_new (ffelex_card_length_ + 1); /* Figure out which column to start parsing at. */ if (continuation_line) { if (continuation_column == 0) { if (ffelex_raw_mode_ != 0) { ffelex_bad_1_ (FFEBAD_BAD_CHAR_CONTINUE, ffelex_linecount_current_, column + 1); } else if (ffelex_token_->type != FFELEX_typeNONE) { ffelex_bad_1_ (FFEBAD_BAD_LEXTOK_CONTINUE, ffelex_linecount_current_, column + 1); } } else if (ffelex_is_free_char_ctx_contin_ (continuation_column)) { /* Line contains only a single "&" as only nonblank character. */ ffelex_bad_1_ (FFEBAD_BAD_FREE_CONTINUE, ffelex_linecount_current_, continuation_column); goto beginning_of_line; /* :::::::::::::::::::: */ } column = continuation_column; } else column = 0; c = ffelex_card_image_[column]; continuation_line = FALSE; /* Here is the main engine for parsing. c holds the character at column. It is already known that c is not a blank, end of line, or shriek, unless ffelex_raw_mode_ is not 0 (indicating we are in a character/hollerith constant). A partially filled token may already exist in ffelex_token_. */ if (ffelex_raw_mode_ != 0) { parse_raw_character: /* :::::::::::::::::::: */ switch (c) { case '&': if (ffelex_is_free_char_ctx_contin_ (column + 1)) { continuation_line = TRUE; goto beginning_of_line; /* :::::::::::::::::::: */ } break; case '\0': ffelex_finish_statement_ (); goto beginning_of_line; /* :::::::::::::::::::: */ default: break; } switch (ffelex_raw_mode_) { case -3: c = ffelex_backslash_ (c, column); if (c == EOF) break; if (!ffelex_backslash_reconsider_) ffelex_append_to_token_ (c); ffelex_raw_mode_ = -1; break; case -2: if (c == ffelex_raw_char_) { ffelex_raw_mode_ = -1; ffelex_append_to_token_ (c); } else { ffelex_raw_mode_ = 0; ffelex_backslash_reconsider_ = TRUE; } break; case -1: if (c == ffelex_raw_char_) ffelex_raw_mode_ = -2; else { c = ffelex_backslash_ (c, column); if (c == EOF) { ffelex_raw_mode_ = -3; break; } ffelex_append_to_token_ (c); } break; default: c = ffelex_backslash_ (c, column); if (c == EOF) break; if (!ffelex_backslash_reconsider_) { ffelex_append_to_token_ (c); --ffelex_raw_mode_; } break; } if (ffelex_backslash_reconsider_) ffelex_backslash_reconsider_ = FALSE; else c = ffelex_card_image_[++column]; if (ffelex_raw_mode_ == 0) { ffelex_send_token_ (); assert (ffelex_raw_mode_ == 0); while (c == ' ') c = ffelex_card_image_[++column]; if ((c == '\0') || (c == '!')) { ffelex_finish_statement_ (); goto beginning_of_line; /* :::::::::::::::::::: */ } if ((c == '&') && ffelex_is_free_nonc_ctx_contin_ (column + 1)) { continuation_line = TRUE; goto beginning_of_line; /* :::::::::::::::::::: */ } goto parse_nonraw_character_noncontin; /* :::::::::::::::::::: */ } goto parse_raw_character; /* :::::::::::::::::::: */ } parse_nonraw_character: /* :::::::::::::::::::: */ if ((c == '&') && ffelex_is_free_nonc_ctx_contin_ (column + 1)) { continuation_line = TRUE; goto beginning_of_line; /* :::::::::::::::::::: */ } parse_nonraw_character_noncontin: /* :::::::::::::::::::: */ switch (ffelex_token_->type) { case FFELEX_typeNONE: if (c == ' ') { /* Otherwise finish-statement/continue-statement already checked. */ while (c == ' ') c = ffelex_card_image_[++column]; if ((c == '\0') || (c == '!')) { ffelex_finish_statement_ (); goto beginning_of_line; /* :::::::::::::::::::: */ } if ((c == '&') && ffelex_is_free_nonc_ctx_contin_ (column + 1)) { continuation_line = TRUE; goto beginning_of_line; /* :::::::::::::::::::: */ } } switch (c) { case '\"': ffelex_token_->type = FFELEX_typeQUOTE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '$': ffelex_token_->type = FFELEX_typeDOLLAR; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '%': ffelex_token_->type = FFELEX_typePERCENT; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '&': ffelex_token_->type = FFELEX_typeAMPERSAND; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '\'': ffelex_token_->type = FFELEX_typeAPOSTROPHE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '(': ffelex_token_->type = FFELEX_typeOPEN_PAREN; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case ')': ffelex_token_->type = FFELEX_typeCLOSE_PAREN; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '*': ffelex_token_->type = FFELEX_typeASTERISK; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '+': ffelex_token_->type = FFELEX_typePLUS; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case ',': ffelex_token_->type = FFELEX_typeCOMMA; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '-': ffelex_token_->type = FFELEX_typeMINUS; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '.': ffelex_token_->type = FFELEX_typePERIOD; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '/': ffelex_token_->type = FFELEX_typeSLASH; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ffelex_token_->type = ffelex_hexnum_ ? FFELEX_typeNAME : FFELEX_typeNUMBER; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_append_to_token_ (c); break; case ':': ffelex_token_->type = FFELEX_typeCOLON; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case ';': ffelex_token_->type = FFELEX_typeSEMICOLON; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_permit_include_ = TRUE; ffelex_send_token_ (); ffelex_permit_include_ = FALSE; break; case '<': ffelex_token_->type = FFELEX_typeOPEN_ANGLE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '=': ffelex_token_->type = FFELEX_typeEQUALS; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '>': ffelex_token_->type = FFELEX_typeCLOSE_ANGLE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); break; case '?': ffelex_token_->type = FFELEX_typeQUESTION; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; case '_': if (1 || ffe_is_90 ()) { ffelex_token_->type = FFELEX_typeUNDERSCORE; ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_send_token_ (); break; } /* Fall through. */ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': c = ffesrc_char_source (c); if (ffesrc_char_match_init (c, 'H', 'h') && ffelex_expecting_hollerith_ != 0) { ffelex_raw_mode_ = ffelex_expecting_hollerith_; ffelex_token_->type = FFELEX_typeHOLLERITH; ffelex_token_->where_line = ffelex_raw_where_line_; ffelex_token_->where_col = ffelex_raw_where_col_; ffelex_raw_where_line_ = ffewhere_line_unknown (); ffelex_raw_where_col_ = ffewhere_column_unknown (); c = ffelex_card_image_[++column]; goto parse_raw_character; /* :::::::::::::::::::: */ } if (ffelex_names_pure_) { ffelex_token_->where_line = ffewhere_line_use (ffelex_token_->currentnames_line = ffewhere_line_use (ffelex_current_wl_)); ffelex_token_->where_col = ffewhere_column_use (ffelex_token_->currentnames_col = ffewhere_column_new (column + 1)); ffelex_token_->type = FFELEX_typeNAMES; } else { ffelex_token_->where_line = ffewhere_line_use (ffelex_current_wl_); ffelex_token_->where_col = ffewhere_column_new (column + 1); ffelex_token_->type = FFELEX_typeNAME; } ffelex_append_to_token_ (c); break; default: ffelex_bad_1_ (FFEBAD_UNRECOGNIZED_CHARACTER, ffelex_linecount_current_, column + 1); ffelex_finish_statement_ (); goto beginning_of_line; /* :::::::::::::::::::: */ } break; case FFELEX_typeNAME: switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': c = ffesrc_char_source (c); /* Fall through. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '_': case '$': if ((c == '$') && !ffe_is_dollar_ok ()) { ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } ffelex_append_to_token_ (c); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeNAMES: switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': c = ffesrc_char_source (c); /* Fall through. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '_': case '$': if ((c == '$') && !ffe_is_dollar_ok ()) { ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } if (ffelex_token_->length < FFEWHERE_indexMAX) { ffewhere_track (&ffelex_token_->currentnames_line, &ffelex_token_->currentnames_col, ffelex_token_->wheretrack, ffelex_token_->length, ffelex_linecount_current_, column + 1); } ffelex_append_to_token_ (c); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeNUMBER: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ffelex_append_to_token_ (c); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeASTERISK: switch (c) { case '*': /* ** */ ffelex_token_->type = FFELEX_typePOWER; ffelex_send_token_ (); break; default: /* * not followed by another *. */ ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeCOLON: switch (c) { case ':': /* :: */ ffelex_token_->type = FFELEX_typeCOLONCOLON; ffelex_send_token_ (); break; default: /* : not followed by another :. */ ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeSLASH: switch (c) { case '/': /* // */ ffelex_token_->type = FFELEX_typeCONCAT; ffelex_send_token_ (); break; case ')': /* /) */ ffelex_token_->type = FFELEX_typeCLOSE_ARRAY; ffelex_send_token_ (); break; case '=': /* /= */ ffelex_token_->type = FFELEX_typeREL_NE; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeOPEN_PAREN: switch (c) { case '/': /* (/ */ ffelex_token_->type = FFELEX_typeOPEN_ARRAY; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeOPEN_ANGLE: switch (c) { case '=': /* <= */ ffelex_token_->type = FFELEX_typeREL_LE; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeEQUALS: switch (c) { case '=': /* == */ ffelex_token_->type = FFELEX_typeREL_EQ; ffelex_send_token_ (); break; case '>': /* => */ ffelex_token_->type = FFELEX_typePOINTS; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; case FFELEX_typeCLOSE_ANGLE: switch (c) { case '=': /* >= */ ffelex_token_->type = FFELEX_typeREL_GE; ffelex_send_token_ (); break; default: ffelex_send_token_ (); goto parse_next_character; /* :::::::::::::::::::: */ } break; default: assert ("Serious error!" == NULL); abort (); break; } c = ffelex_card_image_[++column]; parse_next_character: /* :::::::::::::::::::: */ if (ffelex_raw_mode_ != 0) goto parse_raw_character; /* :::::::::::::::::::: */ if ((c == '\0') || (c == '!')) { ffelex_finish_statement_ (); goto beginning_of_line; /* :::::::::::::::::::: */ } goto parse_nonraw_character; /* :::::::::::::::::::: */ } /* See the code in com.c that calls this to understand why. */ #if FFECOM_targetCURRENT == FFECOM_targetGCC void ffelex_hash_kludge (FILE *finput) { /* If you change this constant string, you have to change whatever code might thus be affected by it in terms of having to use ffelex_getc_() instead of getc() in the lexers and _hash_. */ static char match[] = "# 1 \""; static int kludge[ARRAY_SIZE (match) + 1]; int c; char *p; int *q; /* Read chars as long as they match the target string. Copy them into an array that will serve as a record of what we read (essentially a multi-char ungetc(), for code that uses ffelex_getc_ instead of getc() elsewhere in the lexer. */ for (p = &match[0], q = &kludge[0], c = getc (finput); (c == *p) && (*p != '\0') && (c != EOF); ++p, ++q, c = getc (finput)) *q = c; *q = c; /* Might be EOF, which requires int. */ *++q = 0; ffelex_kludge_chars_ = &kludge[0]; if (*p == 0) { ffelex_kludge_flag_ = TRUE; ++ffelex_kludge_chars_; ffelex_hash_ (finput); /* Handle it NOW rather than later. */ ffelex_kludge_flag_ = FALSE; } } #endif void ffelex_init_1 () { unsigned int i; ffelex_final_nontab_column_ = ffe_fixed_line_length (); ffelex_card_size_ = FFELEX_columnINITIAL_SIZE_; ffelex_card_image_ = malloc_new_ksr (malloc_pool_image (), "FFELEX card image", FFELEX_columnINITIAL_SIZE_ + 9); ffelex_card_image_[0] = '\0'; for (i = 0; i < 256; ++i) ffelex_first_char_[i] = FFELEX_typeERROR; ffelex_first_char_['\t'] = FFELEX_typeRAW; ffelex_first_char_['\n'] = FFELEX_typeCOMMENT; ffelex_first_char_['\v'] = FFELEX_typeCOMMENT; ffelex_first_char_['\f'] = FFELEX_typeCOMMENT; ffelex_first_char_['\r'] = FFELEX_typeRAW; ffelex_first_char_[' '] = FFELEX_typeRAW; ffelex_first_char_['!'] = FFELEX_typeCOMMENT; ffelex_first_char_['*'] = FFELEX_typeCOMMENT; ffelex_first_char_['/'] = FFELEX_typeSLASH; ffelex_first_char_['&'] = FFELEX_typeRAW; ffelex_first_char_['#'] = FFELEX_typeHASH; for (i = '0'; i <= '9'; ++i) ffelex_first_char_[i] = FFELEX_typeRAW; if ((ffe_case_match () == FFE_caseNONE) || ((ffe_case_match () == FFE_caseUPPER) && (ffe_case_source () != FFE_caseLOWER)) /* Idiot! :-) */ || ((ffe_case_match () == FFE_caseLOWER) && (ffe_case_source () == FFE_caseLOWER))) { ffelex_first_char_['C'] = FFELEX_typeCOMMENT; ffelex_first_char_['D'] = FFELEX_typeCOMMENT; } if ((ffe_case_match () == FFE_caseNONE) || ((ffe_case_match () == FFE_caseLOWER) && (ffe_case_source () != FFE_caseUPPER)) /* Idiot! :-) */ || ((ffe_case_match () == FFE_caseUPPER) && (ffe_case_source () == FFE_caseUPPER))) { ffelex_first_char_['c'] = FFELEX_typeCOMMENT; ffelex_first_char_['d'] = FFELEX_typeCOMMENT; } ffelex_linecount_current_ = 0; ffelex_linecount_next_ = 1; ffelex_raw_mode_ = 0; ffelex_set_include_ = FALSE; ffelex_permit_include_ = FALSE; ffelex_names_ = TRUE; /* First token in program is a names. */ ffelex_names_pure_ = FALSE; /* Free-form lexer does NAMES only for FORMAT. */ ffelex_hexnum_ = FALSE; ffelex_expecting_hollerith_ = 0; ffelex_raw_where_line_ = ffewhere_line_unknown (); ffelex_raw_where_col_ = ffewhere_column_unknown (); ffelex_token_ = ffelex_token_new_ (); ffelex_token_->type = FFELEX_typeNONE; ffelex_token_->uses = 1; ffelex_token_->where_line = ffewhere_line_unknown (); ffelex_token_->where_col = ffewhere_column_unknown (); ffelex_token_->text = NULL; ffelex_handler_ = NULL; } /* ffelex_is_names_expected -- Is the current parser expecting NAMES vs. NAME? if (ffelex_is_names_expected()) // Deliver NAMES token else // Deliver NAME token Must be called while lexer is active, obviously. */ bool ffelex_is_names_expected () { return ffelex_names_; } /* Current card image, which has the master linecount number ffelex_linecount_current_. */ char * ffelex_line () { return ffelex_card_image_; } /* ffelex_line_length -- Return length of current lexer line printf("Length is %lu\n",ffelex_line_length()); Must be called while lexer is active, obviously. */ ffewhereColumnNumber ffelex_line_length () { return ffelex_card_length_; } /* Master line count of current card image, or 0 if no card image is current. */ ffewhereLineNumber ffelex_line_number () { return ffelex_linecount_current_; } /* ffelex_set_expecting_hollerith -- Set hollerith expectation status ffelex_set_expecting_hollerith(0); Lex initially assumes no hollerith constant is about to show up. If syntactic analysis expects one, it should call this function with the number of characters expected in the constant immediately after recognizing the decimal number preceding the "H" and the constant itself. Then, if the next character is indeed H, the lexer will interpret it as beginning a hollerith constant and ship the token formed by reading the specified number of characters (interpreting blanks and otherwise-comments too) from the input file. It is up to syntactic analysis to call this routine again with 0 to turn hollerith detection off immediately upon receiving the token that might or might not be HOLLERITH. Also call this after seeing an APOSTROPHE or QUOTE token that begins a character constant. Pass the expected termination character (apostrophe or quote). Pass for length either the length of the hollerith (must be > 0), -1 meaning expecting a character constant, or 0 to cancel expectation of a hollerith only after calling it with a length of > 0 and receiving the next token (which may or may not have been a HOLLERITH token). Pass for which either an apostrophe or quote when passing length of -1. Else which is a don't-care. Pass for line and column the line/column info for the token beginning the character or hollerith constant, for use in error messages, when passing a length of -1 -- this function will invoke ffewhere_line/column_use to make its own copies. Else line and column are don't-cares (when length is 0) and the outstanding copies of the previous line/column info, if still around, are killed. 21-Feb-90 JCB 3.1 When called with length of 0, also zero ffelex_raw_mode_. This is so ffest_save_ can undo the effects of replaying tokens like APOSTROPHE and QUOTE. 25-Jan-90 JCB 3.0 New line, column arguments allow error messages to point to the true beginning of a character/hollerith constant, rather than the beginning of the content part, which makes them more consistent and helpful. 05-Nov-89 JCB 2.0 New "which" argument allows caller to specify termination character, which should be apostrophe or double-quote, to support Fortran 90. */ void ffelex_set_expecting_hollerith (long length, char which, ffewhereLine line, ffewhereColumn column) { /* First kill the pending line/col info, if any (should only be pending when this call has length==0, the previous call had length>0, and a non-HOLLERITH token was sent in between the calls, but play it safe). */ ffewhere_line_kill (ffelex_raw_where_line_); ffewhere_column_kill (ffelex_raw_where_col_); /* Now handle the length function. */ switch (length) { case 0: ffelex_expecting_hollerith_ = 0; ffelex_raw_mode_ = 0; ffelex_raw_where_line_ = ffewhere_line_unknown (); ffelex_raw_where_col_ = ffewhere_column_unknown (); return; /* Don't set new line/column info from args. */ case -1: ffelex_raw_mode_ = -1; ffelex_raw_char_ = which; break; default: /* length > 0 */ ffelex_expecting_hollerith_ = length; break; } /* Now set new line/column information from passed args. */ ffelex_raw_where_line_ = ffewhere_line_use (line); ffelex_raw_where_col_ = ffewhere_column_use (column); } /* ffelex_set_handler -- Set handler for tokens before calling _fixed or _free ffelex_set_handler((ffelexHandler) my_first_handler); Must be called before calling ffelex_file_fixed or ffelex_file_free or after they return, but not while they are active. */ void ffelex_set_handler (ffelexHandler first) { ffelex_handler_ = first; } /* ffelex_set_hexnum -- Set hexnum flag ffelex_set_hexnum(TRUE); Lex normally interprets a token starting with [0-9] as a NUMBER token, so if it sees a [A-Za-z] in it, it stops parsing the NUMBER and leaves the character as the first of the next token. But when parsing a hexadecimal number, by calling this function with TRUE before starting the parse of the token itself, lex will interpret [0-9] as the start of a NAME token. */ void ffelex_set_hexnum (bool f) { ffelex_hexnum_ = f; } /* ffelex_set_include -- Set INCLUDE file to be processed next ffewhereFile wf; // The ffewhereFile object for the file. bool free_form; // TRUE means read free-form file, FALSE fixed-form. FILE *fi; // The file to INCLUDE. ffelex_set_include(wf,free_form,fi); Must be called only after receiving the EOS token following a valid INCLUDE statement specifying a file that has already been successfully opened. */ void ffelex_set_include (ffewhereFile wf, bool free_form, FILE *fi) { assert (ffelex_permit_include_); assert (!ffelex_set_include_); ffelex_set_include_ = TRUE; ffelex_include_free_form_ = free_form; ffelex_include_file_ = fi; ffelex_include_wherefile_ = wf; } /* ffelex_set_names -- Set names/name flag, names = TRUE ffelex_set_names(FALSE); Lex initially assumes multiple names should be formed. If this function is called with FALSE, then single names are formed instead. The differences are a difference in the token type (FFELEX_typeNAMES vs. FFELEX_typeNAME) and in whether full source-location tracking is performed (it is for multiple names, not for single names), which is more expensive in terms of CPU time. */ void ffelex_set_names (bool f) { ffelex_names_ = f; if (!f) ffelex_names_pure_ = FALSE; } /* ffelex_set_names_pure -- Set names/name (pure) flag, names = TRUE ffelex_set_names_pure(FALSE); Like ffelex_set_names, except affects both lexers. Normally, the free-form lexer need not generate NAMES tokens because adjacent NAME tokens must be separated by spaces which causes the lexer to generate separate tokens for analysis (whereas in fixed-form the spaces are ignored resulting in one long token). But in FORMAT statements, for some reason, the Fortran 90 standard specifies that spaces can occur anywhere within a format-item-list with no effect on the format spec (except of course within character string edit descriptors), which means that "1PE14.2" and "1 P E 1 4 . 2" are equivalent. For the FORMAT statement handling, the existence of spaces makes it hard to deal with, because each token is seen distinctly (i.e. seven tokens in the latter example). But when no spaces are provided, as in the former example, then only four tokens are generated, NUMBER("1"), NAME("PE14"), PERIOD, NUMBER ("2"). By generating a NAMES instead of NAME, three things happen: One, ffest_kw_format_ does a substring rather than full-string match, and thus matches "PE14" to "PE"; two, ffelex_token_xyz_from_names functions may be used to pull NAME/NAMES and NUMBER tokens out of the NAMES token; and three, error reporting can point to the actual character rather than at or prior to it. The first two things could be resolved by providing alternate functions fairly easy, thus allowing FORMAT handling to expect both lexers to generate NAME tokens instead of NAMES (with otherwise minor changes to FORMAT parsing), but the third, error reporting, would suffer, and when one makes mistakes in a FORMAT, believe me, one wants a pointer to exactly where the compilers thinks the problem is, to even begin to get a handle on it. So there. */ void ffelex_set_names_pure (bool f) { ffelex_names_pure_ = f; ffelex_names_ = f; } /* ffelex_splice_tokens -- Splice off and send tokens from a NAMES return (ffelexHandler) ffelex_splice_tokens(first_handler,master_token, start_char_index); Returns first_handler if start_char_index chars into master_token (which must be a NAMES token) is '\0'. Else, creates a subtoken from that char, either NUMBER (if it is a digit), a NAME (if a valid firstnamechar), an UNDERSCORE (if an underscore), or DOLLAR (if a dollar sign) and sends it to first_handler. If anything other than NAME is sent, the character at the end of it in the master token is examined to see if it begins a NAME, NUMBER, UNDERSCORE, or DOLLAR, and, if so, the handler returned by first_handler is invoked with that token, and this process is repeated until the end of the master token or a NAME token is reached. */ ffelexHandler ffelex_splice_tokens (ffelexHandler first, ffelexToken master, ffeTokenLength start) { char *p; ffeTokenLength i; ffelexToken t; p = ffelex_token_text (master) + (i = start); while (*p != '\0') { if (isdigit (*p)) { t = ffelex_token_number_from_names (master, i); p += ffelex_token_length (t); i += ffelex_token_length (t); } else if (ffesrc_is_name_init (*p)) { t = ffelex_token_name_from_names (master, i, 0); p += ffelex_token_length (t); i += ffelex_token_length (t); } else if ((*p == '$') && !ffe_is_dollar_ok ()) { t = ffelex_token_dollar_from_names (master, i); ++p; ++i; } else if (*p == '_') { t = ffelex_token_uscore_from_names (master, i); ++p; ++i; } else { assert ("not a valid NAMES character" == NULL); t = NULL; } assert (first != NULL); first = (ffelexHandler) (*first) (t); ffelex_token_kill (t); } return first; } /* ffelex_swallow_tokens -- Eat all tokens delivered to me return ffelex_swallow_tokens; Return this handler when you don't want to look at any more tokens in the statement because you've encountered an unrecoverable error in the statement. */ ffelexHandler ffelex_swallow_tokens (ffelexToken t, ffelexHandler handler) { assert (handler != NULL); if ((t != NULL) && ((ffelex_token_type (t) == FFELEX_typeEOS) || (ffelex_token_type (t) == FFELEX_typeSEMICOLON))) return (ffelexHandler) (*handler) (t); ffelex_eos_handler_ = handler; return (ffelexHandler) ffelex_swallow_tokens_; } /* ffelex_token_dollar_from_names -- Return a dollar from within a names token ffelexToken t; t = ffelex_token_dollar_from_names(t,6); It's as if you made a new token of dollar type having the dollar at, in the example above, the sixth character of the NAMES token. */ ffelexToken ffelex_token_dollar_from_names (ffelexToken t, ffeTokenLength start) { ffelexToken nt; assert (t != NULL); assert (ffelex_token_type (t) == FFELEX_typeNAMES); assert (start < t->length); assert (t->text[start] == '$'); /* Now make the token. */ nt = ffelex_token_new_ (); nt->type = FFELEX_typeDOLLAR; nt->length = 0; nt->uses = 1; ffewhere_set_from_track (&nt->where_line, &nt->where_col, t->where_line, t->where_col, t->wheretrack, start); nt->text = NULL; return nt; } /* ffelex_token_kill -- Decrement use count for token, kill if no uses left ffelexToken t; ffelex_token_kill(t); Complements a call to ffelex_token_use or ffelex_token_new_.... */ void ffelex_token_kill (ffelexToken t) { assert (t != NULL); assert (t->uses > 0); if (--t->uses != 0) return; --ffelex_total_tokens_; if (t->type == FFELEX_typeNAMES) ffewhere_track_kill (t->where_line, t->where_col, t->wheretrack, t->length); ffewhere_line_kill (t->where_line); ffewhere_column_kill (t->where_col); if (t->text != NULL) malloc_kill_ksr (malloc_pool_image (), t->text, t->size + 1); malloc_kill_ks (malloc_pool_image (), t, sizeof (*t)); } /* Make a new NAME token that is a substring of a NAMES token. */ ffelexToken ffelex_token_name_from_names (ffelexToken t, ffeTokenLength start, ffeTokenLength len) { ffelexToken nt; assert (t != NULL); assert (ffelex_token_type (t) == FFELEX_typeNAMES); assert (start < t->length); if (len == 0) len = t->length - start; else { assert (len > 0); assert ((start + len) <= t->length); } assert (ffelex_is_firstnamechar (t->text[start])); nt = ffelex_token_new_ (); nt->type = FFELEX_typeNAME; nt->size = len; /* Assume nobody's gonna fiddle with token text. */ nt->length = len; nt->uses = 1; ffewhere_set_from_track (&nt->where_line, &nt->where_col, t->where_line, t->where_col, t->wheretrack, start); nt->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", len + 1); strncpy (nt->text, t->text + start, len); nt->text[len] = '\0'; return nt; } /* Make a new NAMES token that is a substring of another NAMES token. */ ffelexToken ffelex_token_names_from_names (ffelexToken t, ffeTokenLength start, ffeTokenLength len) { ffelexToken nt; assert (t != NULL); assert (ffelex_token_type (t) == FFELEX_typeNAMES); assert (start < t->length); if (len == 0) len = t->length - start; else { assert (len > 0); assert ((start + len) <= t->length); } assert (ffelex_is_firstnamechar (t->text[start])); nt = ffelex_token_new_ (); nt->type = FFELEX_typeNAMES; nt->size = len; /* Assume nobody's gonna fiddle with token text. */ nt->length = len; nt->uses = 1; ffewhere_set_from_track (&nt->where_line, &nt->where_col, t->where_line, t->where_col, t->wheretrack, start); ffewhere_track_copy (nt->wheretrack, t->wheretrack, start, len); nt->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", len + 1); strncpy (nt->text, t->text + start, len); nt->text[len] = '\0'; return nt; } /* Make a new CHARACTER token. */ ffelexToken ffelex_token_new_character (char *s, ffewhereLine l, ffewhereColumn c) { ffelexToken t; t = ffelex_token_new_ (); t->type = FFELEX_typeCHARACTER; t->length = t->size = strlen (s); /* Assume it won't get bigger. */ t->uses = 1; t->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", t->size + 1); strcpy (t->text, s); t->where_line = ffewhere_line_use (l); t->where_col = ffewhere_column_new (c); return t; } /* Make a new EOF token right after end of file. */ ffelexToken ffelex_token_new_eof () { ffelexToken t; t = ffelex_token_new_ (); t->type = FFELEX_typeEOF; t->uses = 1; t->text = NULL; t->where_line = ffewhere_line_new (ffelex_linecount_current_); t->where_col = ffewhere_column_new (1); return t; } /* Make a new NAME token. */ ffelexToken ffelex_token_new_name (char *s, ffewhereLine l, ffewhereColumn c) { ffelexToken t; assert (ffelex_is_firstnamechar (*s)); t = ffelex_token_new_ (); t->type = FFELEX_typeNAME; t->length = t->size = strlen (s); /* Assume it won't get bigger. */ t->uses = 1; t->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", t->size + 1); strcpy (t->text, s); t->where_line = ffewhere_line_use (l); t->where_col = ffewhere_column_new (c); return t; } /* Make a new NAMES token. */ ffelexToken ffelex_token_new_names (char *s, ffewhereLine l, ffewhereColumn c) { ffelexToken t; assert (ffelex_is_firstnamechar (*s)); t = ffelex_token_new_ (); t->type = FFELEX_typeNAMES; t->length = t->size = strlen (s); /* Assume it won't get bigger. */ t->uses = 1; t->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", t->size + 1); strcpy (t->text, s); t->where_line = ffewhere_line_use (l); t->where_col = ffewhere_column_new (c); ffewhere_track_clear (t->wheretrack, t->length); /* Assume contiguous names. */ return t; } /* Make a new NUMBER token. The first character of the string must be a digit, and only the digits are copied into the new number. So this may be used to easily extract a NUMBER token from within any text string. Then the length of the resulting token may be used to calculate where the digits stopped in the original string. */ ffelexToken ffelex_token_new_number (char *s, ffewhereLine l, ffewhereColumn c) { ffelexToken t; ffeTokenLength len; /* How long is the string of decimal digits at s? */ len = strspn (s, "0123456789"); /* Make sure there is at least one digit. */ assert (len != 0); /* Now make the token. */ t = ffelex_token_new_ (); t->type = FFELEX_typeNUMBER; t->length = t->size = len; /* Assume it won't get bigger. */ t->uses = 1; t->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", len + 1); strncpy (t->text, s, len); t->text[len] = '\0'; t->where_line = ffewhere_line_use (l); t->where_col = ffewhere_column_new (c); return t; } /* Make a new token of any type that doesn't contain text. A private function that is used by public macros in the interface file. */ ffelexToken ffelex_token_new_simple_ (ffelexType type, ffewhereLine l, ffewhereColumn c) { ffelexToken t; t = ffelex_token_new_ (); t->type = type; t->uses = 1; t->text = NULL; t->where_line = ffewhere_line_use (l); t->where_col = ffewhere_column_new (c); return t; } /* Make a new NUMBER token from an existing NAMES token. Like ffelex_token_new_number, this function calculates the length of the digit string itself. */ ffelexToken ffelex_token_number_from_names (ffelexToken t, ffeTokenLength start) { ffelexToken nt; ffeTokenLength len; assert (t != NULL); assert (ffelex_token_type (t) == FFELEX_typeNAMES); assert (start < t->length); /* How long is the string of decimal digits at s? */ len = strspn (t->text + start, "0123456789"); /* Make sure there is at least one digit. */ assert (len != 0); /* Now make the token. */ nt = ffelex_token_new_ (); nt->type = FFELEX_typeNUMBER; nt->size = len; /* Assume nobody's gonna fiddle with token text. */ nt->length = len; nt->uses = 1; ffewhere_set_from_track (&nt->where_line, &nt->where_col, t->where_line, t->where_col, t->wheretrack, start); nt->text = malloc_new_ksr (malloc_pool_image (), "FFELEX token text", len + 1); strncpy (nt->text, t->text + start, len); nt->text[len] = '\0'; return nt; } /* Make a new UNDERSCORE token from a NAMES token. */ ffelexToken ffelex_token_uscore_from_names (ffelexToken t, ffeTokenLength start) { ffelexToken nt; assert (t != NULL); assert (ffelex_token_type (t) == FFELEX_typeNAMES); assert (start < t->length); assert (t->text[start] == '_'); /* Now make the token. */ nt = ffelex_token_new_ (); nt->type = FFELEX_typeUNDERSCORE; nt->uses = 1; ffewhere_set_from_track (&nt->where_line, &nt->where_col, t->where_line, t->where_col, t->wheretrack, start); nt->text = NULL; return nt; } /* ffelex_token_use -- Return another instance of a token ffelexToken t; t = ffelex_token_use(t); In a sense, the new token is a copy of the old, though it might be the same with just a new use count. We use the use count method (easy). */ ffelexToken ffelex_token_use (ffelexToken t) { if (t == NULL) assert ("_token_use: null token" == NULL); t->uses++; return t; }