%{ #line 3 "prelang.y" /* The first 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> #if defined(_SEQUENT_) #include <malloc.h> #endif #include <memory.h> #include <sys/types.h> #include <sys/stat.h> #if defined(sun) #include <alloca.h> #endif #include "config.h" #include "lint.h" #include "interface.h" #include "object.h" #include "instrs.h" #include "incralloc.h" #include "switch.h" /* This is to see to it that we always go through xalloc() in main.c even from within the yacc-parser */ #ifdef malloc #undef malloc #endif #define malloc xalloc #define YYMAXDEPTH 600 #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]; static int pre_cond_first_address, pre_cond_last_address; static int post_cond_first_address, post_cond_last_address; static int invariant_first_address, invariant_last_address; /* * 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_MASK, (t) & TYPE_MASK) ||\ (((e) & TYPE_MOD_POINTER) && ((t) & TYPE_MOD_POINTER) &&\ BASIC_TYPE((e) & (TYPE_MASK & ~TYPE_MOD_POINTER),\ (t) & (TYPE_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 INHERIT(n) ((struct inherit *)mem_block[A_INHERITS].block + (n)) #define align(x) ( ((x) + (sizeof(void*)-1) ) & ~(sizeof(void*)-1) ) /* * 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 */ extern int pragma_save_binary; /* Save this in the binary shadow dir */ extern int pragma_no_inherit; extern int pragma_no_clone; int approved_object; /* How I hate all these global variables */ static int true_varargs; extern int total_num_prog_blocks, total_prog_block_size; extern int prog_code_size, prog_func_size, prog_var_size, prog_inherit_size, prog_string_size, prog_line_size; extern int num_parse_error; extern int d_flag; 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 char *get_type_name(int); /* * There is always function starting at address 0, which will execute * the initialization code. This code is spread all over the program, * with jumps to next initializer. The next variable keeps track of * the previous jump. After the last initializer, the jump will be changed * into a return(0) statement instead. * * A function named '.CTOR' will be defined, which will contain the * initialization code. If there was no initialization code, then the * function will not be defined. That is the usage of the * first_last_initializer_end variable. * * When inheriting from another object, a call will automatically be made * to call .CTOR in that code from the current .CTOR. */ static int last_initializer_end; static int first_last_initializer_end; void epilog(); static int check_declared (char *str); static void prolog(); static struct program NULL_program; /* marion - clean neat empty struct */ static char *get_two_types (int type1, int type2); void free_all_local_names(); int add_local_name (char *, int), smart_log (char *, int, char *); extern int yylex(); static int verify_declared (char *); static void copy_variables(); static void copy_inherits (struct program *, int, char *); void type_error (char *, int); static int check_inherits(struct program *); 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; /* * 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. */ 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. */ static int compatible_types(int t1, int t2) { if (t1 == TYPE_UNKNOWN || t2 == TYPE_UNKNOWN) return 0; if (t1 == t2) return 1; if ((t1|TYPE_MOD_NO_MASK|TYPE_MOD_STATIC|TYPE_MOD_PRIVATE|TYPE_MOD_PUBLIC) == (t2|TYPE_MOD_NO_MASK|TYPE_MOD_STATIC|TYPE_MOD_PRIVATE|TYPE_MOD_PUBLIC)) return 1; if (t1 == TYPE_ANY || t2 == TYPE_ANY) return 1; if ((t1 & TYPE_MOD_POINTER) && (t2 & TYPE_MOD_POINTER)) { if ((t1 & TYPE_MASK) == (TYPE_ANY|TYPE_MOD_POINTER) || (t2 & TYPE_MASK) == (TYPE_ANY|TYPE_MOD_POINTER)) return 1; } if (t1 == TYPE_MAPPING) return 1; return 0; } /* * Add another argument type to the argument type stack */ INLINE static void add_arg_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 = (char *) 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(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(int arg, int n) { return ((unsigned short *) (type_of_arguments.block + type_of_arguments.current_size))[arg - n]; } INLINE static void add_to_mem_block(int n, char *data, int size) { struct mem_block *mbp = &mem_block[n]; while (mbp->current_size + size > mbp->max_size) { mbp->max_size <<= 1; mbp->block = (char *) realloc((char *)mbp->block, mbp->max_size); } memcpy(mbp->block + mbp->current_size, data, size); mbp->current_size += size; } INLINE static unsigned mem_block_size(n, size) int n; int size; { return mem_block[n].current_size / size; } INLINE static void ins_byte(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. */ INLINE static void ins_short(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(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(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(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); } /* * Return 1 on success, 0 on failure. */ static int defined_function(char *s) { int offset; int inh; struct function *funp; char *super_name = 0; char *sub_name; char *real_name; char *search; extern char *findstring(char *); real_name = strrchr(s, ':') + 1; sub_name = strchr(s, ':') + 2; real_name = (findstring((real_name == (char *)1) ? s : real_name)); if(!real_name) return 0; if (sub_name == (char *)2) for (offset = 0; offset < mem_block[A_FUNCTIONS].current_size; offset += sizeof (struct function)) { funp = (struct function *)&mem_block[A_FUNCTIONS].block[offset]; /* Only index, prog, and type will be defined. */ if (real_name == funp->name) { function_index_found = offset / sizeof (struct function); function_prog_found = 0; function_type_mod_found = funp->type_flags & TYPE_MOD_MASK; function_inherit_found = mem_block[A_INHERITS].current_size / sizeof(struct inherit); return 1; } } else if (sub_name - s > 2) { super_name = xalloc(sub_name - s - 1); memcpy(super_name, s, sub_name - s - 2); super_name[sub_name - s - 2] = 0; if (!strcmp(super_name, "this")) return defined_function(sub_name); } else s = sub_name; /* Look for the function in the inherited programs */ for (inh = mem_block[A_INHERITS].current_size / sizeof (struct inherit) - 1; inh >= 0; inh -= ((struct inherit *)mem_block[A_INHERITS].block)[inh].prog-> num_inherited) { if (super_name && !strcmp(super_name, ((struct inherit *)mem_block[A_INHERITS].block)[inh].name)) search = sub_name; else search = s; if (search_for_ext_function (search, ((struct inherit *)mem_block[A_INHERITS].block)[inh].prog,-1)) { /* Adjust for inherit-type */ int type = ((struct inherit *)mem_block[A_INHERITS].block)[inh].type; if (function_type_mod_found & TYPE_MOD_PRIVATE) type &= ~TYPE_MOD_PUBLIC; if (function_type_mod_found & TYPE_MOD_PUBLIC) type &= ~TYPE_MOD_PRIVATE; function_type_mod_found |= type & TYPE_MOD_MASK; function_inherit_found += inh - (((struct inherit *)mem_block[A_INHERITS].block)[inh].prog-> num_inherited - 1); return 1; } } return 0; } /* * 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(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]; } static void define_new_function(char *name, char num_arg, unsigned char num_local, int offset, int type_flags, char first_default) { struct function fun; struct function *funp; unsigned short argument_start_index; if (defined_function (name)) { /* The function is already defined. * If it was defined by inheritance, make a new definition, * unless nomask applies. * If it was defined in the current program, use that definition. */ /* Point to the function definition found */ if (function_prog_found) { funp = &function_prog_found->functions[function_index_found]; } else funp = FUNCTION(function_index_found); /* If it was declared in the current program, and not a prototype, * it is a double definition. */ if (!funp->type_flags & NAME_PROTOTYPE && !function_prog_found) { char buff[500]; sprintf (buff, "Redeclaration of function %s", name); yyerror (buff); return; } /* If neither the new nor the old definition is a prototype, * it must be a redefinition of an inherited function. * Check for nomask. */ if ((funp->type_flags & TYPE_MOD_NO_MASK) && !(funp->type_flags & NAME_PROTOTYPE) && !(type_flags & NAME_PROTOTYPE)) { char buff[500]; sprintf (buff, "Illegal to redefine nomask function %s", name); yyerror (buff); return; } /* Check types */ if (exact_types && ((funp->type_flags & TYPE_MASK) != TYPE_UNKNOWN)) { if (funp->num_arg != num_arg && !(funp->type_flags & TYPE_MOD_VARARGS)) { yyerror("Incorrect number of arguments"); return; } /* * This is just a nuisance! /JnA else if (!(funp->type_flags & NAME_STRICT_TYPES)) { yyerror("Function called not compiled with type testing"); return; } */ #if 0 else { int i; /* Now check argument types */ for (i=0; i < num_arg; i++) { } } #endif } /* If it is a prototype for a function that has already been defined, * we don't need it. */ if (type_flags & NAME_PROTOTYPE && !function_prog_found) return; /* If the function was defined in an inherited program, we need to * make a new definition here. */ if (function_prog_found) funp = &fun; } else /* Function was not defined before, we need a new definition */ funp = &fun; funp->offset = offset; funp->type_flags = type_flags; funp->num_arg = num_arg; funp->num_local = num_local; funp->first_default = first_default; #ifdef PROFILE_FUNS funp->num_calls = 0; funp->time_spent = 0; #endif if (exact_types) funp->type_flags |= NAME_STRICT_TYPES; if (!exact_types || 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, (char *)&type_of_locals[i], sizeof type_of_locals[i]); } if (funp == &fun) { funp->name = make_shared_string (name); add_to_mem_block (A_FUNCTIONS, (char *)&fun, sizeof fun); add_to_mem_block(A_ARGUMENT_INDEX, (char *)&argument_start_index, sizeof argument_start_index); } else { memcpy(&mem_block[A_ARGUMENT_INDEX]. block[function_index_found * sizeof(argument_start_index)], (char *)&argument_start_index, sizeof(argument_start_index)); } return; } static int is_simul_efun (char *name) { struct object *s_ob; extern char *findstring (char *); s_ob = (struct object *) query_simul_efun_ob(); if (s_ob != 0 && search_for_function (name, s_ob->prog) && !(function_type_mod_found & (TYPE_MOD_PRIVATE | TYPE_MOD_STATIC))) return 1; return 0; } static void define_variable(char *name, int type) { struct variable dummy; int n; n = check_declared(name); if (n != -1 && (n & TYPE_MOD_NO_MASK)) { char *p = (char *)alloca(80 + strlen(name)); sprintf(p, "Illegal to redefine 'nomask' variable \"%s\"", name); yyerror(p); } dummy.name = make_shared_string(name); dummy.type = type; variable_index_found = mem_block_size(A_VARIABLES,sizeof(struct variable)); variable_inherit_found = 255; add_to_mem_block(A_VARIABLES, (char *)&dummy, sizeof dummy); } unsigned short get_short(char *addr) { unsigned short ret; ((char *)&ret)[0] = addr[0]; ((char *)&ret)[1] = addr[1]; return ret; } unsigned short store_prog_string(char *str) { unsigned short addr; int i; for (i = mem_block[A_STRTAB].current_size - sizeof(short); i >= 0; i -= sizeof(short)) { char *str2; unsigned short offset; ((char *)&offset)[0] = mem_block[A_STRTAB].block[i]; ((char *)&offset)[1] = mem_block[A_STRTAB].block[i + 1]; str2 = mem_block[A_RODATA].block + offset; if (!strcmp(str, str2)) return offset; } if (mem_block[A_RODATA].current_size >= 0x10000) { yyerror("Too large rodata segment!\n"); mem_block[A_RODATA].current_size = 0; } addr = mem_block[A_RODATA].current_size; add_to_mem_block(A_STRTAB, (char *)&addr, sizeof(addr)); add_to_mem_block(A_RODATA, str, strlen(str) + 1); return addr; } struct label { unsigned short address; unsigned short link; }; static int make_label() { static struct label lbl = { -1, -1}; int ret; ret = mem_block[A_LABELS].current_size / sizeof(struct label); add_to_mem_block(A_LABELS, (char *)&lbl, sizeof(lbl)); return ret; } static void ins_label(int lbl) { struct label *l; unsigned short here; here = mem_block[A_PROGRAM].current_size; l = &((struct label *)mem_block[A_LABELS].block)[lbl]; if (l->address != (unsigned short)-1) ins_short(l->address); else { ins_short(l->link); l->link = here; } } static void set_label(int lbl, unsigned short addr) { struct label *l; unsigned short link, next; char *pgm = mem_block[A_PROGRAM].block; l = ((struct label *)mem_block[A_LABELS].block) + lbl; l->address = addr; for (link = l->link; link != (unsigned short)-1; link = next) { next = read_short(link); upd_short(link, addr); } l->link = -1; } static int cmp_case_keys(struct case_heap_entry *entry1, struct case_heap_entry *entry2, int is_str) { if (is_str) return strcmp(mem_block[A_RODATA].block + (unsigned short)entry1->key, mem_block[A_RODATA].block + (unsigned short)entry2->key); else return entry1->key - entry2->key; } void add_to_case_heap(int block_index, struct case_heap_entry *entry, struct case_heap_entry *entry2) { char *heap_start; int offset, parent; int current_heap; struct case_heap_entry *heap_top, *heap_entry; int found = 0; int is_str; int from, to, size; if (block_index == A_CASE_NUMBERS ) { current_heap = current_case_number_heap; is_str = 0; } else { current_heap = current_case_string_heap; is_str = 1; } if (entry2 && cmp_case_keys(entry, entry2, is_str) > 0) return; heap_top = (struct case_heap_entry *) (mem_block[block_index].block + mem_block[block_index].current_size); heap_entry = (struct case_heap_entry *) (mem_block[block_index].block + current_heap); for (; heap_entry < heap_top; heap_entry++) { if (cmp_case_keys(heap_entry, entry, is_str) > 0) break; if (heap_entry->addr == -1) { if (cmp_case_keys(++heap_entry, entry, is_str) >= 0) { /* Duplicate case label! */ char buff[100]; sprintf(buff, "Duplicate case label (line %d)", heap_entry->line); yyerror(buff); break; } } } if (heap_entry < heap_top && (!cmp_case_keys(heap_entry, entry, is_str) || entry2 && (cmp_case_keys(entry2, heap_entry, is_str) >= 0))) { /* Duplicate case label! */ char buff[100]; sprintf(buff, "Duplicate case label (line %d)", heap_entry->line); yyerror(buff); } to = ((char *)(heap_entry + 1 + (entry2 != NULL))) - mem_block[block_index].block; from = ((char *)heap_entry) - mem_block[block_index].block; size = (heap_top - heap_entry) * sizeof(*entry); add_to_mem_block(block_index, (char *)entry, sizeof(*entry)); if (entry2) add_to_mem_block(block_index, (char *)entry2, sizeof(*entry2)); if (heap_entry != heap_top) { memmove(mem_block[block_index].block + to, mem_block[block_index].block + from, size); memcpy(mem_block[block_index].block + from, entry, sizeof(*entry)); if (entry2) memcpy(mem_block[block_index].block + from + sizeof(*entry), entry2, sizeof(*entry)); } } /* * Arrange a jump to the current position for the initialization code * to continue. */ static void transfer_init_control() { if (mem_block[A_PROGRAM].current_size - 2 == last_initializer_end) mem_block[A_PROGRAM].current_size -= 3; else { /* * Change the address of the last jump after the last * initializer to this point. */ upd_short(last_initializer_end, mem_block[A_PROGRAM].current_size); } } void add_new_init_jump(); static int init_arg_stack[256]; static init_arg_stack_index; void clear_init_arg_stack() { init_arg_stack_index = 0; } void push_init_arg_address(int address) { init_arg_stack[init_arg_stack_index++] = address; } static void ins_f_byte(unsigned int); void dump_init_arg_table(int arg) { int i; #ifdef DEBUG if (init_arg_stack_index != arg) fatal("Not correct number of init addresses!\n"); #endif for (i = 0; i < init_arg_stack_index; i++) ins_short(init_arg_stack[i]); } %} /* * These values are used by the stack machine, and can not be directly * called from LPC. */ %token F_EXT F_JUMP F_JUMP_WHEN_ZERO F_JUMP_WHEN_NON_ZERO %token F_POP_VALUE F_DUP %token F_STORE %token F_CALL_NON_VIRT F_CALL_VIRT %token F_PUSH_IDENTIFIER_LVALUE F_PUSH_LOCAL_VARIABLE_LVALUE %token F_PUSH_INDEXED_LVALUE F_INDIRECT F_INDEX %token F_CONST0 F_CONST1 %token F_FAIL_PRECOND F_FAIL_POSTCOND F_FAIL_INVARIANT F_FAIL_ASSERTION %token F_ASSERTION F_DO_POSTCOND F_DO_PRECOND F_DO_INVARIANTS /* * 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 F_FLOATC %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 %token F_DESCRIBE %token F_CONTINUE %token F_MOD F_MOD_EQ F_INHERIT F_COLON_COLON %token F_STATIC %token F_ARROW F_AGGREGATE F_M_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 F_CALL_C F_CALL_SIMUL %token F_OBJECT F_VOID F_MIXED F_PRIVATE F_NO_MASK F_NOT F_MAPPING F_FLOAT %token F_PROTECTED F_PUBLIC %token F_VARARGS F_RANGE F_VARARG