/***************************************************************************

  gbc_reserved_make.c

  (c) 2000-2017 Benoît Minisini <benoit.minisini@gambas-basic.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  MA 02110-1301, USA.

***************************************************************************/

#define __GBC_RESERVED_MAKE_C

#define PROJECT_COMP

#include <ctype.h>
#include <errno.h>

#include "gb_common.h"
#include "gb_reserved.h"
#include "gb_type_common.h"
#include "gb_pcode.h"
#include "gb_reserved_keyword.h"

#define HASH_SIZE 109

/*uint hash(const char *key, int len)
{
	int i;
	uint h = 0;
	for (i = 0; i < len; i++)
		h = (h << 4) + (h ^ (tolower(key[i])));
	
	return h % HASH_SIZE;
}*/

char buffer[1024];

char *read_line(FILE *f)
{
	return fgets(buffer, sizeof(buffer), f);
}

void read_write_until(FILE *in, FILE *out, char *until)
{
	char *line;
	
	for(;;)
	{
		line = read_line(in);
		if (!line)
			break;
		if (out) fputs(line, out);
		while (*line && ((unsigned char)*line <= ' '))
			line++;
		if (!strncmp(line, until, strlen(until)))
			break;
	}
}

#if 0
int main(int argc, char **argv)
{
  COMP_INFO *info;
  SUBR_INFO *subr;
  int len;
  int i, p, n;
	uint h;
	char c;
	char last[16];
	char next[16];
	FILE *in, *out;

	in = fopen("../share/gb_reserved_temp.h", "r");
	if (!in)
	{
		fprintf(stderr, "unable to open '../share/gb_reserved_temp.h': %s\n", strerror(errno));
		exit(1);
	}
	
	out = fopen("../share/gb_reserved_temp.h.tmp", "w");
	
	read_write_until(in, out, "int RESERVED_find_word(const char *word, int len)");

	read_write_until(in, out, "static void *jump[] = {");
	
	for (h = 0; h < 12; h++)
	{
		if ((h % 8) == 0)
			fprintf(out, "\t\t");
		fprintf(out, "&&__%02d, ", h);
		if ((h % 8) == 7)
			fprintf(out, "\n");
	}
	fprintf(out, "\n\t};\n\n");

	//printf("  goto *jump[h %% %d];\n\n", HASH_SIZE);
	fprintf(out, "\tgoto *jump[len];\n\n");
	
	fprintf(out, "__00:\n__01:\n\treturn -1;\n");
	
	for (h = 2; h < 12; h++)
	{
		fprintf(out, "__%02d:\n", h);
		
		*last = 0;
	
		for(;;)
		{
			n = -1;
			
			*next = 0;
			
			for (info = &COMP_res_info[1], i = 1; info->name; info++, i++)
			{
				len = strlen(info->name);
				if (len != h)
					continue;
				
				if (strcmp(info->name, last) <= 0)
					continue;
				
				if (*next == 0 || strcmp(info->name, next) <= 0)
				{
					strcpy(next, info->name);
					n = i;
				}
			}
			
			if (n < 0)
				break;
			
			info = &COMP_res_info[n];
			len = strlen(info->name);

			strcpy(last, info->name);
			
			fprintf(out, "\tif (");
			for (p = 0; p < len; p++)
			{
				if (p)
					fprintf(out, " && ");
				c = info->name[p];
				if (isalpha(c))
					fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(info->name[p]));
				else if (c == '\\')
					fprintf(out, "word[%d] == '\\\\'", p);
				else
					fprintf(out, "word[%d] == '%c'", p, info->name[p]);
			}
			/*printf("  if (!strncmp(\"");
			for (p = 0; p < len; p++) 
			{
				c = info->name[p];
				if (c == '\\')
					printf("\\\\");
				else
					putchar(tolower(info->name[p]));
			}
			printf("\", word, %d)", len);*/
			fprintf(out, ") return %d;\n", n);
		}
		
		fprintf(out, "\treturn -1;\n");
	}
	
	read_write_until(in, NULL, "}");
	read_write_until(in, NULL, "}");
	fprintf(out, "}\n");

	read_write_until(in, out, "static void *jump[] = {");
	
	for (h = 0; h < 12; h++)
	{
		if ((h % 8) == 0)
			fprintf(out, "\t\t");
		fprintf(out, "&&__%02d, ", h);
		if ((h % 8) == 7)
			fprintf(out, "\n");
	}
	fprintf(out, "\n\t};\n\n");

	fprintf(out, "\tgoto *jump[len];\n\n");
	
	fprintf(out, "__00:\n__01:\n\treturn -1;\n");

	for (h = 2; h < 12; h++)
	{
		fprintf(out, "__%02d:\n", h);
		
		*last = 0;
		
		for(;;)
		{
			n = -1;
			
			*next = 0;
			
			for (subr = &COMP_subr_info[0], i = 0; subr->name; subr++, i++)
			{
				len = strlen(subr->name);
				if (len != h)
					continue;
				
				if (strcmp(subr->name, last) <= 0)
					continue;
				
				if (*next == 0 || strcmp(subr->name, next) <= 0)
				{
					strcpy(next, subr->name);
					n = i;
				}
			}
			
			if (n < 0)
				break;
			
			subr = &COMP_subr_info[n];
			len = strlen(subr->name);

			strcpy(last, subr->name);
			
			fprintf(out, "\tif ("); //COMPARE_%d(\"%s\", word)) return %d;\n", len, len, info->name, i);
			for (p = 0; p < len; p++)
			{
				if (p)
					fprintf(out, " && ");
				c = subr->name[p];
				if (isalpha(c))
					fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(subr->name[p]));
				else if (c == '\\')
					fprintf(out, "word[%d] == '\\\\'", p);
				else
					fprintf(out, "word[%d] == '%c'", p, subr->name[p]);
			}
			fprintf(out, ") return %d;\n", n);
		}
		
		fprintf(out, "\treturn -1;\n");
	}
	
	fprintf(out, "}\n");
	
	fclose(in);
	fclose(out);
	
	unlink("../share/gb_reserved_temp.h");
	rename("../share/gb_reserved_temp.h.tmp", "../share/gb_reserved_temp.h");
	
	return 0;
}
#endif

static int compare_name(const char *name1, const char* name2)
{
	int l1 = strlen(name1);
	int l2 = strlen(name2);
	
	if (l1 < l2)
		return -1;
	if (l1 > l2)
		return 1;
	return strcasecmp(name1, name2);
}

int main(int argc, char **argv)
{
  COMP_INFO *info;
  SUBR_INFO *subr;
  int len;
  int i, p, n;
	uint h;
	char c;
	char last[16];
	char next[16];
	FILE *in, *out;

	in = fopen("../share/gb_reserved_temp.h", "r");
	if (!in)
	{
		fprintf(stderr, "unable to open '../share/gb_reserved_temp.h': %s\n", strerror(errno));
		exit(1);
	}
	
	out = fopen("../share/gb_reserved_temp.h.tmp", "w");
	if (!out)
	{
		fprintf(stderr, "unable to create '../share/gb_reserved_temp.h.tmp': %s\n", strerror(errno));
		exit(1);
	}
	
	read_write_until(in, out, "int RESERVED_find_word(const char *word, int len)");

	read_write_until(in, out, "static void *jump[] = {");
	
	for (h = 32; h < 127; h++)
	{
		if ((h % 8) == 0)
			fprintf(out, "\t\t");
		fprintf(out, "&&__%02X, ", h);
		if ((h % 8) == 7)
			fprintf(out, "\n");
	}
	fprintf(out, "\n\t};\n\n");

	//printf("  goto *jump[h %% %d];\n\n", HASH_SIZE);
	fprintf(out, "\tgoto *jump[*word - 32];\n\n");
	
	for (h = 32; h < 127; h++)
	{
		if (islower(h))
			continue;
		
		fprintf(out, "__%02X:\n", h);
		if (isupper(h))
			fprintf(out, "__%02X:\n", tolower(h));
		
		*last = 0;
	
		for(;;)
		{
			n = -1;
			
			*next = 0;
			
			for (info = &COMP_res_info[1], i = 1; info->name; info++, i++)
			{
				if  (tolower(*info->name) != tolower(h) || !info->name[1])
					continue;
				
				if (compare_name(info->name, last) <= 0)
					continue;
				
				if (*next == 0 || compare_name(info->name, next) <= 0)
				{
					strcpy(next, info->name);
					n = i;
				}
			}
			
			if (n < 0)
				break;
			
			info = &COMP_res_info[n];
			len = strlen(info->name);

			strcpy(last, info->name);
			
			fprintf(out, "\tif (len == %d", len);
			for (p = 1; p < len; p++)
			{
				fprintf(out, " && ");
				c = info->name[p];
				if (isalpha(c))
					fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(info->name[p]));
				else if (c == '\\')
					fprintf(out, "word[%d] == '\\\\'", p);
				else
					fprintf(out, "word[%d] == '%c'", p, info->name[p]);
			}
			/*printf("  if (!strncmp(\"");
			for (p = 0; p < len; p++) 
			{
				c = info->name[p];
				if (c == '\\')
					printf("\\\\");
				else
					putchar(tolower(info->name[p]));
			}
			printf("\", word, %d)", len);*/
			fprintf(out, ") return %d;\n", n);
		}
		
		fprintf(out, "\treturn -1;\n");
	}
	
	read_write_until(in, NULL, "}");
	read_write_until(in, NULL, "}");
	fprintf(out, "}\n");

	read_write_until(in, out, "static void *jump[] = {");
	
	for (h = 32; h < 127; h++)
	{
		if ((h % 8) == 0)
			fprintf(out, "\t\t");
		fprintf(out, "&&__%02X, ", h);
		if ((h % 8) == 7)
			fprintf(out, "\n");
	}
	fprintf(out, "\n\t};\n\n");

	fprintf(out, "\tgoto *jump[*word - 32];\n\n");
	
	for (h = 32; h < 127; h++)
	{
		if (islower(h))
			continue;
		
		fprintf(out, "__%02X:\n", h);
		if (isupper(h))
			fprintf(out, "__%02X:\n", tolower(h));
		
		*last = 0;
		
		for(;;)
		{
			n = -1;
			
			*next = 0;
			
			for (subr = &COMP_subr_info[0], i = 0; subr->name; subr++, i++)
			{
				if  (tolower(*subr->name) != tolower(h) || !subr->name[1])
					continue;
				
				if (compare_name(subr->name, last) <= 0)
					continue;
				
				if (*next == 0 || compare_name(subr->name, next) <= 0)
				{
					strcpy(next, subr->name);
					n = i;
				}
			}
			
			if (n < 0)
				break;
			
			subr = &COMP_subr_info[n];
			len = strlen(subr->name);

			strcpy(last, subr->name);
			
			fprintf(out, "\tif (len == %d", len);
			for (p = 1; p < len; p++)
			{
				fprintf(out, " && ");
				c = subr->name[p];
				if (isalpha(c))
					fprintf(out, "tolower(word[%d]) == '%c'", p, tolower(subr->name[p]));
				else if (c == '\\')
					fprintf(out, "word[%d] == '\\\\'", p);
				else
					fprintf(out, "word[%d] == '%c'", p, subr->name[p]);
			}
			fprintf(out, ") return %d;\n", n);
		}
		
		fprintf(out, "\treturn -1;\n");
	}
	
	fprintf(out, "}\n");
	
	fclose(in);
	fclose(out);
	
	unlink("../share/gb_reserved_temp.h");
	rename("../share/gb_reserved_temp.h.tmp", "../share/gb_reserved_temp.h");
	
	return 0;
}
