| /* Implementation of Fortran lexer |
| Copyright (C) 1995, 1996, 1997, 1998, 2001, 2002, 2003 |
| Free Software Foundation, Inc. |
| Contributed by James Craig Burley. |
| |
| 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 "top.h" |
| #include "bad.h" |
| #include "com.h" |
| #include "lex.h" |
| #include "malloc.h" |
| #include "src.h" |
| #include "debug.h" |
| #include "flags.h" |
| #include "input.h" |
| #include "toplev.h" |
| #include "output.h" |
| #include "ggc.h" |
| |
| 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); |
| static int ffelex_get_directive_line_ (char **text, FILE *finput); |
| static int ffelex_hash_ (FILE *f); |
| 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 GTY (()) 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 GTY(()) 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); |
| } |
| 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 |
| |
| 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': |
| 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': |
| return TARGET_BELL; |
| |
| case 'v': |
| return TARGET_VT; |
| |
| case 'e': |
| case 'E': |
| case '(': |
| case '{': |
| case '[': |
| case '%': |
| if (pedantic) |
| { |
| char m[2]; |
| |
| m[0] = c; |
| m[1] = '\0'; |
| /* xgettext:no-c-format */ |
| ffebad_start_msg_lex ("Non-ISO-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'; |
| /* xgettext:no-c-format */ |
| 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) |
| { |
| /* xgettext:no-c-format */ |
| 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); |
| /* xgettext:no-c-format */ |
| 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 (ISXDIGIT (c)) |
| { |
| code = (code * 16) + hex_value (c); |
| if (code != 0 || count != 0) |
| { |
| if (count == 0) |
| firstdig = code; |
| count++; |
| } |
| nonnull = 1; |
| return EOF; |
| } |
| |
| state = 0; |
| |
| if (! nonnull) |
| { |
| /* xgettext:no-c-format */ |
| 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))) |
| { |
| /* xgettext:no-c-format */ |
| 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))) |
| { |
| /* xgettext:no-c-format */ |
| 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); |
| } |
| |
| 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); |
| } |
| |
| 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': |
| code = 0; |
| count = 0; |
| nonnull = 0; |
| while (1) |
| { |
| c = getc (finput); |
| if (! ISXDIGIT (c)) |
| { |
| *use_d = 1; |
| *d = c; |
| break; |
| } |
| code = (code * 16) + hex_value (c); |
| 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': |
| return TARGET_BELL; |
| |
| case 'v': |
| 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-ISO 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; |
| } |
| |
| /* A miniature version of the C front-end lexer. */ |
| |
| 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; |
| if (q == &buff[0]) |
| { |
| q = xmalloc (buffer_length); |
| memcpy (q, buff, bytes_used); |
| } |
| else |
| q = 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 = 0; |
| |
| switch (c) |
| { |
| case '\"': |
| c = getc (finput); |
| done = TRUE; |
| break; |
| |
| case '\\': /* ~~~~~ */ |
| c = ffelex_cfebackslash_ (&use_d, &d, finput); |
| break; |
| |
| case EOF: |
| case '\n': |
| error ("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; |
| if (q == &buff[0]) |
| { |
| q = xmalloc (buffer_length); |
| memcpy (q, buff, bytes_used); |
| } |
| else |
| q = 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; |
| } |
| |
| static void |
| ffelex_file_pop_ (const char *filename) |
| { |
| if (input_file_stack->next) |
| { |
| struct file_stack *p = input_file_stack; |
| input_file_stack = p->next; |
| free (p); |
| input_file_stack_tick++; |
| (*debug_hooks->end_source_file) (input_file_stack->location.line); |
| } |
| 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->location.file = filename; |
| } |
| |
| static void |
| ffelex_file_push_ (int old_lineno, const char *filename) |
| { |
| struct file_stack *p = xmalloc (sizeof (struct file_stack)); |
| |
| input_file_stack->location.line = old_lineno; |
| p->next = input_file_stack; |
| p->location.file = filename; |
| input_file_stack = p; |
| input_file_stack_tick++; |
| |
| (*debug_hooks->start_source_file) (0, filename); |
| |
| /* Now that we've pushed or popped the input stack, |
| update the name in the top element. */ |
| if (input_file_stack) |
| input_file_stack->location.file = filename; |
| } |
| |
| /* 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_ (void) |
| { |
| 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 (); |
| /* Make sure the token has some text, might as well fill up with spaces. */ |
| do |
| { |
| ffelex_append_to_token_ (' '); |
| } while (--ffelex_raw_mode_ > 0); |
| 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_ (void) |
| { |
| 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. */ |
| |
| 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 = 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 = 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) |
| error ("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 |
| one of these (or an EOF). */ |
| |
| /* Handle backslash. */ |
| char_escaped = (c == '\\' && ! char_escaped); |
| } |
| } |
| |
| /* 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. */ |
| |
| 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 (ISALPHA(c)) |
| { |
| 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; |
| } |
| } |
| 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); |
| |
| if (debug_info_level == DINFO_LEVEL_VERBOSE) |
| (*debug_hooks->define) (input_line, text); |
| |
| 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); |
| |
| if (debug_info_level == DINFO_LEVEL_VERBOSE) |
| (*debug_hooks->undef) (input_line, text); |
| |
| 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 cpp. */ |
| |
| /* 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 (! flag_no_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 `# <nonletter>'. |
| 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)) |
| { |
| location_t old_loc = input_location; |
| 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. */ |
| input_line = 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; |
| } |
| |
| input_line = l; |
| |
| if (ffelex_kludge_flag_) |
| input_filename = ggc_strdup (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->location.file = 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_) |
| { |
| input_line = 1; |
| input_filename = old_loc.file; |
| error ("use `#line ...' instead of `# ...' in first line"); |
| } |
| |
| if (num == 1) |
| { |
| /* Pushing to a new file. */ |
| ffelex_file_push_ (old_loc.line, 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_) |
| { |
| input_line = 1; |
| input_filename = old_loc.file; |
| error ("use `#line ...' instead of `# ...' in first line"); |
| } |
| if (c == '\n' || c == EOF) |
| { |
| if (token != NULL && !ffelex_kludge_flag_) |
| ffelex_token_kill (token); |
| return c; |
| } |
| } |
| 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; |
| } |
| |
| /* "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) |
| { |
| ffewhereColumnNumber old_column = 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. */ |
| overflow: /* :::::::::::::::::::: */ |
| |
| ffelex_bad_line_ = TRUE; |
| strcpy (&ffelex_card_image_[column - 3], "..."); |
| ffelex_card_length_ = column; |
| ffelex_bad_1_ (FFEBAD_LINE_TOO_LONG, |
| ffelex_linecount_current_, column + 1); |
| 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; |
| /* xgettext:no-c-format */ |
| 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; |
| } |
| |
| if (column < old_column) |
| { |
| column = old_column; |
| goto overflow; /* :::::::::::::::::::: */ |
| } |
| |
| return column; |
| } |
| |
| static void |
| ffelex_include_ (void) |
| { |
| 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); |
| location_t old_loc = input_location; |
| |
| 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); |
| |
| ffelex_file_push_ (old_loc.line, ffewhere_file_name (include_wherefile)); |
| |
| if (ffelex_include_free_form_) |
| ffelex_file_free (include_wherefile, include_file); |
| else |
| ffelex_file_fixed (include_wherefile, include_file); |
| |
| ffelex_file_pop_ (ffewhere_file_name (current_wf)); |
| |
| ffewhere_file_set (current_wf, TRUE, linecount_offset); |
| |
| ffecom_close_include (include_file); |
| |
| if (card_length != 0) |
| { |
| assert (ffelex_card_size_ >= card_length); /* It shrunk?? */ |
| memcpy (ffelex_card_image_, card_image, card_length); |
| } |
| ffelex_card_image_[card_length] = '\0'; |
| |
| input_location = old_loc; |
| 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 <n> spaces starting at ffelex_card_image_[col] up through |
| the null character, where <n> 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 <n> spaces starting at ffelex_card_image_[col] up through |
| the null character or '!', where <n> 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_ (void) |
| { |
| ffelex_linecount_current_ = ffelex_linecount_next_; |
| ++ffelex_linecount_next_; |
| ++input_line; |
| } |
| |
| static void |
| ffelex_send_token_ (void) |
| { |
| ++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_ (void) |
| { |
| ffelexToken t; |
| |
| ++ffelex_total_tokens_; |
| |
| t = malloc_new_ks (malloc_pool_image (), "FFELEX token", sizeof (*t)); |
| t->id_ = ffelex_token_nextid_++; |
| return t; |
| } |
| |
| static const char * |
| ffelex_type_string_ (ffelexType type) |
| { |
| static const char *const 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 (void) |
| { |
| 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 = 0; /* Character currently under consideration. */ |
| register ffewhereColumnNumber column = 0; /* Not really; 0 means column 1... */ |
| bool disallow_continuation_line; |
| bool ignore_disallowed_continuation = FALSE; |
| 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); |
| |
| input_line = 0; |
| input_filename = ffewhere_file_name (wf); |
| 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: /* :::::::::::::::::::: */ |
| |
| 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); |
| } |
| else if (lextype == FFELEX_typeHASH) |
| c = ffelex_hash_ (f); |
| 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 = 0; /* Character currently under consideration. */ |
| register ffewhereColumnNumber column = 0; /* Not really; 0 means column 1... */ |
| bool continuation_line = FALSE; |
| ffewhereColumnNumber continuation_column; |
| int latest_char_in_file = 0; /* 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); |
| |
| input_line = 0; |
| input_filename = ffewhere_file_name (wf); |
| 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 == '#') |
| c = ffelex_hash_ (f); |
| |
| 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_); |
| ffel
|