/* This is to make emacs edit this in C mode: -*-C-*- */
/*
 * Compile an LPC file.
 */
void lpc_compile_file P3(FILE *, fin, FILE *, fout, char *, ident)
{
    int yyparse();

    output_file = fout;
    prolog(fin);
    yyparse();
    epilog(ident);
    fclose(fout);
}

/*
 * The program has been compiled. Prepare a 'struct program' to be returned.
 */
static void epilog P1(char *, ident)
{
    int size, i;
    char *p;
    struct function *funp;
	void save_binary PROT((struct program *prog, struct mem_block *includes,
		struct mem_block *patches));


    if (num_parse_error > 0 || inherit_file) {
        clean_parser();
        return;
    }

#ifdef DEBUG
    if (type_of_arguments.current_size != 0)
	fatal("Failed to deallocate argument type stack\n");
#endif
    /*
     * Define the __INIT function, but only if there was any code
     * to initialize.
     */
    if (mem_block[A_INITIALIZER].current_size) {
	define_new_function("__INIT", 0, init_dirty, 0, 0, 0);
	/* write out the initializer function */
	ins_string("\nstatic int _lpc___INIT(svalue *ret) {\n");
	add_to_mem_block(A_PROGRAM,mem_block[A_INITIALIZER].block, mem_block[A_INITIALIZER].current_size);
	ins_string("c_return(ret,&const0);\n}\n\n");
    }

    ins_string("int (*__FUNCS");
    if (ident) {
      ins_string("_");
      ins_string(ident);
    }
    ins_string("[])() = {\n"); 
    
    /*
     * If functions are undefined, replace them by definitions done
     * by inheritance. All explicit "name::func" are already resolved.
     */
    for (i = 0; i < mem_block[A_FUNCTIONS].current_size; i += sizeof *funp) {
      funp = (struct function *)(mem_block[A_FUNCTIONS].block + i);
      if (!(funp->flags & NAME_UNDEFINED) &&
	  !(funp->flags & NAME_INHERITED)) {
	ins_string("_lpc_");
	ins_string(funp->name);
	ins_string(",\n");
      } else {
	ins_string("0,\n");
      }
      if ((funp->flags & NAME_UNDEFINED) &&
	  (!find_in_table(funp, i))) { /* enforce proper M.I. semantics */
	  find_inherited(funp);
	}
    }
    ins_string("};\n");

    size = align(sizeof (struct program));
    for (i=0; i<NUMPAREAS; i++)
	if (i != A_LINENUMBERS && i != A_ARGUMENT_TYPES && i != A_ARGUMENT_INDEX)
	    size += align(mem_block[i].current_size);
    p = (char *)DXALLOC(size, 56, "epilog: 1");
    prog = (struct program *)p;
    *prog = NULL_program;
    prog->p.i.total_size = size;
    prog->p.i.ref = 0;
    prog->p.i.heart_beat = heart_beat;
    prog->name = string_copy(current_file);
    prog->p.i.id_number = get_id_number();
    total_prog_block_size += prog->p.i.total_size;
    total_num_prog_blocks += 1;

    prog->p.i.line_swap_index = -1;
    prog->p.i.line_numbers = (unsigned short *)
	XALLOC(mem_block[A_LINENUMBERS].current_size + sizeof(short));
    memcpy(((char*)&prog->p.i.line_numbers[1]),
	   mem_block[A_LINENUMBERS].block,
	   mem_block[A_LINENUMBERS].current_size);
    prog->p.i.line_numbers[0] =
	(mem_block[A_LINENUMBERS].current_size / sizeof(short)) + 1;
    
    p += align(sizeof (struct program));
    prog->p.i.program = p;
    fwrite(mem_block[A_PROGRAM].block, mem_block[A_PROGRAM].current_size, 1, output_file);
    prog->p.i.program_size = 0;

    prog->p.i.functions = (struct function *)p;
    prog->p.i.num_functions = mem_block[A_FUNCTIONS].current_size /
	sizeof (struct function);
    if (mem_block[A_FUNCTIONS].current_size)
	memcpy(p, mem_block[A_FUNCTIONS].block,
	       mem_block[A_FUNCTIONS].current_size);

    p += align(mem_block[A_FUNCTIONS].current_size);
    prog->p.i.strings = (char **)p;
    prog->p.i.num_strings = mem_block[A_STRINGS].current_size /
	sizeof (char *);
    if (mem_block[A_STRINGS].current_size)
	memcpy(p, mem_block[A_STRINGS].block,
	       mem_block[A_STRINGS].current_size);

    p += align(mem_block[A_STRINGS].current_size);
    prog->p.i.variable_names = (struct variable *)p;
    prog->p.i.num_variables = mem_block[A_VARIABLES].current_size /
	sizeof (struct variable);
    if (mem_block[A_VARIABLES].current_size)
	memcpy(p, mem_block[A_VARIABLES].block,
	       mem_block[A_VARIABLES].current_size);

    p += align(mem_block[A_VARIABLES].current_size);
    prog->p.i.num_inherited = mem_block[A_INHERITS].current_size /
	sizeof (struct inherit);
    if (prog->p.i.num_inherited) {
	memcpy(p, mem_block[A_INHERITS].block,
	       mem_block[A_INHERITS].current_size);
	prog->p.i.inherit = (struct inherit *)p;
    } else
	prog->p.i.inherit = 0;
    
    prog->p.i.argument_types = 0;	/* For now. Will be fixed someday */
    prog->p.i.type_start = 0;

#ifdef SAVE_BINARIES
#ifdef ALWAYS_SAVE_COMPILED_BINARIES
	save_binary(prog, &mem_block[A_INCLUDES], &mem_block[A_PATCH]);
#else
/* Always save binaries for compiled stuff.  They take quite a bit longer
 * to compile.
 */
    if (pragma_save_binaries || prog->p.i.program_size == 0) {
	save_binary(prog, &mem_block[A_INCLUDES], &mem_block[A_PATCH]);
    }
#endif
#endif

    swap_line_numbers(prog); /* do this after saving binary */
    
    for (i=0; i<NUMAREAS; i++)
        FREE((char *)mem_block[i].block);

    /*  marion
	Do referencing here - avoid multiple referencing when an object
	inherits more than one object and one of the inherited is already
	loaded and not the last inherited
    */
    reference_prog (prog, "epilog");
    for (i = 0; (unsigned)i < prog->p.i.num_inherited; i++) {
	reference_prog (prog->p.i.inherit[i].prog, "inheritance");
    }
    end_new_file();
}

/*
 * Initialize the environment that the compiler needs.
 */
static void prolog P1(FILE *, f) {
    int i;
    
	last_expression = 0;
    if (type_of_arguments.block == 0) {
	type_of_arguments.max_size = 100;
	type_of_arguments.block =
		DXALLOC(type_of_arguments.max_size, 57, "prolog: 1");
    }
    type_of_arguments.current_size = 0;
    approved_object = 0;
    prog = 0;		/* 0 means fail to load. */
    heart_beat = -1;
    comp_stackp = 0;	/* Local temp stack used by compiler */
    num_parse_error = 0;
    free_all_local_names();	/* In case of earlier error */
    /* Initialize memory blocks where the result of the compilation
     * will be stored.
     */
    for (i=0; i < NUMAREAS; i++) {
	mem_block[i].block = DXALLOC(START_BLOCK_SIZE, 58, "prolog: 2");
	mem_block[i].current_size = 0;
	mem_block[i].max_size = START_BLOCK_SIZE;
    }
    memset(string_tags, 0, sizeof(string_tags));
    freed_string = -1;
    current_block = A_PROGRAM;
    init_dirty = -1;
    registers_in_use = 0; registers_need_free = 0; dirty_register = -1;
#ifdef DEBUG
    on_stack = 0; num_prefab = 0;
#endif
    needs_comma = 0;
    stack_sp = simul_stack;
    keep_in_register = 0;
    ins_string("#include \"lpc_to_c.h\"\n");
    start_new_file(f);
}

/*
 * The program has errors, clean things up.
 */
static void clean_parser() {
    int i;
    struct function *funp;
    struct variable dummy;
    char *s;
    
    /*
     *  Free the simul_stack
     */

    while (stack_sp>simul_stack) stack_pop();

    /*
     * Free function stuff.
     */
    for (i = 0; i < mem_block[A_FUNCTIONS].current_size; i += sizeof *funp) {
	funp = (struct function *)(mem_block[A_FUNCTIONS].block + i);
	if (funp->name)
            free_string(funp->name);
    }
    for (i = 0; i < mem_block[A_STRINGS].current_size; i += sizeof(char *)) {
        ((char *)&s)[0] = mem_block[A_STRINGS].block[i + 0];
        ((char *)&s)[1] = mem_block[A_STRINGS].block[i + 1];
        ((char *)&s)[2] = mem_block[A_STRINGS].block[i + 2];
        ((char *)&s)[3] = mem_block[A_STRINGS].block[i + 3];
        free_string(s);
    }
    for (i = 0; i < mem_block[A_VARIABLES].current_size; i += sizeof dummy) {
        memcpy(&dummy, mem_block[A_VARIABLES].block + i, sizeof dummy);
        free_string(dummy.name);
    }
    
    prog = 0;
    for (i=0; i<NUMAREAS; i++)
        FREE(mem_block[i].block);
    if (num_parse_error)
	smart_log (NULL,0,NULL,1);
}

#ifdef DEBUG
static void lpc_fatal P1(char *, s)
{
  struct mem_block *mbp = &mem_block[A_PROGRAM];
  mbp->block[mbp->current_size]=0;
  printf("----------------SOURCE-------------------\n%s\n", mbp->block);
  printf("----------------STACK--------------------\n");
  printf("Svalues on stack: %i    prefabs in progress: %i\n", on_stack, num_prefab);
  print_stack();
  fatal(s);
}

static char buf[256];

static void check_for_garbage() {
  if (stack_sp > simul_stack) {
    sprintf(buf,"garbage on simul_stack at line %i in %s.\n",
	    current_line,current_file);
    lpc_fatal(buf);
  }
  if (on_stack || num_prefab) {
    sprintf(buf,"garbage on param_stack at line %i in %s.\n",
	    current_line,current_file);
    lpc_fatal(buf);
  }
  if (registers_in_use) {
    sprintf(buf,"lost registers (%x) at line %i in %s.\n",
	    registers_in_use,current_line,current_file);
    lpc_fatal(buf);
  }
  if (registers_need_free) {
    sprintf(buf,"unfreed registers (%x) at line %i in %s.\n",
	    registers_need_free,current_line,current_file);
    lpc_fatal(buf);
  }
}
#endif

static void insert_pop_value() {
  int backpatch;
  stack_pop();
  if (last_expression)
    switch ( last_expression_code ) {
    case F_ASSIGN:
      backpatch = F_VOID_ASSIGN;
      registers_need_free &= ~(1 << last_expression_register);
      break;
    case F_ADD_EQ:
      backpatch = F_VOID_ADD_EQ;
      registers_need_free &= ~(1 << last_expression_register);
      break;
    case F_PRE_INC:
    case F_POST_INC:
      backpatch = F_INC;
      registers_need_free &= ~(1 << last_expression_register);
      break;
    case F_PRE_DEC:
    case F_POST_DEC:
      backpatch = F_DEC;
      registers_need_free &= ~(1 << last_expression_register);
      break;
    default:
      backpatch = 0;
    } else backpatch = 0;
  generate_frees();
#ifdef LPC_DEBUG
  if (needs_comma) {
#ifdef DEBUG
    check_for_garbage();
#endif
    ins_string(",c_debug(\"");
    ins_string(current_file);
    ins_string_with_num("\",%i,0)\n",current_line);
  }
#endif
  needs_comma = 0;
  if (backpatch)
    change_last_expression(backpatch);
  last_expression = 0;
}
