Skip to content
Snippets Groups Projects
Select Git revision
  • b4185edb9428e63a1d5f4e1fb2167fc7f0e6973b
  • master default protected
  • 9.0
  • 8.0
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • nt-tools
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
  • v8.0.1982
  • v8.0.1980
  • v8.0.1978
  • v8.0.1976
  • v8.0.1974
  • v8.0.1972
  • v8.0.1970
  • v8.0.1968
  • v8.0.1966
41 results

error.c

Blame
  • las.c 47.91 KiB
    /*\
    ||| 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.
    \*/
    #include "global.h"
    RCSID("$Id: las.c,v 1.66 1998/08/29 22:15:17 grubba Exp $");
    
    #include "language.h"
    #include "interpret.h"
    #include "las.h"
    #include "array.h"
    #include "object.h"
    #include "stralloc.h"
    #include "dynamic_buffer.h"
    #include "lex.h"
    #include "pike_types.h"
    #include "constants.h"
    #include "mapping.h"
    #include "multiset.h"
    #include "error.h"
    #include "docode.h"
    #include "main.h"
    #include "pike_memory.h"
    #include "operators.h"
    #include "callback.h"
    #include "pike_macros.h"
    #include "peep.h"
    #include "builtin_functions.h"
    #include "cyclic.h"
    
    #define LASDEBUG
    
    int lasdebug=0;
    
    static node *eval(node *);
    static void optimize(node *n);
    
    node *init_node = 0;
    int num_parse_error;
    int cumulative_parse_error=0;
    extern char *get_type_name(int);
    
    #define MAX_GLOBAL 2048
    
    int car_is_node(node *n)
    {
      switch(n->token)
      {
      case F_EXTERNAL:
      case F_IDENTIFIER:
      case F_CONSTANT:
      case F_LOCAL:
        return 0;
    
      default:
        return !!CAR(n);
      }
    }
    
    int cdr_is_node(node *n)
    {
      switch(n->token)
      {
      case F_EXTERNAL:
      case F_IDENTIFIER:
      case F_CONSTANT:
      case F_LOCAL:
      case F_CAST:
        return 0;
    
      default:
        return !!CDR(n);
      }
    }
    
    #ifdef DEBUG
    void check_tree(node *n, int depth)
    {
      if(!d_flag) return;
      if(!n) return;
      if(n->token==USHRT_MAX)
        fatal("Free node in tree.\n");
    
      if(!(depth & 1023))
      {
        node *q;
        for(q=n->parent;q;q=q->parent)
          if(q->parent==n)
    	fatal("Cyclic node structure found.\n");
      }
      depth++;
    
      if(car_is_node(n))
      {
        if(CAR(n)->parent != n)
          fatal("Parent is wrong.\n");
    
        check_tree(CAR(n),depth);
      }
    
      if(cdr_is_node(n))
      {
        if(CDR(n)->parent != n)
          fatal("Parent is wrong.\n");
    
        check_tree(CDR(n),depth);
      }
    }
    #endif
    
    INT32 count_args(node *n)
    {
      int a,b;
      check_tree(n,0);
      if(!n) return 0;
      switch(n->token)
      {
      case F_VAL_LVAL:
      case F_ARG_LIST:
        a=count_args(CAR(n));
        if(a==-1) return -1;
        b=count_args(CDR(n));
        if(b==-1) return -1;
        return a+b;
    
      case F_CAST:
        if(n->type == void_type_string)
          return 0;
        else
          return count_args(CAR(n));
    
      case F_CASE:
      case F_FOR:
      case F_DO:
      case F_INC_LOOP:
      case F_DEC_LOOP:
      case F_DEC_NEQ_LOOP:
      case F_INC_NEQ_LOOP:
      case F_BREAK:
      case F_RETURN:
      case F_CONTINUE:
      case F_FOREACH:
        return 0;
    
      case '?':
      {
        int tmp1,tmp2;
        tmp1=count_args(CADR(n));
        tmp2=count_args(CDDR(n));
        if(tmp1==-1 || tmp2==-2) return -1;
        if(tmp1 < tmp2) return tmp1;
        return tmp2;
      }
    
      case F_PUSH_ARRAY:
        return -1;
    
      default:
        if(n->type == void_type_string) return 0;
        return 1;
      }
    }
    
    struct pike_string *find_return_type(node *n)
    {
      struct pike_string *a,*b;
    
      check_tree(n,0);
    
      if(!n) return 0;
      if(!(n->tree_info & OPT_RETURN)) return 0;
      if(car_is_node(n))
        a=find_return_type(CAR(n));
      else
        a=0;
    
      if(cdr_is_node(n))
        b=find_return_type(CDR(n));
      else
        b=0;
    
      if(a)
      {
        if(b && a!=b) return mixed_type_string;
        return a;
      }
      return b;
    }
    
    
    #define NODES 256
    
    struct node_chunk 
    {
      struct node_chunk *next;
      node nodes[NODES];
    };
    
    static struct node_chunk *node_chunks=0;
    static node *free_nodes=0;
    
    void free_all_nodes(void)
    {
      if(!compiler_frame)
      {
        node *tmp;
        struct node_chunk *tmp2;
        int e=0;
    
    #ifndef DEBUG
        if(cumulative_parse_error)
        {
    #endif
          
          for(tmp2=node_chunks;tmp2;tmp2=tmp2->next) e+=NODES;
          for(tmp=free_nodes;tmp;tmp=CAR(tmp)) e--;
          if(e)
          {
    	int e2=e;
    	for(tmp2=node_chunks;tmp2;tmp2=tmp2->next)
    	{
    	  for(e=0;e<NODES;e++)
    	  {
    	    for(tmp=free_nodes;tmp;tmp=CAR(tmp))
    	      if(tmp==tmp2->nodes+e)
    		break;
    	    
    	    if(!tmp)
    	    {
    	      tmp=tmp2->nodes+e;
    #ifdef DEBUG
    	      if(!cumulative_parse_error)
    	      {
    		fprintf(stderr,"Free node at %p, (%s:%d) (token=%d).\n",tmp, tmp->current_file->str, tmp->line_number, tmp->token);
    		if(tmp->token==F_CONSTANT)
    		  print_tree(tmp);
    	      }
    	      else
    #endif
    	      {
    		/* Free the node and be happy */
    		/* Make sure we don't free any nodes twice */
    		if(car_is_node(tmp)) CAR(tmp)=0;
    		if(cdr_is_node(tmp)) CDR(tmp)=0;
    		debug_malloc_touch(tmp->type);
    		free_node(tmp);
    	      }
    	    }
    	  }
    	}
    #ifdef DEBUG
    	if(!cumulative_parse_error)
    	  fatal("Failed to free %d nodes when compiling!\n",e2);
    #endif
          }
    #ifndef DEBUG
        }
    #endif
        while(node_chunks)
        {
          tmp2=node_chunks;
          node_chunks=tmp2->next;
          free((char *)tmp2);
        }
        free_nodes=0;
        cumulative_parse_error=0;
      }
    }
    
    void free_node(node *n)
    {
      if(!n) return;
      switch(n->token)
      {
      case USHRT_MAX:
        fatal("Freeing node again!\n");
        break;
    
      case F_CONSTANT:
        free_svalue(&(n->u.sval));
        break;
    
      default:
        if(car_is_node(n)) free_node(CAR(n));
        if(cdr_is_node(n)) free_node(CDR(n));
      }
      n->token=USHRT_MAX;
      if(n->type) free_string(n->type);
    #ifdef DEBUG
      if(n->current_file) free_string(n->current_file);
    #endif
      CAR(n)=free_nodes;
      free_nodes=n;
    }
    
    
    /* here starts routines to make nodes */
    static node *mkemptynode(void)
    {
      node *res;
      if(!free_nodes)
      {
        int e;
        struct node_chunk *tmp=ALLOC_STRUCT(node_chunk);
        tmp->next=node_chunks;
        node_chunks=tmp;
    
        for(e=0;e<NODES-1;e++)
          CAR(tmp->nodes+e)=tmp->nodes+e+1;
        CAR(tmp->nodes+e)=0;
        free_nodes=tmp->nodes;
      }
      res=free_nodes;
      free_nodes=CAR(res);
      res->token=0;
      res->line_number=lex.current_line;
    #ifdef DEBUG
      copy_shared_string(res->current_file, lex.current_file);
    #endif
      res->type=0;
      res->node_info=0;
      res->tree_info=0;
      res->parent=0;
      return res;
    }
    
    node *mknode(short token,node *a,node *b)
    {
      node *res;
      check_tree(a,0);
      check_tree(b,0);
      res = mkemptynode();
      CAR(res) = a;
      CDR(res) = b;
      res->node_info = 0;
      res->tree_info = 0;
      if(a) a->parent = res;
      if(b) b->parent = res;
    
      /* We try to optimize most things, but argument lists are hard... */
      if(token != F_ARG_LIST && (a || b))
        res->node_info |= OPT_TRY_OPTIMIZE;
    
      switch(token)
      {
      case F_CATCH:
        res->node_info |= OPT_SIDE_EFFECT;
        break;
    
      case F_APPLY:
        if(a && a->token == F_CONSTANT &&
           a->u.sval.type == T_FUNCTION &&
           a->u.sval.subtype == FUNCTION_BUILTIN)
        {
          res->node_info |= a->u.sval.u.efun->flags;
        }else{
          res->node_info |= OPT_SIDE_EFFECT | OPT_EXTERNAL_DEPEND; /* for now */
        }
        break;
    
      case F_UNDEFINED:
        res->node_info |= OPT_EXTERNAL_DEPEND | OPT_SIDE_EFFECT;
        break;
    
      case F_RETURN:
        res->node_info |= OPT_RETURN;
        break;
    
      case F_BREAK:
        res->node_info |= OPT_BREAK;
        break;
    
      case F_CONTINUE:
        res->node_info |= OPT_CONTINUE;
        break;
    
      case F_DEFAULT:
      case F_CASE:
        res->node_info |= OPT_CASE;
        break;
    
      case F_SSCANF:
        if(!b || count_args(b) == 0) break;
        /* fall through */
    
      case F_ASSIGN:
      case F_INC:
      case F_DEC:
      case F_POST_INC:
      case F_POST_DEC:
        res->node_info |= OPT_ASSIGNMENT;
        break;
        
        default:
          res->tree_info = res->node_info;
          if(a) res->tree_info |= a->tree_info;
          if(b) res->tree_info |= b->tree_info;
      }
      res->token = token;
      res->type = 0;
    
    
    #ifdef DEBUG
      if(d_flag > 3)
        verify_shared_strings_tables();
    #endif
    
      if(!num_parse_error && compiler_pass==2)
      {
        check_tree(res,0);
        optimize(res);
        check_tree(res,0);
      }
    
    #ifdef DEBUG
      if(d_flag > 3)
        verify_shared_strings_tables();
    #endif
    
      return res;
    }
    
    node *mkstrnode(struct pike_string *str)
    {
      node *res = mkemptynode();
      res->token = F_CONSTANT;
      copy_shared_string(res->type, string_type_string);
      res->node_info = 0;
      res->u.sval.type = T_STRING;
    #ifdef __CHECKER__
      res->u.sval.subtype = 0;
    #endif
      copy_shared_string(res->u.sval.u.string, str);
      return res;
    }
    
    node *mkintnode(int nr)
    {
      node *res = mkemptynode();
      res->token = F_CONSTANT;
      if(nr)
        copy_shared_string(res->type, int_type_string);
      else
        copy_shared_string(res->type, mixed_type_string);
      res->node_info = 0; 
      res->u.sval.type = T_INT;
      res->u.sval.subtype = NUMBER_NUMBER;
      res->u.sval.u.integer = nr;
      return res;
    }
    
    node *mkfloatnode(FLOAT_TYPE foo)
    {
      node *res = mkemptynode();
      res->token = F_CONSTANT;
      copy_shared_string(res->type, float_type_string);
      res->u.sval.type = T_FLOAT;
    #ifdef __CHECKER__
      res->u.sval.subtype = 0;
    #endif
      res->u.sval.u.float_number = foo;
      return res;
    }
    
    node *mkapplynode(node *func,node *args)
    {
      return mknode(F_APPLY, func, args);
    }
    
    node *mkefuncallnode(char *function, node *args)
    {
      struct pike_string *name;
      node *n;
      name = findstring(function);
      if(!name || !(n=find_module_identifier(name)))
      {
        my_yyerror("Internally used efun undefined: %s",function);
        return mkintnode(0);
      }
      n=mkapplynode(n, args);
      return n;
    }
    
    node *mkopernode(char *oper_id, node *arg1, node *arg2)
    {
      if(arg1 && arg2)
        arg1=mknode(F_ARG_LIST,arg1,arg2);
    
      return mkefuncallnode(oper_id, arg1);
    }
    
    node *mklocalnode(int var)
    {
      node *res = mkemptynode();
      res->token = F_LOCAL;
      copy_shared_string(res->type, compiler_frame->variable[var].type);
      res->node_info = OPT_NOT_CONST;
      res->tree_info=res->node_info;
    #ifdef __CHECKER__
      CDR(res)=0;
    #endif
      res->u.number = var;
      return res;
    }
    
    node *mkidentifiernode(int i)
    {
      node *res = mkemptynode();
      res->token = F_IDENTIFIER;
      copy_shared_string(res->type, ID_FROM_INT(new_program, i)->type);
    
      /* FIXME */
      if(IDENTIFIER_IS_CONSTANT(ID_FROM_INT(new_program, i)->identifier_flags))
      {
        res->node_info = OPT_EXTERNAL_DEPEND;
      }else{
        res->node_info = OPT_NOT_CONST;
      }
      res->tree_info=res->node_info;
    
    #ifdef __CHECKER__
      CDR(res)=0;
    #endif
      res->u.number = i;
      check_tree(res,0);
      return res;
    }
    
    node *mkexternalnode(int level,
    		     int i,
    		     struct identifier *id)
    {
      node *res = mkemptynode();
      res->token = F_EXTERNAL;
    
      copy_shared_string(res->type, id->type);
    
      /* FIXME */
      if(IDENTIFIER_IS_CONSTANT(ID_FROM_INT(parent_compilation(level), i)->identifier_flags))
      {
        res->node_info = OPT_EXTERNAL_DEPEND;
      }else{
        res->node_info = OPT_NOT_CONST;
      }
      res->tree_info=res->node_info;
    
    #ifdef __CHECKER__
      CDR(res)=0;
    #endif
      res->u.integer.a = level;
      res->u.integer.b = i;
      return res;
    }
    
    node *mkcastnode(struct pike_string *type,node *n)
    {
      node *res;
      if(!n) return 0;
      if(type==n->type) return n;
      res = mkemptynode();
      res->token = F_CAST;
      copy_shared_string(res->type,type);
    
      if(match_types(object_type_string, type) ||
         match_types(object_type_string, type))
        res->node_info |= OPT_SIDE_EFFECT;
    
      CAR(res) = n;
    #ifdef __CHECKER__
      CDR(res)=0;
    #endif
      n->parent=res;
      return res;
    }
    
    void resolv_constant(node *n)
    {
      struct identifier *i;
      struct program *p;
      INT32 numid;
    
      check_tree(n,0);
    
      if(!n)
      {
        push_int(0);
      }else{
        switch(n->token)
        {
        case F_CONSTANT:
          push_svalue(& n->u.sval);
          return;
    
        case F_EXTERNAL:
          p=parent_compilation(n->u.integer.a);
          if(!p)
          {
    	yyerror("Failed to resolv external constant");
    	push_int(0);
    	return;
          }
          numid=n->u.integer.b;
          break;
    
        case F_IDENTIFIER:
          p=new_program;
          numid=n->u.number;
          break;
    
        case F_LOCAL:
          /* FIXME: Ought to have the name of the identifier in the message. */
          yyerror("Expected constant, got local variable");
          push_int(0);
          return;
    
        case F_GLOBAL:
          /* FIXME: Ought to have the name of the identifier in the message. */
          yyerror("Expected constant, got global variable");
          push_int(0);
          return;
    
        case F_UNDEFINED:
          if(compiler_pass==2) {
    	/* FIXME: Ought to have the name of the identifier in the message. */
    	yyerror("Expected constant, got undefined identifier");
          }
          push_int(0);
          return;
    
        default:
          yyerror("Expected constant, got something else");
          push_int(0);
          return;
        }
    
        i=ID_FROM_INT(p, numid);
        
        if(IDENTIFIER_IS_CONSTANT(i->identifier_flags))
        {
          push_svalue(PROG_FROM_INT(p, numid)->constants +
    		  i->func.offset);
        }else{
          my_yyerror("Identifier '%s' is not a constant", i->name->str);
          push_int(0);
        }
      }
    }
    
    void resolv_program(node *n)
    {
      check_tree(n,0);
    
      resolv_constant(n);
      switch(sp[-1].type)
      {
        case T_OBJECT:
          if(!sp[-1].u.object->prog)
          {
    	pop_stack();
    	push_int(0);
          }else{
    	f_object_program(1);
          }
          break;
          
        case T_FUNCTION:
          if(program_from_function(sp-1))
    	break;
          
        default:
          yyerror("Illegal program identifier");
          pop_stack();
          push_int(0);
          
        case T_PROGRAM:
          break;
      }
    }
    
    
    
    node *index_node(node *n, char *node_name, struct pike_string *id)
    {
      node *ret;
      JMP_BUF tmp;
    
      check_tree(n,0);
    
      if(SETJMP(tmp))
      {
        ONERROR tmp;
        SET_ONERROR(tmp,exit_on_error,"Error in handle_error in master object!");
        assign_svalue_no_free(sp++, & throw_value);
        APPLY_MASTER("handle_error", 1);
        pop_stack();
        UNSET_ONERROR(tmp);
    
        if (node_name) {
          my_yyerror("Couldn't index module '%s'.", node_name);
        } else {
          yyerror("Couldn't index module.");
        }
        push_int(0);
      }else{
        resolv_constant(n);
        switch(sp[-1].type)
        {
        case T_INT:
          if(!num_parse_error) {
    	if (node_name) {
    	  my_yyerror("Failed to index module '%s' (module doesn't exist?)",
    		     node_name);
    	} else {
    	  yyerror("Failed to index module (module doesn't exist?)");
    	}
          }
          break;
    
        case T_PROGRAM:
        case T_FLOAT:
        case T_STRING:
        case T_ARRAY:
          if (node_name) {
    	my_yyerror("Failed to index module '%s' (Not a module?)",
    		   node_name);
          } else {
    	yyerror("Failed to index module (Not a module?)");
          }
          pop_stack();
          push_int(0);
          break;
    
        default:
        {
          DECLARE_CYCLIC();
          if(BEGIN_CYCLIC(sp[-1].u.refs, id))
          {
    	my_yyerror("Recursive module dependency in '%s'.",id->str);
    	pop_stack();
    	push_int(0);
          }else{
    	SET_CYCLIC_RET(1);
    	ref_push_string(id);
    	f_index(2);
          
    	if(sp[-1].type == T_INT &&
    	   !sp[-1].u.integer &&
    	   sp[-1].subtype==NUMBER_UNDEFINED)
    	{
    	  if (node_name) {
    	    my_yyerror("Index '%s' not present in module '%s'.",
    		       id->str, node_name);
    	  } else {
    	    my_yyerror("Index '%s' not present in module.", id->str);
    	  }
    	}
    	END_CYCLIC();
          }
        }
        }
      }
      UNSETJMP(tmp);
      ret=mkconstantsvaluenode(sp-1);
      pop_stack();
      return ret;
    }
    
    
    int node_is_eq(node *a,node *b)
    {
      check_tree(a,0);
      check_tree(b,0);
    
      if(a == b) return 1;
      if(!a || !b) return 0;
      if(a->token != b->token) return 0;
    
      switch(a->token)
      {
      case F_LOCAL:
      case F_IDENTIFIER:
        return a->u.number == b->u.number;
    
      case F_CAST:
        return a->type == b->type && node_is_eq(CAR(a), CAR(b));
    
      case F_CONSTANT:
        return is_equal(&(a->u.sval), &(b->u.sval));
    
      default:
        if( a->type != b->type ) return 0;
        if(car_is_node(a) && !node_is_eq(CAR(a), CAR(b))) return 0;
        if(cdr_is_node(a) && !node_is_eq(CDR(a), CDR(b))) return 0;
        return 1;
      }
    }
    
    node *mkconstantsvaluenode(struct svalue *s)
    {
      node *res = mkemptynode();
      res->token = F_CONSTANT;
      assign_svalue_no_free(& res->u.sval, s);
      if(s->type == T_OBJECT || (s->type==T_FUNCTION && s->subtype!=FUNCTION_BUILTIN))
      {
        res->node_info|=OPT_EXTERNAL_DEPEND;
      }
      res->type = get_type_of_svalue(s);
      return res;
    }
    
    node *mkliteralsvaluenode(struct svalue *s)
    {
      node *res = mkconstantsvaluenode(s);
    
      if(s->type!=T_STRING && s->type!=T_INT && s->type!=T_FLOAT)
        res->node_info|=OPT_EXTERNAL_DEPEND;
    
      return res;
    }
    
    node *mksvaluenode(struct svalue *s)
    {
      switch(s->type)
      {
      case T_ARRAY:
        return make_node_from_array(s->u.array);
        
      case T_MULTISET:
        return make_node_from_multiset(s->u.multiset);
    
      case T_MAPPING:
        return make_node_from_mapping(s->u.mapping);
    
      case T_OBJECT:
        if(s->u.object == fake_object)
        {
          return mkefuncallnode("this_object", 0);
        }
        if(s->u.object->next == s->u.object)
        {
          int x=0;
          struct object *o;
          node *n=mkefuncallnode("this_object", 0);
          for(o=fake_object;o!=s->u.object;o=o->parent)
          {
    	n=mkefuncallnode("function_object",
    			 mkefuncallnode("object_program",n));
          }
          return n;
        }
        break;
    
      case T_FUNCTION:
      {
        if(s->subtype != FUNCTION_BUILTIN)
        {
          if(s->u.object == fake_object)
    	return mkidentifiernode(s->subtype);
    
          if(s->u.object->next == s->u.object)
          {
    	int x=0;
    	struct object *o;
    	for(o=fake_object->parent;o!=s->u.object;o=o->parent) x++;
    	return mkexternalnode(x, s->subtype,
    			      ID_FROM_INT(o->prog, s->subtype));
    
          }
    
    /*      yyerror("Non-constant function pointer! (should not happen!)"); */
        }
      }
      }
    
      return mkconstantsvaluenode(s);
    }
    
    
    /* these routines operates on parsetrees and are mostly used by the
     * optimizer
     */
    
    node *copy_node(node *n)
    {
      node *b;
      check_tree(n,0);
      if(!n) return n;
      switch(n->token)
      {
      case F_LOCAL:
      case F_IDENTIFIER:
        b=mkintnode(0);
        *b=*n;
        copy_shared_string(b->type, n->type);
        return b;
    
      case F_CAST:
        b=mkcastnode(n->type,copy_node(CAR(n)));
        break;
    
      case F_CONSTANT:
        b=mksvaluenode(&(n->u.sval));
        break;
    
      default:
        switch((car_is_node(n) << 1) | cdr_is_node(n))
        {
        default: fatal("fooo?\n");
    
        case 3:
          b=mknode(n->token, copy_node(CAR(n)), copy_node(CDR(n)));
          break;
        case 2:
          b=mknode(n->token, copy_node(CAR(n)), CDR(n));
          break;
    
        case 1:
          b=mknode(n->token, CAR(n), copy_node(CDR(n)));
          break;
    
        case 0:
          b=mknode(n->token, CAR(n), CDR(n));
        }
        if(n->type)
          copy_shared_string(b->type, n->type);
        else
          b->type=0;
      }
      b->line_number = n->line_number;
      b->node_info = n->node_info;
      b->tree_info = n->tree_info;
      return b;
    }
    
    
    int is_const(node *n)
    {
      if(!n) return 1;
      return !(n->tree_info & (OPT_SIDE_EFFECT |
    			   OPT_NOT_CONST |
    			   OPT_ASSIGNMENT |
    			   OPT_CASE |
    			   OPT_CONTINUE |
    			   OPT_BREAK |
    			   OPT_RETURN
    			   ));
    }
    
    int node_is_tossable(node *n)
    {
      return !(n->tree_info & (OPT_SIDE_EFFECT |
    			   OPT_ASSIGNMENT |
    			   OPT_CASE |
    			   OPT_CONTINUE |
    			   OPT_BREAK |
    			   OPT_RETURN
    			   ));
    }
    
    /* this one supposes that the value is optimized */
    int node_is_true(node *n)
    {
      if(!n) return 0;
      switch(n->token)
      {
        case F_CONSTANT: return !IS_ZERO(& n->u.sval);
        default: return 0;
      }
    }
    
    /* this one supposes that the value is optimized */
    int node_is_false(node *n)
    {
      if(!n) return 0;
      switch(n->token)
      {
        case F_CONSTANT: return IS_ZERO(& n->u.sval);
        default: return 0;
      }
    }
    
    int node_may_overload(node *n, int lfun)
    {
      if(!n) return 0;
      if(!n->type) return 1;
      return type_may_overload(n->type->str, lfun);
    }
    
    node **last_cmd(node **a)
    {
      node **n;
      if(!a || !*a) return (node **)NULL;
      if((*a)->token == F_CAST) return last_cmd(&CAR(*a));
      if((*a)->token != F_ARG_LIST) return a;
      if(CDR(*a))
      {
        if(CDR(*a)->token != F_CAST && CAR(*a)->token != F_ARG_LIST)
          return &CDR(*a);
        if((n=last_cmd(&CDR(*a))))
          return n;
      }
      if(CAR(*a))
      {
        if(CAR(*a)->token != F_CAST && CAR(*a)->token != F_ARG_LIST)
          return &CAR(*a);
        if((n=last_cmd(&CAR(*a))))
          return n;
      }
      return 0;
    }
    
    static node **low_get_arg(node **a,int *nr)
    {
      node **n;
      if(a[0]->token != F_ARG_LIST)
      {
        if(!(*nr)--)
          return a;
        else
          return NULL;
      }
      if(CAR(*a))
        if((n=low_get_arg(&CAR(*a),nr)))
          return n;
    
      if(CDR(*a))
        if((n=low_get_arg(&CDR(*a),nr)))
          return n;
    
      return 0;
    }
    
    node **my_get_arg(node **a,int n) { return low_get_arg(a,&n); }
    
    node **is_call_to(node *n, c_fun f)
    {
      switch(n->token)
      {
        case F_APPLY:
          if(CAR(n) &&
    	 CAR(n)->token == F_CONSTANT &&
    	 CAR(n)->u.sval.type == T_FUNCTION &&
    	 CAR(n)->u.sval.subtype == FUNCTION_BUILTIN &&
    	 CAR(n)->u.sval.u.efun->function == f)
    	return & CDR(n);
      }
      return 0;
    }
    
    
    static void low_print_tree(node *foo,int needlval)
    {
      if(!foo) return;
      switch(foo->token)
      {
      case F_LOCAL:
        if(needlval) putchar('&');
        printf("$%ld",(long)foo->u.number);
        break;
    
      case '?':
        printf("(");
        low_print_tree(CAR(foo),0);
        printf(")?(");
        low_print_tree(CADR(foo),0);
        printf("):(");
        low_print_tree(CDDR(foo),0);
        printf(")");
        break;
    
      case F_IDENTIFIER:
        if(needlval) putchar('&');
        printf("%s",ID_FROM_INT(new_program, foo->u.number)->name->str);
        break;
    
      case F_ASSIGN:
        low_print_tree(CDR(foo),1);
        printf("=");
        low_print_tree(CAR(foo),0);
        break;
    
      case F_CAST:
      {
        char *s;
        init_buf();
        low_describe_type(foo->type->str);
        s=simple_free_buf();
        printf("(%s){",s);
        free(s);
        low_print_tree(CAR(foo),0);
        printf("}");
        break;
      }
    
      case F_ARG_LIST:
        low_print_tree(CAR(foo),0);
        if(CAR(foo) && CDR(foo))
        {
          if(CAR(foo)->type == void_type_string &&
    	 CDR(foo)->type == void_type_string)
    	printf(";\n");
          else
    	putchar(',');
        }
        low_print_tree(CDR(foo),needlval);
        return;
    
        case F_ARRAY_LVALUE:
          putchar('[');
          low_print_tree(CAR(foo),1);
          putchar(']');
          break;
    
      case F_LVALUE_LIST:
        low_print_tree(CAR(foo),1);
        if(CAR(foo) && CDR(foo)) putchar(',');
        low_print_tree(CDR(foo),1);
        return;
    
      case F_CONSTANT:
      {
        char *s;
        init_buf();
        describe_svalue(& foo->u.sval, 0, 0);
        s=simple_free_buf();
        printf("%s",s);
        free(s);
        break;
      }
    
      case F_VAL_LVAL:
        low_print_tree(CAR(foo),0);
        printf(",&");
        low_print_tree(CDR(foo),0);
        return;
    
      case F_APPLY:
        low_print_tree(CAR(foo),0);
        printf("(");
        low_print_tree(CDR(foo),0);
        printf(")");
        return;
    
      default:
        if(!car_is_node(foo) && !cdr_is_node(foo))
        {
          printf("%s",get_token_name(foo->token));
          return;
        }
        if(foo->token<256)
        {
          printf("%c(",foo->token);
        }else{
          printf("%s(",get_token_name(foo->token));
        }
        if(car_is_node(foo)) low_print_tree(CAR(foo),0);
        if(car_is_node(foo) && cdr_is_node(foo))
          putchar(',');
        if(cdr_is_node(foo)) low_print_tree(CDR(foo),0);
        printf(")");
        return;
      }
    }
    
    void print_tree(node *n)
    {
      check_tree(n,0);
      low_print_tree(n,0);
      printf("\n");
      fflush(stdout);
    }
    
    
    /* The following routines needs much better commenting */
    struct used_vars
    {
      int err;
      char locals[MAX_LOCAL];
      char globals[MAX_GLOBAL];
    };
    
    #define VAR_BLOCKED   0
    #define VAR_UNUSED    1
    #define VAR_USED      3
    
    static void do_and_vars(struct used_vars *a,struct used_vars *b)
    {
      int e;
      for(e=0;e<MAX_LOCAL;e++) a->locals[e]|=b->locals[e];
      for(e=0;e<MAX_GLOBAL;e++) a->globals[e]|=b->globals[e];
      a->err|=b->err;
      free((char *)b);
    }
    
    static struct used_vars *copy_vars(struct used_vars *a)
    {
      struct used_vars *ret;
      ret=(struct used_vars *)xalloc(sizeof(struct used_vars));
      MEMCPY((char *)ret,(char *)a,sizeof(struct used_vars));
      return ret;
    }
    
    static int find_used_variables(node *n,
    			       struct used_vars *p,
    			       int noblock,
    			       int overwrite)
    {
      struct used_vars *a;
      char *q;
    
      if(!n) return 0;
      switch(n->token)
      {
      case F_LOCAL:
        q=p->locals+n->u.number;
        goto set_pointer;
    
      case F_IDENTIFIER:
        q=p->globals+n->u.number;
        if(n->u.number > MAX_GLOBAL)
        {
          p->err=1;
          return 0;
        }
    
      set_pointer:
        if(overwrite)
        {
          if(*q == VAR_UNUSED && !noblock) *q = VAR_BLOCKED;
        }
        else
        {
          if(*q == VAR_UNUSED) *q = VAR_USED;
        }
        break;
    
      case F_ASSIGN:
        find_used_variables(CAR(n),p,noblock,0);
        find_used_variables(CDR(n),p,noblock,1);
        break;
    
      case '?':
        find_used_variables(CAR(n),p,noblock,0);
        a=copy_vars(p);
        find_used_variables(CADR(n),a,noblock,0);
        find_used_variables(CDDR(n),p,noblock,0);
        do_and_vars(p,a);
        break;
    
      case F_INC_NEQ_LOOP:
      case F_DEC_NEQ_LOOP:
      case F_INC_LOOP:
      case F_DEC_LOOP:
      case F_FOREACH:
      case F_FOR:
        find_used_variables(CAR(n),p,noblock,0);
        a=copy_vars(p);
        find_used_variables(CDR(n),a,noblock,0);
        do_and_vars(p,a);
        break;
    
      case F_SWITCH:
        find_used_variables(CAR(n),p,noblock,0);
        a=copy_vars(p);
        find_used_variables(CDR(n),a,1,0);
        do_and_vars(p,a);
        break;
    
       case F_DO:
        a=copy_vars(p);
        find_used_variables(CAR(n),a,noblock,0);
        do_and_vars(p,a);
        find_used_variables(CDR(n),p,noblock,0);
        break;
    
      default:
        if(car_is_node(n)) find_used_variables(CAR(n),p,noblock,0);
        if(cdr_is_node(n)) find_used_variables(CDR(n),p,noblock,0);
      }
      return 0;
    }
    
    /* no subtility needed */
    static void find_written_vars(node *n,
    			      struct used_vars *p,
    			      int lvalue)
    {
      if(!n) return;
    
      switch(n->token)
      {
      case F_LOCAL:
        if(lvalue) p->locals[n->u.number]=VAR_USED;
        break;
    
      case F_IDENTIFIER:
         if(lvalue)
         {
           if(n->u.number>=MAX_GLOBAL)
           {
    	 p->err=1;
    	 return;
           }
           p->globals[n->u.number]=VAR_USED;
         }
        break;
    
      case F_APPLY:
        if(n->tree_info & OPT_SIDE_EFFECT)
          MEMSET(p->globals, VAR_USED, MAX_GLOBAL);
        break;
    
      case F_INDEX:
      case F_ARROW:
        find_written_vars(CAR(n), p, lvalue);
        find_written_vars(CDR(n), p, 0);
        break;
    
      case F_INC:
      case F_DEC:
      case F_POST_INC:
      case F_POST_DEC:
        find_written_vars(CAR(n), p, 1);
        break;
    
      case F_ASSIGN:
        find_written_vars(CAR(n), p, 0);
        find_written_vars(CDR(n), p, 1);
        break;
    
      case F_SSCANF:
        find_written_vars(CAR(n), p, 0);
        find_written_vars(CDR(n), p, 1);
        break;
    
        case F_ARRAY_LVALUE:
        find_written_vars(CAR(n), p, 1);
        break;
          
      case F_LVALUE_LIST:
        find_written_vars(CAR(n), p, 1);
        find_written_vars(CDR(n), p, 1);
        break;
    
      case F_VAL_LVAL:
        find_written_vars(CAR(n), p, 0);
        find_written_vars(CDR(n), p, 1);
        break;
        
      default:
        if(car_is_node(n)) find_written_vars(CAR(n), p, 0);
        if(cdr_is_node(n)) find_written_vars(CDR(n), p, 0);
      }
    }
    
    /* return 1 if A depends on B */
    static int depend_p2(node *a,node *b)
    {
      struct used_vars aa,bb;
      int e;
    
      if(!a || !b || is_const(a)) return 0;
      aa.err=0;
      bb.err=0;
      MEMSET((char *)aa.locals, VAR_UNUSED, MAX_LOCAL);
      MEMSET((char *)bb.locals, VAR_UNUSED,  MAX_LOCAL);
      MEMSET((char *)aa.globals, VAR_UNUSED, MAX_GLOBAL);
      MEMSET((char *)bb.globals, VAR_UNUSED, MAX_GLOBAL);
    
      find_used_variables(a,&aa,0,0);
      find_written_vars(b,&bb,0);
    
      if(aa.err || bb.err) return 1;
    
      for(e=0;e<MAX_LOCAL;e++)
        if(aa.locals[e]==VAR_USED && bb.locals[e]!=VAR_UNUSED)
          return 1;
    
      for(e=0;e<MAX_GLOBAL;e++)
        if(aa.globals[e]==VAR_USED && bb.globals[e]!=VAR_UNUSED)
          return 1;
      return 0;
    }
    
    static int depend_p(node *a,node *b)
    {
      if(!b) return 0;
    #if 0
      if(!(b->tree_info & OPT_SIDE_EFFECT) && 
         (b->tree_info & OPT_EXTERNAL_DEPEND))
        return 1;
    #endif
    
      if((a->tree_info & OPT_EXTERNAL_DEPEND)) return 1;
        
      return depend_p2(a,b);
    }
    
    static int cntargs(node *n)
    {
      if(!n) return 0;
      switch(n->token)
      {
      case F_CAST:
      case F_APPLY:
        return n->type != void_type_string;
    
      case F_FOREACH:
      case F_INC_NEQ_LOOP:
      case F_DEC_NEQ_LOOP:
      case F_INC_LOOP:
      case F_DEC_LOOP:  return 0;
    
      case F_VAL_LVAL:
      case F_LVALUE_LIST:
      case F_ARG_LIST:
        return cntargs(CAR(n))+cntargs(CDR(n));
    
        /* this might not be true, but it doesn't matter very much */
      default: return 1;
      }
    }
    
    static void low_build_function_type(node *n)
    {
      if(!n) return;
      switch(n->token)
      {
      case F_ARG_LIST:
        low_build_function_type(CDR(n));
        low_build_function_type(CAR(n));
        break;
    
      case F_PUSH_ARRAY: /* We let this ruin type-checking for now.. */
        reset_type_stack();
        push_type(T_MIXED);
        push_type(T_MIXED); /* is varargs */
        push_type(T_MANY);
        return;
    
      default:
        if(n->type)
        {
          if(n->type == void_type_string) return;
          push_finished_type(n->type);
        }else{
          push_type(T_MIXED);
        }
      }
    }
    
    
    void fix_type_field(node *n)
    {
      struct pike_string *type_a,*type_b;
    
      if(n->type) return; /* assume it is correct */
    
      switch(n->token)
      {
      case F_LAND:
      case F_LOR:
        if(!match_types(CAR(n)->type,mixed_type_string))
          yyerror("Bad conditional expression.\n");
    
        if(!match_types(CDR(n)->type,mixed_type_string))
          yyerror("Bad conditional expression.\n");
    
        if(CAR(n)->type == CDR(n)->type)
        {
          copy_shared_string(n->type,CAR(n)->type);
        }else{
          copy_shared_string(n->type,mixed_type_string);
        }
        break;
    
      case F_ASSIGN:
        if(CAR(n) && CDR(n) && 
           !match_types(CDR(n)->type,CAR(n)->type))
          my_yyerror("Bad type in assignment.");
        copy_shared_string(n->type, CAR(n)->type);
        break;
    
      case F_INDEX:
      case F_ARROW:
        type_a=CAR(n)->type;
        type_b=CDR(n)->type;
        if(!check_indexing(type_a, type_b, n))
          if(!catch_level)
            my_yyerror("Indexing on illegal type.");
        n->type=index_type(type_a,n);
        break;
    
      case F_APPLY:
      {
        struct pike_string *s;
        struct pike_string *f;
        INT32 max_args,args;
        push_type(T_MIXED); /* match any return type, even void */
        push_type(T_VOID); /* not varargs */
        push_type(T_MANY);
        low_build_function_type(CDR(n));
        push_type(T_FUNCTION);
        s=pop_type();
        f=CAR(n)->type?CAR(n)->type:mixed_type_string;
        n->type=check_call(s,f);
        args=count_arguments(s);
        max_args=count_arguments(f);
        if(max_args<0) max_args=0x7fffffff;
    
        if(!n->type)
        {
          char *name;
          switch(CAR(n)->token)
          {
          case F_IDENTIFIER:
    	name=ID_FROM_INT(new_program, CAR(n)->u.number)->name->str;
    	break;
    	
          case F_CONSTANT:
    	switch(CAR(n)->u.sval.type)
    	{
    	  case T_FUNCTION:
    	    if(CAR(n)->u.sval.subtype == FUNCTION_BUILTIN)
    	    {
    	      name=CAR(n)->u.sval.u.efun->name->str;
    	    }else{
    	      name=ID_FROM_INT(CAR(n)->u.sval.u.object->prog,
    			       CAR(n)->u.sval.subtype)->name->str;
    	    }
    	    break;
    
    	  case T_ARRAY:
    	    name="array call";
    	    break;
    
    	  case T_PROGRAM:
    	    name="clone call";
    	    break;
    
    	  default:
    	    name="`() (function call)";
    	    break;
    	}
    	break;
    
          default:
    	name="unknown function";
          }
    
          if(max_args < args)
          {
    	my_yyerror("Too many arguments to %s.\n",name);
          }
          else if(max_correct_args == args)
          {
    	my_yyerror("Too few arguments to %s.\n",name);
          }else{
    	my_yyerror("Bad argument %d to %s.",
    		   max_correct_args+1, name);
          }
          copy_shared_string(n->type, mixed_type_string);
        }
        free_string(s);
        break;
      }
    
      case '?':
        if(!match_types(CAR(n)->type,mixed_type_string))
          yyerror("Bad conditional expression.\n");
    
        if(!CADR(n) || !CDDR(n))
        {
          copy_shared_string(n->type,void_type_string);
          return;
        }
        
        if(CADR(n)->type == CDDR(n)->type)
        {
          copy_shared_string(n->type,CADR(n)->type);
          return;
        }
    
        copy_shared_string(n->type,mixed_type_string);
        break;
    
      case F_RETURN:
        if(CAR(n)->type == void_type_string)
        {
          yyerror("You cannot return a void expression");
        }
        if(compiler_frame &&
           compiler_frame->current_return_type &&
           !match_types(compiler_frame->current_return_type,CAR(n)->type) &&
           !(
    	 compiler_frame->current_return_type==void_type_string &&
    	 CAR(n)->token == F_CONSTANT &&
    	 IS_ZERO(& CAR(n)->u.sval)
    	 )
           )
        {
          yyerror("Wrong return type.");
        }
    
        /* Fall through */
    
      case F_INC_LOOP:
      case F_DEC_LOOP:
      case F_DEC_NEQ_LOOP:
      case F_INC_NEQ_LOOP:
      case F_CASE:
      case F_CONTINUE:
      case F_BREAK:
        copy_shared_string(n->type,void_type_string);
        break;
    
      case F_DO:
        if(!match_types(CDR(n)->type,mixed_type_string))
          yyerror("Bad conditional expression do - while().\n");
        copy_shared_string(n->type,void_type_string);
        break;
        
      case F_FOR:
        if(!match_types(CAR(n)->type,mixed_type_string))
          yyerror("Bad conditional expression for().\n");
        copy_shared_string(n->type,void_type_string);
        break;
    
      case F_SWITCH:
        if(!match_types(CAR(n)->type,mixed_type_string))
          yyerror("Bad switch expression.\n");
        copy_shared_string(n->type,void_type_string);
        break;
    
      case F_CONSTANT:
        n->type = get_type_of_svalue(& n->u.sval);
        break;
    
      case F_ARG_LIST:
        if(!CAR(n) || CAR(n)->type==void_type_string)
        {
          if(CDR(n))
    	copy_shared_string(n->type,CDR(n)->type);
          else
    	copy_shared_string(n->type,void_type_string);
          return;
        }
    
        if(!CDR(n) || CDR(n)->type==void_type_string)
        {
          if(CAR(n))
    	copy_shared_string(n->type,CAR(n)->type);
          else
    	copy_shared_string(n->type,void_type_string);
          return;
        }
    
      default:
        copy_shared_string(n->type,mixed_type_string);
      }
    }
    
    static void zapp_try_optimize(node *n)
    {
      if(!n) return;
      n->node_info &=~ OPT_TRY_OPTIMIZE;
      n->tree_info &=~ OPT_TRY_OPTIMIZE;
      if(car_is_node(n)) zapp_try_optimize(CAR(n));
      if(cdr_is_node(n)) zapp_try_optimize(CDR(n));
    }
    
    static void optimize(node *n)
    {
      node *tmp1, *tmp2, *tmp3;
      INT32 save_line = lex.current_line;
      do
      {
        if(car_is_node(n) && !(CAR(n)->node_info & OPT_OPTIMIZED))
        {
          n=CAR(n);
          continue;
        }
        if(cdr_is_node(n) && !(CDR(n)->node_info & OPT_OPTIMIZED))
        {
          n=CDR(n);
          continue;
        }
        lex.current_line = n->line_number;
    
    
        n->tree_info = n->node_info;
        if(car_is_node(n)) n->tree_info |= CAR(n)->tree_info;
        if(cdr_is_node(n)) n->tree_info |= CDR(n)->tree_info;
    
        if(!n->parent) break;
        
        if(n->tree_info & (OPT_NOT_CONST|
    		       OPT_SIDE_EFFECT|
    		       OPT_EXTERNAL_DEPEND|
    		       OPT_ASSIGNMENT))
        {
          if(car_is_node(n) &&
    	 !(CAR(n)->tree_info & (OPT_NOT_CONST|
    				OPT_SIDE_EFFECT|
    				OPT_EXTERNAL_DEPEND|
    				OPT_ASSIGNMENT)) &&
    	 (CAR(n)->tree_info & OPT_TRY_OPTIMIZE) &&
    	 CAR(n)->token != ':')
          {
    	CAR(n) = eval(CAR(n));
    	if(CAR(n)) CAR(n)->parent = n;
    	zapp_try_optimize(CAR(n)); /* avoid infinite loops */
    	continue;
          }
          if(cdr_is_node(n) &&
    	 !(CDR(n)->tree_info & (OPT_NOT_CONST|
    				OPT_SIDE_EFFECT|
    				OPT_EXTERNAL_DEPEND|
    				OPT_ASSIGNMENT)) &&
    	 (CDR(n)->tree_info & OPT_TRY_OPTIMIZE) &&
    	 CDR(n)->token != ':')
          {
    	CDR(n) = eval(CDR(n));
    	if(CDR(n)) CDR(n)->parent = n;
    	zapp_try_optimize(CDR(n)); /* avoid infinite loops */
    	continue;
          }
        }
        fix_type_field(n);
        debug_malloc_touch(n->type);
    
    #ifdef DEBUG
        if(l_flag > 3 && n)
        {
          fprintf(stderr,"Optimizing (tree info=%x):",n->tree_info);
          print_tree(n);
        }
    #endif    
    
        switch(n->token)
        {
        case F_APPLY:
          if(CAR(n)->token == F_CONSTANT &&
    	 CAR(n)->u.sval.type == T_FUNCTION &&
    	 CAR(n)->u.sval.subtype == FUNCTION_BUILTIN && /* driver fun? */
    	 CAR(n)->u.sval.u.efun->optimize)
          {
    	if((tmp1=CAR(n)->u.sval.u.efun->optimize(n)))
    	{
    	  goto use_tmp1;
    	}
          }
          break;
    
        case F_ARG_LIST:
        case F_LVALUE_LIST:
          if(!CAR(n)) goto use_cdr;
          if(!CDR(n)) goto use_car;
    
          /*
           * { X; break;  Y; } -> { X; return; }
           * { X; return;  Y; } -> { X; return; }
           * { X; continue; Y; } -> { X; return; }
           */
          if((CAR(n)->token==F_RETURN ||
    	  CAR(n)->token==F_BREAK ||
    	  CAR(n)->token==F_CONTINUE ||
    	  (CAR(n)->token==F_ARG_LIST &&
    	   CDAR(n) &&
    	   (CDAR(n)->token==F_RETURN ||
    	    CDAR(n)->token==F_BREAK ||
    	    CDAR(n)->token==F_CONTINUE))) &&
    	 !(CDR(n)->tree_info & OPT_CASE))
    	goto use_car;
    
          break;
    
        case F_LOR:
          /* !x || !y -> !(x && y) */
          if(CAR(n)->token==F_NOT && CDR(n)->token==F_NOT)
          {
    	tmp1=mknode(F_NOT,mknode(F_LAND,CAAR(n),CADR(n)),0);
    	CAAR(n)=CADR(n)=0;
    	goto use_tmp1;
          }
          break;
    
        case F_LAND: 
          /* !x && !y -> !(x || y) */
          if(CAR(n)->token==F_NOT && CDR(n)->token==F_NOT)
          {
    	tmp1=mknode(F_NOT,mknode(F_LOR,CAAR(n),CADR(n)),0);
    	CAAR(n)=CADR(n)=0;
    	goto use_tmp1;
          }
          break;
    
        case '?':
          /* (! X) ? Y : Z  -> X ? Z : Y */
          if(CAR(n)->token == F_NOT)
          {
    	tmp1=mknode('?',CAAR(n),mknode(':',CDDR(n),CADR(n)));
    	CAAR(n)=CDDR(n)=CADR(n)=0;
    	goto use_tmp1;
          }
          break;
    
        case F_ADD_EQ:
          if(CDR(n)->type == int_type_string)
          {
    	if(CDR(n)->token == F_CONSTANT && CDR(n)->u.sval.type == T_INT)
    	{
    	  /* a+=0 -> a */
    	  if(n->u.sval.u.integer == 0) goto use_car;
    
    	  /* a+=1 -> ++a */
    	  if(n->u.sval.u.integer == 1)
    	  {
    	    tmp1=mknode(F_INC,CDR(n),0);
    	    CDR(n)=0;
    	    goto use_tmp1;
    	  }
    
    	  /* a+=-1 -> --a */
    	  if(n->u.sval.u.integer == -1)
    	  {
    	    tmp1=mknode(F_DEC, CDR(n), 0);
    	    CDR(n)=0;
    	    goto use_tmp1;
    	  }
    	}
          }
          break;
    
        case F_SUB_EQ:
          if(CDR(n)->type == int_type_string)
          {
    	if(CDR(n)->token == F_CONSTANT && CDR(n)->u.sval.type == T_INT)
    	{
    	  /* a-=0 -> a */
    	  if(n->u.sval.u.integer == 0) goto use_car;
    
    	  /* a-=-1 -> ++a */
    	  if(n->u.sval.u.integer == -1)
    	  {
    	    tmp1=mknode(F_INC, CDR(n), 0);
    	    CDR(n)=0;
    	    goto use_tmp1;
    	  }
    
    	  /* a-=-1 -> --a */
    	  if(n->u.sval.u.integer == 1)
    	  {
    	    tmp1=mknode(F_DEC, CDR(n), 0);
    	    CDR(n)=0;
    	    goto use_tmp1;
    	  }
    	}
          }
          break;
    
        case F_ARROW:
          if(CAR(n)->token==F_CONSTANT &&
    	 CAR(n)->u.sval.type==T_OBJECT &&
    	 CAR(n)->u.sval.u.object->prog &&
    	 CDR(n)->token==F_CONSTANT &&
    	 CAR(n)->u.sval.type==T_STRING &&
    	 find_identifier("`->",CAR(n)->u.sval.u.object->prog)==-1)
          {
    	int i;
    	i=find_shared_string_identifier(CDR(n)->u.sval.u.string,
    					CAR(n)->u.sval.u.object->prog);
    	if(i)
    	{
    	  struct identifier *id;
    	  id=ID_FROM_INT(CAR(n)->u.sval.u.object->prog, i);
    	  if(IDENTIFIER_IS_VARIABLE(id->identifier_flags)) break;
    	}
    	ref_push_object(CAR(n)->u.sval.u.object);
    	ref_push_string(CDR(n)->u.sval.u.string);
    	f_index(2);
    	tmp1=mksvaluenode(sp-1);
    	pop_stack();
    	goto use_tmp1;
          }
          break;
    
        case F_FOR:
        {
          node **last;
          int inc;
          int token;
    
          /* for(;0; X) Y; -> 0; */
          if(node_is_false(CAR(n)))
          {
    	tmp1=mkintnode(0);
    	goto use_tmp1;
          }
    
          /* for(;1;); -> for(;1;) sleep(255); (saves cpu) */
          if(node_is_true(CAR(n)) &&
    	 (!CDR(n) || (CDR(n)->token==':' && !CADR(n) && !CDDR(n))))
          {
    	tmp1=mknode(F_FOR, CAR(n), mknode(':',mkefuncallnode("sleep",mkintnode(255)),0));
    	CAR(n)=0;
    	goto use_tmp1;
          }
    
          /*
           * if X and Y are free from 'continue' or X is null,
           * then the following optimizations can be done:
           * for(;++e; X) Y; -> ++ne_loop(e, -1) { Y ; X }
           * for(;e++; X) Y; -> ++ne_loop(e, 0) { Y; X }
           * for(;--e; X) Y; -> --ne_loop(e, 1) { Y; X }
           * for(;e--; X) Y; -> --ne_loop(e, 0) { Y; X }
           */
          if(CAR(n) &&
    	 (CAR(n)->token==F_INC ||
    	  CAR(n)->token==F_POST_INC ||
    	  CAR(n)->token==F_DEC ||
    	  CAR(n)->token==F_POST_DEC ) &&
    	 (!CDDR(n) || !(CDDR(n)->tree_info & OPT_CONTINUE)) &&
    	 (!CADR(n) || !(CADR(n)->tree_info & OPT_CONTINUE)) )
          {
    	/* Check which of the above cases.. */
    	switch(CAR(n)->token)
    	{
    	case F_POST_DEC: token=F_DEC_NEQ_LOOP; inc=-1; break;
    	case F_DEC:      token=F_DEC_NEQ_LOOP; inc=0; break;
    	case F_POST_INC: token=F_INC_NEQ_LOOP; inc=1; break;
    	case F_INC:      token=F_INC_NEQ_LOOP; inc=0; break;
    	default: fatal("Impossible error\n"); return;
    	}
    
    	/* Build new tree */
    	tmp1=mknode(token,
    		    mknode(F_VAL_LVAL,
    			   mkintnode(inc),
    			   CAAR(n)),
    		    mknode(F_ARG_LIST,
    			   mkcastnode(void_type_string, CADR(n)),
    			   mkcastnode(void_type_string, CDDR(n))));
    	CDDR(n)=CADR(n)=CAAR(n)=0;
    	goto use_tmp1;
          }
    
          /* Last is a pointer to the place where the incrementor is in the
           * tree. This is needed so we can nullify this pointer later and
           * free the rest of the tree
           */
          last=&(CDDR(n));
          tmp1=*last;
    
          /* We're not interested in casts to void */
          while(tmp1 && 
    	    tmp1->token == F_CAST &&
    	    tmp1->type == void_type_string)
          {
    	last=&CAR(tmp1);
    	tmp1=*last;
          }
    
          /* If there is an incrementor, and it is one of x++, ++x, x-- or ++x */
          if(tmp1 && (tmp1->token==F_INC ||
    		  tmp1->token==F_POST_INC ||
    		  tmp1->token==F_DEC ||
    		  tmp1->token==F_POST_DEC))
          {
    	node *opnode, **arg1, **arg2;
    	int oper;
    
    	/* does it increment or decrement ? */
    	if(tmp1->token==F_INC || tmp1->token==F_POST_INC)
    	  inc=1;
    	else
    	  inc=0;
    
    	/* for(; arg1 oper arg2; z ++) p; */
    
    	opnode=CAR(n);
    
    	if(opnode->token == F_APPLY &&
    	   CAR(opnode) &&
    	   CAR(opnode)->token == F_CONSTANT &&
    	   CAR(opnode)->u.sval.type == T_FUNCTION &&
    	   CAR(opnode)->u.sval.subtype == FUNCTION_BUILTIN)
    	{
    	  if(CAR(opnode)->u.sval.u.efun->function == f_gt)
    	    oper=F_GT;
    	  else if(CAR(opnode)->u.sval.u.efun->function == f_ge)
    	    oper=F_GE;
    	  else if(CAR(opnode)->u.sval.u.efun->function == f_lt)
    	    oper=F_LT;
    	  else if(CAR(opnode)->u.sval.u.efun->function == f_le)
    	    oper=F_LE;
    	  else if(CAR(opnode)->u.sval.u.efun->function == f_ne)
    	    oper=F_NE;
    	  else
    	    break;
    	}else{
    	  break;
    	}
    
    	if(count_args(CDR(opnode)) != 2) break;
    	arg1=my_get_arg(&CDR(opnode), 0);
    	arg2=my_get_arg(&CDR(opnode), 1);
    
    	/* it was not on the form for(; x op y; z++) p; */
    	if(!node_is_eq(*arg1,CAR(tmp1)) || /* x == z */
    	   depend_p(*arg2,*arg2) ||	/* does y depend on y? */
    	   depend_p(*arg2,*arg1) ||	/* does y depend on x? */
    	   depend_p(*arg2,CADR(n))) /* does y depend on p? */
    	{
    	  /* it was not on the form for(; x op y; x++) p; */
    	  if(!node_is_eq(*arg2,CAR(tmp1)) || /* y == z */
    	     depend_p(*arg1,*arg2) || /* does x depend on y? */
    	     depend_p(*arg1,*arg1) || /* does x depend on x? */
    	     depend_p(*arg1,CADR(n)) /* does x depend on p? */
    	     )
    	  {
    	    /* it was not on the form for(; x op y; y++) p; */
    	    break;
    	  }else{
    	    node **tmparg;
    	    /* for(; x op y; y++) p; -> for(; y op^-1 x; y++) p; */
    
    	    switch(oper)
    	    {
    	    case F_LT: oper=F_GT; break;
    	    case F_LE: oper=F_GE; break;
    	    case F_GT: oper=F_LT; break;
    	    case F_GE: oper=F_LE; break;
    	    }
    	    
    	    tmparg=arg1;
    	    arg1=arg2;
    	    arg2=tmparg;
    	  }
    	}
    	if(inc)
    	{
    	  if(oper==F_LE)
    	    tmp3=mkopernode("`+",*arg2,mkintnode(1));
    	  else if(oper==F_LT)
    	    tmp3=*arg2;
    	  else
    	    break;
    	}else{
    	  if(oper==F_GE)
    	    tmp3=mkopernode("`-",*arg2,mkintnode(1));
    	  else if(oper==F_GT)
    	    tmp3=*arg2;
    	  else
    	    break;
    	}
    
    	*last=0;
    	if(oper==F_NE)
    	{
    	  if(inc)
    	    token=F_INC_NEQ_LOOP;
    	  else
    	    token=F_DEC_NEQ_LOOP;
    	}else{
    	  if(inc)
    	    token=F_INC_LOOP;
    	  else
    	    token=F_DEC_LOOP;
    	}
    	tmp2=mknode(token,mknode(F_VAL_LVAL,tmp3,*arg1),CADR(n));
    	*arg1 = *arg2 = CADR(n) =0;
    
    	if(inc)
    	{
    	  tmp1->token=F_DEC;
    	}else{
    	  tmp1->token=F_INC;
    	}
    
    	tmp1=mknode(F_ARG_LIST,mkcastnode(void_type_string,tmp1),tmp2);
    	goto use_tmp1;
          }
          break;
        }
    
        use_car:
          tmp1=CAR(n);
          CAR(n)=0;
          goto use_tmp1;
    
        use_cdr:
          tmp1=CDR(n);
          CDR(n)=0;
          goto use_tmp1;
    
        use_tmp1:
          if(CAR(n->parent) == n)
    	CAR(n->parent) = tmp1;
          else
    	CDR(n->parent) = tmp1;
          
          if(tmp1) tmp1->parent = n->parent;
          free_node(n);
          n=tmp1;
    #ifdef DEBUG
          if(l_flag > 3)
          {
    	fprintf(stderr,"Result: ");
    	print_tree(n);
          }
    #endif    
          continue;
    
        }
        n->node_info |= OPT_OPTIMIZED;
        n=n->parent;
      }while(n);
      lex.current_line = save_line;
    }
    
    struct timer_oflo
    {
      INT32 counter;
      int yes;
    };
    
    static void check_evaluation_time(struct callback *cb,void *tmp,void *ignored)
    {
      struct timer_oflo *foo=(struct timer_oflo *)tmp;
      if(foo->counter-- < 0)
      {
        foo->yes=1;
        pike_throw();
      }
    }
    
    int eval_low(node *n)
    {
      unsigned INT16 num_strings, num_constants;
      INT32 jump;
      struct svalue *save_sp = sp;
      int ret;
    
    #ifdef DEBUG
      if(l_flag > 3 && n)
      {
        fprintf(stderr,"Evaluating (tree info=%x):",n->tree_info);
        print_tree(n);
      }
    #endif
    
      if(num_parse_error) return -1; 
    
      num_strings=new_program->num_strings;
      num_constants=new_program->num_constants;
      jump=PC;
    
      store_linenumbers=0;
      docode(n);
      ins_f_byte(F_DUMB_RETURN);
      store_linenumbers=1;
    
      ret=-1;
      if(!num_parse_error)
      {
        struct callback *tmp_callback;
        struct timer_oflo foo;
    
        /* This is how long we try to optimize before giving up... */
        foo.counter=10000;
        foo.yes=0;
    
        tmp_callback=add_to_callback(&evaluator_callbacks,
    				 check_evaluation_time,
    				 (void *)&foo,0);
    				 
        if(apply_low_safe_and_stupid(fake_object, jump))
        {
          /* Generate error message */
          if(!catch_level)
          {
            if(throw_value.type == T_ARRAY && throw_value.u.array->size)
            {
    	  union anything *a;
    	  a=low_array_get_item_ptr(throw_value.u.array, 0, T_STRING);
    	  if(a)
    	  {
    	    yyerror(a->string->str);
    	  }else{
    	    yyerror("Nonstandard error format.");
    	  }
    	}else{
    	  yyerror("Nonstandard error format.");
    	}
          }
        }else{
          if(foo.yes)
    	pop_n_elems(sp-save_sp);
          else
    	ret=sp-save_sp;
        }
    
        remove_callback(tmp_callback);
      }
    
      while(new_program->num_strings > num_strings)
      {
        new_program->num_strings--;
        free_string(new_program->strings[new_program->num_strings]);
      }
    
      while(new_program->num_constants > num_constants)
      {
        new_program->num_constants--;
        free_svalue(new_program->constants + new_program->num_constants);
      }
    
      new_program->num_program=jump;
    
      return ret;
    }
    
    static node *eval(node *n)
    {
      int args;
      extern struct svalue *sp;
      if(!is_const(n) || n->token==':')
        return n;
      args=eval_low(n);
    
      switch(args)
      {
      case -1:
        return n;
        break;
    
      case 0:
        if(catch_level) return n;
        free_node(n);
        n=0;
        break;
    
      case 1:
        if(catch_level && IS_ZERO(sp-1))
        {
          pop_stack();
          return n;
        }
        free_node(n);
        n=mksvaluenode(sp-1);
        pop_stack();
        break;
    
      default:
        free_node(n);
        n=NULL;
        while(args--)
        {
          n=mknode(F_ARG_LIST,mksvaluenode(sp-1),n);
          pop_stack();
        }
      }
      return n;
    }
    
    INT32 last_function_opt_info;
    
    static int stupid_args(node *n, int expected,int vargs)
    {
      if(!n) return expected;
      switch(n->token)
      {
      case F_PUSH_ARRAY:
        if(!vargs) return -1;
    
        if(stupid_args(CAR(n), expected,vargs) == expected+1)
          return 65535;
        return -1;
    
      case F_ARG_LIST:
        expected=stupid_args(CAR(n), expected,vargs);
        if(expected==-1) return -1;
        return stupid_args(CDR(n), expected,vargs);
      case F_LOCAL:
        return n->u.number==expected ? expected + 1 : -1;
      default:
        return -1;
      }
    }
    
    static int is_null_branch(node *n)
    {
      if(!n) return 1;
      if(n->token==F_CAST && n->type==void_type_string)
        return is_null_branch(CAR(n));
      if(n->token==F_ARG_LIST)
        return is_null_branch(CAR(n)) && is_null_branch(CDR(n));
      return 0;
    }
    
    static struct svalue *is_stupid_func(node *n,
    				     int args,
    				     int vargs)
    {
      node *a,*b;
      int tmp;
      while(1)
      {
        if(!n) return 0;
    
        if(n->token == F_ARG_LIST)
        {
          if(is_null_branch(CAR(n)))
    	n=CDR(n);
          else
    	n=CAR(n);
          continue;
        }
    
        if(n->token == F_CAST && n->type==void_type_string)
        {
          n=CAR(n);
          continue;
        }
        break;
      }
    
      if(!n || n->token != F_RETURN) return 0;
      n=CAR(n);
    
      if(!n || n->token != F_APPLY) return 0;
    
      tmp=stupid_args(CDR(n),0,vargs);
      if(!(vargs?tmp==65535:tmp==args)) return 0;
    
      n=CAR(n);
      if(!n || n->token != F_CONSTANT) return 0;
    
      if((count_arguments(n->type) < 0) == !!vargs)
        return &n->u.sval;
    
      return 0;
    }
    
    int dooptcode(struct pike_string *name,
    	      node *n,
    	      struct pike_string *type,
    	      int modifiers)
    {
      union idptr tmp;
      int args, vargs, ret;
      struct svalue *foo;
    
      check_tree(n,0);
    
    #ifdef DEBUG
      if(a_flag > 1)
        fprintf(stderr,"Doing function '%s' at %x\n",name->str,PC);
    #endif
    
      args=count_arguments(type);
      if(args < 0) 
      {
        args=~args;
        vargs=IDENTIFIER_VARARGS;
      }else{
        vargs=0;
      }
      if(compiler_pass==1)
      {
        tmp.offset=-1;
      }else{
        n=mknode(F_ARG_LIST,n,0);
        
        if((foo=is_stupid_func(n, args, vargs)))
        {
          if(foo->type == T_FUNCTION && foo->subtype==FUNCTION_BUILTIN)
          {
    	tmp.c_fun=foo->u.efun->function;
    	ret=define_function(name,
    			    type,
    			    modifiers,
    			    IDENTIFIER_C_FUNCTION | vargs,
    			    &tmp);
    	free_node(n);
    #ifdef DEBUG
    	if(a_flag > 1)
    	  fprintf(stderr,"Identifer (C) = %d\n",ret);
    #endif
    	return ret;
          }
        }
    
        tmp.offset=PC;
        add_to_program(compiler_frame->max_number_of_locals);
        add_to_program(args);
      
    #ifdef DEBUG
        if(a_flag > 2)
        {
          fprintf(stderr,"Coding: ");
          print_tree(n);
        }
    #endif
        if(!num_parse_error)
        {
          extern int remove_clear_locals;
          remove_clear_locals=args;
          if(vargs) remove_clear_locals++;
          do_code_block(n);
          remove_clear_locals=0x7fffffff;
        }
      }
      
      ret=define_function(name,
    		      type,
    		      modifiers,
    		      IDENTIFIER_PIKE_FUNCTION | vargs,
    		      &tmp);
    
    
    #ifdef DEBUG
      if(a_flag > 1)
        fprintf(stderr,"Identifer = %d\n",ret);
    #endif
    
      free_node(n);
      return ret;
    }