/* -*- C -*- */
/*\
||| This file a part of Pike, and is copyright by Fredrik Hubinette
||| Pike is distributed as GPL (General Public License)
||| See the files COPYING and DISCLAIMER for more information.
\*/
/**/
%pure_parser

%token TOK_ARROW

/*
 * Basic value pushing
 */
%token TOK_CONSTANT TOK_FLOAT TOK_STRING
%token TOK_NUMBER

/*
 * These are the predefined functions that can be accessed from Pike.
 */

%token TOK_INC TOK_DEC
%token TOK_RETURN

%token TOK_EQ TOK_GE TOK_LE TOK_NE
%token TOK_NOT
%token TOK_LSH TOK_RSH
%token TOK_LAND TOK_LOR

%token TOK_SWITCH TOK_SSCANF TOK_CATCH
%token TOK_FOREACH

/* This is the end of file marker used by the lexer
 * to enable nicer EOF in error handling.
 */
%token TOK_LEX_EOF

%token TOK_ADD_EQ
%token TOK_AND_EQ
%token TOK_ARRAY_ID
%token TOK_BREAK
%token TOK_CASE
%token TOK_CLASS
%token TOK_COLON_COLON
%token TOK_CONTINUE 
%token TOK_DEFAULT
%token TOK_DIV_EQ
%token TOK_DO
%token TOK_DOT_DOT
%token TOK_DOT_DOT_DOT
%token TOK_ELSE
%token TOK_ENUM
%token TOK_EXTERN
%token TOK_FLOAT_ID
%token TOK_FOR
%token TOK_FUNCTION_ID
%token TOK_GAUGE
%token TOK_IDENTIFIER
%token TOK_IF
%token TOK_IMPORT
%token TOK_INHERIT
%token TOK_INLINE
%token TOK_LOCAL_ID
%token TOK_FINAL_ID
%token TOK_INT_ID
%token TOK_LAMBDA
%token TOK_MULTISET_ID
%token TOK_MULTISET_END
%token TOK_MULTISET_START
%token TOK_LSH_EQ
%token TOK_MAPPING_ID
%token TOK_MIXED_ID
%token TOK_MOD_EQ
%token TOK_MULT_EQ
%token TOK_NO_MASK
%token TOK_OBJECT_ID
%token TOK_OR_EQ
%token TOK_PRIVATE
%token TOK_PROGRAM_ID
%token TOK_PROTECTED
%token TOK_PREDEF
%token TOK_PUBLIC
%token TOK_RSH_EQ
%token TOK_STATIC
%token TOK_STRING_ID
%token TOK_SUB_EQ
%token TOK_TYPEDEF
%token TOK_TYPEOF
%token TOK_VARIANT
%token TOK_VOID_ID
%token TOK_WHILE
%token TOK_XOR_EQ
%token TOK_OPTIONAL


%right '='
%right '?'
%left TOK_LOR
%left TOK_LAND
%left '|'
%left '^'
%left '&'
%left TOK_EQ TOK_NE
%left '>' TOK_GE '<' TOK_LE  /* nonassoc? */
%left TOK_LSH TOK_RSH
%left '+' '-'
%left '*' '%' '/'
%right TOK_NOT '~'
%nonassoc TOK_INC TOK_DEC

%{
/* This is the grammar definition of Pike. */

#include "global.h"
RCSID("$Id: language.yacc,v 1.255 2001/07/02 20:09:17 mast Exp $");
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif

#include "interpret.h"
#include "array.h"
#include "object.h"
#include "stralloc.h"
#include "las.h"
#include "interpret.h"
#include "program.h"
#include "pike_types.h"
#include "constants.h"
#include "pike_macros.h"
#include "pike_error.h"
#include "docode.h"
#include "machine.h"
#include "main.h"
#include "opcodes.h"
#include "operators.h"

#define YYMAXDEPTH	1000

#ifdef PIKE_DEBUG
#ifndef YYDEBUG
/* May also be defined by machine.h */
#define YYDEBUG 1
#endif /* YYDEBUG */
#endif

/* #define LAMBDA_DEBUG	1 */

int add_local_name(struct pike_string *, struct pike_type *, node *);
int low_add_local_name(struct compiler_frame *,
		       struct pike_string *, struct pike_type *, node *);
static node *lexical_islocal(struct pike_string *);
static void safe_inc_enum(void);

static int inherit_depth;
static struct program_state *inherit_state = NULL;

/*
 * Kludge for Bison not using prototypes.
 */
#ifndef __GNUC__
#ifndef __cplusplus
static void __yy_memcpy(char *to, char *from, YY_COUNT_TYPE count);
#endif /* !__cplusplus */
#endif /* !__GNUC__ */

%}

%union
{
  int number;
  FLOAT_TYPE fnum;
  struct node_s *n;
  char *str;
}

%{
/* Needs to be included after YYSTYPE is defined. */
#include "lex.h"
%}

%{
/* Include <stdio.h> our selves, so that we can do our magic
 * without being disturbed... */
#include <stdio.h>
int yylex(YYSTYPE *yylval);
/* Bison is stupid, and tries to optimize for space... */
#ifdef YYBISON
#define short int
#endif /* YYBISON */
%}

%type <fnum> TOK_FLOAT

%type <number> TOK_ARRAY_ID
%type <number> TOK_BREAK
%type <number> TOK_CASE
%type <number> TOK_CATCH
%type <number> TOK_CONTINUE
%type <number> TOK_DEFAULT
%type <number> TOK_DO
%type <number> TOK_ELSE
%type <number> TOK_FLOAT_ID
%type <number> TOK_FOR
%type <number> TOK_FOREACH
%type <number> TOK_FUNCTION_ID
%type <number> TOK_GAUGE
%type <number> TOK_IF
%type <number> TOK_INHERIT
%type <number> TOK_INLINE
%type <number> TOK_INT_ID
%type <number> TOK_LAMBDA
%type <number> TOK_LOCAL_ID
%type <number> TOK_MAPPING_ID
%type <number> TOK_MIXED_ID
%type <number> TOK_MULTISET_ID
%type <number> TOK_NO_MASK
%type <number> TOK_OBJECT_ID
%type <number> TOK_PREDEF
%type <number> TOK_PRIVATE
%type <number> TOK_PROGRAM_ID
%type <number> TOK_PROTECTED
%type <number> TOK_PUBLIC
%type <number> TOK_RETURN
%type <number> TOK_SSCANF
%type <number> TOK_STATIC
%type <number> TOK_STRING_ID
%type <number> TOK_SWITCH
%type <number> TOK_VOID_ID
%type <number> TOK_WHILE
%type <number> arguments
%type <number> arguments2
%type <number> func_args
%type <number> optional_create_arguments
%type <number> create_arguments
%type <number> create_arguments2
%type <number> create_arg
%type <number> assign
%type <number> modifier
%type <number> modifier_list
%type <number> modifiers
%type <number> inherit_specifier
%type <number> function_type_list
%type <number> function_type_list2
%type <number> optional_dot_dot_dot
%type <number> optional_comma
%type <number> optional_stars

%type <str> magic_identifiers
%type <str> magic_identifiers1
%type <str> magic_identifiers2
%type <str> magic_identifiers3

/* The following symbols return type information */

%type <n> number_or_minint
%type <n> number_or_maxint
%type <n> cast
%type <n> soft_cast
%type <n> simple_type
%type <n> simple_type2
%type <n> simple_identifier_type
%type <n> string_constant
%type <n> string
%type <n> TOK_STRING
%type <n> TOK_NUMBER
%type <n> optional_rename_inherit
%type <n> optional_identifier
%type <n> TOK_IDENTIFIER
%type <n> assoc_pair
%type <n> block
%type <n> optional_block
%type <n> failsafe_block
%type <n> close_paren_or_missing
%type <n> block_or_semi
%type <n> break
%type <n> case
%type <n> catch
%type <n> catch_arg
%type <n> class
%type <n> enum
%type <n> safe_comma_expr
%type <n> comma_expr
%type <n> comma_expr2
%type <n> comma_expr_or_maxint
%type <n> comma_expr_or_zero
%type <n> cond
%type <n> continue
%type <n> default
%type <n> do
%type <n> safe_expr0
%type <n> expr00
%type <n> expr01
%type <n> expr1
%type <n> expr2
%type <n> expr3 expr0
%type <n> expr4
%type <n> expr_list
%type <n> expr_list2
%type <n> for
%type <n> for_expr
%type <n> foreach
%type <n> gauge
%type <n> idents
%type <n> idents2
%type <n> labeled_statement
%type <n> lambda
%type <n> local_name_list
%type <n> local_name_list2
%type <n> low_idents
%type <n> safe_lvalue
%type <n> lvalue
%type <n> lvalue_list
%type <n> low_lvalue_list
%type <n> m_expr_list
%type <n> m_expr_list2
%type <n> new_local_name
%type <n> new_local_name2
%type <n> normal_label_statement
%type <n> optional_else_part
%type <n> optional_label
%type <n> return
%type <n> sscanf
%type <n> statement
%type <n> statements
%type <n> statement_with_semicolon
%type <n> switch
%type <n> typeof
%type <n> unused
%type <n> unused2
%type <n> while
%type <n> optional_comma_expr
%type <n> low_program_ref
%type <n> local_function
%type <n> local_function2
%type <n> magic_identifier
%type <n> simple_identifier
%type <n> foreach_lvalues
%type <n> foreach_optional_lvalue
%%

all: program { YYACCEPT; }
  | program TOK_LEX_EOF { YYACCEPT; }
/*  | error TOK_LEX_EOF { YYABORT; } */
  ;

program: program def
  | program ';'
  |  /* empty */
  ;

string_constant: string
  | string_constant '+' string
  {
    struct pike_string *a,*b;
    copy_shared_string(a,$1->u.sval.u.string);
    copy_shared_string(b,$3->u.sval.u.string);
    free_node($1);
    free_node($3);
    a=add_and_free_shared_strings(a,b);
    $$=mkstrnode(a);
    free_string(a);
  }
  ;

optional_rename_inherit: ':' TOK_IDENTIFIER { $$=$2; }
  | ':' bad_identifier { $$=0; }
  | ':' error { $$=0; }
  | { $$=0; }
  ;

/* NOTE: This rule pushes a string "name" on the stack in addition
 * to resolving the program reference.
 */
low_program_ref: string_constant
  {
    ref_push_string($1->u.sval.u.string);
    ref_push_string($1->u.sval.u.string);
    ref_push_string(lex.current_file);
    
    if (error_handler && error_handler->prog) {
      ref_push_object(error_handler);
      SAFE_APPLY_MASTER("handle_inherit", 3);
    } else {
      SAFE_APPLY_MASTER("handle_inherit", 2);
    }

    if(Pike_sp[-1].type != T_PROGRAM)
      my_yyerror("Couldn't cast string \"%s\" to program",
		 $1->u.sval.u.string->str);
    free_node($1);
    $$=mksvaluenode(Pike_sp-1);
    if($$->name) free_string($$->name);
    add_ref( $$->name=Pike_sp[-2].u.string );
    pop_stack();
  }
  | idents
  {
    if(Pike_compiler->last_identifier)
    {
      ref_push_string(Pike_compiler->last_identifier);
    }else{
      push_constant_text("");
    }
    $$=$1;
  }
  ;

/* NOTE: Pushes the resolved program on the stack. */
program_ref: low_program_ref
  {
    resolv_program($1);
    free_node($1);
  }
  ;
      
inheritance: modifiers TOK_INHERIT low_program_ref optional_rename_inherit ';'
  {
    if (($1 & ID_EXTERN) && (Pike_compiler->compiler_pass == 1)) {
      yywarning("Extern declared inherit.");
    }
    if(!(Pike_compiler->new_program->flags & PROGRAM_PASS_1_DONE))
    {
      struct pike_string *s=Pike_sp[-1].u.string;
      if($4) s=$4->u.sval.u.string;
      compiler_do_inherit($3,$1,s);
    }
    if($4) free_node($4);
    pop_n_elems(1);
    free_node($3);
  }
  | modifiers TOK_INHERIT low_program_ref error ';'
  {
    free_node($3); yyerrok;
  }
  | modifiers TOK_INHERIT low_program_ref error TOK_LEX_EOF
  {
    free_node($3);
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file.");
  }
  | modifiers TOK_INHERIT low_program_ref error '}'
  {
    free_node($3); yyerror("Missing ';'.");
  }
  | modifiers TOK_INHERIT error ';' { yyerrok; }
  | modifiers TOK_INHERIT error TOK_LEX_EOF
  {
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file.");
  }
  | modifiers TOK_INHERIT error '}' { yyerror("Missing ';'."); }
  ;

import: TOK_IMPORT idents ';'
  {
    resolv_constant($2);
    free_node($2);
    use_module(Pike_sp-1);
    pop_stack();
  }
  | TOK_IMPORT string ';'
  {
    ref_push_string($2->u.sval.u.string);
    free_node($2);
    ref_push_string(lex.current_file);
    if (error_handler && error_handler->prog) {
      ref_push_object(error_handler);
      SAFE_APPLY_MASTER("handle_import", 3);
    } else {
      SAFE_APPLY_MASTER("handle_import", 2);
    }
    use_module(Pike_sp-1);
    pop_stack();
  }
  | TOK_IMPORT error ';' { yyerrok; }
  | TOK_IMPORT error TOK_LEX_EOF
  {
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file.");
  }
  | TOK_IMPORT error '}' { yyerror("Missing ';'."); }
  ;

constant_name: TOK_IDENTIFIER '=' safe_expr0
  {
    /* This can be made more lenient in the future */

    /* Ugly hack to make sure that $3 is optimized */
    {
      int tmp=Pike_compiler->compiler_pass;
      $3=mknode(F_COMMA_EXPR,$3,0);
      Pike_compiler->compiler_pass=tmp;
    }

    if ((Pike_compiler->current_modifiers & ID_EXTERN) &&
	(Pike_compiler->compiler_pass == 1)) {
      yywarning("Extern declared constant.");
    }

    if(!is_const($3))
    {
      if(Pike_compiler->compiler_pass==2)
	yyerror("Constant definition is not constant.");
      else
	add_constant($1->u.sval.u.string, 0,
		     Pike_compiler->current_modifiers & ~ID_EXTERN);
    } else {
      if(!Pike_compiler->num_parse_error)
      {
	ptrdiff_t tmp=eval_low($3);
	if(tmp < 1)
	{
	  yyerror("Error in constant definition.");
	}else{
	  pop_n_elems(DO_NOT_WARN((INT32)(tmp - 1)));
	  add_constant($1->u.sval.u.string, Pike_sp-1,
		       Pike_compiler->current_modifiers & ~ID_EXTERN);
	  pop_stack();
	}
      }
    }
    if($3) free_node($3);
    free_node($1);
  }
  | bad_identifier '=' safe_expr0 { if ($3) free_node($3); }
  | error '=' safe_expr0 { if ($3) free_node($3); }
  ;

constant_list: constant_name
  | constant_list ',' constant_name
  ;

constant: modifiers TOK_CONSTANT constant_list ';' {}
  | modifiers TOK_CONSTANT error ';' { yyerrok; }
  | modifiers TOK_CONSTANT error TOK_LEX_EOF
  {
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file.");
  }
  | modifiers TOK_CONSTANT error '}' { yyerror("Missing ';'."); }
  ;

block_or_semi: block
  {
    $$ = check_node_hash(mknode(F_COMMA_EXPR,$1,mknode(F_RETURN,mkintnode(0),0)));
    if ($1) $$->line_number = $1->line_number;
  }
  | ';' { $$ = NULL; }
  | TOK_LEX_EOF { yyerror("Expected ';'."); $$ = NULL; }
  | error { $$ = NULL; }
  ;


type_or_error: simple_type
  {
#ifdef PIKE_DEBUG
    check_type_string(check_node_hash($1)->u.sval.u.type);
#endif /* PIKE_DEBUG */
    if(Pike_compiler->compiler_frame->current_type)
      free_type(Pike_compiler->compiler_frame->current_type); 
    copy_pike_type(Pike_compiler->compiler_frame->current_type,
		   $1->u.sval.u.type);
    free_node($1);
  }
  ;

close_paren_or_missing: ')'
  {
    /* Used to hold line-number info */
    $$ = mkintnode(0);
  }
  | /* empty */
  {
    yyerror("Missing ')'.");
    /* Used to hold line-number info */
    $$ = mkintnode(0);
  }
  ;

close_brace_or_missing: '}'
  | /* empty */
  {
    yyerror("Missing '}'.");
  }
  ;

close_bracket_or_missing: ']'
  | /* empty */
  {
    yyerror("Missing ']'.");
  }
  ;

push_compiler_frame0: /* empty */
  {
    push_compiler_frame(SCOPE_LOCAL);

    if(!Pike_compiler->compiler_frame->previous ||
       !Pike_compiler->compiler_frame->previous->current_type)
    {
      yyerror("Internal compiler fault.");
      copy_pike_type(Pike_compiler->compiler_frame->current_type,
		mixed_type_string);
    }else{
      copy_pike_type(Pike_compiler->compiler_frame->current_type,
		Pike_compiler->compiler_frame->previous->current_type);
    }
  }
  ;

def: modifiers type_or_error optional_stars TOK_IDENTIFIER push_compiler_frame0
  '(' arguments close_paren_or_missing
  {
    int e;
    /* construct the function type */
    push_finished_type(Pike_compiler->compiler_frame->current_type);
    if ($3 && (Pike_compiler->compiler_pass == 2)) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    while(--$3>=0) push_type(T_ARRAY);
    
    if(Pike_compiler->compiler_frame->current_return_type)
      free_type(Pike_compiler->compiler_frame->current_return_type);
    Pike_compiler->compiler_frame->current_return_type = compiler_pop_type();
    
    push_finished_type(Pike_compiler->compiler_frame->current_return_type);
    
    e=$7-1;
    if(Pike_compiler->varargs)
    {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
      e--;
      Pike_compiler->varargs=0;
      pop_type_stack(T_ARRAY);
    }else{
      push_type(T_VOID);
    }
    push_type(T_MANY);
    for(; e>=0; e--)
    {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
#ifdef USE_PIKE_TYPE
      push_type(T_FUNCTION);
#endif /* USE_PIKE_TYPE */
    }
#ifndef USE_PIKE_TYPE
    push_type(T_FUNCTION);
#endif /* USE_PIKE_TYPE */

    {
      struct pike_type *s=compiler_pop_type();
      int i = isidentifier($4->u.sval.u.string);

      if (Pike_compiler->compiler_pass == 1) {
	if ($1 & ID_VARIANT) {
	  /* FIXME: Lookup the type of any existing variant */
	  /* Or the types. */
	  fprintf(stderr, "Pass %d: Identifier %s:\n",
		  Pike_compiler->compiler_pass, $4->u.sval.u.string->str);

	  if (i >= 0) {
	    struct identifier *id = ID_FROM_INT(Pike_compiler->new_program, i);
	    if (id) {
	      struct pike_type *new_type;
	      fprintf(stderr, "Defined, type:\n");
#ifdef PIKE_DEBUG
	      simple_describe_type(id->type);
#endif

	      new_type = or_pike_types(s, id->type, 1);
	      free_type(s);
	      s = new_type;

	      fprintf(stderr, "Resulting type:\n");
#ifdef PIKE_DEBUG
	      simple_describe_type(s);
#endif
	    } else {
	      my_yyerror("Lost identifier %s (%d).",
			 $4->u.sval.u.string->str, i);
	    }
	  } else {
	    fprintf(stderr, "Not defined.\n");
	  }
	  fprintf(stderr, "New type:\n");
#ifdef PIKE_DEBUG
	  simple_describe_type(s);
#endif
	}
      } else {
	/* FIXME: Second pass reuses the type from the end of
	 * the first pass if this is a variant function.
	 */
	if (i >= 0) {
	  if (Pike_compiler->new_program->identifier_references[i].id_flags &
	      ID_VARIANT) {
	    struct identifier *id = ID_FROM_INT(Pike_compiler->new_program, i);
	    fprintf(stderr, "Pass %d: Identifier %s:\n",
		    Pike_compiler->compiler_pass, $4->u.sval.u.string->str);

	    free_type(s);
	    copy_pike_type(s, id->type);

	    fprintf(stderr, "Resulting type:\n");
#ifdef PIKE_DEBUG
	    simple_describe_type(s);
#endif
	  }
	} else {
	  my_yyerror("Identifier %s lost after first pass.",
		     $4->u.sval.u.string->str);
	}
      }

      $<n>$ = mktypenode(s);
      free_type(s);
    }


/*    if(Pike_compiler->compiler_pass==1) */
    {
      /* FIXME:
       * set current_function_number for local functions as well
       */
      Pike_compiler->compiler_frame->current_function_number=
	define_function(check_node_hash($4)->u.sval.u.string,
			check_node_hash($<n>$)->u.sval.u.type,
			$1 & (~ID_EXTERN),
			IDENTIFIER_PIKE_FUNCTION,
			0,
			OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);

      if ($1 & ID_VARIANT) {
	fprintf(stderr, "Function number: %d\n",
		Pike_compiler->compiler_frame->current_function_number);
      }
    }
  }
  block_or_semi
  {
    int e;
    if($10)
    {
      int f;
      node *check_args = NULL;
      int save_line = lex.current_line;
      int num_required_args = 0;
      struct identifier *i;
#ifdef PIKE_DEBUG
      struct pike_string *save_file = lex.current_file;
      lex.current_file = $8->current_file;
#endif
      lex.current_line = $8->line_number;

      if (($1 & ID_EXTERN) && (Pike_compiler->compiler_pass == 1)) {
	yywarning("Extern declared function definition.");
      }

      for(e=0; e<$7; e++)
      {
	if(!Pike_compiler->compiler_frame->variable[e].name ||
	   !Pike_compiler->compiler_frame->variable[e].name->len)
	{
	  my_yyerror("Missing name for argument %d.",e);
	} else {
	  if (Pike_compiler->compiler_pass == 2) {
	    if ($1 & ID_VARIANT) {
	      struct pike_type *arg_type =
		Pike_compiler->compiler_frame->variable[e].type;

	      /* FIXME: Generate code that checks the arguments. */
	      /* If there is a bad argument, call the fallback, and return. */
	      if (! pike_types_le(void_type_string, arg_type)) {
		/* Argument my not be void.
		 * ie it's required.
		 */
		num_required_args++;
	      }
	    } else {
	      /* FIXME: Should probably use some other flag. */
	      if ((runtime_options & RUNTIME_CHECK_TYPES) &&
		  (Pike_compiler->compiler_frame->variable[e].type !=
		   mixed_type_string)) {
		node *local_node;

		/* fprintf(stderr, "Creating soft cast node for local #%d\n", e);*/

		local_node = mklocalnode(e, 0);

		/* The following is needed to go around the optimization in
		 * mksoftcastnode().
		 */
		free_type(local_node->type);
		copy_pike_type(local_node->type, mixed_type_string);

		check_args =
		  mknode(F_COMMA_EXPR, check_args,
			 mksoftcastnode(Pike_compiler->compiler_frame->variable[e].type,
					local_node));
	      }
	    }
	  }
	}
      }

      if ($1 & ID_VARIANT) {
	struct pike_string *bad_arg_str;
	MAKE_CONSTANT_SHARED_STRING(bad_arg_str,
				    "Bad number of arguments!\n");

	fprintf(stderr, "Required args: %d\n", num_required_args);

	check_args =
	  mknode('?',
		 mkopernode("`<",
			    mkefuncallnode("query_num_arg", NULL),
			    mkintnode(num_required_args)),
		 mknode(':',
			mkefuncallnode("throw",
				       mkefuncallnode("aggregate",
						      mkstrnode(bad_arg_str))),
			NULL));
	free_string(bad_arg_str);
      }

      {
	int l = $10->line_number;
#ifdef PIKE_DEBUG
	struct pike_string *f = $10->current_file;
#endif
	if (check_args) {
	  /* Prepend the arg checking code. */
	  $10 = mknode(F_COMMA_EXPR, mknode(F_POP_VALUE, check_args, NULL), $10);
	}
	lex.current_line = l;
#ifdef PIKE_DEBUG
	lex.current_file = f;
#endif
      }

      f=dooptcode(check_node_hash($4)->u.sval.u.string,
		  check_node_hash($10),
		  check_node_hash($<n>9)->u.sval.u.type,
		  $1);

      i = ID_FROM_INT(Pike_compiler->new_program, f);
      i->opt_flags = Pike_compiler->compiler_frame->opt_flags;

      if ($1 & ID_VARIANT) {
	fprintf(stderr, "Function number: %d\n", f);
      }

#ifdef PIKE_DEBUG
      if(Pike_interpreter.recoveries &&
	 ((Pike_sp - Pike_interpreter.evaluator_stack) <
	  Pike_interpreter.recoveries->stack_pointer))
	fatal("Stack error (underflow)\n");

      if((Pike_compiler->compiler_pass == 1) &&
	 (f != Pike_compiler->compiler_frame->current_function_number))
	fatal("define_function screwed up! %d != %d\n",
	      f, Pike_compiler->compiler_frame->current_function_number);
#endif

      lex.current_line = save_line;
#ifdef PIKE_DEBUG
      lex.current_file = save_file;
#endif
    }
    pop_compiler_frame();
    free_node($4);
    free_node($8);
    free_node($<n>9);
  }
  | modifiers type_or_error optional_stars TOK_IDENTIFIER push_compiler_frame0
    error
  {
    pop_compiler_frame();
    free_node($4);
  }
  | modifiers type_or_error optional_stars bad_identifier
  {
    free_type(compiler_pop_type());
  }
    '(' arguments ')' block_or_semi
  {
    if ($9) free_node($9);
  }
  | modifiers type_or_error name_list ';' {}
  | inheritance {}
  | import {}
  | constant {}
  | class { free_node($1); }
  | enum { free_node($1); }
  | typedef {}
  | error TOK_LEX_EOF
  {
    reset_type_stack();
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file");
  }
  | error ';'
  {
    reset_type_stack();
    yyerrok;
/*     if(Pike_compiler->num_parse_error>5) YYACCEPT; */
  }
  | error '}'
  {
    reset_type_stack();
    yyerror("Missing ';'.");
   /* yychar = '}';	*/ /* Put the '}' back on the input stream */
  }
  | modifiers
   '{' 
    {
      $<number>$=lex.pragmas;
      lex.pragmas|=$1;
    }
      program
   '}'
    {
      lex.pragmas=$<number>3;
    }
  ;

optional_dot_dot_dot: TOK_DOT_DOT_DOT { $$=1; }
  | TOK_DOT_DOT
  {
    yyerror("Range indicator ('..') where elipsis ('...') expected.");
    $$=1;
  }
  | /* empty */ { $$=0; }
  ;

optional_identifier: TOK_IDENTIFIER
  | bad_identifier { $$=0; }
  | /* empty */ { $$=0; }
  ;

new_arg_name: type7 optional_dot_dot_dot optional_identifier
  {
    if(Pike_compiler->varargs) yyerror("Can't define more arguments after ...");

    if($2)
    {
      push_type(T_ARRAY);
      Pike_compiler->varargs=1;
    }

    if(!$3)
    {
      struct pike_string *s;
      MAKE_CONSTANT_SHARED_STRING(s,"");
      $3=mkstrnode(s);
      free_string(s);
    }

    if($3->u.sval.u.string->len &&
       islocal($3->u.sval.u.string) >= 0)
      my_yyerror("Variable '%s' appears twice in argument list.",
		 $3->u.sval.u.string->str);
    
    add_local_name($3->u.sval.u.string, compiler_pop_type(),0);
    free_node($3);
  }
  ;

func_args: '(' arguments close_paren_or_missing
  {
    free_node($3);
    $$=$2;
  }
  ;

arguments: /* empty */ optional_comma { $$=0; }
  | arguments2 optional_comma
  ;

arguments2: new_arg_name { $$ = 1; }
  | arguments2 ',' new_arg_name { $$ = $1 + 1; }
  | arguments2 ':' new_arg_name
  {
    yyerror("Unexpected ':' in argument list.");
    $$ = $1 + 1;
  }
  ;

modifier:
    TOK_NO_MASK    { $$ = ID_NOMASK; }
  | TOK_FINAL_ID   { $$ = ID_NOMASK; }
  | TOK_STATIC     { $$ = ID_STATIC; }
  | TOK_EXTERN     { $$ = ID_EXTERN; }
  | TOK_OPTIONAL   { $$ = ID_OPTIONAL; }
  | TOK_PRIVATE    { $$ = ID_PRIVATE | ID_STATIC; }
  | TOK_LOCAL_ID   { $$ = ID_INLINE; }
  | TOK_PUBLIC     { $$ = ID_PUBLIC; }
  | TOK_PROTECTED  { $$ = ID_PROTECTED; }
  | TOK_INLINE     { $$ = ID_INLINE; }
  | TOK_VARIANT    { $$ = ID_VARIANT; }
  ;

magic_identifiers1:
    TOK_NO_MASK    { $$ = "nomask"; }
  | TOK_FINAL_ID   { $$ = "final"; }
  | TOK_STATIC     { $$ = "static"; }
  | TOK_EXTERN	   { $$ = "extern"; }
  | TOK_PRIVATE    { $$ = "private"; }
  | TOK_LOCAL_ID   { $$ = "local"; }
  | TOK_PUBLIC     { $$ = "public"; }
  | TOK_PROTECTED  { $$ = "protected"; }
  | TOK_INLINE     { $$ = "inline"; }
  | TOK_OPTIONAL   { $$ = "optional"; }
  | TOK_VARIANT    { $$ = "variant"; }
  ;

magic_identifiers2:
    TOK_VOID_ID       { $$ = "void"; }
  | TOK_MIXED_ID      { $$ = "mixed"; }
  | TOK_ARRAY_ID      { $$ = "array"; }
  | TOK_MAPPING_ID    { $$ = "mapping"; }
  | TOK_MULTISET_ID   { $$ = "multiset"; }
  | TOK_OBJECT_ID     { $$ = "object"; }
  | TOK_FUNCTION_ID   { $$ = "function"; }
  | TOK_PROGRAM_ID    { $$ = "program"; }
  | TOK_STRING_ID     { $$ = "string"; }
  | TOK_FLOAT_ID      { $$ = "float"; }
  | TOK_INT_ID        { $$ = "int"; }
  | TOK_ENUM	      { $$ = "enum"; }
  | TOK_TYPEDEF       { $$ = "typedef"; }
  ;

magic_identifiers3:
    TOK_IF         { $$ = "if"; }
  | TOK_DO         { $$ = "do"; }
  | TOK_FOR        { $$ = "for"; }
  | TOK_WHILE      { $$ = "while"; }
  | TOK_ELSE       { $$ = "else"; }
  | TOK_FOREACH    { $$ = "foreach"; }
  | TOK_CATCH      { $$ = "catch"; }
  | TOK_GAUGE      { $$ = "gauge"; }
  | TOK_CLASS      { $$ = "class"; }
  | TOK_BREAK      { $$ = "break"; }
  | TOK_CASE       { $$ = "case"; }
  | TOK_CONSTANT   { $$ = "constant"; }
  | TOK_CONTINUE   { $$ = "continue"; }
  | TOK_DEFAULT    { $$ = "default"; }
  | TOK_IMPORT     { $$ = "import"; }
  | TOK_INHERIT    { $$ = "inherit"; }
  | TOK_LAMBDA     { $$ = "lambda"; }
  | TOK_PREDEF     { $$ = "predef"; }
  | TOK_RETURN     { $$ = "return"; }
  | TOK_SSCANF     { $$ = "sscanf"; }
  | TOK_SWITCH     { $$ = "switch"; }
  | TOK_TYPEOF     { $$ = "typeof"; }
  ;

magic_identifiers: magic_identifiers1 | magic_identifiers2 | magic_identifiers3 ;

magic_identifier: TOK_IDENTIFIER 
  | magic_identifiers
  {
    struct pike_string *tmp=make_shared_string($1);
    $$=mkstrnode(tmp);
    free_string(tmp);
  }
  ;

modifiers: modifier_list
 {
   $$=Pike_compiler->current_modifiers=$1 | (lex.pragmas & ID_MODIFIER_MASK);
 }
 ;

modifier_list: /* empty */ { $$ = 0; }
  | modifier modifier_list { $$ = $1 | $2; }
  ;

optional_stars: optional_stars '*' { $$=$1 + 1; }
  | /* empty */ { $$=0; }
  ;

cast: '(' type ')'
    {
      struct pike_type *s = compiler_pop_type();
      $$ = mktypenode(s);
      free_type(s);
    }
    ;

soft_cast: '[' type ']'
    {
      struct pike_type *s = compiler_pop_type();
      $$ = mktypenode(s);
      free_type(s);
    }
    ;

full_type: type4
  | full_type '*'
  {
    if (Pike_compiler->compiler_pass == 2) {
       yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    push_type(T_ARRAY);
  }
  ;

type6: type | identifier_type ;
  
type: type '*'
  {
    if (Pike_compiler->compiler_pass == 2) {
       yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    push_type(T_ARRAY);
  }
  | type2
  ;

type7: type7 '*'
  {
    if (Pike_compiler->compiler_pass == 2) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    push_type(T_ARRAY);
  }
  | type4
  ;

simple_type: type4
  {
    struct pike_type *s = compiler_pop_type();
    $$ = mktypenode(s);
#ifdef PIKE_DEBUG
    if ($$->u.sval.u.type != s) {
      fatal("mktypenode(%p) created node with %p\n", s, $$->u.sval.u.type);
    }
#endif /* PIKE_DEBUG */
    free_type(s);
  }
  ;

simple_type2: type2
  {
    struct pike_type *s = compiler_pop_type();
    $$ = mktypenode(s);
#ifdef PIKE_DEBUG
    if ($$->u.sval.u.type != s) {
      fatal("mktypenode(%p) created node with %p\n", s, $$->u.sval.u.type);
    }
#endif /* PIKE_DEBUG */
    free_type(s);
  }
  ;

simple_identifier_type: identifier_type
  {
    struct pike_type *s = compiler_pop_type();
    $$ = mktypenode(s);
#ifdef PIKE_DEBUG
    if ($$->u.sval.u.type != s) {
      fatal("mktypenode(%p) created node with %p\n", s, $$->u.sval.u.type);
    }
#endif /* PIKE_DEBUG */
    free_type(s);
  }
  ;

type4: type4 '|' type8 { push_type(T_OR); }
  | type8
  ;

type2: type2 '|' type8 { push_type(T_OR); }
  | basic_type 
  ;

type8: basic_type | identifier_type ;

basic_type:
    TOK_FLOAT_ID                      { push_type(T_FLOAT); }
  | TOK_VOID_ID                       { push_type(T_VOID); }
  | TOK_MIXED_ID                      { push_type(T_MIXED); }
  | TOK_STRING_ID                     { push_type(T_STRING); }
  | TOK_INT_ID      opt_int_range     {}
  | TOK_MAPPING_ID  opt_mapping_type  {}
  | TOK_FUNCTION_ID opt_function_type {}
  | TOK_OBJECT_ID   opt_object_type   {}
  | TOK_PROGRAM_ID  opt_object_type   { push_type(T_PROGRAM); }
  | TOK_ARRAY_ID    opt_array_type    { push_type(T_ARRAY); }
  | TOK_MULTISET_ID opt_array_type    { push_type(T_MULTISET); }
  ;

identifier_type: idents
  { 
    resolv_constant($1);

    if (Pike_sp[-1].type == T_TYPE) {
      /* "typedef" */
      push_finished_type(Pike_sp[-1].u.type);
    } else {
      /* object type */
      struct program *p = NULL;

      if (Pike_sp[-1].type == T_OBJECT) {
	if(!Pike_sp[-1].u.object->prog)
	{
	  pop_stack();
	  push_int(0);
	  yyerror("Destructed object used as program identifier.");
	}else{
	  extern void f_object_program(INT32);
	  int f=FIND_LFUN(Pike_sp[-1].u.object->prog,LFUN_CALL);
	  if(f!=-1)
	  {
	    Pike_sp[-1].subtype=f;
	    Pike_sp[-1].type=T_FUNCTION;
	  }else{
	    f_object_program(1);
	  }
	}
      }

      switch(Pike_sp[-1].type) {
	case T_FUNCTION:
        if((p = program_from_function(Pike_sp-1)))
	  push_object_type(0, p?(p->id):0);
	else
	{
	  struct pike_type *a,*b;
	  a=get_type_of_svalue(Pike_sp-1);
	  b=check_call(function_type_string,a,0);
	  push_finished_type(b);
	  free_type(a);
	  free_type(b);
	}
	break;

      
      default:
	if (Pike_compiler->compiler_pass!=1)
	  yyerror("Illegal program identifier.");
	pop_stack();
	push_int(0);
	push_object_type(0, 0);
	break;
	
      case T_PROGRAM:
	p = Pike_sp[-1].u.program;
	push_object_type(0, p?(p->id):0);
	break;
      }
    }
#ifdef USE_PIKE_TYPE
    /* Attempt to name the type. */
    if (Pike_compiler->last_identifier) {
      push_type_name(Pike_compiler->last_identifier);
    }
#endif /* USE_PIKE_TYPE */
    pop_stack();
    free_node($1);
  }
  ;

number_or_maxint: /* Empty */
  {
    $$ = mkintnode(MAX_INT32);
  }
  | TOK_NUMBER
  | '-' TOK_NUMBER
  {
#ifdef PIKE_DEBUG
    if (($2->token != F_CONSTANT) || ($2->u.sval.type != T_INT)) {
      fatal("Unexpected number in negative int-range.\n");
    }
#endif /* PIKE_DEBUG */
    $$ = mkintnode(-($2->u.sval.u.integer));
    free_node($2);
  }
  ;

number_or_minint: /* Empty */
  {
    $$ = mkintnode(MIN_INT32);
  }
  | TOK_NUMBER
  | '-' TOK_NUMBER
  {
#ifdef PIKE_DEBUG
    if (($2->token != F_CONSTANT) || ($2->u.sval.type != T_INT)) {
      fatal("Unexpected number in negative int-range.\n");
    }
#endif /* PIKE_DEBUG */
    $$ = mkintnode(-($2->u.sval.u.integer));
    free_node($2);
  }
  ;

expected_dot_dot: TOK_DOT_DOT
  | TOK_DOT_DOT_DOT
  {
    yyerror("Elipsis ('...') where range indicator ('..') expected.");
  }
  ;

opt_int_range: /* Empty */
  {
    push_int_type(MIN_INT32, MAX_INT32);
  }
  | '(' number_or_minint expected_dot_dot number_or_maxint ')'
  {
    INT32 min = MIN_INT32;
    INT32 max = MAX_INT32;

    /* FIXME: Check that $4 is >= $2. */
    if($4->token == F_CONSTANT && $4->u.sval.type == T_INT)
    {
      max = $4->u.sval.u.integer;
    }

    if($2->token == F_CONSTANT && $2->u.sval.type == T_INT)
    {
      min = $2->u.sval.u.integer;
    }

    push_int_type(min, max);

    free_node($2);
    free_node($4);
  }
  ;

opt_object_type:  /* Empty */ { push_object_type(0, 0); }
  | '(' program_ref ')'
  {
    /* NOTE: On entry, there are two items on the stack:
     *   Pike_sp-2:	Name of the program reference (string).
     *   Pike_sp-1:	The resolved program (program|function|zero).
     */
    struct program *p=program_from_svalue(Pike_sp-1);

    if(!p) {
      if (Pike_compiler->compiler_pass!=1) {
	if ((Pike_sp[-2].type == T_STRING) && (Pike_sp[-2].u.string->len > 0) &&
	    (Pike_sp[-2].u.string->len < 256)) {
	  my_yyerror("Not a valid program specifier: '%s'",
		     Pike_sp[-2].u.string->str);
	} else {
	  yyerror("Not a valid program specifier.");
	}
      }
    }
    push_object_type(0, p?(p->id):0);
#ifdef USE_PIKE_TYPE
    /* Attempt to name the type. */
    if (Pike_sp[-2].type == T_STRING) {
      push_type_name(Pike_sp[-2].u.string);
    }
#endif /* USE_PIKE_TYPE */
    pop_n_elems(2);
  }
  ;

opt_function_type: '('
  {
    type_stack_mark();
#ifndef USE_PIKE_TYPE
    type_stack_mark();
#endif /* !USE_PIKE_TYPE */
  }
  function_type_list optional_dot_dot_dot ':'
  {
    /* Add the many type if there is none. */
    if ($4)
    {
      if (!$3) {
	/* function_type_list ends with a comma, or is empty.
	 * FIXME: Should this be a syntax error or not?
	 */
	if (Pike_compiler->compiler_pass == 1) {
	  yyerror("Missing type before ... .");
	}
#ifndef USE_PIKE_TYPE
	type_stack_reverse();
	type_stack_mark();
#endif /* !USE_PIKE_TYPE */
	push_type(T_MIXED);
      }
    }else{
#ifndef USE_PIKE_TYPE
      type_stack_reverse();
      type_stack_mark();
#endif /* !USE_PIKE_TYPE */
      push_type(T_VOID);
    }
#ifndef USE_PIKE_TYPE
    /* Rotate T_MANY into the proper position. */
    push_type(T_MANY);
    type_stack_reverse();
    type_stack_mark();
#endif /* !USE_PIKE_TYPE */
  }
  type7 ')'
  {
#ifdef USE_PIKE_TYPE
    push_reverse_type(T_MANY);
    Pike_compiler->pike_type_mark_stackp--;
    while (*Pike_compiler->pike_type_mark_stackp+1 <
	   Pike_compiler->type_stackp) {
      push_reverse_type(T_FUNCTION);
    }
#else /* !USE_PIKE_TYPE */
    type_stack_reverse();
    type_stack_reverse();
    push_type(T_FUNCTION); 
#endif /* USE_PIKE_TYPE */
  }
  | /* empty */
  {
   push_type(T_MIXED);
   push_type(T_VOID);
   push_type(T_OR);

   push_type(T_ZERO);
   push_type(T_VOID);
   push_type(T_OR);

   push_type(T_MANY);
#ifndef USE_PIKE_TYPE
   push_type(T_FUNCTION); 
#endif /* !USE_PIKE_TYPE */
  }
  ;

function_type_list: /* Empty */ optional_comma { $$=0; }
  | function_type_list2 optional_comma { $$=!$2; }
  ;

function_type_list2: type7 { $$=1; }
  | function_type_list2 ','
  {
#ifndef USE_PIKE_TYPE
    type_stack_reverse();
    type_stack_mark();
#endif /* !USE_PIKE_TYPE */
  }
  type7
  ;

opt_array_type: '(' type7 ')'
  |  { push_type(T_MIXED); }
  ;

opt_mapping_type: '('
  { 
#ifndef USE_PIKE_TYPE
    type_stack_mark();
    type_stack_mark();
#endif /* !USE_PIKE_TYPE */
  }
  type7 ':'
  { 
#ifndef USE_PIKE_TYPE
    type_stack_reverse();
    type_stack_mark();
#endif /* !USE_PIKE_TYPE */
  }
  type7
  { 
#ifdef USE_PIKE_TYPE
    push_reverse_type(T_MAPPING);
#else /* !USE_PIKE_TYPE */
    type_stack_reverse();
    type_stack_reverse();
    push_type(T_MAPPING);
#endif /* USE_PIKE_TYPE */
  }
  ')'
  | /* empty */ 
  {
    push_type(T_MIXED);
    push_type(T_MIXED);
    push_type(T_MAPPING);
  }
  ;



name_list: new_name
  | name_list ',' new_name
  ;

new_name: optional_stars TOK_IDENTIFIER
  {
    struct pike_type *type;
    push_finished_type(Pike_compiler->compiler_frame->current_type);
    if ($1 && (Pike_compiler->compiler_pass == 2)) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    while($1--) push_type(T_ARRAY);
    type=compiler_pop_type();
    define_variable($2->u.sval.u.string, type,
		    Pike_compiler->current_modifiers);
    free_type(type);
    free_node($2);
  }
  | optional_stars bad_identifier {}
  | optional_stars TOK_IDENTIFIER '='
  {
    struct pike_type *type;
    push_finished_type(Pike_compiler->compiler_frame->current_type);
    if ($1 && (Pike_compiler->compiler_pass == 2)) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    while($1--) push_type(T_ARRAY);
    type=compiler_pop_type();
    if ((Pike_compiler->current_modifiers & ID_EXTERN) &&
	(Pike_compiler->compiler_pass == 1)) {
      yywarning("Extern declared variable has initializer.");
    }
    $<number>$=define_variable($2->u.sval.u.string, type,
			       Pike_compiler->current_modifiers & (~ID_EXTERN));
    free_type(type);
  }
  expr0
  {
    Pike_compiler->init_node=mknode(F_COMMA_EXPR,Pike_compiler->init_node,
		     mkcastnode(void_type_string,
				mknode(F_ASSIGN,$5,
				       mkidentifiernode($<number>4))));
    free_node($2);
  }
  | optional_stars TOK_IDENTIFIER '=' error
  {
    free_node($2);
  }
  | optional_stars TOK_IDENTIFIER '=' TOK_LEX_EOF
  {
    yyerror("Unexpected end of file in variable definition.");
    free_node($2);
  }
  | optional_stars bad_identifier '=' expr0
  {
    free_node($4);
  }
  ;


new_local_name: optional_stars TOK_IDENTIFIER
  {    
    push_finished_type($<n>0->u.sval.u.type);
    if ($1 && (Pike_compiler->compiler_pass == 2)) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    while($1--) push_type(T_ARRAY);
    add_local_name($2->u.sval.u.string, compiler_pop_type(),0);
    $$=mknode(F_ASSIGN,mkintnode(0),mklocalnode(islocal($2->u.sval.u.string),0));
    free_node($2);
  }
  | optional_stars bad_identifier { $$=0; }
  | optional_stars TOK_IDENTIFIER '=' expr0 
  {
    push_finished_type($<n>0->u.sval.u.type);
    if ($1 && (Pike_compiler->compiler_pass == 2)) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    while($1--) push_type(T_ARRAY);
    add_local_name($2->u.sval.u.string, compiler_pop_type(),0);
    $$=mknode(F_ASSIGN,$4,mklocalnode(islocal($2->u.sval.u.string),0));
    free_node($2);
  }
  | optional_stars bad_identifier '=' expr0
  {
    free_node($4);
    $$=0;
  }
  | optional_stars TOK_IDENTIFIER '=' error
  {
    free_node($2);
    /* No yyerok here since we aren't done yet. */
    $$=0;
  }
  | optional_stars TOK_IDENTIFIER '=' TOK_LEX_EOF
  {
    yyerror("Unexpected end of file in local variable definition.");
    free_node($2);
    /* No yyerok here since we aren't done yet. */
    $$=0;
  }
  ;

new_local_name2: TOK_IDENTIFIER
  {
    add_ref($<n>0->u.sval.u.type);
    add_local_name($1->u.sval.u.string, $<n>0->u.sval.u.type, 0);
    $$=mknode(F_ASSIGN,mkintnode(0),mklocalnode(islocal($1->u.sval.u.string),0));
    free_node($1);
  }
  | bad_identifier { $$=0; }
  | TOK_IDENTIFIER '=' safe_expr0
  {
    add_ref($<n>0->u.sval.u.type);
    add_local_name($1->u.sval.u.string, $<n>0->u.sval.u.type, 0);
    $$=mknode(F_ASSIGN,$3, mklocalnode(islocal($1->u.sval.u.string),0));
    free_node($1);
  }
  | bad_identifier '=' safe_expr0 { $$=$3; }
  ;

block:'{'
  {
    $<number>1=Pike_compiler->num_used_modules;
    $<number>$=Pike_compiler->compiler_frame->current_number_of_locals;
  } 
  {
    /* Trick to store more than one number on compiler stack - Hubbe */
    $<number>$=Pike_compiler->compiler_frame->last_block_level;

    if($<number>$ == -1) /* if 'first block' */
      Pike_compiler->compiler_frame->last_block_level=0; /* all variables */
    else
      Pike_compiler->compiler_frame->last_block_level=$<number>2;
  }
  {
    $<number>$=lex.current_line;
  }
  statements end_block
  {
    unuse_modules(Pike_compiler->num_used_modules - $<number>1);
    pop_local_variables($<number>2);
    Pike_compiler->compiler_frame->last_block_level=$<number>3;
    if ($5) $5->line_number = $<number>4;
    $$=$5;
  }
  ;

end_block: '}'
  | TOK_LEX_EOF
  {
    yyerror("Missing '}'.");
    yyerror("Unexpected end of file.");
  }
  ;

failsafe_block: block
              | error { $$=0; }
              | TOK_LEX_EOF { yyerror("Unexpected end of file."); $$=0; }
              ;


local_name_list: new_local_name
  | local_name_list ',' { $<n>$=$<n>0; } new_local_name
    { $$ = mknode(F_COMMA_EXPR, mkcastnode(void_type_string, $1), $4); }
  ;

local_name_list2: new_local_name2
  | local_name_list2 ',' { $<n>$=$<n>0; } new_local_name
    { $$ = mknode(F_COMMA_EXPR, mkcastnode(void_type_string, $1), $4); }
  ;


local_constant_name: TOK_IDENTIFIER '=' safe_expr0
  {
    struct pike_type *type;

    /* Ugly hack to make sure that $3 is optimized */
    {
      int tmp=Pike_compiler->compiler_pass;
      $3=mknode(F_COMMA_EXPR,$3,0);
      optimize_node($3);
      Pike_compiler->compiler_pass=tmp;
      type=$3->u.node.a->type;
    }

    if(!is_const($3))
    {
      if(Pike_compiler->compiler_pass==2)
	yyerror("Constant definition is not constant.");
    }else{
      ptrdiff_t tmp=eval_low($3);
      if(tmp < 1)
      {
	yyerror("Error in constant definition.");
      }else{
	pop_n_elems(DO_NOT_WARN((INT32)(tmp - 1)));
	if($3) free_node($3);
	$3=mksvaluenode(Pike_sp-1);
	type=$3->type;
	pop_stack();
      }
    }
    if(!type) type = mixed_type_string;
    add_ref(type);
    low_add_local_name(Pike_compiler->compiler_frame, /*->previous,*/
		       $1->u.sval.u.string,
		       type, $3);
    free_node($1);
  }
  | bad_identifier '=' safe_expr0 { if ($3) free_node($3); }
  | error '=' safe_expr0 { if ($3) free_node($3); }
  ;

local_constant_list: local_constant_name
  | local_constant_list ',' local_constant_name
  ;

local_constant: TOK_CONSTANT local_constant_list ';'
  | TOK_CONSTANT error ';' { yyerrok; }
  | TOK_CONSTANT error TOK_LEX_EOF
  {
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file.");
  }
  | TOK_CONSTANT error '}' { yyerror("Missing ';'."); }
  ;


statements: { $$=0; }
  | statements statement
  {
    $$ = mknode(F_COMMA_EXPR, $1, mkcastnode(void_type_string, $2));
  }
  ;

statement_with_semicolon: unused2 optional_block
  {
    if($2)
    {
      $$=recursive_add_call_arg($1,$2);
    }else{
      $$=$1;
    }
  }
;

normal_label_statement: statement_with_semicolon
  | import { $$=0; }
  | cond
  | return
  | local_constant { $$=0; }
  | block
  | break expected_semicolon
  | continue expected_semicolon
  | error ';' { reset_type_stack(); $$=0; yyerrok; }
  | error TOK_LEX_EOF
  {
    reset_type_stack();
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file.");
    $$=0;
  }
  | error '}'
  {
    reset_type_stack();
    yyerror("Missing ';'.");
/*    yychar = '}'; */	/* Put the '}' back on the input stream. */
    $$=0;
  }
  | ';' { $$=0; } 
  ;

statement: normal_label_statement
  {
    Pike_compiler->compiler_frame->opt_flags &= ~OPT_CUSTOM_LABELS;
  }
  | while
  | do
  | for
  | foreach
  | switch
  | case
  | default
  | labeled_statement
  ;

labeled_statement: TOK_IDENTIFIER
  {
    $<number>$ = lex.current_line;
    Pike_compiler->compiler_frame->opt_flags &= ~OPT_CUSTOM_LABELS;
  }
  ':' statement
  {
    $$ = mknode(Pike_compiler->compiler_frame->opt_flags & OPT_CUSTOM_LABELS ?
		F_CUSTOM_STMT_LABEL : F_NORMAL_STMT_LABEL,
		$1, $4);

    /* FIXME: This won't be correct if the node happens to be shared.
     * That's an issue to be solved with shared nodes in general,
     * though. */
    $$->line_number = $<number>2;
  }
  ;

optional_label: TOK_IDENTIFIER
  | /* empty */ {$$ = 0;}
  ;

break: TOK_BREAK optional_label { $$=mknode(F_BREAK,$2,0); } ;
default: TOK_DEFAULT ':'  { $$=mknode(F_DEFAULT,0,0); }
  | TOK_DEFAULT
  {
    $$=mknode(F_DEFAULT,0,0); yyerror("Expected ':' after default.");
  }
  ;

continue: TOK_CONTINUE optional_label { $$=mknode(F_CONTINUE,$2,0); } ;

push_compiler_frame1: /* empty */
  {
    push_compiler_frame(SCOPE_LOCAL);
  }
  ;

lambda: TOK_LAMBDA push_compiler_frame1
  {
    debug_malloc_touch(Pike_compiler->compiler_frame->current_return_type);
    if(Pike_compiler->compiler_frame->current_return_type)
      free_type(Pike_compiler->compiler_frame->current_return_type);
    copy_pike_type(Pike_compiler->compiler_frame->current_return_type,
		   any_type_string);
  }
  func_args
  {
    $<number>$ = Pike_compiler->varargs;
    Pike_compiler->varargs = 0;
  }
  failsafe_block
  {
    struct pike_type *type;
    char buf[40];
    int f,e;
    struct pike_string *name;
    
    debug_malloc_touch($6);
    $6=mknode(F_COMMA_EXPR,$6,mknode(F_RETURN,mkintnode(0),0));
    type=find_return_type($6);

    if(type) {
      push_finished_type(type);
      free_type(type);
    } else
      push_type(T_MIXED);
    
    e=$4-1;
    if($<number>5)
    {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
      e--;
      pop_type_stack(T_ARRAY);
    }else{
      push_type(T_VOID);
    }
    Pike_compiler->varargs=0;
    push_type(T_MANY);
    for(; e>=0; e--) {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
#ifdef USE_PIKE_TYPE
      push_type(T_FUNCTION);
#endif /* USE_PIKE_TYPE */
    }
#ifndef USE_PIKE_TYPE
    push_type(T_FUNCTION);
#endif /* !USE_PIKE_TYPE */
    
    type=compiler_pop_type();

    sprintf(buf,"__lambda_%ld_%ld",
	    (long)Pike_compiler->new_program->id,
	    (long)(Pike_compiler->local_class_counter++ & 0xffffffff)); /* OSF/1 cc bug. */
    name=make_shared_string(buf);

#ifdef LAMBDA_DEBUG
    fprintf(stderr, "%d: LAMBDA: %s 0x%08lx 0x%08lx\n",
	    Pike_compiler->compiler_pass, buf, (long)Pike_compiler->new_program->id, Pike_compiler->local_class_counter-1);
#endif /* LAMBDA_DEBUG */
    
    f=dooptcode(name,
		$6,
		type,
		ID_STATIC | ID_PRIVATE | ID_INLINE);

    if(Pike_compiler->compiler_frame->lexical_scope & SCOPE_SCOPED) {
      $$ = mktrampolinenode(f);
    } else {
      $$ = mkidentifiernode(f);
    }
    free_string(name);
    free_type(type);
    pop_compiler_frame();
  }
  | TOK_LAMBDA push_compiler_frame1 error
  {
    pop_compiler_frame();
    $$ = mkintnode(0);
  }
  ;

local_function: TOK_IDENTIFIER push_compiler_frame1 func_args 
  {
    char buf[40];
    struct pike_string *name;
    struct pike_type *type;
    int id,e;
    node *n;
    struct identifier *i=0;

    debug_malloc_touch(Pike_compiler->compiler_frame->current_return_type);
    if(Pike_compiler->compiler_frame->current_return_type)
      free_type(Pike_compiler->compiler_frame->current_return_type);
    copy_pike_type(Pike_compiler->compiler_frame->current_return_type,
		   $<n>0->u.sval.u.type);


    /***/
    push_finished_type(Pike_compiler->compiler_frame->current_return_type);
    
    e=$3-1;
    if(Pike_compiler->varargs)
    {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
      e--;
      Pike_compiler->varargs=0;
      pop_type_stack(T_ARRAY);
    }else{
      push_type(T_VOID);
    }
    push_type(T_MANY);
    for(; e>=0; e--) {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
#ifdef USE_PIKE_TYPE
    push_type(T_FUNCTION);
#endif /* USE_PIKE_TYPE */
    }
#ifndef USE_PIKE_TYPE
    push_type(T_FUNCTION);
#endif /* !USE_PIKE_TYPE */
    
    type=compiler_pop_type();
    /***/

    sprintf(buf,"__lambda_%ld_%ld",
	    (long)Pike_compiler->new_program->id,
	    (long)(Pike_compiler->local_class_counter++ & 0xffffffff)); /* OSF/1 cc bug. */

#ifdef LAMBDA_DEBUG
    fprintf(stderr, "%d: LAMBDA: %s 0x%08lx 0x%08lx\n",
	    Pike_compiler->compiler_pass, buf, (long)Pike_compiler->new_program->id, Pike_compiler->local_class_counter-1);
#endif /* LAMBDA_DEBUG */

    name=make_shared_string(buf);

    id=define_function(name,
		       type,
		       0,
		       IDENTIFIER_PIKE_FUNCTION,
		       0,
		       OPT_SIDE_EFFECT|OPT_EXTERNAL_DEPEND);
    n=0;
#if 0
    if(Pike_compiler->compiler_pass > 1 &&
       (i=ID_FROM_INT(Pike_compiler->new_program, id)))
      if(!(i->identifier_flags & IDENTIFIER_SCOPED))
	n = mkidentifiernode(id);
#endif

    low_add_local_name(Pike_compiler->compiler_frame->previous,
		       $1->u.sval.u.string, type, n);

    $<number>$=id;
    free_string(name);
  }
  failsafe_block
  {
    int localid;
    struct identifier *i=ID_FROM_INT(Pike_compiler->new_program, $<number>4);

    $5=mknode(F_COMMA_EXPR,$5,mknode(F_RETURN,mkintnode(0),0));

    debug_malloc_touch($5);
    dooptcode(i->name,
	      $5,
	      i->type,
	      ID_STATIC | ID_PRIVATE | ID_INLINE);

    i->opt_flags = Pike_compiler->compiler_frame->opt_flags;

    pop_compiler_frame();
    free_node($1);

    /* WARNING: If the local function adds more variables we are screwed */
    /* WARNING2: if add_local_name stops adding local variables at the end,
     *           this has to be fixed.
     */

    localid=Pike_compiler->compiler_frame->current_number_of_locals-1;
    if(Pike_compiler->compiler_frame->variable[localid].def)
    {
      $$=copy_node(Pike_compiler->compiler_frame->variable[localid].def);
    }else{
      if(Pike_compiler->compiler_frame->lexical_scope & 
	 (SCOPE_SCOPE_USED | SCOPE_SCOPED))
      {
	$$ = mknode(F_ASSIGN, mktrampolinenode($<number>3),
		    mklocalnode(localid,0));
      }else{
	$$ = mknode(F_ASSIGN, mkidentifiernode($<number>3),
		    mklocalnode(localid,0));
      }
    }
  }
  | TOK_IDENTIFIER push_compiler_frame1 error
  {
    pop_compiler_frame();
    $$=mkintnode(0);
  }
  ;

local_function2: optional_stars TOK_IDENTIFIER push_compiler_frame1 func_args 
  {
    char buf[40];
    struct pike_string *name;
    struct pike_type *type;
    int id,e;
    node *n;
    struct identifier *i=0;

    /***/
    debug_malloc_touch(Pike_compiler->compiler_frame->current_return_type);
    
    push_finished_type($<n>0->u.sval.u.type);
    if ($1 && (Pike_compiler->compiler_pass == 2)) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    while($1--) push_type(T_ARRAY);

    if(Pike_compiler->compiler_frame->current_return_type)
      free_type(Pike_compiler->compiler_frame->current_return_type);
    Pike_compiler->compiler_frame->current_return_type=compiler_pop_type();

    /***/
    push_finished_type(Pike_compiler->compiler_frame->current_return_type);
    
    e=$4-1;
    if(Pike_compiler->varargs)
    {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
      e--;
      Pike_compiler->varargs=0;
      pop_type_stack(T_ARRAY);
    }else{
      push_type(T_VOID);
    }
    push_type(T_MANY);
    for(; e>=0; e--) {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
#ifdef USE_PIKE_TYPE
      push_type(T_FUNCTION);
#endif /* USE_PIKE_TYPE */
    }
#ifndef USE_PIKE_TYPE
    push_type(T_FUNCTION);
#endif /* !USE_PIKE_TYPE */
    
    type=compiler_pop_type();
    /***/


    sprintf(buf,"__lambda_%ld_%ld",
	    (long)Pike_compiler->new_program->id,
	    (long)(Pike_compiler->local_class_counter++ & 0xffffffff)); /* OSF/1 cc bug. */

#ifdef LAMBDA_DEBUG
    fprintf(stderr, "%d: LAMBDA: %s 0x%08lx 0x%08lx\n",
	    Pike_compiler->compiler_pass, buf, (long)Pike_compiler->new_program->id, Pike_compiler->local_class_counter-1);
#endif /* LAMBDA_DEBUG */

    name=make_shared_string(buf);


    id=define_function(name,
		       type,
		       0,
		       IDENTIFIER_PIKE_FUNCTION,
		       0,
		       OPT_SIDE_EFFECT|OPT_EXTERNAL_DEPEND);
    n=0;
#if 0
    if(Pike_compiler->compiler_pass > 1 &&
       (i=ID_FROM_INT(Pike_compiler->new_program, id)))
      if(!(i->identifier_flags & IDENTIFIER_SCOPED))
	n = mkidentifiernode(id);
#endif

    low_add_local_name(Pike_compiler->compiler_frame->previous,
		       $2->u.sval.u.string, type, n);
    $<number>$=id;
    free_string(name);
  }
  failsafe_block
  {
    int localid;
    struct identifier *i=ID_FROM_INT(Pike_compiler->new_program, $<number>5);

    debug_malloc_touch($6);
    $6=mknode(F_COMMA_EXPR,$6,mknode(F_RETURN,mkintnode(0),0));


    debug_malloc_touch($6);
    dooptcode(i->name,
	      $6,
	      i->type,
	      ID_STATIC | ID_PRIVATE | ID_INLINE);

    i->opt_flags = Pike_compiler->compiler_frame->opt_flags;

    pop_compiler_frame();
    free_node($2);

    /* WARNING: If the local function adds more variables we are screwed */
    /* WARNING2: if add_local_name stops adding local variables at the end,
     *           this has to be fixed.
     */

    localid=Pike_compiler->compiler_frame->current_number_of_locals-1;
    if(Pike_compiler->compiler_frame->variable[localid].def)
    {
      $$=copy_node(Pike_compiler->compiler_frame->variable[localid].def);
    }else{
      if(Pike_compiler->compiler_frame->lexical_scope & 
	 (SCOPE_SCOPE_USED | SCOPE_SCOPED))
      {
        $$ = mknode(F_ASSIGN, mktrampolinenode($<number>5),
	  mklocalnode(localid,0));
      }else{
        $$ = mknode(F_ASSIGN, mkidentifiernode($<number>5),
	  mklocalnode(localid,0));
      }
    }
  }
  | optional_stars TOK_IDENTIFIER push_compiler_frame1 error
  {
    pop_compiler_frame();
    free_node($2);
    $$=mkintnode(0);
  }
  ;

create_arg: modifiers type_or_error optional_stars optional_dot_dot_dot TOK_IDENTIFIER
  {
    struct pike_type *type;

    if (Pike_compiler->varargs) {
      yyerror("Can't define more variables after ...");
    }

    push_finished_type(Pike_compiler->compiler_frame->current_type);
    if ($3 && (Pike_compiler->compiler_pass == 2)) {
      yywarning("The *-syntax in types is obsolete. Use array instead.");
    }
    while($3--) push_type(T_ARRAY);
    if ($4) {
      push_type(T_ARRAY);
      Pike_compiler->varargs = 1;
    }
    type=compiler_pop_type();

    if(islocal($5->u.sval.u.string) >= 0)
      my_yyerror("Variable '%s' appears twice in create argument list.",
		 $5->u.sval.u.string->str);

    /* Add the identifier both globally and locally. */
    define_variable($5->u.sval.u.string, type,
		    Pike_compiler->current_modifiers);
    add_local_name($5->u.sval.u.string, type, 0);

    /* free_type(type); */
    free_node($5);
    $$=0;
  }
  | modifiers type_or_error optional_stars bad_identifier { $$=0; }
  ;

create_arguments2: create_arg { $$ = 1; }
  | create_arguments2 ',' create_arg { $$ = $1 + 1; }
  | create_arguments2 ':' create_arg
  {
    yyerror("Unexpected ':' in create argument list.");
    $$ = $1 + 1;
  }
  ;

create_arguments: /* empty */ optional_comma { $$=0; }
  | create_arguments2 optional_comma
  ;

push_compiler_frame01: /* empty */
  {
    push_compiler_frame(SCOPE_LOCAL);
  }
  ;

optional_create_arguments: /* empty */ { $$ = 0; }
  | '(' push_compiler_frame01 create_arguments close_paren_or_missing
  {
    int e;
    node *create_code = NULL;
    struct pike_type *type = NULL;
    struct pike_string *create_string = NULL;
    int f;

    MAKE_CONSTANT_SHARED_STRING(create_string, "create");

    /* First: Deduce the type for the create() function. */
    push_type(T_VOID); /* Return type. */
    e = $3-1;
    if (Pike_compiler->varargs) {
      /* Varargs */
      push_finished_type(Pike_compiler->compiler_frame->variable[e--].type);
      pop_type_stack(T_ARRAY); /* Pop one level of array. */
      Pike_compiler->varargs = 0;
    } else {
      /* Not varargs. */
      push_type(T_VOID);
    }
    push_type(T_MANY);
    for(; e >= 0; e--) {
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
#ifdef USE_PIKE_TYPE
      push_type(T_FUNCTION);
#endif /* USE_PIKE_TYPE */
    }
#ifndef USE_PIKE_TYPE
    push_type(T_FUNCTION);
#endif /* !USE_PIKE_TYPE */

    type = compiler_pop_type();

    /* Second: Declare the function. */

    Pike_compiler->compiler_frame->current_function_number=
      define_function(create_string, type,
		      ID_STATIC, IDENTIFIER_PIKE_FUNCTION, 0,
		      OPT_SIDE_EFFECT);

    /* Third: Generate the initialization code.
     *
     * global_arg = [type]local_arg;
     * [,..]
     */

    for(e=0; e<$3; e++)
    {
      if(!Pike_compiler->compiler_frame->variable[e].name ||
	 !Pike_compiler->compiler_frame->variable[e].name->len)
      {
	my_yyerror("Missing name for argument %d.",e);
      } else {
	node *local_node = mklocalnode(e, 0);

	/* FIXME: Should probably use some other flag. */
	if ((runtime_options & RUNTIME_CHECK_TYPES) &&
	    (Pike_compiler->compiler_pass == 2) &&
	    (Pike_compiler->compiler_frame->variable[e].type !=
	     mixed_type_string)) {
	  /* fprintf(stderr, "Creating soft cast node for local #%d\n", e);*/

	  /* The following is needed to go around the optimization in
	   * mksoftcastnode().
	   */
	  free_type(local_node->type);
	  copy_pike_type(local_node->type, mixed_type_string);
	  
	  local_node = mksoftcastnode(Pike_compiler->compiler_frame->
				      variable[e].type, local_node);
	}
	create_code =
	  mknode(F_COMMA_EXPR, create_code,
		 mknode(F_ASSIGN, local_node,
			mkidentifiernode(isidentifier(Pike_compiler->
						      compiler_frame->
						      variable[e].name))));
      }
    }

    /* Fourth: Add a return 0; at the end. */

    create_code = mknode(F_COMMA_EXPR,
			 mknode(F_POP_VALUE, create_code, NULL),
			 mknode(F_RETURN, mkintnode(0), NULL));

    /* Fifth: Define the function. */

    f=dooptcode(create_string, check_node_hash(create_code),
		type, ID_STATIC);

#ifdef PIKE_DEBUG
    if(Pike_interpreter.recoveries &&
       Pike_sp-Pike_interpreter.evaluator_stack < Pike_interpreter.recoveries->stack_pointer)
      fatal("Stack error (underflow)\n");

    if(Pike_compiler->compiler_pass == 1 &&
       f!=Pike_compiler->compiler_frame->current_function_number)
      fatal("define_function screwed up! %d != %d\n",
	    f, Pike_compiler->compiler_frame->current_function_number);
#endif

    /* Done. */

    pop_compiler_frame();
    free_node($4);
    free_type(type);
    free_string(create_string);
  }
  ;

failsafe_program: '{' program end_block
                | error { yyerrok; }
                | TOK_LEX_EOF
                {
		  yyerror("End of file where program definition expected.");
		}
                ;

class: modifiers TOK_CLASS optional_identifier
  {
    extern int num_parse_error;
    int num_errors=Pike_compiler->num_parse_error;
    if(!$3)
    {
      struct pike_string *s;
      char buffer[42];
      sprintf(buffer,"__class_%ld_%ld",(long)Pike_compiler->new_program->id,
	      (long)Pike_compiler->local_class_counter++);
      s=make_shared_string(buffer);
      $3=mkstrnode(s);
      free_string(s);
      $1|=ID_STATIC | ID_PRIVATE | ID_INLINE;
    }
    /* fprintf(stderr, "LANGUAGE.YACC: CLASS start\n"); */
    if(Pike_compiler->compiler_pass==1)
    {
      if ($1 & ID_EXTERN) {
	yywarning("Extern declared class definition.");
      }
      low_start_new_program(0, $3->u.sval.u.string,
			    $1,
			    &$<number>$);

      /* fprintf(stderr, "Pass 1: Program %s has id %d\n",
	 $3->u.sval.u.string->str, Pike_compiler->new_program->id); */

      if(lex.current_file)
      {
	store_linenumber(lex.current_line, lex.current_file);
	debug_malloc_name(Pike_compiler->new_program, lex.current_file->str,
			  lex.current_line);
      }
    }else{
      int i;
      struct identifier *id;
      int tmp=Pike_compiler->compiler_pass;
      i=isidentifier($3->u.sval.u.string);
      if(i<0)
      {
	low_start_new_program(Pike_compiler->new_program,0,
			      $1,
			      &$<number>$);
	yyerror("Pass 2: program not defined!");
      }else{
	id=ID_FROM_INT(Pike_compiler->new_program, i);
	if(IDENTIFIER_IS_CONSTANT(id->identifier_flags))
	{
	  struct svalue *s;
	  s=&PROG_FROM_INT(Pike_compiler->new_program,i)->constants[id->func.offset].sval;
	  if(s->type==T_PROGRAM)
	  {
	    low_start_new_program(s->u.program,
				  $3->u.sval.u.string,
				  $1,
				  &$<number>$);

	    /* fprintf(stderr, "Pass 2: Program %s has id %d\n",
	       $3->u.sval.u.string->str, Pike_compiler->new_program->id); */

	  }else{
	    yyerror("Pass 2: constant redefined!");
	    low_start_new_program(Pike_compiler->new_program, 0,
				  $1,
				  &$<number>$);
	  }
	}else{
	  yyerror("Pass 2: class constant no longer constant!");
	  low_start_new_program(Pike_compiler->new_program, 0,
				$1,
				&$<number>$);
	}
      }
      Pike_compiler->compiler_pass=tmp;
    }
    Pike_compiler->num_parse_error=num_errors; /* Kluge to prevent gazillion error messages */
  }
  optional_create_arguments failsafe_program
  {
    struct program *p;
    if(Pike_compiler->compiler_pass == 1)
      p=end_first_pass(0);
    else
      p=end_first_pass(1);

    /* fprintf(stderr, "LANGUAGE.YACC: CLASS end\n"); */

    if(!p) {
      yyerror("Class definition failed.");
    }else{
      free_program(p);
    }

    $$=mkidentifiernode($<number>4);

    free_node($3);
    check_tree($$,0);
  }
  ;

simple_identifier: TOK_IDENTIFIER
  | bad_identifier { $$ = 0; }
  ;

enum_value: /* EMPTY */
  {
    safe_inc_enum();
  }
  | '=' safe_expr0
  {
    pop_stack();

    /* This can be made more lenient in the future */

    /* Ugly hack to make sure that $2 is optimized */
    {
      int tmp=Pike_compiler->compiler_pass;
      $2=mknode(F_COMMA_EXPR,$2,0);
      Pike_compiler->compiler_pass=tmp;
    }

    if(!is_const($2))
    {
      if(Pike_compiler->compiler_pass==2)
	yyerror("Enum definition is not constant.");
      push_int(0);
    } else {
      if(!Pike_compiler->num_parse_error)
      {
	ptrdiff_t tmp=eval_low($2);
	if(tmp < 1)
	{
	  yyerror("Error in enum definition.");
	  push_int(0);
	}else{
	  pop_n_elems(DO_NOT_WARN((INT32)(tmp - 1)));
	}
      } else {
	push_int(0);
      }
    }
    if($2) free_node($2);
  }
  ;

enum_def: /* EMPTY */
  | simple_identifier enum_value
  {
    if ($1) {
      add_constant($1->u.sval.u.string, Pike_sp-1,
		   (Pike_compiler->current_modifiers & ~ID_EXTERN) | ID_INLINE);
    }
    free_node($1);
    /* Update the type. */
    {
      struct pike_type *current = pop_unfinished_type();
      struct pike_type *new = get_type_of_svalue(Pike_sp-1);
      struct pike_type *res = or_pike_types(new, current, 1);
      free_type(current);
      free_type(new);
      type_stack_mark();
      push_finished_type(res);
      free_type(res);
    }
  }
  ;

enum_list: enum_def
  | enum_list ',' enum_def
  ;

enum: modifiers TOK_ENUM
  {
    if ((Pike_compiler->current_modifiers & ID_EXTERN) &&
	(Pike_compiler->compiler_pass == 1)) {
      yywarning("Extern declared enum.");
    }

    push_int(-1);	/* Last enum-value. */
    type_stack_mark();
    push_type(T_ZERO);	/* Joined type so far. */
  }
  optional_identifier '{' enum_list end_block
  {
    struct pike_type *t = pop_unfinished_type();
    pop_stack();
    if ($4) {
      ref_push_type_value(t);
      add_constant($4->u.sval.u.string, Pike_sp-1,
		   (Pike_compiler->current_modifiers & ~ID_EXTERN) | ID_INLINE);
      pop_stack();
      free_node($4);
    }
    $$ = mktypenode(t);
    free_type(t);
  }
  ;

typedef: modifiers TOK_TYPEDEF full_type simple_identifier ';'
  {
    struct pike_type *t = compiler_pop_type();

    if ((Pike_compiler->current_modifiers & ID_EXTERN) &&
	(Pike_compiler->compiler_pass == 1)) {
      yywarning("Extern declared typedef.");
    }

    if ($4) {
      ref_push_type_value(t);
      add_constant($4->u.sval.u.string, Pike_sp-1,
		   (Pike_compiler->current_modifiers & ~ID_EXTERN) | ID_INLINE);
      pop_stack();
      free_node($4);
    }
    free_type(t);
  }
  ;

cond: TOK_IF
  {
    $<number>$=Pike_compiler->compiler_frame->current_number_of_locals;
  }
  {
    /* Trick to store more than one number on compiler stack - Hubbe */
    $<number>$=Pike_compiler->compiler_frame->last_block_level;
    Pike_compiler->compiler_frame->last_block_level=$<number>2;
  }
  '(' safe_comma_expr end_cond statement optional_else_part
  {
    $$=mknode('?',$5,mknode(':',$7,$8));
    $$->line_number=$1;
    $$=mkcastnode(void_type_string, $$);
    $$->line_number=$1;
    pop_local_variables($<number>2);
    Pike_compiler->compiler_frame->last_block_level=$<number>3;
  }
  ;

end_cond: ')'
  | '}' { yyerror("Missing ')'."); }
  | TOK_LEX_EOF
  {
    yyerror("Missing ')'.");
    yyerror("Unexpected end of file.");
  }
  ;

optional_else_part: { $$=0; }
  | TOK_ELSE statement { $$=$2; }
  ;      

safe_lvalue: lvalue
  | error { $$=0; }
  ;

safe_expr0: expr0
  | TOK_LEX_EOF { yyerror("Unexpected end of file."); $$=0; }
  | error { $$=0; }
  ;


foreach_optional_lvalue: /* empty */ { $$=0; }
   | safe_lvalue
   ;

foreach_lvalues:  ',' safe_lvalue { $$=$2; }
  | ';' foreach_optional_lvalue ';' foreach_optional_lvalue
  { $$=mknode(':',$2,$4); }
  ;

foreach: TOK_FOREACH
  {
    $<number>$=Pike_compiler->compiler_frame->current_number_of_locals;
  }
  {
    /* Trick to store more than one number on compiler stack - Hubbe */
    $<number>$=Pike_compiler->compiler_frame->last_block_level;
    Pike_compiler->compiler_frame->last_block_level=$<number>2;
  }
  '(' expr0 foreach_lvalues end_cond statement
  {
    if ($6) {
      $$=mknode(F_FOREACH,
		mknode(F_VAL_LVAL,$5,$6),
		$8);
      $$->line_number=$1;
    } else {
      /* Error in lvalue */
      free_node($5);
      $$=$8;
    }
    pop_local_variables($<number>2);
    Pike_compiler->compiler_frame->last_block_level=$<number>3;
    Pike_compiler->compiler_frame->opt_flags |= OPT_CUSTOM_LABELS;
  }
  ;

do: TOK_DO statement TOK_WHILE '(' safe_comma_expr end_cond expected_semicolon
  {
    $$=mknode(F_DO,$2,$5);
    $$->line_number=$1;
    Pike_compiler->compiler_frame->opt_flags |= OPT_CUSTOM_LABELS;
  }
  | TOK_DO statement TOK_WHILE TOK_LEX_EOF
  {
    $$=0;
    yyerror("Missing '(' in do-while loop.");
    yyerror("Unexpected end of file.");
  }
  | TOK_DO statement TOK_LEX_EOF
  {
    $$=0;
    yyerror("Missing 'while' in do-while loop.");
    yyerror("Unexpected end of file.");
  }
  ;

expected_semicolon: ';'
  | TOK_LEX_EOF
  {
    yyerror("Missing ';'.");
    yyerror("Unexpected end of file.");
  }
  ;

for: TOK_FOR
  {
    $<number>$=Pike_compiler->compiler_frame->current_number_of_locals;
  }
  {
    /* Trick to store more than one number on compiler stack - Hubbe */
    $<number>$=Pike_compiler->compiler_frame->last_block_level;
    Pike_compiler->compiler_frame->last_block_level=$<number>2;
  }
  '(' unused expected_semicolon for_expr expected_semicolon unused end_cond
  statement
  {
    int i=lex.current_line;
    lex.current_line=$1;
    $$=mknode(F_COMMA_EXPR, mkcastnode(void_type_string, $5),
	      mknode(F_FOR,$7,mknode(':',$11,$9)));
    lex.current_line=i;
    pop_local_variables($<number>2);
    Pike_compiler->compiler_frame->last_block_level=$<number>3;
    Pike_compiler->compiler_frame->opt_flags |= OPT_CUSTOM_LABELS;
  }
  ;


while:  TOK_WHILE
  {
    $<number>$=Pike_compiler->compiler_frame->current_number_of_locals;
  }
  {
    /* Trick to store more than one number on compiler stack - Hubbe */
    $<number>$=Pike_compiler->compiler_frame->last_block_level;
    Pike_compiler->compiler_frame->last_block_level=$<number>2;
  }
  '(' safe_comma_expr end_cond statement
  {
    int i=lex.current_line;
    lex.current_line=$1;
    $$=mknode(F_FOR,$5,mknode(':',$7,NULL));
    lex.current_line=i;
    pop_local_variables($<number>2);
    Pike_compiler->compiler_frame->last_block_level=$<number>3;
    Pike_compiler->compiler_frame->opt_flags |= OPT_CUSTOM_LABELS;
  }
  ;

for_expr: /* EMPTY */ { $$=mkintnode(1); }
  | safe_comma_expr
  ;

switch:	TOK_SWITCH
  {
    $<number>$=Pike_compiler->compiler_frame->current_number_of_locals;
  }
  {
    /* Trick to store more than one number on compiler stack - Hubbe */
    $<number>$=Pike_compiler->compiler_frame->last_block_level;
    Pike_compiler->compiler_frame->last_block_level=$<number>2;
  }
  '(' safe_comma_expr end_cond statement
  {
    $$=mknode(F_SWITCH,$5,$7);
    $$->line_number=$1;
    pop_local_variables($<number>2);
    Pike_compiler->compiler_frame->last_block_level=$<number>3;
  }
  ;

case: TOK_CASE safe_comma_expr expected_colon
  {
    $$=mknode(F_CASE,$2,0);
  }
  | TOK_CASE safe_comma_expr expected_dot_dot optional_comma_expr expected_colon
  {
     $$=mknode(F_CASE,$4?$2:0,$4?$4:$2);
  }
  ;

expected_colon: ':'
  | ';'
  {
    yyerror("Missing ':'.");
  }
  | '}'
  {
    yyerror("Missing ':'.");
  }
  | TOK_LEX_EOF
  {
    yyerror("Missing ':'.");
    yyerror("Unexpected end of file.");
  }
  ;

return: TOK_RETURN expected_semicolon
  {
    if(!TEST_COMPAT(0,6) &&
       !match_types(Pike_compiler->compiler_frame->current_return_type,
		    void_type_string))
    {
      yyerror("Must return a value for a non-void function.");
    }
    $$=mknode(F_RETURN,mkintnode(0),0);
  }
  | TOK_RETURN safe_comma_expr expected_semicolon
  {
    $$=mknode(F_RETURN,$2,0);
  }
  ;
	
unused: { $$=0; }
  | safe_comma_expr { $$=mkcastnode(void_type_string, $1);  }
  ;

unused2: comma_expr { $$=mkcastnode(void_type_string, $1);  } ;

optional_comma_expr: { $$=0; }
  | safe_comma_expr
  ;

safe_comma_expr: comma_expr
  | error { $$=0; }
  ;

comma_expr: comma_expr2
  | simple_type2 local_name_list { $$=$2; free_node($1); }
  | simple_identifier_type local_name_list2 { $$=$2; free_node($1); }
  | simple_identifier_type local_function { $$=$2; free_node($1); }
  | simple_type2 local_function2 { $$=$2; free_node($1); }
  ;
          

comma_expr2: expr0
  | comma_expr2 ',' expr0
  {
    $$ = mknode(F_COMMA_EXPR, mkcastnode(void_type_string, $1), $3); 
  }
  ;

expr00: expr0
      | '@' expr0 { $$=mknode(F_PUSH_ARRAY,$2,0); };

expr0: expr01
  | expr4 '=' expr0  { $$=mknode(F_ASSIGN,$3,$1); }
  | expr4 '=' error { $$=$1; reset_type_stack(); yyerrok; }
  | bad_expr_ident '=' expr0 { $$=$3; }
  | '[' low_lvalue_list ']' '=' expr0  { $$=mknode(F_ASSIGN,$5,mknode(F_ARRAY_LVALUE,$2,0)); }
  | expr4 assign expr0  { $$=mknode($2,$1,$3); }
  | expr4 assign error { $$=$1; reset_type_stack(); yyerrok; }
  | bad_expr_ident assign expr0 { $$=$3; }
  | '[' low_lvalue_list ']' assign expr0  { $$=mknode($4,mknode(F_ARRAY_LVALUE,$2,0),$5); }
  | '[' low_lvalue_list ']' error { $$=$2; reset_type_stack(); yyerrok; }
/*  | error { $$=0; reset_type_stack(); } */
  ;

expr01: expr1
  | expr1 '?' expr01 ':' expr01 { $$=mknode('?',$1,mknode(':',$3,$5)); }
  ;

assign: TOK_AND_EQ { $$=F_AND_EQ; }
  | TOK_OR_EQ  { $$=F_OR_EQ; }
  | TOK_XOR_EQ { $$=F_XOR_EQ; }
  | TOK_LSH_EQ { $$=F_LSH_EQ; }
  | TOK_RSH_EQ { $$=F_RSH_EQ; }
  | TOK_ADD_EQ { $$=F_ADD_EQ; }
  | TOK_SUB_EQ { $$=F_SUB_EQ; }
  | TOK_MULT_EQ{ $$=F_MULT_EQ; }
  | TOK_MOD_EQ { $$=F_MOD_EQ; }
  | TOK_DIV_EQ { $$=F_DIV_EQ; }
  ;

optional_comma: { $$=0; } | ',' { $$=1; };

expr_list: { $$=0; }
  | expr_list2 optional_comma
  ;
         

expr_list2: expr00
  | expr_list2 ',' expr00 { $$=mknode(F_ARG_LIST,$1,$3); }
  ;

m_expr_list: { $$=0; }
  | m_expr_list2 optional_comma
  ;

m_expr_list2: assoc_pair
  | m_expr_list2 ',' assoc_pair
  {
    if ($3) {
      $$=mknode(F_ARG_LIST,$1,$3);
    } else {
      /* Error in assoc_pair */
      $$=$1;
    }
  }
  | m_expr_list2 ',' error
  ;

assoc_pair:  expr0 expected_colon expr0 { $$=mknode(F_ARG_LIST,$1,$3); }
  | expr0 expected_colon error { free_node($1); $$=0; }
  ;

expr1: expr2
  | expr1 TOK_LOR expr1  { $$=mknode(F_LOR,$1,$3); }
  | expr1 TOK_LAND expr1 { $$=mknode(F_LAND,$1,$3); }
  | expr1 '|' expr1    { $$=mkopernode("`|",$1,$3); }
  | expr1 '^' expr1    { $$=mkopernode("`^",$1,$3); }
  | expr1 '&' expr1    { $$=mkopernode("`&",$1,$3); }
  | expr1 TOK_EQ expr1   { $$=mkopernode("`==",$1,$3); }
  | expr1 TOK_NE expr1   { $$=mkopernode("`!=",$1,$3); }
  | expr1 '>' expr1    { $$=mkopernode("`>",$1,$3); }
  | expr1 TOK_GE expr1   { $$=mkopernode("`>=",$1,$3); }
  | expr1 '<' expr1    { $$=mkopernode("`<",$1,$3); }
  | expr1 TOK_LE expr1   { $$=mkopernode("`<=",$1,$3); }
  | expr1 TOK_LSH expr1  { $$=mkopernode("`<<",$1,$3); }
  | expr1 TOK_RSH expr1  { $$=mkopernode("`>>",$1,$3); }
  | expr1 '+' expr1    { $$=mkopernode("`+",$1,$3); }
  | expr1 '-' expr1    { $$=mkopernode("`-",$1,$3); }
  | expr1 '*' expr1    { $$=mkopernode("`*",$1,$3); }
  | expr1 '%' expr1    { $$=mkopernode("`%",$1,$3); }
  | expr1 '/' expr1    { $$=mkopernode("`/",$1,$3); }
  | expr1 TOK_LOR error 
  | expr1 TOK_LAND error
  | expr1 '|' error   
  | expr1 '^' error   
  | expr1 '&' error   
  | expr1 TOK_EQ error  
  | expr1 TOK_NE error  
  | expr1 '>' error   
  | expr1 TOK_GE error  
  | expr1 '<' error   
  | expr1 TOK_LE error  
  | expr1 TOK_LSH error 
  | expr1 TOK_RSH error 
  | expr1 '+' error   
  | expr1 '-' error   
  | expr1 '*' error   
  | expr1 '%' error   
  | expr1 '/' error
  ;

expr2: expr3
  | cast expr2
  {
    $$ = mkcastnode($1->u.sval.u.type, $2);
    free_node($1);
  }
  | soft_cast expr2
  {
    $$ = mksoftcastnode($1->u.sval.u.type, $2);
    free_node($1);
  }
  | TOK_INC expr4       { $$=mknode(F_INC,$2,0); }
  | TOK_DEC expr4       { $$=mknode(F_DEC,$2,0); }
  | TOK_NOT expr2        { $$=mkopernode("`!",$2,0); }
  | '~' expr2          { $$=mkopernode("`~",$2,0); }
  | '-' expr2          { $$=mkopernode("`-",$2,0); }
  ;

expr3: expr4
  | expr4 TOK_INC       { $$=mknode(F_POST_INC,$1,0); }
  | expr4 TOK_DEC       { $$=mknode(F_POST_DEC,$1,0); }
  ;

/* FIXMEs
 * It would be nice if 'return' would exit from
 * the surrounding function rather than from the
 * implicit lambda. (I think) So beware that the
 * behaviour of 'return' might change some day.
 * -Hubbe
 *
 * It would also be nice if it was possible to send
 * arguments to the implicit function, but it would
 * require using ugly implicit variables or extending
 * the syntax, and if you extend the syntax you might
 * as well use lambda() instead.
 * -Hubbe
 *
 * We might want to allow having more than block after
 * a function ( ie. func(args) {} {} {} {} )
 * -Hubbe
 */

optional_block: ';' /* EMPTY */ { $$=0; }
  | '{' push_compiler_frame0
  {
    debug_malloc_touch(Pike_compiler->compiler_frame->current_return_type);
    if(Pike_compiler->compiler_frame->current_return_type)
      free_type(Pike_compiler->compiler_frame->current_return_type);
    copy_pike_type(Pike_compiler->compiler_frame->current_return_type,
		   any_type_string);

    /* block code */
    $<number>1=Pike_compiler->num_used_modules;
    $<number>$=Pike_compiler->compiler_frame->current_number_of_locals;
  }
  statements end_block
  {
    struct pike_type *type;
    char buf[40];
    int f/*, e */;
    struct pike_string *name;

    /* block code */
    unuse_modules(Pike_compiler->num_used_modules - $<number>1);
    pop_local_variables($<number>3);

    debug_malloc_touch($4);
    $4=mknode(F_COMMA_EXPR,$4,mknode(F_RETURN,mkintnode(0),0));

    type=find_return_type($4);

    if(type) {
      push_finished_type(type);
      free_type(type);
    } else
      push_type(T_MIXED);
    
    push_type(T_VOID);
    push_type(T_MANY);
/*
    e=$4-1;
    for(; e>=0; e--)
      push_finished_type(Pike_compiler->compiler_frame->variable[e].type);
*/
    
#ifndef USE_PIKE_TYPE
    push_type(T_FUNCTION);
#endif /* !USE_PIKE_TYPE */
    type=compiler_pop_type();

    sprintf(buf,"__lambda_%ld_%ld",
	    (long)Pike_compiler->new_program->id,
	    (long)(Pike_compiler->local_class_counter++ & 0xffffffff)); /* OSF/1 cc bug. */
    name=make_shared_string(buf);

#ifdef LAMBDA_DEBUG
    fprintf(stderr, "%d: IMPLICIT LAMBDA: %s 0x%08lx 0x%08lx\n",
	    Pike_compiler->compiler_pass, buf, (long)Pike_compiler->new_program->id, Pike_compiler->local_class_counter-1);
#endif /* LAMBDA_DEBUG */
    
    f=dooptcode(name,
		$4,
		type,
		ID_STATIC | ID_PRIVATE | ID_INLINE);

    if(Pike_compiler->compiler_frame->lexical_scope & SCOPE_SCOPED) {
      $$ = mktrampolinenode(f);
    } else {
      $$ = mkidentifiernode(f);
    }
    free_string(name);
    free_type(type);
    pop_compiler_frame();
  }
  ;

expr4: string
  | TOK_NUMBER 
  | TOK_FLOAT { $$=mkfloatnode((FLOAT_TYPE)$1); }
  | catch
  | gauge
  | typeof
  | sscanf
  | lambda
  | class
  | enum
  | idents2
  | expr4 '(' expr_list  ')'
    {
      $$=mkapplynode($1,$3);
    }
  | expr4 '(' error ')' { $$=mkapplynode($1, NULL); yyerrok; }
  | expr4 '(' error TOK_LEX_EOF
  {
    yyerror("Missing ')'."); $$=mkapplynode($1, NULL);
    yyerror("Unexpected end of file.");
  }
  | expr4 '(' error ';' { yyerror("Missing ')'."); $$=mkapplynode($1, NULL); }
  | expr4 '(' error '}' { yyerror("Missing ')'."); $$=mkapplynode($1, NULL); }
  | expr4 '[' expr0 ']' { $$=mknode(F_INDEX,$1,$3); }
  | expr4 '['  comma_expr_or_zero expected_dot_dot comma_expr_or_maxint ']'
  {
    $$=mknode(F_RANGE,$1,mknode(F_ARG_LIST,$3,$5));
  }
  | expr4 '[' error ']' { $$=$1; yyerrok; }
  | expr4 '[' error TOK_LEX_EOF
  {
    $$=$1; yyerror("Missing ']'.");
    yyerror("Unexpected end of file.");
  }
  | expr4 '[' error ';' { $$=$1; yyerror("Missing ']'."); }
  | expr4 '[' error '}' { $$=$1; yyerror("Missing ']'."); }
  | expr4 '[' error ')' { $$=$1; yyerror("Missing ']'."); }
  | '(' comma_expr2 ')' { $$=$2; }
  | '(' '{' expr_list close_brace_or_missing ')'
    { $$=mkefuncallnode("aggregate",$3); }
  | '(' '[' m_expr_list close_bracket_or_missing ')'
    { $$=mkefuncallnode("aggregate_mapping",$3); }
  | TOK_MULTISET_START expr_list TOK_MULTISET_END
    { $$=mkefuncallnode("aggregate_multiset",$2); }
  | TOK_MULTISET_START expr_list ')'
    {
      yyerror("Missing '>'.");
      $$=mkefuncallnode("aggregate_multiset",$2);
    }
  | '(' error ')' { $$=0; yyerrok; }
  | '(' error TOK_LEX_EOF
  {
    $$=0; yyerror("Missing ')'.");
    yyerror("Unexpected end of file.");
  }
  | '(' error ';' { $$=0; yyerror("Missing ')'."); }
  | '(' error '}' { $$=0; yyerror("Missing ')'."); }
  | TOK_MULTISET_START error TOK_MULTISET_END { $$=0; yyerrok; }
  | TOK_MULTISET_START error ')' {
    yyerror("Missing '>'.");
    $$=0; yyerrok;
  }
  | TOK_MULTISET_START error TOK_LEX_EOF
  {
    $$=0; yyerror("Missing '>)'.");
    yyerror("Unexpected end of file.");
  }
  | TOK_MULTISET_START error ';' { $$=0; yyerror("Missing '>)'."); }
  | TOK_MULTISET_START error '}' { $$=0; yyerror("Missing '>)'."); }
  | expr4 TOK_ARROW magic_identifier
  {
    $$=mknode(F_ARROW,$1,$3);
  }
  | expr4 TOK_ARROW error {}
  ;

idents2: idents
  | TOK_LOCAL_ID TOK_COLON_COLON TOK_IDENTIFIER
  {
    int i;

    if(Pike_compiler->last_identifier) free_string(Pike_compiler->last_identifier);
    copy_shared_string(Pike_compiler->last_identifier, $3->u.sval.u.string);

    if (((i = find_shared_string_identifier(Pike_compiler->last_identifier,
					    Pike_compiler->new_program)) >= 0) ||
	((i = really_low_find_shared_string_identifier(Pike_compiler->last_identifier,
						       Pike_compiler->new_program,
						       SEE_STATIC|
						       SEE_PRIVATE)) >= 0)) {
      if (!(Pike_compiler->new_program->identifier_references[i].id_flags & ID_HIDDEN)) {
	/* We need to generate a new reference. */
	int d;
	struct reference funp = Pike_compiler->new_program->identifier_references[i];
	funp.id_flags |= ID_HIDDEN;
	i = -1;
	for(d = 0; d < (int)Pike_compiler->new_program->num_identifier_references; d++) {
	  struct reference *refp;
	  refp = Pike_compiler->new_program->identifier_references + d;

	  if(!MEMCMP((char *)refp,(char *)&funp,sizeof funp)) {
	    i = d;
	    break;
	  }
	}
	if (i < 0) {
	  add_to_identifier_references(funp);
	  i = Pike_compiler->new_program->num_identifier_references - 1;
	}
      }
      $$ = mkidentifiernode(i);
    } else {
      if (!Pike_compiler->num_parse_error) {
	if (Pike_compiler->compiler_pass == 2) {
	  my_yyerror("'%s' not defined in local scope.", Pike_compiler->last_identifier->str);
	  $$ = 0;
	} else {
	  $$ = mknode(F_UNDEFINED, 0, 0);
	}
      } else {
	$$ = mkintnode(0);
      }
    }

    free_node($3);
  }
  | TOK_LOCAL_ID TOK_COLON_COLON bad_identifier
  {
    $$=0;
  }
  ;

idents: low_idents
  | idents '.' TOK_IDENTIFIER
  {
    $$=index_node($1, Pike_compiler->last_identifier?Pike_compiler->last_identifier->str:NULL,
		  $3->u.sval.u.string);
    free_node($1);
    if(Pike_compiler->last_identifier) free_string(Pike_compiler->last_identifier);
    copy_shared_string(Pike_compiler->last_identifier, $3->u.sval.u.string);
    free_node($3);
  }
  | '.' TOK_IDENTIFIER
  {
    node *tmp;
    push_text(".");
    ref_push_string(lex.current_file);
    if (error_handler && error_handler->prog) {
      ref_push_object(error_handler);
      SAFE_APPLY_MASTER("handle_import", 3);
    } else {
      SAFE_APPLY_MASTER("handle_import", 2);
    }
    tmp=mkconstantsvaluenode(Pike_sp-1);
    pop_stack();
    $$=index_node(tmp, ".", $2->u.sval.u.string);
    free_node(tmp);
    if(Pike_compiler->last_identifier) free_string(Pike_compiler->last_identifier);
    copy_shared_string(Pike_compiler->last_identifier, $2->u.sval.u.string);
    free_node($2);
  }
  | idents '.' bad_identifier {}
  | idents '.' error {}
  ;

inherit_specifier: TOK_IDENTIFIER TOK_COLON_COLON
  {
    int e = 0;

    inherit_state = Pike_compiler;

    for (inherit_depth = -1; inherit_depth < compilation_depth;
	 inherit_depth++, inherit_state = inherit_state->previous) {
      if ((e = find_inherit(inherit_state->new_program, $1->u.sval.u.string)))
	break;
    }
    if (!e) {
      my_yyerror("No such inherit %s.", $1->u.sval.u.string->str);
    }
    free_node($1);
    $$ = e;
  }
  | inherit_specifier TOK_IDENTIFIER TOK_COLON_COLON
  {
    if ($1) {
      int e = 0;
#if 0
      /* FIXME: The inherit modifiers aren't kept. */
      if (!(inherit_state->new_program->inherits[$1].flags & ID_PRIVATE)) {
#endif /* 0 */
	e = find_inherit(inherit_state->new_program->inherits[$1].prog,
			 $2->u.sval.u.string);
#if 0
      }
#endif /* 0 */
      if (!e) {
	if (inherit_state->new_program->inherits[$1].name) {
	  my_yyerror("No such inherit %s::%s.",
		     inherit_state->new_program->inherits[$1].name->str,
		     $2->u.sval.u.string->str);
	} else {
	  my_yyerror("No such inherit %s.", $2->u.sval.u.string->str);
	}
	$$ = 0;
      } else {
	/* We know stuff about the inherit structure... */
	$$ = e + $1;
      }
    }
    free_node($2);
  }
  | inherit_specifier bad_identifier TOK_COLON_COLON { $$ = 0; }
  ;

low_idents: TOK_IDENTIFIER
  {
    int i;

    if(Pike_compiler->last_identifier) free_string(Pike_compiler->last_identifier);
    copy_shared_string(Pike_compiler->last_identifier, $1->u.sval.u.string);

    if(($$=lexical_islocal(Pike_compiler->last_identifier)))
    {
      /* done, nothing to do here */
    }else if((i=isidentifier(Pike_compiler->last_identifier))>=0){
      $$=mkidentifiernode(i);
    }else if(!($$=find_module_identifier(Pike_compiler->last_identifier,1))){
      if(!Pike_compiler->num_parse_error)
      {
	if(Pike_compiler->compiler_pass==2)
	{
	  my_yyerror("'%s' undefined.", Pike_compiler->last_identifier->str);
	  $$=0;
	}else{
	  $$=mknode(F_UNDEFINED,0,0);
	}
      }else{
	$$=mkintnode(0);
      }
    }
    free_node($1);
  }
  | TOK_PREDEF TOK_COLON_COLON TOK_IDENTIFIER
  {
    node *tmp2;
    extern dynamic_buffer used_modules;

    if(Pike_compiler->last_identifier) free_string(Pike_compiler->last_identifier);
    copy_shared_string(Pike_compiler->last_identifier, $3->u.sval.u.string);

    tmp2=mkconstantsvaluenode((struct svalue *) used_modules.s.str );
    $$=index_node(tmp2, "predef", $3->u.sval.u.string);
    if(!$$->name)
      add_ref( $$->name=$3->u.sval.u.string );
    free_node(tmp2);
    free_node($3);
  }
  | TOK_PREDEF TOK_COLON_COLON bad_identifier
  {
    $$=0;
  }
  | inherit_specifier TOK_IDENTIFIER
  {
    if ($1) {
      int id;

      if(Pike_compiler->last_identifier) free_string(Pike_compiler->last_identifier);
      copy_shared_string(Pike_compiler->last_identifier, $2->u.sval.u.string);

      id = low_reference_inherited_identifier(inherit_state,
					      $1,
					      Pike_compiler->last_identifier,
					      SEE_STATIC);

      if (id != -1) {
	if (inherit_depth >= 0) {
	  $$ = mkexternalnode(inherit_state->new_program, id);
	} else {
	  $$ = mkidentifiernode(id);
	}
      } else if(ISCONSTSTR(Pike_compiler->last_identifier, "`->") ||
		ISCONSTSTR(Pike_compiler->last_identifier, "`[]")) {
	$$ = mknode(F_MAGIC_INDEX, mknewintnode($1),
		    mknewintnode(inherit_depth+1));
      } else if(ISCONSTSTR(Pike_compiler->last_identifier, "`->=") ||
		ISCONSTSTR(Pike_compiler->last_identifier, "`[]=")) {
	$$ = mknode(F_MAGIC_SET_INDEX, mknewintnode($1),
		    mknewintnode(inherit_depth+1));
      } else {
	if (inherit_state->new_program->inherits[$1].name) {
	  my_yyerror("Undefined identifier %s::%s.",
		     inherit_state->new_program->inherits[$1].name->str,
		     Pike_compiler->last_identifier->str);
	} else {
	  my_yyerror("Undefined identifier %s.", Pike_compiler->last_identifier->str);
	}
	$$=0;
      }
    } else {
      $$=0;
    }

    free_node($2);
  }
  | inherit_specifier bad_identifier { $$=0; }
  | inherit_specifier error { $$=0; }
  | TOK_COLON_COLON TOK_IDENTIFIER
  {
    int e,i;

    if(Pike_compiler->last_identifier) free_string(Pike_compiler->last_identifier);
    copy_shared_string(Pike_compiler->last_identifier, $2->u.sval.u.string);

    $$=0;
    for(e=1;e<(int)Pike_compiler->new_program->num_inherits;e++)
    {
      if(Pike_compiler->new_program->inherits[e].inherit_level!=1) continue;
      i=low_reference_inherited_identifier(0,e,$2->u.sval.u.string,SEE_STATIC);
      if(i==-1) continue;
      if($$)
      {
	$$=mknode(F_ARG_LIST,$$,mkidentifiernode(i));
      }else{
	$$=mkidentifiernode(i);
      }
    }
    if(!$$)
    {
	if(ISCONSTSTR($2->u.sval.u.string,"`->") ||
	   ISCONSTSTR($2->u.sval.u.string,"`[]") )
	{
	  $$=mknode(F_MAGIC_INDEX,mknewintnode(0),mknewintnode(0));
	}
	else if(ISCONSTSTR($2->u.sval.u.string,"`->=") ||
		ISCONSTSTR($2->u.sval.u.string,"`[]=") )
	{
	  $$=mknode(F_MAGIC_SET_INDEX,mknewintnode(0),mknewintnode(0));
	}
	else
	{
	  $$=mkintnode(0);
	}
    }else{
      if($$->token==F_ARG_LIST) $$=mkefuncallnode("aggregate",$$);
    }
    free_node($2);
  }
  | TOK_COLON_COLON bad_identifier
  {
    $$=0;
  }
  ;

comma_expr_or_zero: /* empty */ { $$=mkintnode(0); }
  | comma_expr
  | TOK_LEX_EOF { yyerror("Unexpected end of file."); $$=0; }
  ;

comma_expr_or_maxint: /* empty */ { $$=mkintnode(0x7fffffff); }
  | comma_expr
  | TOK_LEX_EOF { yyerror("Unexpected end of file."); $$=mkintnode(0x7fffffff); }
  ;

gauge: TOK_GAUGE catch_arg
  {
#ifdef HAVE_GETHRVTIME
    $$=mkefuncallnode("abs",
		  mkopernode("`/", 
			     mkopernode("`-", mkefuncallnode("gethrvtime",0),
					mknode(F_COMMA_EXPR,
					       mknode(F_POP_VALUE, $2, NULL),
					       mkefuncallnode("gethrvtime",0))),
			     mkfloatnode((FLOAT_TYPE)1000000.0)));
#else
  $$=mkefuncallnode("abs",
	mkopernode("`/", 
		mkopernode("`-",
			 mknode(F_INDEX,mkefuncallnode("rusage",0),
				mkintnode(GAUGE_RUSAGE_INDEX)),
			   mknode(F_COMMA_EXPR, mknode(F_POP_VALUE, $2, NULL),
				mknode(F_INDEX,mkefuncallnode("rusage",0),
				       mkintnode(GAUGE_RUSAGE_INDEX)))),
		mkfloatnode((FLOAT_TYPE)1000.0)));
#endif
  };

typeof: TOK_TYPEOF '(' expr0 ')'
  {
    struct pike_type *t;
    node *tmp;

    /* FIXME: Why build the node at all? */
    /* Because the optimizer cannot optimize the root node of the
     * tree properly -Hubbe
     */
    tmp=mknode(F_COMMA_EXPR, $3, 0);
    optimize_node(tmp);

    t=(tmp && CAR(tmp) && CAR(tmp)->type ? CAR(tmp)->type : mixed_type_string);
    if(TEST_COMPAT(7,0))
    {
      struct pike_string *s=describe_type(t);
      $$ = mkstrnode(s);
      free_string(s);
    }else{
      $$ = mktypenode(t);
    }
    free_node(tmp);
  } 
  | TOK_TYPEOF '(' error ')' { $$=0; yyerrok; }
  | TOK_TYPEOF '(' error '}' { $$=0; yyerror("Missing ')'."); }
  | TOK_TYPEOF '(' error TOK_LEX_EOF
  {
    $$=0; yyerror("Missing ')'.");
    yyerror("Unexpected end of file.");
  }
  | TOK_TYPEOF '(' error ';' { $$=0; yyerror("Missing ')'."); }
  ;
 
catch_arg: '(' comma_expr ')'  { $$=$2; }
  | '(' error ')' { $$=0; yyerrok; }
  | '(' error TOK_LEX_EOF
  {
    $$=0; yyerror("Missing ')'.");
    yyerror("Unexpected end of file.");
  }
  | '(' error '}' { $$=0; yyerror("Missing ')'."); }
  | '(' error ';' { $$=0; yyerror("Missing ')'."); }
  | block
  | error { $$=0; yyerror("Bad expression for catch."); }
  ; 

catch: TOK_CATCH
     {
       Pike_compiler->catch_level++;
     }
     catch_arg
     {
       $$=mknode(F_CATCH,$3,NULL);
       Pike_compiler->catch_level--;
     }
     ;

sscanf: TOK_SSCANF '(' expr0 ',' expr0 lvalue_list ')'
  {
    $$=mknode(F_SSCANF,mknode(F_ARG_LIST,$3,$5),$6);
  }
  | TOK_SSCANF '(' expr0 ',' expr0 error ')'
  {
    $$=0;
    free_node($3);
    free_node($5);
    yyerrok;
  }
  | TOK_SSCANF '(' expr0 ',' expr0 error TOK_LEX_EOF
  {
    $$=0;
    free_node($3);
    free_node($5);
    yyerror("Missing ')'.");
    yyerror("Unexpected end of file.");
  }
  | TOK_SSCANF '(' expr0 ',' expr0 error '}'
  {
    $$=0;
    free_node($3);
    free_node($5);
    yyerror("Missing ')'.");
  }
  | TOK_SSCANF '(' expr0 ',' expr0 error ';'
  {
    $$=0;
    free_node($3);
    free_node($5);
    yyerror("Missing ')'.");
  }
  | TOK_SSCANF '(' expr0 error ')'
  {
    $$=0;
    free_node($3);
    yyerrok;
  }
  | TOK_SSCANF '(' expr0 error TOK_LEX_EOF
  {
    $$=0;
    free_node($3);
    yyerror("Missing ')'.");
    yyerror("Unexpected end of file.");
  }
  | TOK_SSCANF '(' expr0 error '}'
  {
    $$=0;
    free_node($3);
    yyerror("Missing ')'.");
  }
  | TOK_SSCANF '(' expr0 error ';'
  {
    $$=0;
    free_node($3);
    yyerror("Missing ')'.");
  }
  | TOK_SSCANF '(' error ')' { $$=0; yyerrok; }
  | TOK_SSCANF '(' error TOK_LEX_EOF
  {
    $$=0; yyerror("Missing ')'.");
    yyerror("Unexpected end of file.");
  }
  | TOK_SSCANF '(' error '}' { $$=0; yyerror("Missing ')'."); }
  | TOK_SSCANF '(' error ';' { $$=0; yyerror("Missing ')'."); }
  ;

lvalue: expr4
  | '[' low_lvalue_list ']' { $$=mknode(F_ARRAY_LVALUE, $2,0); }
  | type6 TOK_IDENTIFIER
  {
    add_local_name($2->u.sval.u.string,compiler_pop_type(),0);
    $$=mklocalnode(islocal($2->u.sval.u.string),0);
    free_node($2);
  }
  | bad_expr_ident
  { $$=mknewintnode(0); }
  ;
low_lvalue_list: lvalue lvalue_list { $$=mknode(F_LVALUE_LIST,$1,$2); }
  ;

lvalue_list: /* empty */ { $$ = 0; }
  | ',' lvalue lvalue_list { $$ = mknode(F_LVALUE_LIST,$2,$3); }
  ;

string: TOK_STRING 
  | string TOK_STRING
  {
    struct pike_string *a,*b;
    copy_shared_string(a,$1->u.sval.u.string);
    copy_shared_string(b,$2->u.sval.u.string);
    free_node($1);
    free_node($2);
    a=add_and_free_shared_strings(a,b);
    $$=mkstrnode(a);
    free_string(a);
  }
  ;

/*
 * Some error-handling
 */

/* FIXME: Should probably set Pike_compiler->last_identifier. */
bad_identifier: bad_expr_ident
  | TOK_ARRAY_ID
  { yyerror("array is a reserved word."); }
  | TOK_CLASS
  { yyerror("class is a reserved word."); }
  | TOK_ENUM
  { yyerror("enum is a reserved word."); }
  | TOK_FLOAT_ID
  { yyerror("float is a reserved word.");}
  | TOK_FUNCTION_ID
  { yyerror("function is a reserved word.");}
  | TOK_INT_ID
  { yyerror("int is a reserved word."); }
  | TOK_MAPPING_ID
  { yyerror("mapping is a reserved word."); }
  | TOK_MIXED_ID
  { yyerror("mixed is a reserved word."); }
  | TOK_MULTISET_ID
  { yyerror("multiset is a reserved word."); }
  | TOK_OBJECT_ID
  { yyerror("object is a reserved word."); }
  | TOK_PROGRAM_ID
  { yyerror("program is a reserved word."); }
  | TOK_STRING_ID
  { yyerror("string is a reserved word."); }
  | TOK_TYPEDEF
  { yyerror("typedef is a reserved word."); }
  | TOK_VOID_ID
  { yyerror("void is a reserved word."); }
  ;

bad_expr_ident:
    TOK_INLINE
  { yyerror("inline is a reserved word."); }
  | TOK_LOCAL_ID
  { yyerror("local is a reserved word."); }
  | TOK_NO_MASK
  { yyerror("nomask is a reserved word."); }
  | TOK_PREDEF
  { yyerror("predef is a reserved word."); }
  | TOK_PRIVATE
  { yyerror("private is a reserved word."); }
  | TOK_PROTECTED
  { yyerror("protected is a reserved word."); }
  | TOK_PUBLIC
  { yyerror("public is a reserved word."); }
  | TOK_OPTIONAL
  { yyerror("optional is a reserved word."); }
  | TOK_VARIANT
  { yyerror("variant is a reserved word."); }
  | TOK_STATIC
  { yyerror("static is a reserved word."); }
  | TOK_EXTERN
  { yyerror("extern is a reserved word."); }
  | TOK_FINAL_ID
  { yyerror("final is a reserved word.");}
  | TOK_DO
  { yyerror("do is a reserved word."); }
  | TOK_ELSE
  { yyerror("else without if."); }
  | TOK_RETURN
  { yyerror("return is a reserved word."); }
  | TOK_IMPORT
  { yyerror("import is a reserved word."); }
  | TOK_INHERIT
  { yyerror("inherit is a reserved word."); }
  | TOK_CATCH
  { yyerror("catch is a reserved word."); }
  | TOK_GAUGE
  { yyerror("gauge is a reserved word."); }
  | TOK_LAMBDA
  { yyerror("lambda is a reserved word."); }
  | TOK_SSCANF
  { yyerror("sscanf is a reserved word."); }
  | TOK_SWITCH
  { yyerror("switch is a reserved word."); }
  | TOK_TYPEOF
  { yyerror("typeof is a reserved word."); }
  | TOK_BREAK
  { yyerror("break is a reserved word."); }
  | TOK_CASE
  { yyerror("case is a reserved word."); }
  | TOK_CONTINUE
  { yyerror("continue is a reserved word."); }
  | TOK_DEFAULT
  { yyerror("default is a reserved word."); }
  | TOK_FOR
  { yyerror("for is a reserved word."); }
  | TOK_FOREACH
  { yyerror("foreach is a reserved word."); }
  | TOK_IF
  { yyerror("if is a reserved word."); }
  ;



%%

void yyerror(char *str)
{
  extern int num_parse_error;
  extern int cumulative_parse_error;

#ifdef PIKE_DEBUG
  if(Pike_interpreter.recoveries && Pike_sp-Pike_interpreter.evaluator_stack < Pike_interpreter.recoveries->stack_pointer)
    fatal("Stack error (underflow)\n");
#endif

  if (Pike_compiler->num_parse_error > 10) return;
  Pike_compiler->num_parse_error++;
  cumulative_parse_error++;

  if ((error_handler && error_handler->prog) || get_master())
  {
    if (lex.current_file) {
      ref_push_string(lex.current_file);
    } else {
      /* yyerror() can be called from define_function(), which
       * can be called by the C module initialization code.
       */
      push_constant_text("");
    }
    push_int(lex.current_line);
    push_text(str);
    safe_apply_handler("compile_error", error_handler, compat_handler, 3);
    pop_stack();
  }else{
    if (lex.current_file) {
      (void)fprintf(stderr, "%s:%ld: %s\n",
		    lex.current_file->str,
		    (long)lex.current_line,
		    str);
    } else {
      (void)fprintf(stderr, "NULL:%ld: %s\n",
		    (long)lex.current_line,
		    str);
    }
    fflush(stderr);
  }
}

static int low_islocal(struct compiler_frame *f,
		       struct pike_string *str)
{
  int e;
  for(e=f->current_number_of_locals-1;e>=0;e--)
    if(f->variable[e].name==str)
      return e;
  return -1;
}



int low_add_local_name(struct compiler_frame *frame,
		       struct pike_string *str,
		       struct pike_type *type,
		       node *def)
{

  if (str->len && !TEST_COMPAT(7,0)) {
    int tmp=low_islocal(frame,str);
    if(tmp>=0 && tmp >= frame->last_block_level)
    {
      if(str->size_shift)
	my_yyerror("Duplicate local variable, "
		   "previous declaration on line %d\n",
		   frame->variable[tmp].line);
      else
	my_yyerror("Duplicate local variable '%s', "
		   "previous declaration on line %d\n",
		   STR0(str), frame->variable[tmp].line);
    }

    if(type == void_type_string)
    {
      if(str->size_shift)
	my_yyerror("Local variables cannot be of type of 'void'.\n");
      else
	my_yyerror("Local variable '%s' is void.\n",STR0(str));
    }
  }

  debug_malloc_touch(def);
  debug_malloc_touch(type);
  debug_malloc_touch(str);
  reference_shared_string(str);
  if (frame->current_number_of_locals == MAX_LOCAL)
  {
    yyerror("Too many local variables.");
    return 0;
  }else {
#ifdef PIKE_DEBUG
    check_type_string(type);
#endif /* PIKE_DEBUG */
    if (pike_types_le(type, void_type_string)) {
      if (Pike_compiler->compiler_pass != 1) {
	yywarning("Declaring local variable with type void "
		  "(converted to type zero).");
      }
      free_type(type);
      copy_pike_type(type, zero_type_string);
    }
    frame->variable[frame->current_number_of_locals].type = type;
    frame->variable[frame->current_number_of_locals].name = str;
    frame->variable[frame->current_number_of_locals].def = def;

    frame->variable[frame->current_number_of_locals].line=lex.current_line;
    frame->variable[frame->current_number_of_locals].file=lex.current_file;
    add_ref(lex.current_file);

    frame->current_number_of_locals++;
    if(frame->current_number_of_locals > 
       frame->max_number_of_locals)
    {
      frame->max_number_of_locals=
	frame->current_number_of_locals;
    }

    return frame->current_number_of_locals-1;
  }
}


/* argument must be a shared string */
/* Note that this function eats a reference to 'type' */
/* If def is nonzero, it also eats a ref to def */
int add_local_name(struct pike_string *str,
		   struct pike_type *type,
		   node *def)
{
  return low_add_local_name(Pike_compiler->compiler_frame,
			    str,
			    type,
			    def);
}



int islocal(struct pike_string *str)
{
  return low_islocal(Pike_compiler->compiler_frame, str);
}

/* argument must be a shared string */
static node *lexical_islocal(struct pike_string *str)
{
  int e,depth=0;
  struct compiler_frame *f=Pike_compiler->compiler_frame;
  
  while(1)
  {
    for(e=f->current_number_of_locals-1;e>=0;e--)
    {
      if(f->variable[e].name==str)
      {
	struct compiler_frame *q=Pike_compiler->compiler_frame;
	if(f->variable[e].def)
	  return copy_node(f->variable[e].def);
	while(q!=f) 
	{
	  q->lexical_scope|=SCOPE_SCOPED;
	  q=q->previous;
	}

	if(depth)
	  q->lexical_scope|=SCOPE_SCOPE_USED;

	return mklocalnode(e,depth);
      }
    }
    if(!(f->lexical_scope & SCOPE_LOCAL)) return 0;
    depth++;
    f=f->previous;
  }
}

static void safe_inc_enum(void)
{
  struct svalue *save_sp = Pike_sp;
  JMP_BUF recovery;

  if (SETJMP(recovery)) {
    free_svalue(&throw_value);
    throw_value.type = T_INT;
    yyerror("Bad implicit enum value (failed to add 1).");
    while(Pike_sp > save_sp) pop_stack();
  } else {
    push_int(1);
    f_add(2);
  }
  UNSETJMP(recovery);
#ifdef PIKE_DEBUG
  if (Pike_sp != save_sp) {
    fatal("stack thrashed in enum.\n");
  }
#endif /* PIKE_DEBUG */
}

void cleanup_compiler(void)
{
}