/* Lexical analyzer for GNU CHILL. -*- C -*- Copyright (C) 1992, 93, 94, 98, 99, 2000 Free Software Foundation, Inc. This file is part of GNU CC. GNU CC 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 CC 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 General Public License for more details. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" #include #include #include "tree.h" #include "input.h" #include "lex.h" #include "ch-tree.h" #include "flags.h" #include "parse.h" #include "obstack.h" #include "toplev.h" #include "tm_p.h" #ifdef DWARF_DEBUGGING_INFO #include "dwarfout.h" #endif #ifdef MULTIBYTE_CHARS #include #endif /* include the keyword recognizers */ #include "hash.h" FILE* finput; #if 0 static int last_token = 0; /* Sun's C compiler warns about the safer sequence do { .. } while 0 when there's a 'return' inside the braces, so don't use it */ #define RETURN_TOKEN(X) { last_token = X; return (X); } #endif /* This is set non-zero to force incoming tokens to lowercase. */ extern int ignore_case; extern int module_number; extern int serious_errors; /* This is non-zero to recognize only uppercase special words. */ extern int special_UC; extern struct obstack permanent_obstack; extern struct obstack temporary_obstack; /* forward declarations */ static void close_input_file PARAMS ((const char *)); static tree convert_bitstring PARAMS ((char *)); static tree convert_integer PARAMS ((char *)); static void maybe_downcase PARAMS ((char *)); static int maybe_number PARAMS ((const char *)); static tree equal_number PARAMS ((void)); static void handle_use_seizefile_directive PARAMS ((int)); static int handle_name PARAMS ((tree)); static char *readstring PARAMS ((int, int *)); static void read_directive PARAMS ((void)); static tree read_identifier PARAMS ((int)); static tree read_number PARAMS ((int)); static void skip_c_comment PARAMS ((void)); static void skip_line_comment PARAMS ((void)); static int skip_whitespace PARAMS ((void)); static tree string_or_char PARAMS ((int, const char *)); static void ch_lex_init PARAMS ((void)); static void skip_directive PARAMS ((void)); static int same_file PARAMS ((const char *, const char *)); static int getlc PARAMS ((FILE *)); /* next variables are public, because ch-actions uses them */ /* the default grantfile name, set by lang_init */ tree default_grant_file = 0; /* These tasking-related variables are NULL at the start of each compiler pass, and are set to an expression tree if and when a compiler directive is parsed containing an expression. The NULL state is significant; it means 'no user-specified signal_code (or whatever) has been parsed'. */ /* process type, set by <> PROCESS_TYPE = number <> */ tree process_type = NULL_TREE; /* send buffer default priority, set by <> SEND_BUFFER_DEFAULT_PRIORITY = number <> */ tree send_buffer_prio = NULL_TREE; /* send signal default priority, set by <> SEND_SIGNAL_DEFAULT_PRIORITY = number <> */ tree send_signal_prio = NULL_TREE; /* signal code, set by <> SIGNAL_CODE = number <> */ tree signal_code = NULL_TREE; /* flag for range checking */ int range_checking = 1; /* flag for NULL pointer checking */ int empty_checking = 1; /* flag to indicate making all procedure local variables to be STATIC */ int all_static_flag = 0; /* flag to indicate -fruntime-checking command line option. Needed for initializing range_checking and empty_checking before pass 2 */ int runtime_checking_flag = 1; /* The elements of `ridpointers' are identifier nodes for the reserved type names and storage classes. It is indexed by a RID_... value. */ tree ridpointers[(int) RID_MAX]; /* Nonzero tells yylex to ignore \ in string constants. */ static int ignore_escape_flag = 0; static int maxtoken; /* Current nominal length of token buffer. */ char *token_buffer; /* Pointer to token buffer. Actual allocated length is maxtoken + 2. This is not static because objc-parse.y uses it. */ /* implement yylineno handling for flex */ #define yylineno lineno static int inside_c_comment = 0; static int saw_eol = 0; /* 1 if we've just seen a '\n' */ static int saw_eof = 0; /* 1 if we've just seen an EOF */ typedef struct string_list { struct string_list *next; char *str; } STRING_LIST; /* list of paths specified on the compiler command line by -L options. */ static STRING_LIST *seize_path_list = (STRING_LIST *)0; /* List of seize file names. Each TREE_VALUE is an identifier (file name) from a <>USE_SEIZE_FILE<> directive. The TREE_PURPOSE is non-NULL if a USE_SEIZE_FILE directive has been written to the grant file. */ static tree files_to_seize = NULL_TREE; /* Last node on files_to_seize list. */ static tree last_file_to_seize = NULL_TREE; /* Pointer into files_to_seize list: Next unparsed file to read. */ static tree next_file_to_seize = NULL_TREE; /* The most recent use_seize_file directive. */ tree use_seizefile_name = NULL_TREE; /* If non-NULL, the name of the seizefile we're currently processing. */ tree current_seizefile_name = NULL_TREE; /* called to reset for pass 2 */ static void ch_lex_init () { current_seizefile_name = NULL_TREE; lineno = 0; saw_eol = 0; saw_eof = 0; /* Initialize these compiler-directive variables. */ process_type = NULL_TREE; send_buffer_prio = NULL_TREE; send_signal_prio = NULL_TREE; signal_code = NULL_TREE; all_static_flag = 0; /* reinitialize rnage checking and empty checking */ range_checking = runtime_checking_flag; empty_checking = runtime_checking_flag; } const char * init_parse (filename) const char *filename; { int lowercase_standard_names = ignore_case || ! special_UC; /* Open input file. */ if (filename == 0 || !strcmp (filename, "-")) { finput = stdin; filename = "stdin"; } else finput = fopen (filename, "r"); if (finput == 0) pfatal_with_name (filename); #ifdef IO_BUFFER_SIZE setvbuf (finput, (char *) xmalloc (IO_BUFFER_SIZE), _IOFBF, IO_BUFFER_SIZE); #endif /* Make identifier nodes long enough for the language-specific slots. */ set_identifier_size (sizeof (struct lang_identifier)); /* Start it at 0, because check_newline is called at the very beginning and will increment it to 1. */ lineno = 0; /* Initialize these compiler-directive variables. */ process_type = NULL_TREE; send_buffer_prio = NULL_TREE; send_signal_prio = NULL_TREE; signal_code = NULL_TREE; maxtoken = 40; token_buffer = xmalloc ((unsigned)(maxtoken + 2)); init_chill_expand (); #define ENTER_STANDARD_NAME(RID, LOWER, UPPER) \ ridpointers[(int) RID] = \ get_identifier (lowercase_standard_names ? LOWER : UPPER) ENTER_STANDARD_NAME (RID_ALL, "all", "ALL"); ENTER_STANDARD_NAME (RID_ASSERTFAIL, "assertfail", "ASSERTFAIL"); ENTER_STANDARD_NAME (RID_ASSOCIATION, "association", "ASSOCIATION"); ENTER_STANDARD_NAME (RID_BIN, "bin", "BIN"); ENTER_STANDARD_NAME (RID_BOOL, "bool", "BOOL"); ENTER_STANDARD_NAME (RID_BOOLS, "bools", "BOOLS"); ENTER_STANDARD_NAME (RID_BYTE, "byte", "BYTE"); ENTER_STANDARD_NAME (RID_CHAR, "char", "CHAR"); ENTER_STANDARD_NAME (RID_DOUBLE, "double", "DOUBLE"); ENTER_STANDARD_NAME (RID_DURATION, "duration", "DURATION"); ENTER_STANDARD_NAME (RID_DYNAMIC, "dynamic", "DYNAMIC"); ENTER_STANDARD_NAME (RID_ELSE, "else", "ELSE"); ENTER_STANDARD_NAME (RID_EMPTY, "empty", "EMPTY"); ENTER_STANDARD_NAME (RID_FALSE, "false", "FALSE"); ENTER_STANDARD_NAME (RID_FLOAT, "float", "FLOAT"); ENTER_STANDARD_NAME (RID_GENERAL, "general", "GENERAL"); ENTER_STANDARD_NAME (RID_IN, "in", "IN"); ENTER_STANDARD_NAME (RID_INLINE, "inline", "INLINE"); ENTER_STANDARD_NAME (RID_INOUT, "inout", "INOUT"); ENTER_STANDARD_NAME (RID_INSTANCE, "instance", "INSTANCE"); ENTER_STANDARD_NAME (RID_INT, "int", "INT"); ENTER_STANDARD_NAME (RID_LOC, "loc", "LOC"); ENTER_STANDARD_NAME (RID_LONG, "long", "LONG"); ENTER_STANDARD_NAME (RID_LONG_REAL, "long_real", "LONG_REAL"); ENTER_STANDARD_NAME (RID_NULL, "null", "NULL"); ENTER_STANDARD_NAME (RID_OUT, "out", "OUT"); ENTER_STANDARD_NAME (RID_OVERFLOW, "overflow", "OVERFLOW"); ENTER_STANDARD_NAME (RID_PTR, "ptr", "PTR"); ENTER_STANDARD_NAME (RID_READ, "read", "READ"); ENTER_STANDARD_NAME (RID_REAL, "real", "REAL"); ENTER_STANDARD_NAME (RID_RANGE, "range", "RANGE"); ENTER_STANDARD_NAME (RID_RANGEFAIL, "rangefail", "RANGEFAIL"); ENTER_STANDARD_NAME (RID_RECURSIVE, "recursive", "RECURSIVE"); ENTER_STANDARD_NAME (RID_SHORT, "short", "SHORT"); ENTER_STANDARD_NAME (RID_SIMPLE, "simple", "SIMPLE"); ENTER_STANDARD_NAME (RID_TIME, "time", "TIME"); ENTER_STANDARD_NAME (RID_TRUE, "true", "TRUE"); ENTER_STANDARD_NAME (RID_UBYTE, "ubyte", "UBYTE"); ENTER_STANDARD_NAME (RID_UINT, "uint", "UINT"); ENTER_STANDARD_NAME (RID_ULONG, "ulong", "ULONG"); ENTER_STANDARD_NAME (RID_UNSIGNED, "unsigned", "UNSIGNED"); ENTER_STANDARD_NAME (RID_USHORT, "ushort", "USHORT"); ENTER_STANDARD_NAME (RID_VOID, "void", "VOID"); return filename; } void finish_parse () { if (finput != NULL) fclose (finput); } static int yywrap PARAMS ((void)); static int yy_refill PARAMS ((void)); #define YY_PUTBACK_SIZE 5 #define YY_BUF_SIZE 1000 static char yy_buffer[YY_PUTBACK_SIZE + YY_BUF_SIZE]; static char *yy_cur = yy_buffer + YY_PUTBACK_SIZE; static char *yy_lim = yy_buffer + YY_PUTBACK_SIZE; static int yy_refill () { char *buf = yy_buffer + YY_PUTBACK_SIZE; int c, result; bcopy (yy_cur - YY_PUTBACK_SIZE, yy_buffer, YY_PUTBACK_SIZE); yy_cur = buf; retry: if (saw_eof) { if (yywrap ()) return EOF; saw_eof = 0; goto retry; } result = 0; while (saw_eol) { c = check_newline (); if (c == EOF) { saw_eof = 1; goto retry; } else if (c != '\n') { saw_eol = 0; buf[result++] = c; } } while (result < YY_BUF_SIZE) { c = getc(finput); if (c == EOF) { saw_eof = 1; break; } buf[result++] = c; /* Because we might switch input files on a compiler directive (that end with '>', don't read past a '>', just in case. */ if (c == '>') break; if (c == '\n') { #ifdef YYDEBUG extern int yydebug; if (yydebug) fprintf (stderr, "-------------------------- finished Line %d\n", yylineno); #endif saw_eol = 1; break; } } yy_lim = yy_cur + result; return yy_lim > yy_cur ? *yy_cur++ : EOF; } #define input() (yy_cur < yy_lim ? *yy_cur++ : yy_refill ()) #define unput(c) (*--yy_cur = (c)) int starting_pass_2 = 0; int yylex () { int nextc; int len; char* tmp; int base; int ch; retry: ch = input (); if (starting_pass_2) { starting_pass_2 = 0; unput (ch); return END_PASS_1; } switch (ch) { case ' ': case '\t': case '\n': case '\f': case '\b': case '\v': case '\r': goto retry; case '[': return LPC; case ']': return RPC; case '{': return LC; case '}': return RC; case '(': nextc = input (); if (nextc == ':') return LPC; unput (nextc); return LPRN; case ')': return RPRN; case ':': nextc = input (); if (nextc == ')') return RPC; else if (nextc == '=') return ASGN; unput (nextc); return COLON; case ',': return COMMA; case ';': return SC; case '+': return PLUS; case '-': nextc = input (); if (nextc == '>') return ARROW; if (nextc == '-') { skip_line_comment (); goto retry; } unput (nextc); return SUB; case '*': return MUL; case '=': return EQL; case '/': nextc = input (); if (nextc == '/') return CONCAT; else if (nextc == '=') return NE; else if (nextc == '*') { skip_c_comment (); goto retry; } unput (nextc); return DIV; case '<': nextc = input (); if (nextc == '=') return LTE; if (nextc == '>') { read_directive (); goto retry; } unput (nextc); return LT; case '>': nextc = input (); if (nextc == '=') return GTE; unput (nextc); return GT; case 'D': case 'd': base = 10; goto maybe_digits; case 'B': case 'b': base = 2; goto maybe_digits; case 'H': case 'h': base = 16; goto maybe_digits; case 'O': case 'o': base = 8; goto maybe_digits; case 'C': case 'c': nextc = input (); if (nextc == '\'') { int byte_val = 0; char *start; int len = 0; /* Number of hex digits seen. */ for (;;) { ch = input (); if (ch == '\'') break; if (ch == '_') continue; if (!ISXDIGIT (ch)) /* error on non-hex digit */ { if (pass == 1) error ("invalid C'xx' "); break; } if (ch >= 'a') ch -= ' '; ch -= '0'; if (ch > 9) ch -= 7; byte_val *= 16; byte_val += (int)ch; if (len & 1) /* collected two digits, save byte */ obstack_1grow (&temporary_obstack, (char) byte_val); len++; } start = obstack_finish (&temporary_obstack); yylval.ttype = string_or_char (len >> 1, start); obstack_free (&temporary_obstack, start); return len == 2 ? SINGLECHAR : STRING; } unput (nextc); goto letter; maybe_digits: nextc = input (); if (nextc == '\'') { char *start; obstack_1grow (&temporary_obstack, ch); obstack_1grow (&temporary_obstack, nextc); for (;;) { ch = input (); if (ISALNUM (ch)) obstack_1grow (&temporary_obstack, ch); else if (ch != '_') break; } obstack_1grow (&temporary_obstack, '\0'); start = obstack_finish (&temporary_obstack); if (ch != '\'') { unput (ch); yylval.ttype = convert_integer (start); /* Pass base? */ return NUMBER; } else { yylval.ttype = convert_bitstring (start); return BITSTRING; } } unput (nextc); goto letter; case 'A': case 'E': case 'F': case 'G': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 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 'e': case 'f': case 'g': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '_': letter: return handle_name (read_identifier (ch)); case '\'': tmp = readstring ('\'', &len); yylval.ttype = string_or_char (len, tmp); free (tmp); return len == 1 ? SINGLECHAR : STRING; case '\"': tmp = readstring ('\"', &len); yylval.ttype = build_chill_string (len, tmp); free (tmp); return STRING; case '.': nextc = input (); unput (nextc); if (ISDIGIT (nextc)) /* || nextc == '_') we don't start numbers with '_' */ goto number; return DOT; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': number: yylval.ttype = read_number (ch); return TREE_CODE (yylval.ttype) == REAL_CST ? FLOATING : NUMBER; default: return ch; } } static void close_input_file (fn) const char *fn; { if (finput == NULL) abort (); if (finput != stdin && fclose (finput) == EOF) { error ("can't close %s", fn); abort (); } finput = NULL; } /* Return an identifier, starting with FIRST and then reading more characters using input(). Return an IDENTIFIER_NODE. */ static tree read_identifier (first) int first; /* First letter of identifier */ { tree id; char *start; for (;;) { obstack_1grow (&temporary_obstack, first); first = input (); if (first == EOF) break; if (! ISALNUM (first) && first != '_') { unput (first); break; } } obstack_1grow (&temporary_obstack, '\0'); start = obstack_finish (&temporary_obstack); maybe_downcase (start); id = get_identifier (start); obstack_free (&temporary_obstack, start); return id; } /* Given an identifier ID, check to see if it is a reserved name, and return the appropriate token type. */ static int handle_name (id) tree id; { struct resword *tp; tp = in_word_set (IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id)); if (tp != NULL && special_UC == ISUPPER ((unsigned char) tp->name[0]) && (tp->flags == RESERVED || tp->flags == PREDEF)) { if (tp->rid != NORID) yylval.ttype = ridpointers[tp->rid]; else if (tp->token == THIS) yylval.ttype = lookup_name (get_identifier ("__whoami")); return tp->token; } yylval.ttype = id; return NAME; } static tree read_number (ch) int ch; /* Initial character */ { tree num; char *start; int is_float = 0; for (;;) { if (ch != '_') obstack_1grow (&temporary_obstack, ch); ch = input (); if (! ISDIGIT (ch) && ch != '_') break; } if (ch == '.') { do { if (ch != '_') obstack_1grow (&temporary_obstack, ch); ch = input (); } while (ISDIGIT (ch) || ch == '_'); is_float++; } if (ch == 'd' || ch == 'D' || ch == 'e' || ch == 'E') { /* Convert exponent indication [eEdD] to 'e'. */ obstack_1grow (&temporary_obstack, 'e'); ch = input (); if (ch == '+' || ch == '-') { obstack_1grow (&temporary_obstack, ch); ch = input (); } if (ISDIGIT (ch) || ch == '_') { do { if (ch != '_') obstack_1grow (&temporary_obstack, ch); ch = input (); } while (ISDIGIT (ch) || ch == '_'); } else { error ("malformed exponent part of floating-point literal"); } is_float++; } if (ch != EOF) unput (ch); obstack_1grow (&temporary_obstack, '\0'); start = obstack_finish (&temporary_obstack); if (is_float) { REAL_VALUE_TYPE value; tree type = double_type_node; errno = 0; value = REAL_VALUE_ATOF (start, TYPE_MODE (type)); obstack_free (&temporary_obstack, start); if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT && REAL_VALUE_ISINF (value) && pedantic) pedwarn ("real number exceeds range of REAL"); num = build_real (type, value); } else num = convert_integer (start); CH_DERIVED_FLAG (num) = 1; return num; } /* Skip to the end of a compiler directive. */ static void skip_directive () { int ch = input (); for (;;) { if (ch == EOF) { error ("end-of-file in '<>' directive"); break; } if (ch == '\n') break; if (ch == '<') { ch = input (); if (ch == '>') break; } ch = input (); } starting_pass_2 = 0; } /* Read a compiler directive. ("<>{WS}" have already been read. ) */ static void read_directive () { struct resword *tp; tree id; int ch = skip_whitespace(); if (ISALPHA (ch) || ch == '_') id = read_identifier (ch); else if (ch == EOF) { error ("end-of-file in '<>' directive"); to_global_binding_level (); return; } else { warning ("unrecognized compiler directive"); skip_directive (); return; } tp = in_word_set (IDENTIFIER_POINTER (id), IDENTIFIER_LENGTH (id)); if (tp == NULL || special_UC != ISUPPER ((unsigned char) tp->name[0])) { if (pass == 1) warning ("unrecognized compiler directive `%s'", IDENTIFIER_POINTER (id)); } else switch (tp->token) { case ALL_STATIC_OFF: all_static_flag = 0; break; case ALL_STATIC_ON: all_static_flag = 1; break; case EMPTY_OFF: empty_checking = 0; break; case EMPTY_ON: empty_checking = 1; break; case IGNORED_DIRECTIVE: break; case PROCESS_TYPE_TOKEN: process_type = equal_number (); break; case RANGE_OFF: range_checking = 0; break; case RANGE_ON: range_checking = 1; break; case SEND_SIGNAL_DEFAULT_PRIORITY: send_signal_prio = equal_number (); break; case SEND_BUFFER_DEFAULT_PRIORITY: send_buffer_prio = equal_number (); break; case SIGNAL_CODE: signal_code = equal_number (); break; case USE_SEIZE_FILE: handle_use_seizefile_directive (0); break; case USE_SEIZE_FILE_RESTRICTED: handle_use_seizefile_directive (1); break; default: if (pass == 1) warning ("unrecognized compiler directive `%s'", IDENTIFIER_POINTER (id)); break; } skip_directive (); } tree build_chill_string (len, str) int len; const char *str; { tree t; push_obstacks (&permanent_obstack, &permanent_obstack); t = build_string (len, str); TREE_TYPE (t) = build_string_type (char_type_node, build_int_2 (len, 0)); CH_DERIVED_FLAG (t) = 1; pop_obstacks (); return t; } static tree string_or_char (len, str) int len; const char *str; { tree result; push_obstacks (&permanent_obstack, &permanent_obstack); if (len == 1) { result = build_int_2 ((unsigned char)str[0], 0); CH_DERIVED_FLAG (result) = 1; TREE_TYPE (result) = char_type_node; } else result = build_chill_string (len, str); pop_obstacks (); return result; } static void maybe_downcase (str) char *str; { if (! ignore_case) return; while (*str) { if (ISUPPER ((unsigned char) *str)) *str = TOLOWER (*str); str++; } } static int maybe_number (s) const char *s; { char fc; /* check for decimal number */ if (*s >= '0' && *s <= '9') { while (*s) { if (*s >= '0' && *s <= '9') s++; else return 0; } return 1; } fc = *s; if (s[1] != '\'') return 0; s += 2; while (*s) { switch (fc) { case 'd': case 'D': if (*s < '0' || *s > '9') return 0; break; case 'h': case 'H': if (!ISXDIGIT ((unsigned char) *s)) return 0; break; case 'b': case 'B': if (*s < '0' || *s > '1') return 0; break; case 'o': case 'O': if (*s < '0' || *s > '7') return 0; break; default: return 0; } s++; } return 1; } static char * readstring (terminator, len) char terminator; int *len; { int c; unsigned allocated = 1024; char *tmp = xmalloc (allocated); unsigned i = 0; for (;;) { c = input (); if (c == terminator) { if ((c = input ()) != terminator) { unput (c); break; } else c = terminator; } if (c == '\n' || c == EOF) goto unterminated; if (c == '^') { c = input(); if (c == EOF || c == '\n') goto unterminated; if (c == '^') goto storeit; if (c == '(') { int cc, count = 0; int base = 10; int next_apos = 0; int check_base = 1; c = 0; while (1) { cc = input (); if (cc == terminator) { if (!(terminator == '\'' && next_apos)) { error ("unterminated control sequence"); serious_errors++; goto done; } } if (cc == EOF || cc == '\n') { c = cc; goto unterminated; } if (next_apos) { next_apos = 0; if (cc != '\'') { error ("invalid integer literal in control sequence"); serious_errors++; goto done; } continue; } if (cc == ' ' || cc == '\t') continue; if (cc == ')') { if ((c < 0 || c > 255) && (pass == 1)) error ("control sequence overflow"); if (! count && pass == 1) error ("invalid control sequence"); break; } else if (cc == ',') { if ((c < 0 || c > 255) && (pass == 1)) error ("control sequence overflow"); if (! count && pass == 1) error ("invalid control sequence"); tmp[i++] = c; if (i == allocated) { allocated += 1024; tmp = xrealloc (tmp, allocated); } c = count = 0; base = 10; check_base = 1; continue; } else if (cc == '_') { if (! count && pass == 1) error ("invalid integer literal in control sequence"); continue; } if (check_base) { if (cc == 'D' || cc == 'd') { base = 10; next_apos = 1; } else if (cc == 'H' || cc == 'h') { base = 16; next_apos = 1; } else if (cc == 'O' || cc == 'o') { base = 8; next_apos = 1; } else if (cc == 'B' || cc == 'b') { base = 2; next_apos = 1; } check_base = 0; if (next_apos) continue; } if (base == 2) { if (cc < '0' || cc > '1') cc = -1; else cc -= '0'; } else if (base == 8) { if (cc < '0' || cc > '8') cc = -1; else cc -= '0'; } else if (base == 10) { if (! ISDIGIT (cc)) cc = -1; else cc -= '0'; } else if (base == 16) { if (!ISXDIGIT (cc)) cc = -1; else { if (cc >= 'a') cc -= ' '; cc -= '0'; if (cc > 9) cc -= 7; } } else { error ("invalid base in read control sequence"); abort (); } if (cc == -1) { /* error in control sequence */ if (pass == 1) error ("invalid digit in control sequence"); cc = 0; } c = (c * base) + cc; count++; } } else c ^= 64; } storeit: tmp[i++] = c; if (i == allocated) { allocated += 1024; tmp = xrealloc (tmp, allocated); } } done: tmp [*len = i] = '\0'; return tmp; unterminated: if (c == '\n') unput ('\n'); *len = 1; if (pass == 1) error ("unterminated string literal"); to_global_binding_level (); tmp[0] = '\0'; return tmp; } /* Convert an integer INTCHARS into an INTEGER_CST. INTCHARS is on the temporary_obstack, and is popped by this function. */ static tree convert_integer (intchars) char *intchars; { #ifdef YYDEBUG extern int yydebug; #endif char *p = intchars; char *oldp = p; int base = 10, tmp; int valid_chars = 0; int overflow = 0; tree type; HOST_WIDE_INT val_lo = 0, val_hi = 0; tree val; /* determine the base */ switch (*p) { case 'd': case 'D': p += 2; break; case 'o': case 'O': p += 2; base = 8; break; case 'h': case 'H': p += 2; base = 16; break; case 'b': case 'B': p += 2; base = 2; break; default: if (!ISDIGIT (*p)) /* this test is for equal_number () */ { obstack_free (&temporary_obstack, intchars); return 0; } break; } while (*p) { tmp = *p++; if ((tmp == '\'') || (tmp == '_')) continue; if (tmp < '0') goto bad_char; if (tmp >= 'a') /* uppercase the char */ tmp -= ' '; switch (base) /* validate the characters */ { case 2: if (tmp > '1') goto bad_char; break; case 8: if (tmp > '7') goto bad_char; break; case 10: if (tmp > '9') goto bad_char; break; case 16: if (tmp > 'F') goto bad_char; if (tmp > '9' && tmp < 'A') goto bad_char; break; default: abort (); } tmp -= '0'; if (tmp > 9) tmp -= 7; if (mul_double (val_lo, val_hi, base, 0, &val_lo, &val_hi)) overflow++; add_double (val_lo, val_hi, tmp, 0, &val_lo, &val_hi); if (val_hi < 0) overflow++; valid_chars++; } bad_char: obstack_free (&temporary_obstack, intchars); if (!valid_chars) { if (pass == 2) error ("invalid number format `%s'", oldp); return 0; } val = build_int_2 (val_lo, val_hi); /* We set the type to long long (or long long unsigned) so that constant fold of literals is less likely to overflow. */ if (int_fits_type_p (val, long_long_integer_type_node)) type = long_long_integer_type_node; else { if (! int_fits_type_p (val, long_long_unsigned_type_node)) overflow++; type = long_long_unsigned_type_node; } TREE_TYPE (val) = type; CH_DERIVED_FLAG (val) = 1; if (overflow) error ("integer literal too big"); return val; } /* Convert a bitstring literal on the temporary_obstack to a bitstring CONSTRUCTOR. Free the literal from the obstack. */ static tree convert_bitstring (p) char *p; { #ifdef YYDEBUG extern int yydebug; #endif int bl = 0, valid_chars = 0, bits_per_char = 0, c, k; tree initlist = NULL_TREE; tree val; /* Move p to stack so we can re-use temporary_obstack for result. */ char *oldp = (char*) alloca (strlen (p) + 1); if (oldp == 0) fatal ("stack space exhausted"); strcpy (oldp, p); obstack_free (&temporary_obstack, p); p = oldp; switch (*p) { case 'h': case 'H': bits_per_char = 4; break; case 'o': case 'O': bits_per_char = 3; break; case 'b': case 'B': bits_per_char = 1; break; } p += 2; while (*p) { c = *p++; if (c == '_' || c == '\'') continue; if (c >= 'a') c -= ' '; c -= '0'; if (c > 9) c -= 7; valid_chars++; for (k = BYTES_BIG_ENDIAN ? bits_per_char - 1 : 0; BYTES_BIG_ENDIAN ? k >= 0 : k < bits_per_char; bl++, BYTES_BIG_ENDIAN ? k-- : k++) { if (c & (1 << k)) initlist = tree_cons (NULL_TREE, build_int_2 (bl, 0), initlist); } } #if 0 /* as long as BOOLS(0) is valid it must tbe possible to specify an empty bitstring */ if (!valid_chars) { if (pass == 2) error ("invalid number format `%s'", oldp); return 0; } #endif val = build (CONSTRUCTOR, build_bitstring_type (size_int (bl)), NULL_TREE, nreverse (initlist)); TREE_CONSTANT (val) = 1; CH_DERIVED_FLAG (val) = 1; return val; } /* Check if two filenames name the same file. This is done by stat'ing both files and comparing their inodes. Note: we have to take care of seize_path_list. Therefore do it the same way as in yywrap. FIXME: This probably can be done better. */ static int same_file (filename1, filename2) const char *filename1; const char *filename2; { struct stat s[2]; const char *fn_input[2]; int i, stat_status; if (grant_only_flag) /* do nothing in this case */ return 0; /* if filenames are equal -- return 1, cause there is no need to search in the include list in this case */ if (strcmp (filename1, filename2) == 0) return 1; fn_input[0] = filename1; fn_input[1] = filename2; for (i = 0; i < 2; i++) { stat_status = stat (fn_input[i], &s[i]); if (stat_status < 0 && strchr (fn_input[i], '/') == 0) { STRING_LIST *plp; char *path; for (plp = seize_path_list; plp != 0; plp = plp->next) { path = (char *)xmalloc (strlen (fn_input[i]) + strlen (plp->str) + 2); sprintf (path, "%s/%s", plp->str, fn_input[i]); stat_status = stat (path, &s[i]); free (path); if (stat_status >= 0) break; } } if (stat_status < 0) pfatal_with_name (fn_input[i]); } return s[0].st_ino == s[1].st_ino && s[0].st_dev == s[1].st_dev; } /* * Note that simply appending included file names to a list in this * way completely eliminates the need for nested files, and the * associated book-keeping, since the EOF processing in the lexer * will simply process the files one at a time, in the order that the * USE_SEIZE_FILE directives were scanned. */ static void handle_use_seizefile_directive (restricted) int restricted; { tree seen; int len; int c = skip_whitespace (); char *use_seizefile_str = readstring (c, &len); if (pass > 1) return; if (c != '\'' && c != '\"') { error ("USE_SEIZE_FILE directive must be followed by string"); return; } use_seizefile_name = get_identifier (use_seizefile_str); CH_USE_SEIZEFILE_RESTRICTED (use_seizefile_name) = restricted; if (!grant_only_flag) { /* If file foo.ch contains a <> use_seize_file "bar.grt" <>, and file bar.ch contains a <> use_seize_file "foo.grt" <>, then if we're compiling foo.ch, we will indirectly be asked to seize foo.grt. Don't. */ extern char *grant_file_name; if (strcmp (use_seizefile_str, grant_file_name) == 0) return; /* Check if the file is already on the list. */ for (seen = files_to_seize; seen != NULL_TREE; seen = TREE_CHAIN (seen)) if (same_file (IDENTIFIER_POINTER (TREE_VALUE (seen)), use_seizefile_str)) return; /* Previously seen; nothing to do. */ } /* Haven't been asked to seize this file yet, so add its name to the list. */ { tree pl = perm_tree_cons (0, use_seizefile_name, NULL_TREE); if (files_to_seize == NULL_TREE) files_to_seize = pl; else TREE_CHAIN (last_file_to_seize) = pl; if (next_file_to_seize == NULL_TREE) next_file_to_seize = pl; last_file_to_seize = pl; } } /* * get input, convert to lower case for comparison */ static int getlc (file) FILE *file; { register int c; c = getc (file); if (ignore_case) c = TOLOWER (c); return c; } #if defined HANDLE_PRAGMA /* Local versions of these macros, that can be passed as function pointers. */ static int pragma_getc () { return getc (finput); } static void pragma_ungetc (arg) int arg; { ungetc (arg, finput); } #endif /* HANDLE_PRAGMA */ #ifdef HANDLE_GENERIC_PRAGMAS /* Handle a generic #pragma directive. BUFFER contains the text we read after `#pragma'. Processes the entire input line and return non-zero iff the pragma was successfully processed. */ static int handle_generic_pragma (buffer) char * buffer; { register int c; for (;;) { char * buff; handle_pragma_token (buffer, NULL); c = getc (finput); while (c == ' ' || c == '\t') c = getc (finput); ungetc (c, finput); if (c == '\n' || c == EOF) return handle_pragma_token (NULL, NULL); /* Read the next word of the pragma into the buffer. */ buff = buffer; do { * buff ++ = c; c = getc (finput); } while (c != EOF && isascii (c) && ! ISSPACE (c) && c != '\n' && buff < buffer + 128); /* XXX shared knowledge about size of buffer. */ ungetc (c, finput); * -- buff = 0; } } #endif /* HANDLE_GENERIC_PRAGMAS */ /* At the beginning of a line, increment the line number and process any #-directive on this line. If the line is a #-directive, read the entire line and return a newline. Otherwise, return the line's first non-whitespace character. (Each language front end has a check_newline() function that is called from lang_init() for that language. One of the things this function must do is read the first line of the input file, and if it is a #line directive, extract the filename from it and use it to initialize main_input_filename. Proper generation of debugging information in the normal "front end calls cpp then calls cc1XXXX environment" depends upon this being done.) */ int check_newline () { register int c; lineno++; /* Read first nonwhite char on the line. */ c = getc (finput); while (c == ' ' || c == '\t') c = getc (finput); if (c != '#' || inside_c_comment) { /* If not #, return it so caller will use it. */ return c; } /* Read first nonwhite char after the `#'. */ c = getc (finput); while (c == ' ' || c == '\t') c = 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 (ignore_case) c = TOLOWER (c); if (c >= 'a' && c <= 'z') { if (c == 'p') { if (getlc (finput) == 'r' && getlc (finput) == 'a' && getlc (finput) == 'g' && getlc (finput) == 'm' && getlc (finput) == 'a' && (c = getlc (finput), ISSPACE (c))) { #ifdef HANDLE_PRAGMA static char buffer [128]; char * buff = buffer; /* Read the pragma name into a buffer. */ while (c = getlc (finput), ISSPACE (c)) continue; do { * buff ++ = c; c = getlc (finput); } while (c != EOF && ! ISSPACE (c) && c != '\n' && buff < buffer + 128); pragma_ungetc (c); * -- buff = 0; if (HANDLE_PRAGMA (pragma_getc, pragma_ungetc, buffer)) goto skipline; #endif /* HANDLE_PRAGMA */ #ifdef HANDLE_GENERIC_PRAGMAS if (handle_generic_pragma (buffer)) goto skipline; #endif /* HANDLE_GENERIC_PRAGMAS */ goto skipline; } } else if (c == 'd') { if (getlc (finput) == 'e' && getlc (finput) == 'f' && getlc (finput) == 'i' && getlc (finput) == 'n' && getlc (finput) == 'e' && (c = getlc (finput), ISSPACE (c))) { #if 0 /*def DWARF_DEBUGGING_INFO*/ if (c != '\n' && (debug_info_level == DINFO_LEVEL_VERBOSE) && (write_symbols == DWARF_DEBUG)) dwarfout_define (lineno, get_directive_line (finput)); #endif /* DWARF_DEBUGGING_INFO */ goto skipline; } } else if (c == 'u') { if (getlc (finput) == 'n' && getlc (finput) == 'd' && getlc (finput) == 'e' && getlc (finput) == 'f' && (c = getlc (finput), ISSPACE (c))) { #if 0 /*def DWARF_DEBUGGING_INFO*/ if (c != '\n' && (debug_info_level == DINFO_LEVEL_VERBOSE) && (write_symbols == DWARF_DEBUG)) dwarfout_undef (lineno, get_directive_line (finput)); #endif /* DWARF_DEBUGGING_INFO */ goto skipline; } } else if (c == 'l') { if (getlc (finput) == 'i' && getlc (finput) == 'n' && getlc (finput) == 'e' && ((c = getlc (finput)) == ' ' || c == '\t')) goto linenum; } #if 0 else if (c == 'i') { if (getlc (finput) == 'd' && getlc (finput) == 'e' && getlc (finput) == 'n' && getlc (finput) == 't' && ((c = getlc (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 = getlc (finput); /* If no argument, ignore the line. */ if (c == '\n') return c; ungetc (c, finput); token = yylex (); if (token != STRING || TREE_CODE (yylval.ttype) != STRING_CST) { error ("invalid #ident"); goto skipline; } if (!flag_no_ident) { #ifdef ASM_OUTPUT_IDENT extern FILE *asm_out_file; ASM_OUTPUT_IDENT (asm_out_file, TREE_STRING_POINTER (yylval.ttype)); #endif } /* Skip the rest of this line. */ goto skipline; } } #endif error ("undefined or invalid # directive"); goto skipline; } linenum: /* Here we have either `#line' or `# '. In either case, it should be a line number; a digit should follow. */ while (c == ' ' || c == '\t') c = getlc (finput); /* If the # is the only nonwhite char on the line, just ignore it. Check the new newline. */ if (c == '\n') return c; /* Something follows the #; read a token. */ if (ISDIGIT(c)) { int old_lineno = lineno; int used_up = 0; int l = 0; extern struct obstack permanent_obstack; do { l = l * 10 + (c - '0'); /* FIXME Not portable */ c = getlc(finput); } while (ISDIGIT(c)); /* subtract one, because it is the following line that gets the specified number */ l--; /* Is this the last nonwhite stuff on the line? */ c = getlc (finput); while (c == ' ' || c == '\t') c = getlc (finput); if (c == '\n') { /* No more: store the line number and check following line. */ lineno = l; return c; } /* More follows: it must be a string constant (filename). */ /* Read the string constant, but don't treat \ as special. */ ignore_escape_flag = 1; ignore_escape_flag = 0; if (c != '\"') { error ("invalid #line"); goto skipline; } for (;;) { c = getc (finput); if (c == EOF || c == '\n') { error ("invalid #line"); return c; } if (c == '\"') { obstack_1grow(&permanent_obstack, 0); input_filename = obstack_finish (&permanent_obstack); break; } obstack_1grow(&permanent_obstack, c); } lineno = l; /* Each change of file name reinitializes whether we are now in a system header. */ in_system_header = 0; if (main_input_filename == 0) main_input_filename = input_filename; /* Is this the last nonwhite stuff on the line? */ c = getlc (finput); while (c == ' ' || c == '\t') c = getlc (finput); if (c == '\n') return c; used_up = 0; /* `1' after file name means entering new file. `2' after file name means just left a file. */ if (ISDIGIT (c)) { if (c == '1') { /* Pushing to a new file. */ struct file_stack *p = (struct file_stack *) xmalloc (sizeof (struct file_stack)); input_file_stack->line = old_lineno; p->next = input_file_stack; p->name = input_filename; input_file_stack = p; input_file_stack_tick++; #ifdef DWARF_DEBUGGING_INFO if (debug_info_level == DINFO_LEVEL_VERBOSE && write_symbols == DWARF_DEBUG) dwarfout_start_new_source_file (input_filename); #endif /* DWARF_DEBUGGING_INFO */ used_up = 1; } else if (c == '2') { /* Popping out of a file. */ if (input_file_stack->next) { struct file_stack *p = input_file_stack; input_file_stack = p->next; free (p); input_file_stack_tick++; #ifdef DWARF_DEBUGGING_INFO if (debug_info_level == DINFO_LEVEL_VERBOSE && write_symbols == DWARF_DEBUG) dwarfout_resume_previous_source_file (input_file_stack->line); #endif /* DWARF_DEBUGGING_INFO */ } else error ("#-lines for entering and leaving files don't match"); used_up = 1; } } /* If we have handled a `1' or a `2', see if there is another number to read. */ if (used_up) { /* Is this the last nonwhite stuff on the line? */ c = getlc (finput); while (c == ' ' || c == '\t') c = getlc (finput); if (c == '\n') return c; used_up = 0; } /* `3' after file name means this is a system header file. */ if (c == '3') in_system_header = 1; } else error ("invalid #-line"); /* skip the rest of this line. */ skipline: while (c != '\n' && c != EOF) c = getc (finput); return c; } tree get_chill_filename () { return (build_chill_string ( strlen (input_filename) + 1, /* +1 to get a zero terminated string */ input_filename)); } tree get_chill_linenumber () { return build_int_2 ((HOST_WIDE_INT)lineno, 0); } /* Assuming '/' and '*' have been read, skip until we've read the terminating '*' and '/'. */ static void skip_c_comment () { int c = input(); int start_line = lineno; inside_c_comment++; for (;;) if (c == EOF) { error_with_file_and_line (input_filename, start_line, "unterminated comment"); break; } else if (c != '*') c = input(); else if ((c = input ()) == '/') break; inside_c_comment--; } /* Assuming "--" has been read, skip until '\n'. */ static void skip_line_comment () { for (;;) { int c = input (); if (c == EOF) return; if (c == '\n') break; } unput ('\n'); } static int skip_whitespace () { for (;;) { int c = input (); if (c == EOF) return c; if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v') continue; if (c == '/') { c = input (); if (c == '*') { skip_c_comment (); continue; } else { unput (c); return '/'; } } if (c == '-') { c = input (); if (c == '-') { skip_line_comment (); continue; } else { unput (c); return '-'; } } return c; } } /* * avoid recursive calls to yylex to parse the ' = digits' or * ' = SYNvalue' which are supposed to follow certain compiler * directives. Read the input stream, and return the value parsed. */ /* FIXME: overflow check in here */ /* FIXME: check for EOF around here */ static tree equal_number () { int c, result; char *tokenbuf; char *cursor; tree retval = integer_zero_node; c = skip_whitespace(); if ((char)c != '=') { if (pass == 2) error ("missing `=' in compiler directive"); return integer_zero_node; } c = skip_whitespace(); /* collect token into tokenbuf for later analysis */ while (TRUE) { if (ISSPACE (c) || c == '<') break; obstack_1grow (&temporary_obstack, c); c = input (); } unput (c); /* put uninteresting char back */ obstack_1grow (&temporary_obstack, '\0'); /* terminate token */ tokenbuf = obstack_finish (&temporary_obstack); maybe_downcase (tokenbuf); if (*tokenbuf == '-') /* will fail in the next test */ result = BITSTRING; else if (maybe_number (tokenbuf)) { if (pass == 1) return integer_zero_node; push_obstacks_nochange (); end_temporary_allocation (); yylval.ttype = convert_integer (tokenbuf); tokenbuf = 0; /* Was freed by convert_integer. */ result = yylval.ttype ? NUMBER : 0; pop_obstacks (); } else result = 0; if (result == NUMBER) { retval = yylval.ttype; } else if (result == BITSTRING) { if (pass == 1) error ("invalid value follows `=' in compiler directive"); goto finish; } else /* not a number */ { cursor = tokenbuf; c = *cursor; if (!ISALPHA (c) && c != '_') { if (pass == 1) error ("invalid value follows `=' in compiler directive"); goto finish; } for (cursor = &tokenbuf[1]; *cursor != '\0'; cursor++) if (ISALPHA ((unsigned char) *cursor) || *cursor == '_' || ISDIGIT (*cursor)) continue; else { if (pass == 1) error ("invalid `%c' character in name", *cursor); goto finish; } if (pass == 1) goto finish; else { tree value = lookup_name (get_identifier (tokenbuf)); if (value == NULL_TREE || TREE_CODE (value) != CONST_DECL || TREE_CODE (DECL_INITIAL (value)) != INTEGER_CST) { if (pass == 2) error ("`%s' not integer constant synonym ", tokenbuf); goto finish; } obstack_free (&temporary_obstack, tokenbuf); tokenbuf = 0; push_obstacks_nochange (); end_temporary_allocation (); retval = convert (chill_taskingcode_type_node, DECL_INITIAL (value)); pop_obstacks (); } } /* check the value */ if (TREE_CODE (retval) != INTEGER_CST) { if (pass == 2) error ("invalid value follows `=' in compiler directive"); } else if (TREE_INT_CST_HIGH (retval) != 0 || TREE_INT_CST_LOW (retval) > TREE_INT_CST_LOW (TYPE_MAX_VALUE (chill_unsigned_type_node))) { if (pass == 2) error ("value out of range in compiler directive"); } finish: if (tokenbuf) obstack_free (&temporary_obstack, tokenbuf); return retval; } /* * add a possible grant-file path to the list */ void register_seize_path (path) const char *path; { int pathlen = strlen (path); char *new_path = (char *)xmalloc (pathlen + 1); STRING_LIST *pl = (STRING_LIST *)xmalloc (sizeof (STRING_LIST)); /* strip off trailing slash if any */ if (path[pathlen - 1] == '/') pathlen--; memcpy (new_path, path, pathlen); pl->str = new_path; pl->next = seize_path_list; seize_path_list = pl; } /* Used by decode_decl to indicate that a <> use_seize_file NAME <> directive has been written to the grantfile. */ void mark_use_seizefile_written (name) tree name; { tree node; for (node = files_to_seize; node != NULL_TREE; node = TREE_CHAIN (node)) if (TREE_VALUE (node) == name) { TREE_PURPOSE (node) = integer_one_node; break; } } static int yywrap () { extern char *chill_real_input_filename; close_input_file (input_filename); use_seizefile_name = NULL_TREE; if (next_file_to_seize && !grant_only_flag) { FILE *grt_in = NULL; char *seizefile_name_chars = IDENTIFIER_POINTER (TREE_VALUE (next_file_to_seize)); /* find a seize file, open it. If it's not at the path the * user gave us, and that path contains no slashes, look on * the seize_file paths, specified by the '-I' options. */ grt_in = fopen (seizefile_name_chars, "r"); if (grt_in == NULL && strchr (seizefile_name_chars, '/') == NULL) { STRING_LIST *plp; char *path; for (plp = seize_path_list; plp != NULL; plp = plp->next) { path = (char *)xmalloc (strlen (seizefile_name_chars) + strlen (plp->str) + 2); sprintf (path, "%s/%s", plp->str, seizefile_name_chars); grt_in = fopen (path, "r"); if (grt_in == NULL) free (path); else { seizefile_name_chars = path; break; } } } if (grt_in == NULL) pfatal_with_name (seizefile_name_chars); finput = grt_in; input_filename = seizefile_name_chars; lineno = 0; current_seizefile_name = TREE_VALUE (next_file_to_seize); next_file_to_seize = TREE_CHAIN (next_file_to_seize); saw_eof = 0; return 0; } if (pass == 1) { next_file_to_seize = files_to_seize; current_seizefile_name = NULL_TREE; if (strcmp (main_input_filename, "stdin")) finput = fopen (chill_real_input_filename, "r"); else finput = stdin; if (finput == NULL) { error ("can't reopen %s", chill_real_input_filename); return 1; } input_filename = main_input_filename; ch_lex_init (); lineno = 0; /* Read a line directive if there is one. */ ungetc (check_newline (), finput); starting_pass_2 = 1; saw_eof = 0; if (module_number == 0) warning ("no modules seen"); return 0; } return 1; }