/*
** $Id: compile.c,v 1.15 2012/06/10 13:07:35 dredd Exp $
**
** $Source: /cvsroot/swlpc/swlpc/lpc/compile.c,v $
** $Revision: 1.15 $
** $Date: 2012/06/10 13:07:35 $
** $State: Exp $
**
** Author: Mike McGaughey & Geoff Wong
** Copyright(C) 1993-1999
** geoff@shattered.org
** mmcg@cs.monash.edu.au
**
** See the file "Copying" distributed with this file.
*/
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#include "lpc.h"
#include "build.h"
#include "lexical.h"
#include "lexer.h"
#include "class.h"
//#define DEBUG
#ifdef DEBUG
#define D(X) printf("%s\n", X)
#else
#define D(X)
#endif
int LPC_parse_errors = 0;
Shared * LPC_error_buffer[MAX_COMPILE_ERRORS+1];
Shared ** inherit_file = NULL;
//#define free(X) 1
// FIX: should really go into the normal error buffer
extern char * load_error;
extern int legal_path(char *);
static double compile_av = 0.0;
void update_compile_av(int lines)
{
extern double consts[5];
static int current_time = 0;
static int last_time = 0;
int n;
double c;
static int acc = 0;
acc += lines;
current_time = time(0);
if (current_time == last_time) return;
n = current_time - last_time;
if (n < sizeof consts / sizeof consts[0])
c = consts[n];
else
c = 0; /* exp(-n / 900.0); */
compile_av = c * compile_av + acc * (1 - c) / n;
last_time = current_time;
acc = 0;
}
//
// Actually compiles up a Class
//
Class * LPC_compile_class(Shared * name, FILE * ofd)
{
int i, x, j;
int num_globs, num_consts, num_vars;
Class * ob = NULL;
Class * tob = NULL;
Val **gtab = NULL, **ttab = NULL;
CVal **ctab;
Class **local_inherit_ob;
extern Func *prog, *prog_head;
extern char *current_file;
extern Val **global_table;
extern CVal **constant_table;
extern int num_globals, num_constants;
extern int total_lines;
extern Class **inherit_ob;
start_new_file(ofd);
prog = 0;
LPC_parse_errors = 0;
inherit_ob = 0;
inherit_file = 0;
D("before yyparse");
/* invoke the parser */
(void) yyparse();
D("after yyparse");
/*
* half the brain damage after here is because
* yyparse() is not re-entrant. If there are inherited files
* they are loaded then this object is re-loaded. This can be
* awfully shit for long inheritance chains!
*
* What can I say but BAD DESIGN...
*
*/
#if 0
printf("S: prog_head %d.\n", prog_head);
#endif
prog = prog_head;
num_vars = num_globals;
num_globs = num_globals;
num_consts = num_constants;
update_compile_av(total_lines);
total_lines = 0;
local_inherit_ob = inherit_ob;
(void) fclose(ofd);
if ((inherit_file == 0) && (LPC_parse_errors > 0 || prog == 0))
{
if (prog)
{
/*
* Set the reference count to one. We don't want to confuse
* free_prog().
*/
(void) free_bad_prog((Func *) prog);
prog = NULL;
}
#if 0
if (LPC_parse_errors == 0 && prog == 0)
error("No program in object !\n");
#endif
yyerror("failed to compile");
goto failed;
}
gtab = global_table;
ctab = constant_table;
/* if inherit file, load them up, throw away progs, and copy vars */
/* an array of inherited files to load? */
if (inherit_file)
{
Shared ** tmp = inherit_file;
Class * tmp_code;
int _i = 0, _x = 0;
if (prog)
{
/* not really necessary; in case of changes later */
free_bad_prog((Func *) prog);
prog = NULL;
}
inherit_file = 0;
while (tmp[_i])
{
if (tmp[_i] == name)
{
/* need to free all of inherit_file */
for (_x = 0; tmp[_x]; _x++)
if (tmp[_x])
free_string(tmp[_x]);
free(tmp);
yyerror("illegal inheritance");
/* efun_error("Illegal to inherit self.\n"); */
goto failed;
}
/* NEED to set up globals so new inherit_ob and inherit_file
* variables are malloced ?? */
/*
* if we use find_object inherited objects have
* reset invoked in them.
* This allows objects us to inherit as yet unloaded objects
*/
#ifdef DEBUG
printf("load inherited class %s\n", tmp[_i]->str);
#endif
tmp_code = find_class(tmp[_i]);
if (tmp_code == NULL)
{
tmp_code = LPC_compile(tmp[_i], NULL, 1);
}
if (tmp_code == NULL)
{
yyerrorf("could not find class (%s) for inheritance", tmp[_i]->str);
/* efun_error("No such object to inherit.\n"); */
goto failed;
}
free_string(tmp[_i]);
tmp[_i] = 0;
_i++;
}
free(tmp);
#ifdef DEBUG
printf("recompile %s", name);
#endif
//rewind(ofd);
//ob = LPC_compile(name, ofd, 0);
//ob = LPC_compile(name, NULL, 1);
ob = LPC_compile(name, NULL, 0);
return ob;
}
// (void) fclose(ofd);
/* it parsed ok - now set up an object */
D("creating empty class");
ob = get_empty_class();
add_class_ref(ob, "Load");
if (local_inherit_ob)
{
int _i, offset;
for (_i = 0; local_inherit_ob[_i]; _i++)
/* this is only a 1 level deep check; it should be more! */
if (local_inherit_ob[_i]->name == name)
{
//free((char *) ob);
free(local_inherit_ob);
inherit_ob = 0;
yyerror("illegal inheritance");
/* efun_error("Illegal to inherit self.\n"); */
goto failed;
}
/* space leak here? */
ob->inherit = local_inherit_ob;
ob->inherit_offsets = (int *) malloc(sizeof(int) * (_i + 1));
offset = 0;
for (_i = 0; local_inherit_ob[_i]; _i++)
{
/* add ref to each inherit_ob */
ob->inherit_offsets[_i] = offset;
/* NEED: to check this for correctness */
offset += local_inherit_ob[_i]->num_variables;
num_vars += local_inherit_ob[_i]->num_variables;
add_class_ref(local_inherit_ob[_i], "Load");
#if 0
printf("add_func (%s:%s) O = %d\n",
name->str, local_inherit_ob[_i]->name->str, offset);
#endif
if (local_inherit_ob[_i]->inherited == 1) continue;
local_inherit_ob[_i]->inherited = 1;
add_functions(local_inherit_ob[_i], local_inherit_ob[_i],
(short) 0);
}
local_inherit_ob = 0;
inherit_ob = 0;
}
ob->name = shared_string_copy(name); /* make a perm copy of name */
ob->num_variables = (short) num_vars;
// Allocate a "default" instantiation of the class
// Mostly a backward compatability hack
// Handle global table stuff
if (num_vars > 0)
{
D("setting up vars");
/* FIX: space leaks in the following two? */
// FIX: ob->variables = (Val *) malloc(num_vars * sizeof(Val));
/* make a list of all global names - for save_object */
ttab = (Val **) malloc(sizeof(Val *) * num_vars);
#if 0
if (gtab)
bcopy(gtab, ttab, num_globals * sizeof(Val *));
#endif
i = 0;
if (ob->inherit)
{
/* i = num_globals; */
for (x = 0; (tob = ob->inherit[x]); x++)
{
for (j = 0; j < (int) tob->num_variables; j++)
{
ttab[i] = tob->global_table[j];
i++;
}
}
}
#if 1
for (j = 0; j < num_globals; j++)
{
ttab[j + i] = gtab[j];
}
#endif
ob->global_table = ttab;
}
else
{
ob->global_table = 0;
}
ob->constant_table = ctab;
ob->num_constants = num_consts;
if (prog)
{
ob->prog = (Func *) prog;
}
else
{
ob->prog = 0;
}
D("processing patches");
// essentially a second parse
if (!process_patches(ob))
{
// printf("Load error in %s\n", ob->name->str);
free_string(ob->name);
yyerror("unknown local function calls");
goto failed;
}
if (LPC_errors() > 0) goto failed;
if (gtab != NULL)
{
free(gtab);
gtab = NULL;
}
D("freeing lex space and returning");
free_lex_space();
D("freed lex");
free(current_file);
current_file = 0;
return ob;
failed:
// We come here on a compile errors
D("compile failed");
if (gtab != NULL)
{
free(gtab);
}
if (LPC_errors() > 0)
{
// FIX: clean up
if (ob != NULL)
{
if (ob->prog != NULL) free_prog(ob->prog);
ob->prog = NULL;
clean_free(ob);
}
}
free(current_file);
current_file = 0;
if (local_inherit_ob != NULL) free(local_inherit_ob);
inherit_ob = 0;
// FIX: ensure this is ok.
//free_lex_space();
return NULL;
}
//
// Compiles up a class and returns an instance of it
//
// FIX: Wiz_Mud should be a parameter or set via an API?
// FIX: path legality should be checked locally (not in runtime)
//
Class * LPC_compile(Shared * name, FILE * fp, int enter)
{
Class * code;
char *real_name = NULL;
char suid_on = 0;
extern int Wiz_Mud;
extern Class **inherit_ob;
D("LPC_compile 1");
if (fp == NULL)
{
struct stat c_st;
int name_length;
#ifdef LODEBUG
printf("LOAD: LPC_compile(%s)\n", name->str);
#endif
D("LPC_compile 2");
// We must clear inherit_ob, to be sure it is initialized to 0. It can
// still be pointing to something, where an error aborted execution.
inherit_ob = 0;
inherit_file = 0;
name_length = name->length;
// First check that the lpc-file exists.
real_name = malloc(name_length + 5);
(void) strcpy(real_name, name->str);
(void) strcat(real_name, ".T");
if ((!Wiz_Mud) || stat(real_name, &c_st) == (-1))
{
(void) strcpy(real_name + strlen(real_name) - 2, ".lpc");
}
if (stat(real_name, &c_st) == -1)
{
// printf("LPC_compile 3 (%s)\n", name->str);
#if 0
fprintf(stderr, "Could not load descr for %s\n", real_name);
#endif
yyerrorf("failed to find file: %s", real_name);
free(real_name);
//strcpy(load_error, "could not find file");
/* efun_error("Failed to load file \"%s\".\n", na); */
D("LPC_compile 3A");
return NULL;
}
suid_on = c_st.st_mode & 0100;
// Check if it's a legal name (argh - don't want go back to runtime).
if (!legal_path(real_name))
{
D("LPC_compile 4");
yyerrorf("illegal pathname: %s", real_name);
free(real_name);
//strcpy(load_error, "illegal path name");
/* efun_error("Illegal path name.\n"); */
return NULL;
}
fp = fopen(real_name, "r");
if (fp == NULL)
{
D("LPC_compile 5");
perror(real_name);
yyerrorf("could not read file: %s", real_name);
free(real_name);
/* efun_error("Could not read the file.\n"); */
return NULL;
}
LPC_set_file(real_name);
LPC_set_line(0);
}
else
{
LPC_set_file(name->str);
LPC_set_line(0);
}
D("LPC_compile_class");
code = LPC_compile_class(name, fp);
D("class compiled");
if (code)
{
code->file_was_suid = suid_on ? 1 : 0;
// enter it into class HT
if (enter) add_class(code);
}
if (real_name != NULL) free(real_name);
D("code returning");
return code;
}
void LPC_clear_errors()
{
int i;
for (i = 0; i < LPC_parse_errors; i++)
{
free_string(LPC_error_buffer[i]);
}
LPC_parse_errors = 0;
}
int LPC_errors()
{
return LPC_parse_errors;
}
/*
* Name: LPC_compile_errors
* Purpose: returns a buffer of errors which occurred during compilation
*/
Shared ** LPC_compile_errors()
{
return LPC_error_buffer;
}
float LPC_compile_av()
{
return (float)compile_av;
}