home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
t
/
toaster.zip
/
Compat
/
prelang.y
< prev
next >
Wrap
Text File
|
1991-12-31
|
21KB
|
723 lines
%{
# line 3 "prelang.y"
/* The above line is to give proper line number references. Please mail me
* if your compiler complains about it.
*/
/*
* This is the grammar definition of LPC. The token table is built
* automatically by make_func. The lang.y is constructed from this file,
* the generated token list and post_lang.y. The reason of this is that there
* is no #include-statment that yacc recognizes.
*/
#include <string.h>
#include <stdio.h>
#include <memory.h>
#if defined(sun)
#include <alloca.h>
#endif
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "exec.h"
#include "config.h"
#include "instrs.h"
#include "incralloc.h"
#include "switch.h"
#if defined(__GNUC__) && !defined(lint) && !defined(DEBUG)
#define INLINE inline
#else
#define INLINE
#endif
#define YYMAXDEPTH 600
/* NUMPAREAS areas are saved with the program code after compilation.
*/
#define A_PROGRAM 0
#define A_FUNCTIONS 1
#define A_STRINGS 2
#define A_VARIABLES 3
#define A_LINENUMBERS 4
#define A_INHERITS 5
#define A_ARGUMENT_TYPES 6
#define A_ARGUMENT_INDEX 7
#define NUMPAREAS 8
#define A_CASE_NUMBERS 8
#define A_CASE_STRINGS 9
#define A_CASE_LABELS 10
#define NUMAREAS 11
#define BREAK_ON_STACK 0x40000
#define BREAK_FROM_CASE 0x80000
/* make shure that this struct has a size that is a power of two */
struct case_heap_entry { int key; short addr; short line; };
#define CASE_HEAP_ENTRY_ALIGN(offset) offset &= -sizeof(struct case_heap_entry)
static struct mem_block mem_block[NUMAREAS];
/*
* Some good macros to have.
*/
#define BASIC_TYPE(e,t) ((e) == TYPE_ANY ||\
(e) == (t) ||\
(t) == TYPE_ANY)
#define TYPE(e,t) (BASIC_TYPE((e) & TYPE_MOD_MASK, (t) & TYPE_MOD_MASK) ||\
(((e) & TYPE_MOD_POINTER) && ((t) & TYPE_MOD_POINTER) &&\
BASIC_TYPE((e) & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER),\
(t) & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER))))
#define FUNCTION(n) ((struct function *)mem_block[A_FUNCTIONS].block + (n))
#define VARIABLE(n) ((struct variable *)mem_block[A_VARIABLES].block + (n))
#define align(x) (((x) + 3) & ~3)
/*
* If the type of the function is given, then strict types are
* checked and required.
*/
static int exact_types;
extern int pragma_strict_types; /* Maintained by lex.c */
extern int pragma_save_types; /* Also maintained by lex.c */
int approved_object; /* How I hate all these global variables */
extern int total_num_prog_blocks, total_prog_block_size;
extern int num_parse_error;
extern int d_flag;
static int heart_beat; /* Number of the heart beat function */
static int current_break_address;
static int current_continue_address;
static int current_case_number_heap;
static int current_case_string_heap;
#define SOME_NUMERIC_CASE_LABELS 0x40000
#define NO_STRING_CASE_LABELS 0x80000
static int zero_case_label;
static int current_type;
static int last_push_indexed;
static int last_push_local;
static int last_push_identifier;
static struct program NULL_program; /* marion - clean neat empty struct */
static char *get_two_types PROT((int type1, int type2));
void free_all_local_names(),
add_local_name PROT((char *, int)), smart_log PROT((char *, int, char *));
extern int yylex();
static int verify_declared PROT((char *));
static void copy_variables();
void type_error PROT((char *, int));
char *xalloc(), *string_copy();
extern int current_line;
/*
* 'inherit_file' is used as a flag. If it is set to a string
* after yyparse(), this string should be loaded as an object,
* and the original object must be loaded again.
*/
extern char *current_file, *inherit_file;
/*
* The names and types of arguments and auto variables.
*/
char *local_names[MAX_LOCAL];
unsigned short type_of_locals[MAX_LOCAL];
int current_number_of_locals = 0;
int current_break_stack_need = 0 ,max_break_stack_need = 0;
/*
* The types of arguments when calling functions must be saved,
* to be used afterwards for checking. And because function calls
* can be done as an argument to a function calls,
* a stack of argument types is needed. This stack does not need to
* be freed between compilations, but will be reused.
*/
static struct mem_block type_of_arguments;
struct program *prog; /* Is returned to the caller of yyparse */
/*
* Compare two types, and return true if they are compatible.
*/
int compatible_types(t1, t2)
int t1, t2;
{
if (t1 == TYPE_UNKNOWN || t2 == TYPE_UNKNOWN)
return 0;
if (t1 == t2)
return 1;
if (t1 == TYPE_ANY || t2 == TYPE_ANY)
return 1;
if ((t1 & TYPE_MOD_POINTER) && (t2 & TYPE_MOD_POINTER)) {
if ((t1 & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER) ||
(t2 & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER))
return 1;
}
return 0;
}
/*
* Add another argument type to the argument type stack
*/
static void add_arg_type(type)
unsigned short type;
{
struct mem_block *mbp = &type_of_arguments;
while (mbp->current_size + sizeof type > mbp->max_size) {
mbp->max_size <<= 1;
mbp->block = realloc((char *)mbp->block, mbp->max_size);
}
memcpy(mbp->block + mbp->current_size, &type, sizeof type);
mbp->current_size += sizeof type;
}
/*
* Pop the argument type stack 'n' elements.
*/
INLINE
static void pop_arg_stack(n)
int n;
{
type_of_arguments.current_size -= sizeof (unsigned short) * n;
}
/*
* Get type of argument number 'arg', where there are
* 'n' arguments in total in this function call. Argument
* 0 is the first argument.
*/
INLINE
int get_argument_type(arg, n)
int arg, n;
{
return
((unsigned short *)
(type_of_arguments.block + type_of_arguments.current_size))[arg - n];
}
INLINE
static void add_to_mem_block(n, data, size)
int n, size;
char *data;
{
struct mem_block *mbp = &mem_block[n];
while (mbp->current_size + size > mbp->max_size) {
mbp->max_size <<= 1;
mbp->block = realloc((char *)mbp->block, mbp->max_size);
}
memcpy(mbp->block + mbp->current_size, data, size);
mbp->current_size += size;
}
static void ins_byte(b)
char b;
{
add_to_mem_block(A_PROGRAM, &b, 1);
}
/*
* Store a 2 byte number. It is stored in such a way as to be sure
* that correct byte order is used, regardless of machine architecture.
* Also beware that some machines can't write a word to odd addresses.
*/
static void ins_short(l)
short l;
{
add_to_mem_block(A_PROGRAM, (char *)&l + 0, 1);
add_to_mem_block(A_PROGRAM, (char *)&l + 1, 1);
}
static void upd_short(offset, l)
int offset;
short l;
{
mem_block[A_PROGRAM].block[offset + 0] = ((char *)&l)[0];
mem_block[A_PROGRAM].block[offset + 1] = ((char *)&l)[1];
}
static short read_short(offset)
int offset;
{
short l;
((char *)&l)[0] = mem_block[A_PROGRAM].block[offset + 0];
((char *)&l)[1] = mem_block[A_PROGRAM].block[offset + 1];
return l;
}
/*
* Store a 4 byte number. It is stored in such a way as to be sure
* that correct byte order is used, regardless of machine architecture.
*/
static void ins_long(l)
int l;
{
add_to_mem_block(A_PROGRAM, (char *)&l+0, 1);
add_to_mem_block(A_PROGRAM, (char *)&l+1, 1);
add_to_mem_block(A_PROGRAM, (char *)&l+2, 1);
add_to_mem_block(A_PROGRAM, (char *)&l+3, 1);
}
static void ins_f_byte(b)
unsigned int b;
{
ins_byte((char)(b - F_OFFSET));
}
/*
* Return the index of the function found, otherwise -1.
*/
static int defined_function(s)
char *s;
{
int offset;
struct function *funp;
for (offset = 0; offset < mem_block[A_FUNCTIONS].current_size;
offset += sizeof (struct function)) {
funp = (struct function *)&mem_block[A_FUNCTIONS].block[offset];
if (funp->flags & NAME_HIDDEN)
continue;
if (strcmp(funp->name, s) == 0)
return offset / sizeof (struct function);
}
return -1;
}
/*
* A mechanism to remember addresses on a stack. The size of the stack is
* defined in config.h.
*/
static int comp_stackp;
static int comp_stack[COMPILER_STACK_SIZE];
static void push_address() {
if (comp_stackp >= COMPILER_STACK_SIZE) {
yyerror("Compiler stack overflow");
comp_stackp++;
return;
}
comp_stack[comp_stackp++] = mem_block[A_PROGRAM].current_size;
}
static void push_explicit(address)
int address;
{
if (comp_stackp >= COMPILER_STACK_SIZE) {
yyerror("Compiler stack overflow");
comp_stackp++;
return;
}
comp_stack[comp_stackp++] = address;
}
static int pop_address() {
if (comp_stackp == 0)
fatal("Compiler stack underflow.\n");
if (comp_stackp > COMPILER_STACK_SIZE) {
--comp_stackp;
return 0;
}
return comp_stack[--comp_stackp];
}
/*
* Initialize the environment that the compiler needs.
*/
static void prolog() {
int i;
if (type_of_arguments.block == 0) {
type_of_arguments.max_size = 100;
type_of_arguments.block = xalloc(type_of_arguments.max_size);
}
type_of_arguments.current_size = 0;
approved_object = 0;
last_push_indexed = -1;
last_push_local = -1;
last_push_identifier = -1;
prog = 0; /* 0 means fail to load. */
heart_beat = -1;
comp_stackp = 0; /* Local temp stack used by compiler */
current_continue_address = 0;
current_break_address = 0;
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 = xalloc(START_BLOCK_SIZE);
mem_block[i].current_size = 0;
mem_block[i].max_size = START_BLOCK_SIZE;
}
}
/*
* Patch a function definition of an inherited function, to what it really
* should be.
* The name of the function can be one of:
* object::name
* ::name
* name
* Where 'object' is the name of the superclass.
*/
static void find_inherited(funp)
struct function *funp;
{
int i;
struct inherit *ip;
int num_inherits, super_length;
char *real_name, *super_name = 0, *p;
real_name = funp->name;
if (real_name[0] == ':')
real_name = real_name + 2; /* There will be exactly two ':' */
else if (p = strchr(real_name, ':')) {
real_name = p+2;
super_name = funp->name;
super_length = real_name - super_name - 2;
}
num_inherits = mem_block[A_INHERITS].current_size /
sizeof (struct inherit);
ip = (struct inherit *)mem_block[A_INHERITS].block;
for (; num_inherits > 0; ip++, num_inherits--) {
if (super_name) {
int l = strlen(ip->prog->name); /* Including .c */
if (l - 2 < super_length)
continue;
if (strncmp(super_name, ip->prog->name + l - 2 - super_length,
super_length) != 0)
continue;
}
for (i=0; i < ip->prog->num_functions; i++) {
if (strcmp(ip->prog->functions[i].name, real_name) != 0)
continue;
if (ip->prog->functions[i].flags & NAME_UNDEFINED)
continue;
funp->offset = ip - (struct inherit *)mem_block[A_INHERITS].block;
funp->flags = ip->prog->functions[i].flags | NAME_INHERITED;
funp->num_local = ip->prog->functions[i].num_local;
funp->num_arg = ip->prog->functions[i].num_arg;
funp->type = ip->prog->functions[i].type;
funp->function_index_offset = i;
return;
}
}
return;
}
/*
* The program has been compiled. Prepare a 'struct program' to be returned.
*/
void epilog() {
int size, i;
char *p;
struct function *funp;
static int current_id_number = 1;
#ifdef DEBUG
if (num_parse_error == 0 && type_of_arguments.current_size != 0)
fatal("Failed to deallocate argument type stack\n");
#endif
/*
* 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->name == 0)
continue;
find_inherited(funp);
}
if (num_parse_error > 0) {
prog = 0;
for (i=0; i<NUMAREAS; i++)
free(mem_block[i].block);
return;
}
size = align(sizeof (struct program));
for (i=0; i<NUMPAREAS; i++)
size += align(mem_block[i].current_size);
p = (char *)xalloc(size);
prog = (struct program *)p;
*prog = NULL_program;
prog->total_size = size;
prog->ref = 0;
prog->heart_beat = heart_beat;
prog->name = string_copy(current_file);
prog->id_number = current_id_number++;
total_prog_block_size += prog->total_size;
total_num_prog_blocks += 1;
p += align(sizeof (struct program));
prog->program = p;
if (mem_block[A_PROGRAM].current_size)
memcpy(p, mem_block[A_PROGRAM].block,
mem_block[A_PROGRAM].current_size);
prog->program_size = mem_block[A_PROGRAM].current_size;
p += align(mem_block[A_PROGRAM].current_size);
prog->line_numbers = (unsigned short *)p;
if (mem_block[A_LINENUMBERS].current_size)
memcpy(p, mem_block[A_LINENUMBERS].block,
mem_block[A_LINENUMBERS].current_size);
p += align(mem_block[A_LINENUMBERS].current_size);
prog->functions = (struct function *)p;
prog->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->strings = (char **)p;
prog->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->variable_names = (struct variable *)p;
prog->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->inherit = (struct inherit *)p;
prog->num_inherited = mem_block[A_INHERITS].current_size /
sizeof (struct inherit);
if (prog->num_inherited)
memcpy(p, mem_block[A_INHERITS].block,
mem_block[A_INHERITS].current_size);
prog->argument_types = 0; /* For now. Will be fixed someday */
prog->type_start = 0;
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; i < prog->num_inherited; i++) {
reference_prog (prog->inherit[i].prog, "inheritance");
}
}
static int define_new_function(name, num_arg, num_local, offset, flags, type)
char *name;
int num_arg, num_local;
int offset, flags, type;
{
int num;
struct function fun;
unsigned short argument_start_index;
num = defined_function(name);
if (num >= 0) {
struct function *funp;
/*
* The function was already defined. It may be one of several reasons:
*
* 1. There has been a prototype.
* 2. There was the same function defined by inheritance.
* 3. This function has been called, but not yet defined.
* 4. The function is double defined.
* 5. A "late" prototype has been encountered.
*/
funp = (struct function *)(mem_block[A_FUNCTIONS].block) + num;
if (!(funp->flags & NAME_UNDEFINED) &&
!(flags & NAME_PROTOTYPE) &&
!(funp->flags & NAME_INHERITED))
{
char buff[500];
sprintf(buff, "Redeclaration of function %s.", name);
yyerror(buff);
return num;
}
/*
* It was either an undefined but used funtion, or an inherited
* function. In both cases, we now consider this to be THE new
* definition. It might also have been a prototype to an already
* defined function.
*
* Check arguments only when types are supposed to be tested,
* and if this function really has been defined already.
*/
if (exact_types && funp->type != TYPE_UNKNOWN) {
int i;
if (funp->num_arg != num_arg && !(funp->type & TYPE_MOD_VARARGS))
yyerror("Incorrect number of arguments.");
else if (!(funp->flags & NAME_STRICT_TYPES))
yyerror("Called function not compiled with type testing.\n");
else {
/* Now check that argument types wasn't changed. */
for (i=0; i < num_arg; i++) {
}
}
}
/* If it was yet another prototype, then simply return. */
if (flags & NAME_PROTOTYPE)
return num;
funp->num_arg = num_arg;
funp->num_local = num_local;
funp->flags = flags;
funp->offset = offset;
funp->function_index_offset = 0;
funp->type = type;
if (exact_types)
funp->flags |= NAME_STRICT_TYPES;
return num;
}
if (strcmp(name, "heart_beat") == 0)
heart_beat = mem_block[A_FUNCTIONS].current_size /
sizeof (struct function);
fun.name = make_shared_string(name);
fun.offset = offset;
fun.flags = flags;
fun.num_arg = num_arg;
fun.num_local = num_local;
fun.function_index_offset = 0;
fun.type = type;
if (exact_types)
fun.flags |= NAME_STRICT_TYPES;
num = mem_block[A_FUNCTIONS].current_size / sizeof fun;
/* Number of local variables will be updated later */
add_to_mem_block(A_FUNCTIONS, (char *)&fun, sizeof fun);
if (exact_types == 0 || num_arg == 0) {
argument_start_index = INDEX_START_NONE;
} else {
int i;
/*
* Save the start of argument types.
*/
argument_start_index =
mem_block[A_ARGUMENT_TYPES].current_size /
sizeof (unsigned short);
for (i=0; i < num_arg; i++) {
add_to_mem_block(A_ARGUMENT_TYPES, &type_of_locals[i],
sizeof type_of_locals[i]);
}
}
add_to_mem_block(A_ARGUMENT_INDEX, &argument_start_index,
sizeof argument_start_index);
return num;
}
static void define_variable(name, type, flags)
char *name;
int type;
int flags;
{
struct variable dummy;
dummy.name = make_shared_string(name);
dummy.type = type;
dummy.flags = flags;
add_to_mem_block(A_VARIABLES, (char *)&dummy, sizeof dummy);
}
short store_prog_string(str)
char *str;
{
short i;
char **p;
p = (char **) mem_block[A_STRINGS].block;
str = make_shared_string(str);
for (i=mem_block[A_STRINGS].current_size / sizeof str -1; i>=0; --i)
if (p[i] == str) {
free_string(str); /* Needed as string is only free'ed once. */
return i;
}
add_to_mem_block(A_STRINGS, &str, sizeof str);
return mem_block[A_STRINGS].current_size / sizeof str - 1;
}
void add_to_case_heap(block_index,entry)
int block_index;
struct case_heap_entry *entry;
{
char *heap_start;
int offset,parent;
int current_heap;
if ( block_index == A_CASE_NUMBERS )
current_heap = current_case_number_heap;
else
current_heap = current_case_string_heap;
offset = mem_block[block_index].current_size - current_heap;
add_to_mem_block(block_index, (char*)entry, sizeof(*entry) );
heap_start = mem_block[block_index].block + current_heap;
for ( ; offset; offset = parent ) {
parent = ( offset - sizeof(struct case_heap_entry) ) >> 1 ;
CASE_HEAP_ENTRY_ALIGN(parent);
if ( ((struct case_heap_entry*)(heap_start+offset))->key <
((struct case_heap_entry*)(heap_start+parent))->key )
{
*(struct case_heap_entry*)(heap_start+offset) =
*(struct case_heap_entry*)(heap_start+parent);
*(struct case_heap_entry*)(heap_start+parent) = *entry;
}
}
}
%}
/*
* These values are used by the stack machine, and can not be directly
* called from LPC.
*/
%token F_JUMP F_JUMP_WHEN_ZERO F_JUMP_WHEN_NON_ZERO
%token F_POP_VALUE F_DUP
%token F_STORE F_CALL_FUNCTION_BY_ADDRESS
%token F_PUSH_IDENTIFIER_LVALUE F_PUSH_LOCAL_VARIABLE_LVALUE
%token F_PUSH_INDEXED_LVALUE F_INDIRECT F_INDEX_INSTR
%token F_CONST0 F_CONST1
/*
* These are the predefined functions that can be accessed from LPC.
*/
%token F_IF F_IDENTIFIER F_LAND F_LOR F_STATUS
%token F_RETURN F_STRING
%token F_INC F_DEC
%token F_POST_INC F_POST_DEC F_COMMA
%token F_NUMBER F_ASSIGN F_INT F_ADD F_SUBTRACT F_MULTIPLY
%token F_DIVIDE F_LT F_GT F_EQ F_GE F_LE
%token F_NE
%token F_ADD_EQ F_SUB_EQ F_DIV_EQ F_MULT_EQ
%token F_NEGATE
%token F_SUBSCRIPT F_WHILE F_BREAK
%token F_DO F_FOR F_SWITCH
%token F_SSCANF F_PARSE_COMMAND F_STRING_DECL F_LOCAL_NAME
%token F_ELSE F_DESCRIBE
%token F_CONTINUE
%token F_MOD F_MOD_EQ F_INHERIT F_COLON_COLON
%token F_STATIC
%token F_ARROW F_AGGREGATE
%token F_COMPL F_AND F_AND_EQ F_OR F_OR_EQ F_XOR F_XOR_EQ
%token F_LSH F_LSH_EQ F_RSH F_RSH_EQ
%token F_CATCH
%token F_OBJECT F_VOID F_MIXED F_PRIVATE F_NO_MASK F_NOT
%token F_PROTECTED F_PUBLIC
%token F_VARARGS