476 lines
12 KiB
C
476 lines
12 KiB
C
/* Type Analyzer for GNU C++.
|
|
Copyright (C) 1987, 89, 92, 93, 94, 1995 Free Software Foundation, Inc.
|
|
Hacked... nay, bludgeoned... by Mark Eichin (eichin@cygnus.com)
|
|
|
|
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
|
|
GNU 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. */
|
|
|
|
|
|
/* This file is the type analyzer for GNU C++. To debug it, define SPEW_DEBUG
|
|
when compiling parse.c and spew.c. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "input.h"
|
|
#include "tree.h"
|
|
#include "lex.h"
|
|
#include "cp-tree.h"
|
|
#include "parse.h"
|
|
#include "flags.h"
|
|
#include "obstack.h"
|
|
|
|
/* This takes a token stream that hasn't decided much about types and
|
|
tries to figure out as much as it can, with excessive lookahead and
|
|
backtracking. */
|
|
|
|
/* fifo of tokens recognized and available to parser. */
|
|
struct token {
|
|
/* The values for YYCHAR will fit in a short. */
|
|
short yychar;
|
|
short end_of_file;
|
|
YYSTYPE yylval;
|
|
};
|
|
|
|
static int do_aggr PROTO((void));
|
|
static int probe_obstack PROTO((struct obstack *, tree, unsigned int));
|
|
static void scan_tokens PROTO((int));
|
|
|
|
#ifdef SPEW_DEBUG
|
|
static int num_tokens PROTO((void));
|
|
static struct token *nth_token PROTO((int));
|
|
static void add_token PROTO((struct token *));
|
|
static void consume_token PROTO((void));
|
|
static int debug_yychar PROTO((int));
|
|
#endif
|
|
|
|
/* From lex.c: */
|
|
/* the declaration found for the last IDENTIFIER token read in.
|
|
yylex must look this up to detect typedefs, which get token type TYPENAME,
|
|
so it is left around in case the identifier is not a typedef but is
|
|
used in a context which makes it a reference to a variable. */
|
|
extern tree lastiddecl; /* let our brains leak out here too */
|
|
extern int yychar; /* the lookahead symbol */
|
|
extern YYSTYPE yylval; /* the semantic value of the */
|
|
/* lookahead symbol */
|
|
extern int end_of_file;
|
|
|
|
struct obstack token_obstack;
|
|
int first_token;
|
|
|
|
#ifdef SPEW_DEBUG
|
|
int spew_debug = 0;
|
|
static unsigned int yylex_ctr = 0;
|
|
static int debug_yychar ();
|
|
#endif
|
|
|
|
/* Initialize token_obstack. Called once, from init_parse. */
|
|
|
|
void
|
|
init_spew ()
|
|
{
|
|
gcc_obstack_init (&token_obstack);
|
|
}
|
|
|
|
#ifdef SPEW_DEBUG
|
|
/* Use functions for debugging... */
|
|
|
|
/* Return the number of tokens available on the fifo. */
|
|
|
|
static int
|
|
num_tokens ()
|
|
{
|
|
return (obstack_object_size (&token_obstack) / sizeof (struct token))
|
|
- first_token;
|
|
}
|
|
|
|
/* Fetch the token N down the line from the head of the fifo. */
|
|
|
|
static struct token*
|
|
nth_token (n)
|
|
int n;
|
|
{
|
|
/* could just have this do slurp_ implicitly, but this way is easier
|
|
to debug... */
|
|
my_friendly_assert (n < num_tokens (), 298);
|
|
return ((struct token*)obstack_base (&token_obstack)) + n + first_token;
|
|
}
|
|
|
|
/* Add a token to the token fifo. */
|
|
|
|
static void
|
|
add_token (t)
|
|
struct token* t;
|
|
{
|
|
obstack_grow (&token_obstack, t, sizeof (struct token));
|
|
}
|
|
|
|
/* Consume the next token out of the fifo. */
|
|
|
|
static void
|
|
consume_token ()
|
|
{
|
|
if (num_tokens () == 1)
|
|
{
|
|
obstack_free (&token_obstack, obstack_base (&token_obstack));
|
|
first_token = 0;
|
|
}
|
|
else
|
|
first_token++;
|
|
}
|
|
|
|
#else
|
|
/* ...otherwise use macros. */
|
|
|
|
#define num_tokens() \
|
|
((obstack_object_size (&token_obstack) / sizeof (struct token)) - first_token)
|
|
|
|
#define nth_token(N) \
|
|
(((struct token*)obstack_base (&token_obstack))+(N)+first_token)
|
|
|
|
#define add_token(T) obstack_grow (&token_obstack, (T), sizeof (struct token))
|
|
|
|
#define consume_token() \
|
|
(num_tokens () == 1 \
|
|
? (obstack_free (&token_obstack, obstack_base (&token_obstack)), \
|
|
(first_token = 0)) \
|
|
: first_token++)
|
|
#endif
|
|
|
|
/* Pull in enough tokens from real_yylex that the queue is N long beyond
|
|
the current token. */
|
|
|
|
static void
|
|
scan_tokens (n)
|
|
int n;
|
|
{
|
|
int i;
|
|
struct token *tmp;
|
|
|
|
/* We cannot read past certain tokens, so make sure we don't. */
|
|
i = num_tokens ();
|
|
if (i > n)
|
|
return;
|
|
while (i-- > 0)
|
|
{
|
|
tmp = nth_token (i);
|
|
/* Never read past these characters: they might separate
|
|
the current input stream from one we save away later. */
|
|
if (tmp->yychar == '{' || tmp->yychar == ':' || tmp->yychar == ';')
|
|
goto pad_tokens;
|
|
}
|
|
|
|
while (num_tokens () <= n)
|
|
{
|
|
obstack_blank (&token_obstack, sizeof (struct token));
|
|
tmp = ((struct token *)obstack_next_free (&token_obstack))-1;
|
|
tmp->yychar = real_yylex ();
|
|
tmp->end_of_file = end_of_file;
|
|
tmp->yylval = yylval;
|
|
end_of_file = 0;
|
|
if (tmp->yychar == '{'
|
|
|| tmp->yychar == ':'
|
|
|| tmp->yychar == ';')
|
|
{
|
|
pad_tokens:
|
|
while (num_tokens () <= n)
|
|
{
|
|
obstack_blank (&token_obstack, sizeof (struct token));
|
|
tmp = ((struct token *)obstack_next_free (&token_obstack))-1;
|
|
tmp->yychar = EMPTY;
|
|
tmp->end_of_file = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Like _obstack_allocated_p, but stop after checking NLEVELS chunks. */
|
|
|
|
static int
|
|
probe_obstack (h, obj, nlevels)
|
|
struct obstack *h;
|
|
tree obj;
|
|
unsigned int nlevels;
|
|
{
|
|
register struct _obstack_chunk* lp; /* below addr of any objects in this chunk */
|
|
register struct _obstack_chunk* plp; /* point to previous chunk if any */
|
|
|
|
lp = (h)->chunk;
|
|
/* We use >= rather than > since the object cannot be exactly at
|
|
the beginning of the chunk but might be an empty object exactly
|
|
at the end of an adjacent chunk. */
|
|
for (; nlevels != 0 && lp != 0 && ((tree)lp >= obj || (tree)lp->limit < obj);
|
|
nlevels -= 1)
|
|
{
|
|
plp = lp->prev;
|
|
lp = plp;
|
|
}
|
|
return nlevels != 0 && lp != 0;
|
|
}
|
|
|
|
/* from lex.c: */
|
|
/* Value is 1 (or 2) if we should try to make the next identifier look like
|
|
a typename (when it may be a local variable or a class variable).
|
|
Value is 0 if we treat this name in a default fashion. */
|
|
extern int looking_for_typename;
|
|
int looking_for_template;
|
|
extern int do_snarf_defarg;
|
|
|
|
extern struct obstack *current_obstack, *saveable_obstack;
|
|
tree got_scope;
|
|
tree got_object;
|
|
|
|
int
|
|
peekyylex ()
|
|
{
|
|
scan_tokens (0);
|
|
return nth_token (0)->yychar;
|
|
}
|
|
|
|
int
|
|
yylex ()
|
|
{
|
|
struct token tmp_token;
|
|
tree trrr;
|
|
int old_looking_for_typename = 0;
|
|
|
|
retry:
|
|
#ifdef SPEW_DEBUG
|
|
if (spew_debug)
|
|
{
|
|
yylex_ctr ++;
|
|
fprintf (stderr, "\t\t## %d ##", yylex_ctr);
|
|
}
|
|
#endif
|
|
|
|
if (do_snarf_defarg)
|
|
{
|
|
my_friendly_assert (num_tokens () == 0, 2837);
|
|
tmp_token.yychar = DEFARG;
|
|
tmp_token.yylval.ttype = snarf_defarg ();
|
|
tmp_token.end_of_file = 0;
|
|
do_snarf_defarg = 0;
|
|
add_token (&tmp_token);
|
|
}
|
|
|
|
/* if we've got tokens, send them */
|
|
else if (num_tokens ())
|
|
{
|
|
tmp_token= *nth_token (0);
|
|
|
|
/* TMP_TOKEN.YYLVAL.TTYPE may have been allocated on the wrong obstack.
|
|
If we don't find it in CURRENT_OBSTACK's current or immediately
|
|
previous chunk, assume it was and copy it to the current obstack. */
|
|
if ((tmp_token.yychar == CONSTANT
|
|
|| tmp_token.yychar == STRING)
|
|
&& ! TREE_PERMANENT (tmp_token.yylval.ttype)
|
|
&& ! probe_obstack (current_obstack, tmp_token.yylval.ttype, 2)
|
|
&& ! probe_obstack (saveable_obstack, tmp_token.yylval.ttype, 2))
|
|
tmp_token.yylval.ttype = copy_node (tmp_token.yylval.ttype);
|
|
}
|
|
else
|
|
{
|
|
/* if not, grab the next one and think about it */
|
|
tmp_token.yychar = real_yylex ();
|
|
tmp_token.yylval = yylval;
|
|
tmp_token.end_of_file = end_of_file;
|
|
add_token (&tmp_token);
|
|
}
|
|
|
|
/* many tokens just need to be returned. At first glance, all we
|
|
have to do is send them back up, but some of them are needed to
|
|
figure out local context. */
|
|
switch (tmp_token.yychar)
|
|
{
|
|
case EMPTY:
|
|
/* This is a lexical no-op. */
|
|
consume_token ();
|
|
#ifdef SPEW_DEBUG
|
|
if (spew_debug)
|
|
debug_yychar (tmp_token.yychar);
|
|
#endif
|
|
goto retry;
|
|
|
|
case IDENTIFIER:
|
|
scan_tokens (1);
|
|
if (nth_token (1)->yychar == SCOPE)
|
|
{
|
|
/* Don't interfere with the setting from an 'aggr' prefix. */
|
|
old_looking_for_typename = looking_for_typename;
|
|
looking_for_typename = 1;
|
|
}
|
|
else if (nth_token (1)->yychar == '<')
|
|
looking_for_template = 1;
|
|
|
|
trrr = lookup_name (tmp_token.yylval.ttype, -2);
|
|
|
|
if (trrr)
|
|
{
|
|
tmp_token.yychar = identifier_type (trrr);
|
|
switch (tmp_token.yychar)
|
|
{
|
|
case TYPENAME:
|
|
case SELFNAME:
|
|
case NSNAME:
|
|
case PTYPENAME:
|
|
lastiddecl = trrr;
|
|
|
|
/* If this got special lookup, remember it. In these cases,
|
|
we don't have to worry about being a declarator-id. */
|
|
if (got_scope || got_object)
|
|
tmp_token.yylval.ttype = trrr;
|
|
break;
|
|
|
|
case PFUNCNAME:
|
|
case IDENTIFIER:
|
|
lastiddecl = trrr;
|
|
break;
|
|
|
|
default:
|
|
my_friendly_abort (101);
|
|
}
|
|
}
|
|
else
|
|
lastiddecl = NULL_TREE;
|
|
got_scope = NULL_TREE;
|
|
/* and fall through to... */
|
|
case IDENTIFIER_DEFN:
|
|
case TYPENAME:
|
|
case TYPENAME_DEFN:
|
|
case PTYPENAME:
|
|
case PTYPENAME_DEFN:
|
|
consume_token ();
|
|
/* If we see a SCOPE next, restore the old value.
|
|
Otherwise, we got what we want. */
|
|
looking_for_typename = old_looking_for_typename;
|
|
looking_for_template = 0;
|
|
break;
|
|
|
|
case SCSPEC:
|
|
case NEW:
|
|
/* do_aggr needs to check if the previous token was RID_NEW,
|
|
so just increment first_token instead of calling consume_token. */
|
|
++first_token;
|
|
break;
|
|
|
|
case TYPESPEC:
|
|
consume_token ();
|
|
break;
|
|
|
|
case AGGR:
|
|
*nth_token (0) = tmp_token;
|
|
do_aggr ();
|
|
/* fall through to output... */
|
|
case ENUM:
|
|
/* Set this again, in case we are rescanning. */
|
|
looking_for_typename = 2;
|
|
/* fall through... */
|
|
default:
|
|
consume_token ();
|
|
}
|
|
|
|
/* class member lookup only applies to the first token after the object
|
|
expression, except for explicit destructor calls. */
|
|
if (tmp_token.yychar != '~')
|
|
got_object = NULL_TREE;
|
|
|
|
/* Clear looking_for_typename if we got 'enum { ... };'. */
|
|
if (tmp_token.yychar == '{' || tmp_token.yychar == ':'
|
|
|| tmp_token.yychar == ';')
|
|
looking_for_typename = 0;
|
|
|
|
yylval = tmp_token.yylval;
|
|
yychar = tmp_token.yychar;
|
|
end_of_file = tmp_token.end_of_file;
|
|
#ifdef SPEW_DEBUG
|
|
if (spew_debug)
|
|
debug_yychar (yychar);
|
|
#endif
|
|
|
|
return yychar;
|
|
}
|
|
|
|
/* token[0] == AGGR (struct/union/enum)
|
|
Thus, token[1] is either a TYPENAME or a TYPENAME_DEFN.
|
|
If token[2] == '{' or ':' then it's TYPENAME_DEFN.
|
|
It's also a definition if it's a forward declaration (as in 'struct Foo;')
|
|
which we can tell if token[2] == ';' *and* token[-1] != FRIEND or NEW. */
|
|
|
|
static int
|
|
do_aggr ()
|
|
{
|
|
int yc1, yc2;
|
|
|
|
scan_tokens (2);
|
|
yc1 = nth_token (1)->yychar;
|
|
if (yc1 != TYPENAME && yc1 != IDENTIFIER && yc1 != PTYPENAME)
|
|
return 0;
|
|
yc2 = nth_token (2)->yychar;
|
|
if (yc2 == ';')
|
|
{
|
|
/* It's a forward declaration iff we were not preceded by
|
|
'friend' or `new'. */
|
|
if (first_token > 0)
|
|
{
|
|
if (nth_token (-1)->yychar == SCSPEC
|
|
&& nth_token (-1)->yylval.ttype == ridpointers[(int) RID_FRIEND])
|
|
return 0;
|
|
if (nth_token (-1)->yychar == NEW)
|
|
return 0;
|
|
}
|
|
}
|
|
else if (yc2 != '{' && yc2 != ':')
|
|
return 0;
|
|
|
|
switch (yc1)
|
|
{
|
|
case TYPENAME:
|
|
nth_token (1)->yychar = TYPENAME_DEFN;
|
|
break;
|
|
case PTYPENAME:
|
|
nth_token (1)->yychar = PTYPENAME_DEFN;
|
|
break;
|
|
case IDENTIFIER:
|
|
nth_token (1)->yychar = IDENTIFIER_DEFN;
|
|
break;
|
|
default:
|
|
my_friendly_abort (102);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef SPEW_DEBUG
|
|
/* debug_yychar takes a yychar (token number) value and prints its name. */
|
|
|
|
static int
|
|
debug_yychar (yy)
|
|
int yy;
|
|
{
|
|
/* In parse.y: */
|
|
extern char *debug_yytranslate ();
|
|
|
|
int i;
|
|
|
|
if (yy<256) {
|
|
fprintf (stderr, "<%d: %c >\n", yy, yy);
|
|
return 0;
|
|
}
|
|
fprintf (stderr, "<%d:%s>\n", yy, debug_yytranslate (yy));
|
|
return 1;
|
|
}
|
|
|
|
#endif
|