/* second.c - Phase II, table generation */

/* Written 1995-2000 by Werner Almesberger, EPFL-LRC/ICA */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>

#include "common.h"
#include "qgen.h"
#include "file.h"


int sym_tables = 0,symbols = 0;
static int unique;


static void dump_required(FIELD *start,FIELD *leader,int group)
{
    FIELD *walk;

    for (walk = start; walk; walk = walk->next) {
	if (walk->structure) {
	    dump_required(walk->my_block,leader,group);
	    continue;
	}
	if (walk->brk) break;
	if (!walk->value) {
	    if (!leader->has_required) {
		to_c("static int required_%d[] = {\n",group);
		leader->has_required = 1;
	    }
	    to_c("    %d, /* %s */\n",walk->field,walk->id);
	}
	else if (walk->value->type == vt_length)
		dump_required(walk->value->block,leader,group);
    }
    if (leader == start && leader && leader->has_required)
	to_c("    -1\n};\n\n");
}


static void find_required(FIELD *start)
{
    FIELD *walk;
    TAG *scan;

    for (walk = start; walk; walk = walk->next) {
	if (walk->structure) {
	    find_required(walk->my_block);
	    continue;
	}
	if (walk->value) 
	    switch  (walk->value->type) {
		case vt_id:
		    break;
		case vt_case:
		case vt_multi:
		    for (scan = walk->value->tags; scan; scan = scan->next)
			if (scan->block) {
			    scan->block->has_required = 0;
			    dump_required(scan->block,scan->block,scan->group);
			    find_required(scan->block);
			}
		    break;
		case vt_length:
		    find_required(walk->value->block);
		    break;
		default:
		    abort();
	    }
    }
}


static void groups(FIELD *start,int group)
{
    FIELD *walk;
    TAG *scan;

    for (walk = start; walk; walk = walk->next) {
	if (walk->structure) {
	    groups(walk->my_block,group);
	    continue;
	}
	if (walk->value)
	    switch  (walk->value->type) {
		case vt_id:
		    break;
		case vt_case:
		case vt_multi:
		    for (scan = walk->value->tags; scan; scan = scan->next) {
			int start_field,length,offset;

			if (scan->block && scan->block->structure &&
			  scan->block->structure->instances < 0) {
			    start_field = scan->block->structure->first_field;
			    length = scan->block->structure->fields;
			    offset = scan->block->my_block->field-
			      scan->block->structure->first_field;
			}
			else {
			    start_field = -1;
			    length = offset = 0;
			}
			if (scan->id) to_test("    \"%s\",\n",scan->id);
			if (scan->block && scan->block->has_required)
			    if (scan->block->id)
				to_c("    { %d, required_%d, %d, %d, %d }, "
				  "/* %s */\n",group,scan->group,start_field,
				  length,offset,scan->block->id);
			    else to_c("    { %d, required_%d, %d, %d, %d },\n",
				  group,scan->group,start_field,length,offset);
			else to_c("    { %d, NULL, %d, %d, %d },\n",group,
			      start_field,length,offset);
			groups(scan->block,scan->group);
		    }
		    break;
		case vt_length:
		    groups(walk->value->block,group);
		    break;
		default:
		    abort();
	    }
    }
}


static void values(FIELD *start)
{
    FIELD *walk;
    TAG *scan;
    VALUE_LIST *tag;

    for (walk = start; walk; walk = walk->next) {
	if (walk->structure) {
	    values(walk->my_block);
	}
	if (walk->value)
	    switch (walk->value->type) {
		case vt_id:
		    break;
		case vt_case:
		    if (*walk->id != '_') {
			to_c("static int values_%d[] = { /* %s */\n",
			  walk->field,walk->id);
			for (scan = walk->value->tags; scan; scan = scan->next)
			  {
			    to_c("    %s, %d,\n",scan->value,scan->group);
			    for (tag = scan->more; tag; tag = tag->next)
				to_c("    %s, %d,\n",tag->value,scan->group);
			    if (scan->deflt) to_c("    -2, %d,\n",scan->group);
				/* could also skip while entry, but maybe we'll
				   want to use the tags later */
			}
			to_c("    -1, -1\n};\n\n");
		    }
		    /* fall through */
		case vt_multi:
		    for (scan = walk->value->tags; scan; scan = scan->next)
			values(scan->block);
		    break;
		case vt_length:
		    values(walk->value->block);
		    break;
		default:
		    abort();
	    }
    }
}


static void fields(FIELD *start,int group)
{
    FIELD *walk;
    TAG *scan;
    VALUE_LIST *tag;

    for (walk = start; walk; walk = walk->next) {
	if (walk->structure) {
	    fields(walk->my_block,group);
	    continue;
	}
	if (*walk->id != '_') {
	    if (walk->value && walk->value->type == vt_case)
		to_c("    { %d, %d, %d, values_%d, %d }, /* %s */\n",group,
		  walk->pos,walk->size,walk->field,walk->var_len,walk->id);
	    else to_c("    { %d, %d, %d, NULL, %d }, /* %s */\n",group,
		  walk->pos,walk->size,walk->var_len,walk->id);
	}
	if (walk->value)
	    switch  (walk->value->type) {
		case vt_id:
		    break;
		case vt_case:
		case vt_multi:
		    for (scan = walk->value->tags; scan; scan = scan->next)
			fields(scan->block,scan->group);
		    to_test("static int unique_%d[] = { /* %s */\n",unique++,
		      walk->id);
		    for (scan = walk->value->tags; scan; scan = scan->next) {
			to_test("    %s,\n",scan->value);
			for (tag = scan->more; tag; tag = tag->next)
			    to_test("    %s,\n",tag->value);
		    }
		    to_test("    -1\n};\n\n");
		    break;
		case vt_length:
		    fields(walk->value->block,group);
		    break;
		default:
		    abort();
	    }
    }
}


static void symbolic_names(FIELD *start)
{
    FIELD *walk;
    NAME *name;
    TAG *scan;
    VALUE_LIST *tag;

    for (walk = start; walk; walk = walk->next) {
	if (walk->structure) {
	    symbolic_names(walk->my_block);
	    continue;
	}
	if (walk->name_list ? walk->name_list->id == -1 : !!walk->value) {
	    if (walk->name_list) {
		to_dump("static SYM_NAME dump_sym_%d[] = { /* %s */\n",
		  walk->seq,walk->name_list->list_name);
		sym_tables++;
		walk->name_list->id = walk->seq;
		for (name = walk->name_list->list; name; name = name->next) {
		    to_dump("    { %s, \"%s\" },\n",name->value,name->name);
		    symbols++;
		}
	    }
	    else {
		to_dump("static SYM_NAME dump_sym_%d[] = {\n",walk->seq);
		sym_tables++;
		switch (walk->value->type) {
		    case vt_id:
			to_dump("    { %s, \"%s\" },\n",walk->value->id,
			  walk->value->id);
			symbols++;
			break;
		    case vt_case:
		    case vt_multi:
			for (scan = walk->value->tags; scan; scan = scan->next)
			  {
			    to_dump("    { %s, \"%s\" },\n",scan->value,
			      scan->value);
			    symbols++;
			    for (tag = scan->more; tag; tag = tag->next) {
				to_dump("    { %s, \"%s\" },\n",tag->value,
				  tag->value);
				symbols++;
			    }
			}
			break;
		    case vt_length:
			break;
		    default:
			abort();
		}
	    }
	    to_dump("    { 0, NULL }\n};\n\n");
	}
	if (walk->value)
	    switch (walk->value->type) {
		case vt_id:
		    break;
		case vt_case:
		case vt_multi:
		    for (scan = walk->value->tags; scan; scan = scan->next)
			symbolic_names(scan->block);
		    break;
		case vt_length:
		    symbolic_names(walk->value->block);
		    break;
		default:
		    abort();
	    }
    }
}


void second(FIELD *def)
{
    int i;

    def->has_required = 0;
    to_c("\n/*\n");
    to_c(" * If a group contains required fields, these are listed in the\n");
    to_c(" * following arrays. Each list ends with -1. The variable names\n");
    to_c(" * end with the group number.\n */\n\n");
    dump_required(def,def,0);
    find_required(def);
    to_c("\n/*\n * Various information about groups.\n */\n\n");
    to_c("typedef struct {\n    int parent;\n    int *required;\n");
    to_c("    int start;\n    int length;\n    int offset;\n} GROUP;\n\n");
    to_c("static GROUP groups[] = {\n");
    if (def->has_required) to_c("    { -1, required_0 },\n");
    else to_c("    { -1, NULL },\n");
    to_test("static const char *groups[] = {\n");
    groups(def,0);
    to_test("    NULL\n};\n\n");
    to_c("};\n\n\n");
    to_c("/*\n * Named case selectors only have a limited set of valid\n");
    to_c(" * values. They are listed in the following arrays, each followed\n");
    to_c(" * by the number of the group it enables.\n */\n\n");
    values(def);
    to_c("\n/*\n * Various information about fields.\n */\n\n");
    to_c("typedef struct {\n    int parent;\n    int pos,size;\n");
    to_c("    int *values;\n    int actual;\n} FIELD;\n\n");
    to_c("static FIELD fields[] = {\n");
    fields(def,0);
    to_c("};\n\n");
    to_test("static int *unique[] = {\n");
    for (i = 0; i < unique; i++) to_test("    unique_%d,\n",i);
    to_test("    NULL\n};\n\n");
    if (dump) {
	to_dump("typedef struct {\n    unsigned long value;\n");
	to_dump("    const char *name;\n} SYM_NAME;\n\n");
	symbolic_names(def);
    }
}
