/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *    		 Crown Copyright (c) 1997, 1998
 *
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 *
 * $TenDRA: tendra/src/producers/common/parse/parse.c,v 1.12 2005/10/16 15:33:43 stefanf Exp $
 */


#include "config.h"
#include "producer.h"

#include "msgcat.h"

#include "c_types.h"
#include "ctype_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "access.h"
#include "char.h"
#include "class.h"
#include "derive.h"
#include "file.h"
#include "hash.h"
#include "identifier.h"
#include "lex.h"
#include "literal.h"
#include "macro.h"
#include "namespace.h"
#include "option.h"
#include "parse.h"
#include "pragma.h"
#include "predict.h"
#include "preproc.h"
#include "redeclare.h"
#include "symbols.h"
#include "syntax.h"
#include "template.h"
#include "tokdef.h"


/*
 *    CURRENT LEXICAL TOKEN NUMBERS
 *
 *    These variables are used by the parser to hold the current and former
 *    lexical token numbers.
 */

int crt_lex_token = lex_unknown;
int last_lex_token = lex_unknown;
int saved_lex_token = lex_unknown;
static int have_template = 0;
int have_syntax_error = 0;


/*
 *    LIST OF ACTIVE LEXICAL TOKENS
 *
 *    The currently active lexical tokens are formed into a list starting
 *    with first_token.  The position of the current token within this list
 *    is given by crt_token.  The main routine for building up this list
 *    is expand_token which either advances crt_token one step along the
 *    list, or, if crt_token points to the end of the list, reads and macro
 *    expands the next token from the input file, appending the resulting
 *    tokens to the end of the list.  The list is periodically garbage
 *    collected by setting first_token to crt_token and freeing all the
 *    tokens from the old first_token.
 */

PPTOKEN *crt_token = NULL;
static PPTOKEN *first_token = NULL;
NAMESPACE crt_lookup = NULL_nspace;
static unsigned long crt_lookup_depth = 0;
static OPTIONS *prev_opts = NULL;
static STACK (int) token_stack = NULL_stack (int);
static STACK (PPTOKEN_P) parser_stack = NULL_stack (PPTOKEN_P);
static STACK (NAMESPACE) lookup_stack = NULL_stack (NAMESPACE);


/*
 *    INITIALISE ACTIVE TOKEN LIST
 *
 *    This routine initialises a new list of active lexical tokens with the
 *    value toks by pushing the old values of first_token and crt_token onto
 *    the stack parser_stack and setting them to point to a new dummy token
 *    whose next field is toks.
 */

void
init_parser(PPTOKEN *toks)
{
	PPTOKEN *q;
	PUSH_int (crt_lex_token, token_stack);
	PUSH_int (last_lex_token, token_stack);
	PUSH_int (saved_lex_token, token_stack);
	PUSH_nspace (crt_lookup, lookup_stack);
	PUSH_pptok (first_token, parser_stack);
	PUSH_pptok (crt_token, parser_stack);
	q = new_pptok ();
	q->tok = lex_ignore_token;
	q->pp_space = 0;
	q->next = toks;
	first_token = q;
	crt_token = q;
	crt_lookup = NULL_nspace;
	crt_lookup_depth = 0;
	crt_lex_token = lex_unknown;
	last_lex_token = lex_unknown;
	saved_lex_token = lex_unknown;
	return;
}


/*
 *    RESTORE ACTIVE TOKEN LIST
 *
 *    This routine restores the previous list of active lexical tokens by
 *    popping the values of first_token and crt_token from the stack.  The
 *    previous list is returned.
 */

PPTOKEN *
restore_parser(void)
{
	PPTOKEN *p = first_token;
	POP_pptok (crt_token, parser_stack);
	POP_pptok (first_token, parser_stack);
	POP_nspace (crt_lookup, lookup_stack);
	POP_int (last_lex_token, token_stack);
	POP_int (saved_lex_token, token_stack);
	POP_int (crt_lex_token, token_stack);
	crt_lookup_depth = 0;
	ASSERT (first_token != NULL);
	return (p);
}


/*
 *    REMOVE A LIST OF TOKENS FROM THE CURRENT LIST
 *
 *    This routine removes the tokens from p to q inclusive from the current
 *    list.  crt_token must not be included within this range.
 */

void
snip_tokens(PPTOKEN *p, PPTOKEN *q)
{
	PPTOKEN *r = first_token;
	if (r == p) {
		first_token = q->next;
	} else {
		while (r->next != p) r = r->next;
		r->next = q->next;
	}
	r = new_pptok ();
	r->tok = lex_eof;
	q->next = r;
	r->next = NULL;
	return;
}


/*
 *    PATCH A NUMBER OF TOKENS INTO THE CURRENT LIST
 *
 *    This routine patches n tokens into the current list immediately after
 *    crt_token, returning the first token.  This is used by the preprocessor
 *    to pass more than one token to the parser.
 */

PPTOKEN *
patch_tokens(int n)
{
	while (n) {
		PPTOKEN *p = new_pptok ();
		p->tok = lex_ignore_token;
		p->next = crt_token->next;
		p->pp_space = WHITE_SPACE;
		crt_token->next = p;
		n--;
	}
	return (crt_token->next);
}


/*
 *    CURRENT PARSER STATE DEPTH
 *
 *    This variable is used to record the current depth of saved parser
 *    states.
 */

int crt_state_depth = 0;


/*
 *    SAVE PARSER STATE
 *
 *    This routine saves the current parser state into s, it also clears the
 *    syntax error flag.  col is true if column numbers are to be considered
 *    during the parsing.
 */

void
save_state(PARSE_STATE *s, int col)
{
	/* Save current location */
	s->loc = crt_loc;
	
	/* Save current namespace */
	s->nspace [0] = crt_namespace;
	s->nspace [1] = templ_namespace;
	s->nstack [0] = namespace_stack;
	s->nstack [1] = crt_nspace_stack;
	s->nstack [2] = local_nspace_stack;
	
	/* Save flag values */
	s->flag [0] = in_class_defn;
	s->flag [1] = in_function_defn;
	s->flag [2] = really_in_class_defn;
	s->flag [3] = really_in_function_defn;
	s->flag [4] = in_declaration;
	s->flag [5] = in_template_decl;
	s->flag [6] = have_syntax_error;
	s->flag [7] = cache_lookup;
	s->flag [8] = crt_col_changed;
	have_syntax_error = 0;
	cache_lookup = old_cache_lookup;
	crt_col_changed = col;
	
	/* Save declaration specifiers */
	s->dspec [0] = crt_access;
	s->dspec [1] = crt_linkage;
	crt_access = dspec_public;
	crt_state_depth++;
	bad_crt_loc++;
	return;
}


/*
 *    RESTORE PARSER STATE
 *
 *    This routine restores the parser state from the value stored in s.
 *    Note that there is some attempt at recovering the state following
 *    some intervening error, particularly with regard to resetting the
 *    current namespace.
 */

void
restore_state(PARSE_STATE *s)
{
	/* Restore current namespace */
	NAMESPACE ns = s->nspace [0];
	NAMESPACE cns = crt_namespace;
	LIST (NAMESPACE) pns = LIST_stack (s->nstack [0]);
	cache_lookup = old_cache_lookup;
	while (!EQ_list (pns, LIST_stack (namespace_stack))) {
		/* Reset name look-up stack */
		remove_namespace ();
	}
	crt_namespace = ns;
	crt_nspace_stack = s->nstack [1];
	local_nspace_stack = s->nstack [2];
	if (!EQ_nspace (ns, cns)) {
		/* Recalculate namespaces if necessary */
		update_namespace ();
	}
	templ_namespace = s->nspace [1];
	
	/* Restore current location */
	crt_loc = s->loc;
	crt_line_changed = 1;
	crt_file_changed = 1;
	
	/* Restore flag values */
	in_class_defn = s->flag [0];
	in_function_defn = s->flag [1];
	really_in_class_defn = s->flag [2];
	really_in_function_defn = s->flag [3];
	in_declaration = s->flag [4];
	in_template_decl = s->flag [5];
	have_syntax_error = s->flag [6];
	cache_lookup = s->flag [7];
	crt_col_changed = s->flag [8];
	
	/* Restore declaration specifiers */
	crt_access = s->dspec [0];
	crt_linkage = s->dspec [1];
	crt_state_depth--;
	bad_crt_loc--;
	return;
}


/*
 *    SET UP FILE LOCATION TOKENS
 *
 *    This routine sets up preprocessing tokens recording the current file
 *    position immediately following p.
 */

static void
make_loc_tokens(PPTOKEN *p)
{
	PPTOKEN *q = new_pptok ();
	if (crt_file_changed) {
		q->tok = lex_builtin_Hfile;
		crt_file_changed = 0;
	} else {
		q->tok = lex_builtin_Hline;
	}
	crt_line_changed = 0;
	q->pp_space = crt_loc.column;
	q->pp_data.loc.line = crt_loc.line;
	q->pp_data.loc.posn = crt_loc.posn;
	q->next = p->next;
	p->next = q;
	return;
}


/*
 *    READ FILE LOCATION TOKENS
 *
 *    This routine adjusts the current line number according to the
 *    preprocessing tokens p.
 */

PPTOKEN *
read_loc_tokens(PPTOKEN *p)
{
	if (p) {
		int t = p->tok;
		if (t == lex_builtin_Hline) {
			/* Set line number */
			crt_loc.column = p->pp_space;
			crt_loc.line = p->pp_data.loc.line;
			crt_loc.posn = p->pp_data.loc.posn;
			crt_line_changed = 1;
			p = p->next;
		} else if (t == lex_builtin_Hfile) {
			/* Set file name */
			crt_loc.column = p->pp_space;
			crt_loc.line = p->pp_data.loc.line;
			crt_loc.posn = p->pp_data.loc.posn;
			crt_line_changed = 1;
			crt_file_changed = 1;
			p = p->next;
		} else if (crt_col_changed) {
			unsigned long sp = p->pp_space;
			if (sp) crt_loc.column = sp;
		}
	}
	return (p);
}


/*
 *    FIND A DESTRUCTOR NAME
 *
 *    This routine returns the destructor name for the class name cid or the
 *    null identifier if cid is not a class name.
 */

#if LANGUAGE_CPP

static IDENTIFIER
find_destr_id(IDENTIFIER cid)
{
	CLASS_TYPE ct = NULL_ctype;
	if (IS_id_class_name_etc (cid)) {
		TYPE t = DEREF_type (id_class_name_etc_defn (cid));
		unsigned tag = TAG_type (t);
		while (tag == type_templ_tag) {
			t = DEREF_type (type_templ_defn (t));
			tag = TAG_type (t);
		}
		if (tag == type_compound_tag) {
			ct = DEREF_ctype (type_compound_defn (t));
		}
	} else {
		ct = find_class (cid);
	}
	if (!IS_NULL_ctype (ct)) {
		/* Find destructor name */
		IDENTIFIER tid = DEREF_id (ctype_destr (ct));
		HASHID nm = DEREF_hashid (id_name (tid));
		tid = DEREF_id (hashid_id (nm));
		set_hashid_loc (tid, cid);
		return (tid);
	}
	return (NULL_id);
}

#endif


/*
 *    CHECK A DESTRUCTOR NAME
 *
 *    This routine checks whether the destructor name cid specified using
 *    nm is legal.  For example, nm might be a typedef-name for the class
 *    rather than the class itself.  d is the result of the previous call
 *    to predict_destr.
 */

#if LANGUAGE_CPP

static void
check_destr_id(IDENTIFIER cid, HASHID nm, int d)
{
	if (d <= 2) {
		HASHID cnm = DEREF_hashid (id_name (cid));
		IDENTIFIER tid = DEREF_id (hashid_destr_tid (cnm));
		HASHID tnm = DEREF_hashid (id_name (tid));
		if (!EQ_hashid (tnm, nm)) {
			/* Destructor names don't match */
			IDENTIFIER id = DEREF_id (hashid_id (nm));
			report (crt_loc, ERR_dcl_typedef_destr (id, cnm));
		}
	}
	return;
}

#endif


/*
 *    READ AND EXPAND THE NEXT TOKEN
 *
 *    This routine reads the next token from the list of all active tokens,
 *    first_token (see above).  This consists either of advancing crt_token
 *    along this list, or reading and expanding a new token from the input
 *    file, appending the result to the list.  The argument store gives the
 *    context for the token.  The usual cases are EXPAND_NORMAL, indicating
 *    that the token is to be used and then discarded (allowing first_token
 *    to be garbage collected up to crt_token), and EXPAND_AHEAD, indicating
 *    that the token is to be stored for use later.  The value EXPAND_RESCAN
 *    can be used to force the current token to be rescanned.  Other values
 *    are used internally.  Note that crt_token is set to the token currently
 *    being defined during all calls to read_token.  The routine can be
 *    considerably simplified for C, and because of the performance critical
 *    nature of the routine this has been done by conditional compilation.
 *
 *    There is a problem with the look-ahead method in that certain tokens
 *    are context dependent, and thus may not mean the same in the initial
 *    scan using expand_token (EXPAND_AHEAD) as they do when the same
 *    tokens are re-analysed using expand_token (EXPAND_NORMAL).  This
 *    is solved by storing all the tokens in these cases and reinterpreting
 *    them later.  This means in particular than a context dependent
 *    interpretation such as an identifier being a type name is not set
 *    in stone by being recorded in the token's tok field.  Instead this
 *    interpretation is returned, but the identifier is only marked as
 *    an identifier.
 *
 *    Note also that SID is always one token ahead of itself.  For example
 *    in:
 *
 *		void f ()
 *		{
 *		    {
 *			extern void g (int);
 *			g (0);
 *		    }
 *		    g (1);
 *		}
 *
 *    g goes out of scope after the first close brace, by which time SID has
 *    already read the following token and resolved it as g.  Thus in these
 *    cases it is necessary to insert an explicit rescan of the current token
 *    (with a store value of EXPAND_RESCAN) after the close brace so that
 *    g is seen not to be in scope.  This problem only ever occurs one
 *    token ahead of the current token.
 */

int
expand_token(int store)
{
	int t;
	int expand;
#if LANGUAGE_CPP
	NAMESPACE ns = crt_lookup;
#endif
	PPTOKEN *prev_tok = crt_token;
	PPTOKEN *this_tok = prev_tok->next;
	
	/* A store value of EXPAND_RESCAN means rescan this token */
	if (store == EXPAND_RESCAN) {
		this_tok = prev_tok;
#if LANGUAGE_CPP
		ns = NULL_nspace;
#endif
	}
	
	/* Get the next token */
	start_label : {
		if (this_tok == NULL) {
			/* Read the token from the file */
			this_tok = new_pptok ();
			this_tok->next = NULL;
			prev_tok->next = this_tok;
			crt_token = this_tok;
			t = read_token ();
			update_column ();
			this_tok->tok = t;
			if (t <= LAST_COMPLEX_TOKEN) {
				token_parts (t, this_tok);
			}
			/* Don't bother with the space field */
			
			if (store == EXPAND_NORMAL) {
				/* Garbage collect stored tokens */
				prev_tok->next = free_tokens;
				free_tokens = first_token;
				first_token = this_tok;
			}
			expand = 1;
			
		} else {
			/* Use a previously stored token */
			t = this_tok->tok;
			if (t == lex_ignore_token) {
				/* Step over any ignored tokens */
				prev_tok = this_tok;
				this_tok = this_tok->next;
				goto start_label;
			} else if (t == lex_builtin_Hline || t == lex_builtin_Hfile) {
				/* Set line number */
				prev_tok = this_tok;
				this_tok = read_loc_tokens (this_tok);
				goto start_label;
			} else if (crt_col_changed) {
				unsigned long sp = this_tok->pp_space;
				if (sp) crt_loc.column = sp;
			}
			crt_token = this_tok;
			expand = 0;
		}
	}
	crt_lookup = NULL_nspace;
	
	/* Deal with context switch */
	if (store == EXPAND_NORMAL) {
		OPTIONS *opts = prev_opts;
		if (opts != crt_opts) set_mode (opts);
		prev_opts = this_tok->pp_opts;
	}
	
	/* Analyse the token */
	switch (t) {
		
	case lex_identifier : {
		/* Deal with identifiers */
		unsigned tag;
		int tt = lex_identifier;
		
		/* Check for macro expansion */
		HASHID nm = this_tok->pp_data.id.hash;
		IDENTIFIER id = DEREF_id (hashid_id (nm));
		expand_label : {
			tag = TAG_id (id);
			switch (tag) {
				
			case id_obj_macro_tag :
			case id_func_macro_tag : {
				/* Check for expansion of macros */
				if (expand) {
					PPTOKEN *toks;
					toks = expand_macro (nm, file_loc, 1);
					this_tok->tok = lex_ignore_token;
					this_tok->next = toks;
					prev_tok = this_tok;
					this_tok = toks;
					goto start_label;
				}
				id = DEREF_id (id_alias (id));
				goto expand_label;
			}
				
			case id_keyword_tag : {
				/* Check on keywords */
				this_tok->pp_data.id.use = id;
				t = (int) DEREF_ulong (id_no (id));
				this_tok->tok = t;
				return (t);
			}
				
			case id_iso_keyword_tag : {
				/* Check on ISO keywords */
				this_tok->pp_data.id.use = id;
				if (!in_pragma_dir) {
					ERROR err;
					t = (int) DEREF_ulong (id_no (id));
					t = primary_form (t);
					err = ERR_lex_digraph_iso (nm, t);
					if (!IS_NULL_err (err)) {
						report (crt_loc, err);
					}
					this_tok->tok = t;
#if LANGUAGE_CPP
					if (t == lex_compl_H1) goto compl_label;
#endif
					return (t);
				}
				break;
			}
				
			case id_c99_keyword_tag :
			case id_reserved_tag : {
				/* Report reserved identifiers */
				if (store == EXPAND_NORMAL && !in_pragma_dir) {
					ERROR err;
				   	if (tag == id_reserved_tag) {
						err = ERR_lex_key_reserve (nm);
					} else {
						err = ERR_lex_key_c99 (nm);
					}
					report (crt_loc, err);
				}
				break;
			}
			}
		}
		if (find_hashid (nm) == lex_pragma_H2) {
			if (operator_pragma ()) {
				this_tok = crt_token->next;
				/* Continue after the closing ) */
				goto start_label;
			}
			/* Otherwise treat _Pragma as an ordinary identifier */
		}
		
		/* Perform name look-up */
#if LANGUAGE_CPP
		if (IS_NULL_nspace (ns)) {
			id = find_id (nm);
		} else {
			IDENTIFIER mid = find_qual_id (ns, nm, 0, 0);
			if (!IS_NULL_id (mid)) id = mid;
		}
#else
		id = find_op_id (nm);
#endif
		tag = TAG_id (id);
		
#if LANGUAGE_CPP
		/* Look ahead for following '::' (C++ only) */
		t = expand_token (EXPAND_CHECK_COLON);
		crt_token = this_tok;
#endif
		
		/* Allow for tokens and templates */
		if (tag == id_token_tag) {
			TOKEN sort = DEREF_tok (id_token_sort (id));
			if (IS_tok_proc (sort)) {
				/* Procedure token application */
#if LANGUAGE_CPP
				/* Following token already read */
#else
				/* Check for following '(' */
				t = expand_token (EXPAND_CHECK_COLON);
				crt_token = this_tok;
#endif
				if (t == lex_open_Hround) {
					PPTOKEN *args = skip_token_args (id);
					id = DEREF_id (id_token_alt (id));
					this_tok->pp_data.tok.id = id;
					this_tok->pp_data.tok.args = args;
					sort = DEREF_tok (tok_proc_res (sort));
					switch (TAG_tok (sort)) {
					case tok_exp_tag :
					case tok_nat_tag :
					case tok_snat_tag : {
						tt = lex_complex_Hexp;
						break;
					}
					case tok_stmt_tag : {
						tt = lex_complex_Hstmt;
						break;
					}
					case tok_member_tag : {
						/* NOT YET IMPLEMENTED */
						tt = lex_complex_Hexp;
						break;
					}
					default : {
						tt = lex_complex_Htype;
#if LANGUAGE_CPP
						/* Check again for following '::' */
						t = expand_token (EXPAND_CHECK_COLON);
						crt_token = this_tok;
#endif
						break;
					}
					}
				} else {
					tt = DEREF_int (tok_proc_key (sort));
					if (store == EXPAND_NORMAL) {
						if (tt != lex_identifier && !in_pragma_dir) {
							ERROR err = ERR_cpp_replace_arg_none (nm);
							report (crt_loc, err);
						}
					}
				}
			}
			
#if LANGUAGE_CPP
		} else if (t == lex_less) {
			/* Check for templates (C++ only) */
			DECL_SPEC ds = DEREF_dspec (id_storage (id));
			if (ds & dspec_template) {
				/* Template application */
				PPTOKEN *args;
				if (ds & dspec_implicit) {
					/* Allow for injected template names */
					IDENTIFIER tid = find_template (id, 0);
					if (!IS_NULL_id (tid)) {
						id = tid;
						tag = TAG_id (id);
					}
				}
				args = skip_template_args (id, 0);
				if (store == EXPAND_TEMPLATE) have_template = 1;
				switch (tag) {
				case id_class_name_tag : {
					tt = lex_template_Htype;
					goto class_template_lab;
				}
				case id_class_alias_tag : {
					tt = lex_complex_Htype;
					goto class_template_lab;
				}
				case id_type_alias_tag : {
					CLASS_TYPE ct = find_class (id);
					tt = lex_complex_Htype;
					if (!IS_NULL_ctype (ct)) {
						goto class_template_lab;
					}
					break;
				}
					class_template_lab : {
						t = expand_token (EXPAND_CHECK_COLON);
						crt_token = this_tok;
						if (t == lex_colon_Hcolon) {
							/* Expand the template class now */
							id = parse_type_template (id, args, 1);
							tt = lex_identifier;
						}
						break;
					}
				case id_enum_name_tag :
				case id_enum_alias_tag : {
					tt = lex_complex_Htype;
					break;
				}
				default : {
					/* Function template */
					tt = lex_template_Hid;
					break;
				}
				}
				if (tt != lex_identifier) {
					this_tok->pp_data.tok.id = id;
					this_tok->pp_data.tok.args = args;
				}
			} else if (store == EXPAND_TEMPLATE) {
				/* Have 'template id < ... >' */
				PPTOKEN *args = skip_template_args (id, 0);
				this_tok->pp_data.tok.id = id;
				this_tok->pp_data.tok.args = args;
				tt = lex_template_Hid;
				have_template = 1;
			}
#endif
		}
		
#if LANGUAGE_CPP
		/* Allow for destructors (C++ only) */
		if (store == EXPAND_DESTRUCTOR && t != lex_colon_Hcolon) {
			int level = 2;
			IDENTIFIER cid = id;
			do {
				switch (TAG_id (cid)) {
				case id_class_name_tag :
				case id_class_alias_tag :
				case id_token_tag : {
					IDENTIFIER tid;
					if (tt == lex_template_Htype) {
						this_tok->tok = tt;
						t = lex_destructor_Hname;
						return (t);
					}
					tid = find_destr_id (cid);
					if (!IS_NULL_id (tid)) {
						this_tok->pp_data.id.use = tid;
						t = lex_destructor_Hname;
						return (t);
					}
					level = 0;
					break;
				}
				default : {
					if (level == 2 && !IS_NULL_nspace (ns)) {
						cid = find_id (nm);
						level = 1;
					} else {
						level = 0;
					}
					break;
				}
				}
			} while (level);
		}
#endif
		
#if LANGUAGE_CPP
		/* Look ahead for further qualifiers (C++ only) */
		if (t == lex_colon_Hcolon) {
			/* Following token is '::' */
			int level = 2;
			IDENTIFIER cid = id;
			do {
				switch (TAG_id (cid)) {
					
				case id_class_name_tag :
				case id_class_alias_tag :
				case id_nspace_name_tag :
				case id_nspace_alias_tag : {
					/* Have namespace or class name */
					ns = find_namespace (cid);
					if (!IS_NULL_nspace (ns)) {
						crt_lookup = ns;
						t = expand_token (EXPAND_IDENTIFIER);
						switch (t) {
						case lex_full_Hname :
						case lex_colon_Hcolon : {
							t = lex_nested_Hname;
							this_tok->tok = lex_ignore_token;
							crt_token->tok = t;
							return (t);
						}
						case lex_full_Hname_Hstar : {
							t = lex_nested_Hname_Hstar;
							this_tok->tok = lex_ignore_token;
							crt_token->tok = t;
							return (t);
						}
						}
						crt_token = this_tok;
					}
					level = 0;
					break;
				}
					
				default : {
					/* Look up namespace or class name */
					if (level == 2) {
						CLASS_TYPE ct = find_class (cid);
						if (!IS_NULL_ctype (ct)) {
							cid = DEREF_id (ctype_name (ct));
						} else {
							if (IS_NULL_nspace (ns)) {
								cid = find_type_id (nm, 3);
							} else {
								cid = find_qual_id (ns, nm, 0, 3);
							}
						}
						if (IS_NULL_id (cid)) {
							level = 0;
						} else {
							level = 1;
						}
					} else {
						level = 0;
					}
					break;
				}
				}
			} while (level);
		}
#endif
		
		/* Deal with context dependent identifiers */
		switch (tag) {
			
#if LANGUAGE_CPP
		case id_class_name_tag :
		case id_enum_name_tag : {
			/* Type names (C++ only) */
			t = lex_type_Hname;
			break;
		}
#endif
			
		case id_class_alias_tag :
		case id_enum_alias_tag :
		case id_type_alias_tag : {
			/* Type aliases */
			t = lex_type_Hname;
			break;
		}
			
#if LANGUAGE_CPP
		case id_nspace_name_tag :
		case id_nspace_alias_tag : {
			/* Namespace names (C++ only) */
			t = lex_namespace_Hname;
			break;
		}
#endif
			
		case id_token_tag : {
			/* Token names */
			TOKEN sort = DEREF_tok (id_token_sort (id));
			if (IS_tok_stmt (sort)) {
				t = lex_statement_Hname;
			} else {
				t = lex_identifier;
			}
			break;
		}
			
		default : {
			/* Other names */
			t = lex_identifier;
			break;
		}
		}
		if (tt == lex_identifier) {
			this_tok->pp_data.id.use = id;
		} else {
			this_tok->tok = tt;
			t = tt;
		}
		break;
	}
		
#if LANGUAGE_CPP
	case lex_colon_Hcolon : {
		/* Deal with qualified names (C++ only) */
		int nt;
		unsigned long depth;
		if (store == EXPAND_CHECK_COLON) {
			/* Look ahead for '::' */
			return (t);
		} else if (IS_NULL_nspace (ns)) {
			/* Initial '::' */
			ns = global_namespace;
			depth = 0;
		} else if (store == EXPAND_IDENTIFIER) {
			/* Following a namespace identifier */
			depth = crt_lookup_depth + 1;
		} else {
			/* Badly placed '::' */
			return (t);
		}
		
		/* Look ahead to further tokens */
		crt_lookup = ns;
		crt_lookup_depth = depth;
		nt = expand_token (EXPAND_COLON_COLON);
		crt_lookup_depth = 0;
		if (nt == lex_nested_Hname) {
			nt = lex_full_Hname;
			this_tok->tok = lex_ignore_token;
			crt_token->tok = nt;
			return (nt);
		}
		if (nt == lex_nested_Hname_Hstar) {
			if (crt_token == this_tok->next) {
				IGNORE check_value (OPT_VAL_scope_qualifiers, depth);
			}
			nt = lex_full_Hname_Hstar;
			this_tok->tok = lex_ignore_token;
			crt_token->tok = nt;
			return (nt);
		}
		IGNORE check_value (OPT_VAL_scope_qualifiers, depth);
		this_tok->pp_data.ns = ns;
		crt_lookup = ns;
		crt_token = this_tok;
		break;
	}
#endif
		
#if LANGUAGE_CPP
	case lex_full_Hname :
	case lex_nested_Hname : {
		/* Deal with stored nested names (C++ only) */
		int nt;
		ns = this_tok->pp_data.ns;
		crt_lookup = ns;
		crt_lookup_depth++;
		nt = expand_token (EXPAND_COLON_COLON);
		crt_lookup_depth = 0;
		if (nt == lex_nested_Hname) {
			this_tok->tok = lex_ignore_token;
			crt_token->tok = t;
			return (nt);
		}
		if (nt == lex_nested_Hname_Hstar) {
			if (t == lex_full_Hname) nt = lex_full_Hname_Hstar;
			this_tok->tok = lex_ignore_token;
			crt_token->tok = nt;
			return (nt);
		}
		crt_lookup = ns;
		crt_token = this_tok;
		break;
	}
#endif
		
#if LANGUAGE_CPP
	case lex_star : {
		/* Deal with pointer to members (C++ only) */
		if (store == EXPAND_COLON_COLON && crt_lookup_depth) {
			IDENTIFIER cid = DEREF_id (nspace_name (ns));
			t = lex_nested_Hname_Hstar;
			this_tok->tok = t;
			this_tok->pp_data.id.use = cid;
		}
		break;
	}
#endif
		
#if LANGUAGE_CPP
	case lex_compl_H1 :
		compl_label : {
			/* Deal with destructors (C++ only) */
			if (store != EXPAND_COLON_COLON) {
				int nt;
				crt_lookup = ns;
				nt = expand_token (EXPAND_DESTRUCTOR);
				if (nt == lex_destructor_Hname) {
					int d = predict_destr (ns);
					if (d) {
						this_tok->tok = lex_ignore_token;
						this_tok = crt_token;
						t = this_tok->tok;
						if (t == lex_template_Htype) {
							/* Template class destructors */
							IDENTIFIER id = this_tok->pp_data.tok.id;
							PPTOKEN *args = this_tok->pp_data.tok.args;
							HASHID nm = DEREF_hashid (id_name (id));
							id = parse_type_template (id, args, 1);
							id = find_destr_id (id);
							this_tok->pp_data.id.hash = nm;
							this_tok->pp_data.id.use = id;
						} else {
							/* Simple destructors */
							IDENTIFIER id = this_tok->pp_data.id.use;
							HASHID nm = this_tok->pp_data.id.hash;
							check_destr_id (id, nm, d);
						}
						this_tok->tok = nt;
						return (nt);
					}
				}
				crt_token = this_tok;
			}
			break;
		}
#endif
		
	case lex_integer_Hlit : {
		/* Deal with integer and floating point literals */
		int nt;
		int pn = pragma_number;
		OPTIONS *opts = crt_opts;
		OPTIONS *nopts = this_tok->pp_opts;
		string n = this_tok->pp_data.text;
		if (opts != nopts) set_mode (nopts);
		this_tok->pp_data.exp = make_literal_exp (n, &nt, pn);
		this_tok->tok = nt;
		if (opts != nopts) set_mode (opts);
		t = nt;
		break;
	}
		
	case lex_char_Hlit :
	case lex_wchar_Hlit : {
		/* Deal with character literals */
		STRING s;
		OPTIONS *opts = crt_opts;
		OPTIONS *nopts = this_tok->pp_opts;
		string sb = this_tok->pp_data.str.start;
		string se = this_tok->pp_data.str.end;
		if (opts != nopts) set_mode (nopts);
		s = new_string_lit (sb, se, t);
		if (t == lex_char_Hlit) {
			t = lex_char_Hexp;
		} else {
			t = lex_wchar_Hexp;
		}
		this_tok->pp_data.exp = make_string_exp (s);
		this_tok->tok = t;
		if (opts != nopts) set_mode (opts);
		break;
	}
		
	case lex_string_Hlit :
	case lex_wstring_Hlit : {
		/* Deal with string literals */
		int nt;
		STRING s;
		OPTIONS *opts = crt_opts;
		OPTIONS *nopts = this_tok->pp_opts;
		string sb = this_tok->pp_data.str.start;
		string se = this_tok->pp_data.str.end;

		/* Don't concatenate string literals while looking at the tokens
		 * following _Pragma.  String concatenation is done later, in
		 * translation phase 6. */
		if (store == EXPAND_PRAGMA) break;

		if (opts != nopts) set_mode (nopts);
		s = new_string_lit (sb, se, t);
		
		/* Concatenate adjacent strings */
		nt = expand_token (EXPAND_STRING);
		if (nt == lex_string_Hlit || nt == lex_wstring_Hlit) {
			/* Combine the following string with this one */
			if (nt != t) {
				/* String types don't match */
				report (crt_loc, ERR_lex_string_concat ());
				t = lex_wstring_Hlit;
			}
			s = concat_string_lit (s, crt_token->pp_data.strlit);
			crt_token->tok = lex_ignore_token;
		}
		crt_token = this_tok;
		if (store == EXPAND_STRING) {
			/* Continue concatenation */
			this_tok->pp_data.strlit = s;
		} else {
			/* Transform string literal into expression */
			if (t == lex_string_Hlit) {
				t = lex_string_Hexp;
			} else {
				t = lex_wstring_Hexp;
			}
			this_tok->pp_data.exp = make_string_exp (s);
			this_tok->tok = t;
		}
		if (opts != nopts) set_mode (opts);
		break;
	}
		
	case lex_ellipsis : {
		/* Ellipses */
		if (store == EXPAND_NORMAL) {
			OPTION opt = option (OPT_ellipsis_ident);
			NAMESPACE cns = crt_namespace;
			if (opt != OPTION_DISALLOW && IS_nspace_block (cns)) {
				t = lex_ellipsis_Hexp;
				this_tok->tok = t;
			}
		}
		break;
	}
		
	case lex_hash_H2 :
	case lex_hash_Hhash_H2 :
	case lex_open_Hbrace_H2 :
	case lex_open_Hsquare_H2 :
	case lex_close_Hbrace_H2 :
	case lex_close_Hsquare_H2 : {
		/* Digraphs */
		t = get_digraph (t);
		this_tok->tok = t;
		break;
	}
		
	case lex_and_H2 :
	case lex_and_Heq_H2 :
	case lex_compl_H2 :
	case lex_logical_Hand_H2 :
	case lex_logical_Hor_H2 :
	case lex_not_H2 :
	case lex_not_Heq_H2 :
	case lex_or_H2 :
	case lex_or_Heq_H2 :
	case lex_xor_H2 :
	case lex_xor_Heq_H2 : {
		/* ISO keywords */
		int tt;
		IDENTIFIER id = this_tok->pp_data.id.use;
		t = (int) DEREF_ulong (id_no (id));
		tt = primary_form (t);
		if (tt != t) {
			HASHID nm;
			if (in_pragma_dir) break;
			nm = this_tok->pp_data.id.hash;
			report (crt_loc, ERR_lex_digraph_iso (nm, tt));
			t = tt;
		}
		this_tok->tok = t;
#if LANGUAGE_CPP
		if (t == lex_compl_H1) goto compl_label;
#endif
		break;
	}
		
	case lex_unknown : {
		/* Unknown characters */
		unsigned long u;
		int ch = CHAR_SIMPLE;
		u = get_multi_char (this_tok->pp_data.buff, &ch);
		if (ch == CHAR_SIMPLE) {
			if (is_legal_char (u)) {
				if (!is_white_char (u)) break;
			} else {
				int c = (int) u;
				report (crt_loc, ERR_lex_pptoken_unknown (c));
			}
		} else {
			report (crt_loc, ERR_lex_pptoken_unicode (u));
		}
		this_tok->tok = lex_ignore_token;
		t = expand_token (store);
		break;
	}
	}
	return (t);
}


/*
 *    READ AND EXPAND THE NEXT TOKEN (PREPROCESSOR VERSION)
 *
 *    This routine is a cut-down version of expand_token with only the macro
 *    expansion and keyword actions.  This is for use with the stand-alone
 *    preprocessor and in the rewriting rules.
 */

int
expand_preproc(int store)
{
	int t;
	int expand;
	PPTOKEN *prev_tok = crt_token;
	PPTOKEN *this_tok = prev_tok->next;
	
	/* Get the next token */
	start_label : {
		if (this_tok == NULL) {
			/* Read the token from the file */
			this_tok = new_pptok ();
			this_tok->next = NULL;
			prev_tok->next = this_tok;
			crt_token = this_tok;
			t = read_token ();
			update_column ();
			this_tok->tok = t;
			if (t <= LAST_COMPLEX_TOKEN) token_parts (t, this_tok);
			
			if (store == EXPAND_NORMAL) {
				/* Garbage collect stored tokens */
				this_tok->pp_space = 0;
				prev_tok->next = free_tokens;
				free_tokens = first_token;
				first_token = this_tok;
			} else {
				this_tok->pp_space = crt_loc.column;
				if (crt_line_changed) {
					/* Record file position */
					make_loc_tokens (prev_tok);
				}
			}
			expand = 1;
			
		} else {
			/* Use a previously stored token */
			if (this_tok->pp_space) {
				/* Increase space count if necessary */
				if (!crt_line_changed) crt_spaces++;
				if (store == EXPAND_AHEAD) this_tok->pp_space = 0;
			}
			t = this_tok->tok;
			if (t == lex_ignore_token) {
				/* Step over any ignored tokens */
				prev_tok = this_tok;
				this_tok = this_tok->next;
				goto start_label;
			}
			crt_token = this_tok;
			expand = 0;
		}
	}
	
	/* Deal with context switch */
	if (store == EXPAND_NORMAL) {
		OPTIONS *opts = prev_opts;
		if (opts != crt_opts) set_mode (opts);
		prev_opts = this_tok->pp_opts;
	}
	
	/* Deal with identifiers */
	if (t == lex_identifier) {
		HASHID nm = this_tok->pp_data.id.hash;
		IDENTIFIER id = DEREF_id (hashid_id (nm));
		expand_label : {
			switch (TAG_id (id)) {
			case id_obj_macro_tag :
			case id_func_macro_tag : {
				if (expand) {
					PPTOKEN *toks = expand_macro (nm, file_loc, 1);
					if (!crt_line_changed) crt_spaces++;
					this_tok->tok = lex_ignore_token;
					this_tok->next = toks;
					prev_tok = this_tok;
					this_tok = toks;
					goto start_label;
				}
				id = DEREF_id (id_alias (id));
				goto expand_label;
			}
			case id_keyword_tag : {
				if (store == EXPAND_AHEAD) {
					t = (int) DEREF_ulong (id_no (id));
					this_tok->tok = t;
				}
				break;
			}
			case id_iso_keyword_tag : {
				if (store == EXPAND_AHEAD) {
					t = (int) DEREF_ulong (id_no (id));
					if (t >= FIRST_SYMBOL && t <= LAST_SYMBOL) {
						/* Will be reinterpreted later */
						t = lex_and_H2;
					}
					this_tok->tok = t;
				}
				break;
			}
			}
		}
		if (find_hashid (nm) == lex_pragma_H2) {
			if (operator_pragma ()) {
				this_tok = crt_token->next;
				/* Continue after the closing ) */
				goto start_label;
			}
			/* Otherwise treat _Pragma as an ordinary identifier */
		}

		this_tok->pp_data.id.use = id;
	}
	return (t);
}


/*
 *    PARSE A TEMPLATE IDENTIFIER
 *
 *    This routine is called after the optional 'template' at the start of
 *    a qualified identifier or a field member selector.  It forces the
 *    following identifier to be treated as a template-id even if it doesn't
 *    seem to be one.
 */

void
rescan_template(NAMESPACE ns)
{
	PPTOKEN *p = crt_token;
	crt_lookup = ns;
	have_template = 0;
	IGNORE expand_token (EXPAND_TEMPLATE);
	if (!have_template) {
		/* Didn't read template-id */
		report (crt_loc, ERR_temp_names_bad ());
	}
	crt_token = p;
	crt_lookup = ns;
	return;
}


syntax highlighted by Code2HTML, v. 0.9.1