📄 scanner.c
字号:
/* Character scanner. Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by Andy VaughtThis file is part of GCC.GCC is free software; you can redistribute it and/or modify it underthe terms of the GNU General Public License as published by the FreeSoftware Foundation; either version 2, or (at your option) any laterversion.GCC is distributed in the hope that it will be useful, but WITHOUT ANYWARRANTY; without even the implied warranty of MERCHANTABILITY orFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public Licensefor more details.You should have received a copy of the GNU General Public Licensealong with GCC; see the file COPYING. If not, write to the FreeSoftware Foundation, 51 Franklin Street, Fifth Floor, Boston, MA02110-1301, USA. *//* Set of subroutines to (ultimately) return the next character to the various matching subroutines. This file's job is to read files and build up lines that are parsed by the parser. This means that we handle continuation lines and "include" lines. The first thing the scanner does is to load an entire file into memory. We load the entire file into memory for a couple reasons. The first is that we want to be able to deal with nonseekable input (pipes, stdin) and there is a lot of backing up involved during parsing. The second is that we want to be able to print the locus of errors, and an error on line 999999 could conflict with something on line one. Given nonseekable input, we've got to store the whole thing. One thing that helps are the column truncation limits that give us an upper bound on the size of individual lines. We don't store the truncated stuff. From the scanner's viewpoint, the higher level subroutines ask for new characters and do a lot of jumping backwards. */#include "config.h"#include "system.h"#include "gfortran.h"#include "toplev.h"/* Structure for holding module and include file search path. */typedef struct gfc_directorylist{ char *path; struct gfc_directorylist *next;}gfc_directorylist;/* List of include file search directories. */static gfc_directorylist *include_dirs;static gfc_file *file_head, *current_file;static int continue_flag, end_flag;gfc_source_form gfc_current_form;static gfc_linebuf *line_head, *line_tail; locus gfc_current_locus;const char *gfc_source_file;static FILE *gfc_src_file;static char *gfc_src_preprocessor_lines[2];/* Main scanner initialization. */voidgfc_scanner_init_1 (void){ file_head = NULL; line_head = NULL; line_tail = NULL; end_flag = 0;}/* Main scanner destructor. */voidgfc_scanner_done_1 (void){ gfc_linebuf *lb; gfc_file *f; while(line_head != NULL) { lb = line_head->next; gfc_free(line_head); line_head = lb; } while(file_head != NULL) { f = file_head->next; gfc_free(file_head->filename); gfc_free(file_head); file_head = f; }}/* Adds path to the list pointed to by list. */voidgfc_add_include_path (const char *path){ gfc_directorylist *dir; const char *p; p = path; while (*p == ' ' || *p == '\t') /* someone might do 'gfortran "-I include"' */ if (*p++ == '\0') return; dir = include_dirs; if (!dir) { dir = include_dirs = gfc_getmem (sizeof (gfc_directorylist)); } else { while (dir->next) dir = dir->next; dir->next = gfc_getmem (sizeof (gfc_directorylist)); dir = dir->next; } dir->next = NULL; dir->path = gfc_getmem (strlen (p) + 2); strcpy (dir->path, p); strcat (dir->path, "/"); /* make '/' last character */}/* Release resources allocated for options. */voidgfc_release_include_path (void){ gfc_directorylist *p; gfc_free (gfc_option.module_dir); while (include_dirs != NULL) { p = include_dirs; include_dirs = include_dirs->next; gfc_free (p->path); gfc_free (p); }}/* Opens file for reading, searching through the include directories given if necessary. If the include_cwd argument is true, we try to open the file in the current directory first. */FILE *gfc_open_included_file (const char *name, const bool include_cwd){ char *fullname; gfc_directorylist *p; FILE *f; if (include_cwd) { f = gfc_open_file (name); if (f != NULL) return f; } for (p = include_dirs; p; p = p->next) { fullname = (char *) alloca(strlen (p->path) + strlen (name) + 1); strcpy (fullname, p->path); strcat (fullname, name); f = gfc_open_file (fullname); if (f != NULL) return f; } return NULL;}/* Test to see if we're at the end of the main source file. */intgfc_at_end (void){ return end_flag;}/* Test to see if we're at the end of the current file. */intgfc_at_eof (void){ if (gfc_at_end ()) return 1; if (line_head == NULL) return 1; /* Null file */ if (gfc_current_locus.lb == NULL) return 1; return 0;}/* Test to see if we're at the beginning of a new line. */intgfc_at_bol (void){ if (gfc_at_eof ()) return 1; return (gfc_current_locus.nextc == gfc_current_locus.lb->line);}/* Test to see if we're at the end of a line. */intgfc_at_eol (void){ if (gfc_at_eof ()) return 1; return (*gfc_current_locus.nextc == '\0');}/* Advance the current line pointer to the next line. */voidgfc_advance_line (void){ if (gfc_at_end ()) return; if (gfc_current_locus.lb == NULL) { end_flag = 1; return; } gfc_current_locus.lb = gfc_current_locus.lb->next; if (gfc_current_locus.lb != NULL) gfc_current_locus.nextc = gfc_current_locus.lb->line; else { gfc_current_locus.nextc = NULL; end_flag = 1; } }/* Get the next character from the input, advancing gfc_current_file's locus. When we hit the end of the line or the end of the file, we start returning a '\n' in order to complete the current statement. No Fortran line conventions are implemented here. Requiring explicit advances to the next line prevents the parse pointer from being on the wrong line if the current statement ends prematurely. */static intnext_char (void){ int c; if (gfc_current_locus.nextc == NULL) return '\n'; c = *gfc_current_locus.nextc++; if (c == '\0') { gfc_current_locus.nextc--; /* Remain on this line. */ c = '\n'; } return c;}/* Skip a comment. When we come here the parse pointer is positioned immediately after the comment character. If we ever implement compiler directives withing comments, here is where we parse the directive. */static voidskip_comment_line (void){ char c; do { c = next_char (); } while (c != '\n'); gfc_advance_line ();}/* Comment lines are null lines, lines containing only blanks or lines on which the first nonblank line is a '!'. */static voidskip_free_comments (void){ locus start; char c; for (;;) { start = gfc_current_locus; if (gfc_at_eof ()) break; do { c = next_char (); } while (gfc_is_whitespace (c)); if (c == '\n') { gfc_advance_line (); continue; } if (c == '!') { skip_comment_line (); continue; } break; } gfc_current_locus = start;}/* Skip comment lines in fixed source mode. We have the same rules as in skip_free_comment(), except that we can have a 'c', 'C' or '*' in column 1, and a '!' cannot be in column 6. Also, we deal with lines with 'd' or 'D' in column 1, if the user requested this. */static voidskip_fixed_comments (void){ locus start; int col; char c; for (;;) { start = gfc_current_locus; if (gfc_at_eof ()) break; c = next_char (); if (c == '\n') { gfc_advance_line (); continue; } if (c == '!' || c == 'c' || c == 'C' || c == '*') { skip_comment_line (); continue; } if (gfc_option.flag_d_lines != -1 && (c == 'd' || c == 'D')) { if (gfc_option.flag_d_lines == 0) { skip_comment_line (); continue; } else *start.nextc = c = ' '; } col = 1; while (gfc_is_whitespace (c)) { c = next_char (); col++; } if (c == '\n') { gfc_advance_line (); continue; } if (col != 6 && c == '!') { skip_comment_line (); continue; } break; } gfc_current_locus = start;}/* Skips the current line if it is a comment. Assumes that we are at the start of the current line. */voidgfc_skip_comments (void){ if (!gfc_at_bol () || gfc_current_form == FORM_FREE) skip_free_comments (); else skip_fixed_comments ();}/* Get the next character from the input, taking continuation lines and end-of-line comments into account. This implies that comment lines between continued lines must be eaten here. For higher-level subroutines, this flattens continued lines into a single logical line. The in_string flag denotes whether we're inside a character context or not. */intgfc_next_char_literal (int in_string){ locus old_loc; int i, c; continue_flag = 0;restart: c = next_char (); if (gfc_at_end ()) return c; if (gfc_current_form == FORM_FREE) { if (!in_string && c == '!') { /* This line can't be continued */ do { c = next_char (); } while (c != '\n'); /* Avoid truncation warnings for comment ending lines. */ gfc_current_locus.lb->truncated = 0; goto done; } if (c != '&') goto done; /* If the next nonblank character is a ! or \n, we've got a continuation line. */ old_loc = gfc_current_locus; c = next_char (); while (gfc_is_whitespace (c)) c = next_char (); /* Character constants to be continued cannot have commentary after the '&'. */ if (in_string && c != '\n') { gfc_current_locus = old_loc; c = '&'; goto done; } if (c != '!' && c != '\n') { gfc_current_locus = old_loc; c = '&'; goto done; } continue_flag = 1; if (c == '!') skip_comment_line (); else gfc_advance_line (); /* We've got a continuation line and need to find where it continues. First eat any comment lines. */ gfc_skip_comments (); /* Now that we have a non-comment line, probe ahead for the first non-whitespace character. If it is another '&', then reading starts at the next character, otherwise we must back up to where the whitespace started and resume from there. */ old_loc = gfc_current_locus; c = next_char (); while (gfc_is_whitespace (c)) c = next_char (); if (c != '&') gfc_current_locus = old_loc; } else { /* Fixed form continuation. */ if (!in_string && c == '!') { /* Skip comment at end of line. */ do { c = next_char (); } while (c != '\n'); /* Avoid truncation warnings for comment ending lines. */ gfc_current_locus.lb->truncated = 0; } if (c != '\n') goto done; continue_flag = 1; old_loc = gfc_current_locus; gfc_advance_line (); gfc_skip_comments (); /* See if this line is a continuation line. */ for (i = 0; i < 5; i++) { c = next_char (); if (c != ' ') goto not_continuation; } c = next_char (); if (c == '0' || c == ' ') goto not_continuation; } /* Ready to read first character of continuation line, which might be another continuation line! */ goto restart;not_continuation: c = '\n'; gfc_current_locus = old_loc;done: continue_flag = 0; return c;}/* Get the next character of input, folded to lowercase. In fixed form mode, we also ignore spaces. When matcher subroutines are parsing character literals, they have to call gfc_next_char_literal(). */intgfc_next_char (void){ int c; do { c = gfc_next_char_literal (0); } while (gfc_current_form == FORM_FIXED && gfc_is_whitespace (c)); return TOLOWER (c);}intgfc_peek_char (void){ locus old_loc; int c; old_loc = gfc_current_locus; c = gfc_next_char (); gfc_current_locus = old_loc; return c;}/* Recover from an error. We try to get past the current statement and get lined up for the next. The next statement follows a '\n' or a ';'. We also assume that we are not within a character constant, and deal with finding a '\'' or '"'. */voidgfc_error_recovery (void){ char c, delim; if (gfc_at_eof ()) return; for (;;) { c = gfc_next_char (); if (c == '\n' || c == ';') break; if (c != '\'' && c != '"') { if (gfc_at_eof ()) break; continue; } delim = c; for (;;) { c = next_char (); if (c == delim) break; if (c == '\n') return; if (c == '\\') {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -