lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
%{
#include "global.h"
#include <ctype.h>
#include <fcntl.h>
#include <stdarg.h>

#include "interpret.h"
#define _YACC_

extern int yydebug;
#ifndef YYDEBUG
#define YYDEBUG
#endif


char *func_spec,*language,*the_lang,*efun_protos;
char *opcode_statistics,*opcode_lang,*opc_cost;


#ifndef BUFSIZ
#define BUFSIZ 		1024
#endif

#define NELEMS(arr) 	(sizeof arr / sizeof arr[0])

int num_buff;
int report_newline=0;

char my_tolower(char s)
{
  if(isupper(s)) return tolower(s);
  return s;
}

char my_toupper(char s)
{
  if(islower(s)) return toupper(s);
  return s;
}

/* For quick sort purposes : */
struct instruction
{
 /* input from func_spec */
  char *key;
  char *buf;
  char has_token;
  char is_efun;

 /* input opcode_statistics */
  int nr;
  char *name;
  int used;
  int compiled;
  float cost;
  float time;

/* output data */
  int eval_cost;
};

struct instruction instrs[MAX_FUNC];

int min_arg = -1, limit_max = 0;

/*
 * arg_types is the types of all arguments. A 0 is used as a delimiter,
 * marking next argument. An argument can have several types.
 */
int arg_types[MAX_FUNC*MAX_LOCAL], last_current_type;

/*
 * Store the types of the current efun. They will be copied into the
 * arg_types list if they were not already there (to save memory).
 */
int curr_arg_types[MAX_LOCAL], curr_arg_type_size;

void yyerror PROT((char *));
int yylex();
int yyparse();
int ungetc PROT((int c, FILE *f));
char *type_str PROT((int)), *etype PROT((int)), *etype1 PROT((int)),
   *ctype PROT((int));
#ifndef toupper
int toupper PROT((int));
#endif

void fatal(char *str, ...) ATTRIBUTE((noreturn,format (printf,1,2)));
void fatal(char *str, ...)
{
  va_list args;

  va_start(args,str);
  VFPRINTF(stderr, str, args);
  va_end(args);
  exit(1);
}

%}
%union {
    int number;
    char *string;
}

%token ID TOKEN NEWLINE

%token VOID INT STRING OBJECT MAPPING LIST MIXED UNKNOWN FLOAT FUNCTION
%token DEFAULT CONST SIDE_FX REGULAR_EXPRESSION

%type <number> type VOID INT STRING OBJECT MAPPING LIST MIXED UNKNOWN REGULAR_EXPRESSION
%type <number> arg_list basic typel CONST cons
%type <number> arg_type typel2 FLOAT FUNCTION

%type <string> ID optional_ID optional_default

%%

funcs: /* empty */ | funcs func ;

optional_ID: ID | /* empty */ { $$ = ""; } ;

optional_default: DEFAULT ':' ID { $$ = $3; } | /* empty */ { $$="0"; } ;

func: type ID optional_ID '(' arg_list optional_default ')' ';'
    {
      char buff[500];
      char f_name[500];
      int i;
      if (min_arg == -1)  min_arg = $5;
      if ($3[0] == '\0')
      {
	int len;
	if (strlen($2) + 1 + 2 > sizeof f_name)
	  fatal("A local buffer was too small!(1)\n");
	sprintf(f_name, "F_%s", $2);
	len = strlen(f_name);
	for (i=0; i < len; i++)
        {
	  if (islower(f_name[i]))  f_name[i] = toupper(f_name[i]);
	}
        instrs[num_buff].has_token=1;
      }else{
	if (strlen($3) + 1 > sizeof f_name)
	  fatal("A local buffer was too small(2)!\n");
	strcpy(f_name, $3);
	instrs[num_buff].has_token=0;
      }
      for(i=0; i < last_current_type; i++)
      {
	int j;
	for (j = 0; j+i<last_current_type && j < curr_arg_type_size; j++)
	{
	  if (curr_arg_types[j] != arg_types[i+j])
  	    break;
	}
	if (j == curr_arg_type_size) break;
      }
      if (i == last_current_type)
      {
	int j;
	for (j=0; j < curr_arg_type_size; j++)
        {
	  arg_types[last_current_type++] = curr_arg_types[j];
	  if (last_current_type == NELEMS(arg_types))
	    yyerror("Array 'arg_types' is too small");
	}
      }
      sprintf(buff, "{\"%s\",%s,%d,%d,%s,%s,%s,%d,%s,f_%s},\n",
	      $2,
	      f_name,
	      min_arg,
	      limit_max ? -1 : $5,
	      ctype($1),
	      etype(0),
	      etype(1),
	      i,
	      $6,
	      $2);
      if (strlen(buff) > sizeof buff)
        fatal("Local buffer overwritten !\n");

      instrs[num_buff].key = (char *) malloc(strlen($2)+1 );
      strcpy(instrs[num_buff].key, $2);

      instrs[num_buff].buf = (char *) malloc(strlen(buff) + 1);
      strcpy(instrs[num_buff].buf, buff);

      instrs[num_buff].is_efun=1;
      num_buff++;
      min_arg = -1;
      limit_max = 0;
      curr_arg_type_size = 0;
    }
   | TOKEN token_list NEWLINE
   ;

token_list:
          | token_list ID
          {
            int e;
            char buff[500];
            char funname[500];

            for(e=0;$2[e];e++) funname[e]=my_tolower($2[e]);

            funname[e]=0;

            instrs[num_buff].key=malloc(strlen(funname)-1);
            strcpy(instrs[num_buff].key,funname+2);

	    sprintf(buff, "{\"%s\",%s,-1,0,0,0,0,0,0,NULL},\n",
		    funname+2,
		    $2);

            instrs[num_buff].is_efun=0;
            instrs[num_buff].buf = (char *) malloc(strlen(buff)+1);
            strcpy(instrs[num_buff].buf, buff);
            instrs[num_buff].has_token=1;
            num_buff++;
          };
          

cons:       { $$=0; }
    | CONST { $$=0x20000; }
    | SIDE_FX { $$=0x40000; }

type: cons basic     { $$ = $1 | $2; }
    | cons basic '*' { $$ = $1 | $2 | 0x10000; };

basic: VOID | FLOAT | FUNCTION | INT | STRING | MIXED | UNKNOWN | OBJECT
   | MAPPING | LIST | REGULAR_EXPRESSION;

arg_list: /* empty */		{ $$ = 0; }
	| typel2		{ $$ = 1; if ($1) min_arg = 0; }
	| arg_list ',' typel2 	{ $$ = $1 + 1; if ($3) min_arg = $$ - 1; } ;

typel2: typel
    {
	$$ = $1;
	curr_arg_types[curr_arg_type_size++] = 0;
	if (curr_arg_type_size == NELEMS(curr_arg_types))
	    yyerror("Too many arguments");
    } ;

arg_type: type
    {
	if ($1 != VOID) {
	    curr_arg_types[curr_arg_type_size++] = $1;
	    if (curr_arg_type_size == NELEMS(curr_arg_types))
		yyerror("Too many arguments");
	}
	$$ = $1;
    } ;

typel: arg_type			{ $$ = ($1 == VOID && min_arg == -1); }
     | typel '|' arg_type 	{ $$ = (min_arg == -1 && ($1 || $3 == VOID));}
     | '.' '.' '.'		{ $$ = min_arg == -1 ; limit_max = 1; } ;

%%

struct type {
    char *name;
    int num;
} types[] = {
{ "void", VOID },
{ "int", INT },
{ "string", STRING },
{ "object", OBJECT },
{ "mapping", MAPPING },
{ "regular_expression", REGULAR_EXPRESSION },
{ "mixed", MIXED },
{ "unknown", UNKNOWN },
{ "float", FLOAT},
{ "function", FUNCTION},
{ "list", LIST },
{ "const", CONST },
{ "side_fx", SIDE_FX },
{ "default", DEFAULT },
};

FILE *f;
int current_line = 1;

typedef int (*cmpfuntyp) (const void *,const void *);

int time_cmp(const struct instruction *a,const struct instruction *b)
{
  return a->time-b->time;
}

void read_opc_statistics()
{
  FILE *ff;
  int e,d;
  int nr,compiled,used,post_cost;
  float time,cost;
  char name[1000];
  char buffer[1000];
  char buffer2[1000];
  char buffer3[1000];

  float sum_time;
  float x;
  int a,b,c;

  for(e=0;e<MAX_FUNC;e++) instrs[e].eval_cost=AVERAGE_COST;
  f=fopen(opcode_statistics,"r");
  ff=fopen(opcode_lang,"r");
  if(f && ff)
  {
    fgets(name,sizeof(name),f);
    for(e=0;!feof(f);e++)
    {
      fgets(buffer2,sizeof(buffer2),f);
      sscanf(buffer2,"%d %d %d %d %f %f %s",&nr,
	     &post_cost,&used,&compiled,&cost,&time,name);
      fseek(ff,0,0);
      d=-1;
      while(!feof(ff))
      {
	fgets(buffer,sizeof(buffer),ff);
	if(!strncmp(buffer,"#define",7))
	{
	  sscanf(buffer+7,"%s %d",buffer3,&d);
	  if(d==nr+F_OFFSET) break;
	}
      }
      if(d!=nr+F_OFFSET) continue;
      for(a=0;a<strlen(buffer3);a++) buffer3[a]=my_tolower(buffer3[a]);

      for(a=0;a<num_buff;a++)
        if(!strcmp(buffer3+2,instrs[a].key)) break;

      instrs[a].nr=nr;
      if(!instrs[a].key)
      {
        instrs[a].key=(char *)malloc(strlen(buffer3+2)+1);
        strcpy(instrs[a].key,buffer3+2);
      }
      instrs[a].used=used;
      instrs[a].compiled=compiled;
      instrs[a].cost=cost;  
      instrs[a].time=time;
    }
  }
  if(f) fclose(f);
  if(ff) fclose(ff);

  qsort((char *)instrs,num_buff,sizeof(instrs[0]),(cmpfuntyp)time_cmp);
  for(a=0;a<num_buff;a++)
    if(instrs[a].time!=0.0)
      break;
  sum_time=instrs[a+(num_buff-a)/2].time;

  for(e=0;e<num_buff;e++)
  {
    if(instrs[e].used)
    {
      x=instrs[e].time * (float)AVERAGE_COST / sum_time;
      if(x>(float)MAX_COST_PER_INSTR)
      {
	x=(float)MAX_COST_PER_INSTR;
	a=1;
      }
      if(x<=1.0)
      {
	x=1.0;
      }
      instrs[e].eval_cost=(int)x;
    }else{
      instrs[e].eval_cost=AVERAGE_COST;
    }
  }

  if(!(f=fopen(opc_cost,"w")))
  {
    fprintf(stderr,"Failed to open file opc_cost.h\n");
    exit(10);
  }
  fprintf(f,"/* This file was automatically generated by make_func, */\n");
  fprintf(f,"/* DO NOT CHANGE IT! -Profezzorn */\n\n");
  
  fprintf(f,"int func_cost_alist[] = {\n");
  b=c=0;
  for(e=0;e<num_buff;e++)
  {
    if(instrs[e].used>0)
    {
      if(!strcmp(instrs[e].key,"return") &&
	 !strcmp(instrs[e].key,"dumb_return"))
	instrs[e].eval_cost=AVERAGE_COST;

      for(d=0;d<strlen(instrs[e].key);d++)
	name[d]=my_toupper(instrs[e].key[d]);
      name[d]=0;
      c+=instrs[e].eval_cost;
      b++;
      fprintf(f,"  F_%-28s, %d,\n",name,instrs[e].eval_cost);
    }
  }
  fprintf(f,"} ;\n");
  fprintf(f,"/* Actual average %f */\n",(float)c/(float)b);
  fclose(f);
}

int used_cmp(struct instruction *a,struct instruction *b)
{
  return b->used-a->used;
}

int main(int argc,char ** argv)
{
  FILE *fdr,*fdw;
  int i;
  char buffer[BUFSIZ + 1],*c;
  void make_efun_table();

#ifdef YYDEBUG
  while(argc>1 &&  argv[1][0]=='-' && argv[1][1]=='y' && argv[1][2]==0)
  {
    argv++;
    argc--;
    yydebug++;
  }
#endif
  if(argc!=8)
  {
    fprintf(stderr,"Wrong number of arguments to make_func.\n");
    return 11;
  }
  func_spec=argv[1];
  language=argv[2];
  the_lang=argv[3];
  efun_protos=argv[4];
  opcode_statistics=argv[5];
  opcode_lang=argv[6];
  opc_cost=argv[7];

  if ((f = fopen(func_spec, "r")) == NULL) 
  {
    perror(func_spec);
    exit(1);
  }
  yyparse();
  fclose(f);

  read_opc_statistics();
  /* Now sort the main_list */
  qsort((char *)instrs,num_buff,sizeof(instrs[0]),(cmpfuntyp)used_cmp);
  make_efun_table();
  /* Now display it... */

  printf("{\n");
  for (i = 0; i < num_buff; i++)
  {
    if(instrs[i].buf)
      printf("%s", instrs[i].buf);
  }

  printf("\n};\nint efun_arg_types[] = {\n");

  for (i=0; i < last_current_type; i++)
  {
    if (arg_types[i] == 0)
      printf("0,\n");
    else
      printf("%s,", ctype(arg_types[i]));
  }
  printf("};\n");
  /*
   * Write all the tokens out.  Do this by copying the
   * pre-include portion of lang.y to lang.y, appending
   * this information, then appending the post-include
   * portion of lang.y.  It's done this way because I don't
   * know how to get YACC to #include %token files.  *grin*
   */
  if (!(fdr = fopen(language, "r"))) {
    perror(language);
    exit(1);
  }

  if (!(fdw = fopen(the_lang, "w")))
  {
    perror(the_lang);
    exit(1);
  }

  while((c=fgets(buffer,BUFSIZ,fdr)))
  {
    fwrite(buffer,1,strlen(buffer),fdw);
    if(!strcmp(c,"/* MAGIC MARKER */\n")) break;
  }
  
  for (i = 0; i < num_buff; i++)
  {
    int ch;
    if (instrs[i].has_token)
    {
      char *str;		/* It's okay to mung instrs[*] now */
      for (str = instrs[i].key; *str; str++)
	*str = my_toupper(*str);
      fprintf(fdw, "%%token F_%s\n", instrs[i].key);
      if(c)
      {
	ch=getc(fdr);
	if(ch!='\n') ungetc(ch,fdr);
      }
    }
  }

  while((c=fgets(buffer,BUFSIZ,fdr)))
    fwrite(buffer,1,strlen(buffer),fdw);

  fclose(fdr);
  fclose(fdw);
  return 0;
}

void yyerror(char *str)
{
  fprintf(stderr, "%s:%d: %s\n", func_spec, current_line, str);
  exit(1);
}

int ident(int c)
{
  char buff[100];
  int len, i;

  for (len=0; isalnum(c) || c == '_' || c=='%'; c = getc(f))
  {
    buff[len++] = c;
    if (len + 1 >= sizeof buff)
      fatal("Local buffer in ident() too small!\n");
    if (len == sizeof buff - 1)
    {
      yyerror("Too long indentifier");
      break;
    }
  }
  (void)ungetc(c, f);
  buff[len] = '\0';
  if(!strcmp(buff,"%token"))
  {
    report_newline=1;
    return TOKEN;
  }
     
  for (i=0; i < NELEMS(types); i++)
  {
    if (strcmp(buff, types[i].name) == 0)
    {
      yylval.number = types[i].num;
      return types[i].num;
    }
  }
  yylval.string = (char *)malloc(strlen(buff)+1);
  strcpy(yylval.string, buff);
  return ID;
}

char *type_str(int n)
{
  int i, type = n & 0xffff;

  for (i=0; i < NELEMS(types); i++)
  {
    if (types[i].num == type)
    {
      if (n & 0x10000)
      {
	static char buff[100];
	if (strlen(types[i].name) + 3 > sizeof buff)
	  fatal("Local buffer too small in type_str()!\n");
	sprintf(buff, "%s *", types[i].name);
	return buff;
      }
      return types[i].name;
    }
  }
  return "What?";
}

int yylex1()
{
  register int c;
    
  for(;;)
  {
    switch(c = getc(f))
    {
    case ' ':
    case '\t': continue;

    case '#':
    {
      int line;
      /* does any operating system support longer pathnames? */
      char aBuf[2048];

      fgets(aBuf, 2047, f);
      if (sscanf(aBuf, "%d", &line) == 2) current_line = line;
      current_line++;
      continue;
    }
    case '\n':
      current_line++;
      if(report_newline)
      {
	report_newline=0;
	return NEWLINE;
      }
      continue;

    case EOF: return -1;
    default:
      if (isalpha(c) || c=='%' || c=='_') return ident(c);
      return c;
    }
  }
}

int yylex() { return yylex1(); }

char *etype1(int n)
{
  if (n & 0x10000) return "BT_POINTER";
  switch(n)
  {
  case FLOAT: return "BT_FLOAT";
  case FUNCTION: return "BT_FUNCTION";
  case INT: return "BT_NUMBER";
  case OBJECT: return "BT_OBJECT";
  case MAPPING: return "BT_MAPPING";
  case REGULAR_EXPRESSION: return "BT_REGEXP";
  case LIST: return "BT_LIST";
  case STRING: return "BT_STRING";
  case MIXED:  return "0";		/* 0 means any type */
  default: yyerror("Illegal type for argument");
  }
  return "What?";
}

char *etype(int n)
{
  int i;
  int local_size = 100;
  char *buff = (char *)malloc(local_size);

  for (i=0; i < curr_arg_type_size; i++)
  {
    if (n == 0) break;
    if (curr_arg_types[i] == 0) n--;
  }
  if (i == curr_arg_type_size) return "0";
  buff[0] = '\0';
  for(; curr_arg_types[i] != 0; i++)
  {
    char *p;
    if (curr_arg_types[i] == VOID) continue;
    if (buff[0] != '\0') strcat(buff, "|");
    p = etype1(curr_arg_types[i]);
    /*
     * The number 2 below is to include the zero-byte and the next
     * '|' (which may not come).
     */
    if (strlen(p) + strlen(buff) + 2 > local_size)
    {
      fprintf(stderr, "Buffer overflow!\n");
      exit(1);
    }
    strcat(buff, etype1(curr_arg_types[i]));
  }
  if (!strcmp(buff, ""))  strcpy(buff, "BT_ANY");
  return buff;
}

char *ctype(int n)
{
  static char buff[200];	/* 100 is such a comfortable size :-) */
  char *p;

  buff[0] = '\0';

  if(n & 0x20000) strcat(buff,"TYPE_MOD_CONSTANT|");
  if(n & 0x40000) strcat(buff,"TYPE_MOD_SIDE_EFFECT|");
  if (n & 0x10000) strcat(buff, "TYPE_MOD_POINTER|");

  n &= 0xffff;
  switch(n)
  {
  case FLOAT: p = "TYPE_FLOAT"; break;
  case FUNCTION: p = "TYPE_FUNCTION"; break;
  case VOID: p = "TYPE_VOID"; break;
  case STRING: p = "TYPE_STRING"; break;
  case INT: p = "TYPE_NUMBER"; break;
  case OBJECT: p = "TYPE_OBJECT"; break;
  case MAPPING: p = "TYPE_MAPPING"; break;
  case REGULAR_EXPRESSION: p = "TYPE_REGULAR_EXPRESSION"; break;
  case LIST:   p = "TYPE_LIST"; break;
  case MIXED: p = "TYPE_ANY"; break;
  case UNKNOWN: p = "TYPE_UNKNOWN"; break;
  default:
    yyerror("Bad type!");
    return "";
  }
  strcat(buff, p);
  if (strlen(buff) + 1 > sizeof buff)
    fatal("Local buffer overwritten in ctype()");
  return buff;
}

void make_efun_table()
{
  FILE *fp;
  int i;

  fp = fopen(efun_protos,"w");
  if (!fp)
  {
    fprintf(stderr,"make_func: unable to open %s\n",efun_protos);
    exit(-1);
  }
  fprintf(fp,"/*\n\tThis file is automatically generated by make_func.\n");
  fprintf(fp,"\tdo not make any manual changes to this file.\n*/\n\n");
  for (i = 0; i < num_buff; i++)
  {
    if(instrs[i].is_efun)
      fprintf(fp,"void f_%s PROT((int, struct svalue *));\n",instrs[i].key);
    else
      fprintf(fp,"void f_%s PROT((void));\n",instrs[i].key);
  }
  fclose(fp);

}