%{ /*--------------------------------------------------------------------------- * make_func: Generation of the parser description and other tables * *--------------------------------------------------------------------------- * make_func is an essential part of build process, as it creates the * actual LPC compiler description from a template, as well as some * internal tables from a list of code and efun specs. * * make_func takes one commandline argument which determines what it * should create: * * make_func instrs * creates efun_defs.c and instrs.h, reads machine.h, config.h and func_spec. * * make_func lang * creates lang.y from prolang.y, reads machine.h and config.h. * * make_func strings * creates stdstrings.[ch], reads machine.h, config.h and string_spec. * * The calls are described in detail below. *--------------------------------------------------------------------------- * make_func instrs * ---------------- * * make_func reads the specifications of all interpreter bytecodes from * the file func_spec and creates the source files needed to compile * the driver. * * The bytecodes both cover interpreter internal instructions as well * as efuns. A complication is that every bytecode can occupy only one * byte, but there are more than 256 codes and functions. This is solved * by the use of multi-byte instructions, where the first byte is * one of a set of prefix bytes, followed by the instruction's opcode * byte. The prefix bytes divide the instructions into several classes: * efuns with no argument, efuns with one argument, etc. The translation * from instruction code to prefix/opcode is written down in the * instrs[] table created by make_func. * * The instructions and their prefix/opcode encodings follow the * following rules: * - all non-efun instructions do not need a prefix byte and start * at instruction code 0. * - the instruction codes for the tabled efuns are consecutive * - the instruction codes for all tabled varargs efuns are consecutive. * * When make_func is run, it first reads the files 'machine.h' and * 'config.h' to determine which defines are used in the compilation of the * driver. For this make_func implements a simple C preprocessor, capable * of numeric expressions and simple macro expansions (no function macros), * which also allows the use of C-like block comments. With the knowledge * of the defines, make_func then reads the file 'func_spec' with the * information about the codes and efuns. * * From this information, make_func generates these files: * efun_defs.c * This file contains the table of all instructions, instrs[]. * The table defines for every machine instruction if it is an * internal code or efun, what arguments of what type it takes, etc. * The file also contains the tables for the driver-specific ctype * implementation. * It is included into the lexer lex.c and compiled there. * * instrs.h * This file defines the bytecodes representing all the efuns * and non-efun instructions which are not compiler tokens. * * * func_spec is divided into several sections, each introduced with its * own %-keyword. The required order and meaning of the sections is this: * * %codes * Internal machine codes used by the compiler to generate code. * * %efuns * The efuns which are represented by one bytecode. * * %tefuns * The tabled efuns. make_func will sort the efuns into * various classes depending on the number of arguments. * Each class is assigned it's own 'efun' prefix code, with * being the number of arguments. Efuns with a variable number * or arguments will be prefixed by 'efunv'. * * The entries in the sections are separated by whitespace. * * A token or code entry follows this syntax: * * [ "name" ] id [op-type] * * is the name used in the compiler source, the generated token * will be called F_. If is specified, it will be used * in tracedumps instead of the plain . * * If the instruction is an operator which can be used as a operator * closure (like +), the optional specifies how many operands * the operator takes. must be one of the keywords 'unary', * 'binary' and 'ternary'. * * * An efun is defined this way: * * ret-type name [ alias ] ([argtype-1, ... , argtype-n]) [ "msg" ]; * * This is the efun , with the compiler source name F_, * which returns a value of type and takes n >= 0 * arguments of type . If is given, it must * be the source name (F_FOO) of another efun this one is an * alias for. * * The types are given in the usual LPC syntax, with the additional * type of 'unknown' for return types. * * Argument types can be the combination of several types in * the form 'type|type|...', e.g. 'string|object'. If one of the * types is 'void', the argument is optional (but then only optional * arguments may follow). * * The last argument can take a default value, which is then specified * as 'default: F_FOO' with F_FOO being the source name of an efun * yielding the desired value. Example: * ..(object default: F_THIS_OBJECT) * * Alternatively, the last argument can be given as '...' if the * efun can handle arbitrarily many arguments. * * If the efun is deprecated, is the warning message to * print when the efun is used and pragma warn_deprecated is in * effect. The message will be prefixed by the compiler * with " is deprecated: ". * * The following types are recognized: * void, int, string, object, mapping, float, closure, symbol * quoted_array, mixed, null, unknown * 'null' is to be used as alternate type when the efun accepts * the number 0 instead of the actual type. *--------------------------------------------------------------------------- * make_func lang * -------------- * * make_func implements a preprocessor used to generate the LPC compiler * lang.y from the file prolang.y . This step is necessary because * no known yacc allows to enable or disable rules conditinally. * * When make_func is run, it first reads the files 'machine.h' and * 'config.h' to determine which defines are used in the compilation of the * driver. For this make_func implements a simple C preprocessor, capable * of numeric expressions and simple macro expansions (no function macros), * which also allows the use of C-like block comments. * * From this information, make_func generates this file: * lang.y * This is the LPC compiler, which is created by reading and * modifying the template prolang.y . During this process, these * keywords are recognized and replaced: * %line: generates a #line statement to synchronize the C compiler. * %typemap: generates a lookup table using types as indices * (see handle_map()). * %hookmap: generates a lookup table using driverhooks as indices. * In addition, the keywords %if, %elif, %else and %endif can be * used in a preprocessor fashion to control which parts of prolang.y * end up in lang.y and which don't. *--------------------------------------------------------------------------- * make_func strings * ----------------- * * make_func reads from the file string_spec the definition of all strings * which the driver shall predefine as shared strings. It creates the * source files stdstrings.[ch] with the implementation. * * The strings are mostly the names of lfuns called by the interpreter. * Predefining them as shared strings and using the resulting pointers * speeds up the function calls. * * string_spec is read line by line, each line defining one string: * * id "string" * * is the symbolic name used in the compiler source, for the * string . The symbolic name will be made upper case and * is used in two forms: * STR_: the pointer to the shared string * SHX_: the index of the shared string in the table of * all predefined strings. * * And that's it. *--------------------------------------------------------------------------- */ #undef lint /* undef so that precompiled headers can be used */ #include "driver.h" #include "my-alloca.h" #include #include #include #include #include "exec.h" #include "hash.h" #include "../mudlib/sys/driver_hook.h" #define lint /* redef again to prevent spurious warnings */ /* driver.h defines its own memory management functions, * which we don't need. */ #undef malloc #undef realloc #undef free /*-------------------------------------------------------------------------*/ /* Filenames used by make_func */ #define FUNC_SPEC "func_spec" /* The input file with the description of all machine instructions * and efuns. */ #define STRING_SPEC "string_spec" /* The input file with the definition of all standard * shared strings. */ #define CONFIG "config.h" /* The configuration file. */ #define FILE_MACHINE "machine.h" /* The machine configuration file. */ #define PRO_LANG "prolang.y" /* The LPC parser template. */ #define THE_LANG "lang.y" /* The LPC parser to generate. */ #define THE_INSTRS "instrs.h" /* The instruction and ctype table declarations to generate. */ #define EFUN_DEFS "efun_defs.c" /* The instruction and ctype table definitions to generate. */ #define STDSTRINGS "stdstrings" /* The basename (without extension) of the standard shared * string implementation files to generate. */ /*-------------------------------------------------------------------------*/ #define MAKE_FUNC_MAXLINE 4096 /* Maximum length of an input line and other buffers. */ #define MAX_FUNC 2048 #define MAX_TOKENS 2048 /* Maximum number of functions and tokens we care to handle. */ #define MAX_ARGTYPES 500 /* Size of the arg_types[] array. */ #define MF_TYPE_MOD_POINTER 0x10000 #define MF_TYPE_MOD_REFERENCE 0x20000 /* Type modifier for array and reference types. * These values are bitflags which are |'ed onto the type values * produced by the parser (INT, STRING, etc). */ /*-------------------------------------------------------------------------*/ #undef isalunum #define isalunum(c) (isascii((unsigned char)c) && (isalnum((unsigned char)c) || (c) == '_')) #define lexwhite(c) (isascii((unsigned char)c) && isspace((unsigned char)c) && (c) != '\n') #undef lexdigit #define lexdigit(c) (isascii((unsigned char)c) && isdigit((unsigned char)c)) /* Our own char classification predicates. */ #define NELEMS(arr) (sizeof arr / sizeof arr[0]) /* Handy macro to statically determine the number of elements in * an array. */ /*-------------------------------------------------------------------------*/ /* Parsed functions and instructions are put into different classes. * These are the class type definitions and associated predicate macros. */ enum ClassCodes { C_CODE = 0 /* Internal machine instructions */ , C_EFUN /* Efuns with own instruction codes */ , C_EFUN0 /* Tabled efuns with 0 arguments */ , C_EFUN1 /* Tabled efuns with 1 argument */ , C_EFUN2 /* Tabled efuns with 2 argument */ , C_EFUN3 /* Tabled efuns with 3 argument */ , C_EFUN4 /* Tabled efuns with 4 argument */ , C_EFUNV /* Tabled efuns with more than 4 or variable arguments */ , C_ALIAS /* Aliased efuns. This must come right after * the last C_EFUN*. */ , C_SEFUN /* Virtual class used by the lexer when parsing * the classes C_EFUN*. */ , C_TOTAL /* Number of different classes */ }; #define C_IS_CODE(x) \ ((x) == C_CODE) #define C_IS_EFUN(x) \ ((x) != C_CODE && (x) != C_ALIAS) /*-------------------------------------------------------------------------*/ static char * classtag[] = { /* C_CODE */ "CODE" , /* C_EFUN */ "EFUN" , /* C_EFUN0 */ "EFUN0" , /* C_EFUN1 */ "EFUN1" , /* C_EFUN2 */ "EFUN2" , /* C_EFUN3 */ "EFUN3" , /* C_EFUN4 */ "EFUN4" , /* C_EFUNV */ "EFUNV" }; /* Tag names of the code classes, used to create the '_OFFSET' * values in efun_defs.c:instrs[]. */ static char * classprefix[] = { /* C_CODE */ "0" , /* C_EFUN */ "0" , /* C_EFUN0 */ "F_EFUN0" , /* C_EFUN1 */ "F_EFUN1" , /* C_EFUN2 */ "F_EFUN2" , /* C_EFUN3 */ "F_EFUN3" , /* C_EFUN4 */ "F_EFUN4" , /* C_EFUNV */ "F_EFUNV" }; /* Instruction names of the prefixes used for the various code classes. */ static int num_buff = 0; /* Total number of instructions encountered so far. */ static int num_instr[C_TOTAL]; /* Number of instructions encountered, counted separately for * every type. */ static int instr_offset[C_TOTAL]; /* Derived from num_instrs, the offset of the instruction classes * within the whole table. */ struct instrdata_s { char *f_name; /* 'F-'-name of code/efun */ char *key; /* internal name of code/efun */ char *buf; /* instrs[] entry for code/efun */ int code_class; /* code class of code/efun */ } instr[MAX_FUNC]; /* Array describing all codes and efuns encountered in FUNC_SPEC. * The array is built in the order found in func_spec and later * sorted by code_class and key. * * Also used to hold all string definitions found in STRING_SPEC: * .key is (in upper case) the symbolic name of the string; * .buf is the actual string text. */ static int arg_types[MAX_ARGTYPES]; /* All distinct function argument signatures encountered so far. * One signature is simply a list of all argument types, identified * by the starting index of the first argument's type. * The description of one argument's type is itself a list of the * typecodes, terminated by 0. * Different signatures may overlap, e.g. a (STRING) signature * could be embedded in a (POINTER, STRING) signature. * * The content of this array is later written as efun_arg_types[] * into EFUN_DEFS and indexed by the instrs[].arg_index entries. */ static long lpc_types[MAX_ARGTYPES]; /* The table of distinct function argument signatures again, this * time expressed in svalue runtime types. * One signature is a list of all arguments' types, identified * by the starting index of the first argument's type. The length * of the signature is stored in the instr structure for the function. * The description of one argument's type is a bitword, where * a 1 is set when the associated type is accepted. * For example, if an argument can be string or object, the * word in the table is set to (1< into a freshly allocated memory block and return that one. * * This function is needed on Ultrix 4.2 which doesn't seem to know strdup(). */ { char *copy = malloc(strlen(str)+1); if (!copy) fatal("strdup failed\n"); strcpy(copy, str); return copy; } /*-------------------------------------------------------------------------*/ static void fatal (const char *str) /* Print on stderr, flush stdout and exit the program with * exitcode 1. */ { fprintf(stderr, "%s", str); fflush(stdout); exit(1); } /*-------------------------------------------------------------------------*/ static char * make_f_name (char *str) /* Take and return a string 'F_' in its own memory block. */ { char f_name[500]; size_t i, len; if (strlen(str) + 1 + 2 > sizeof f_name) fatal("A local buffer was too small!(1)\n"); sprintf(f_name, "F_%s", str); len = strlen(f_name); for (i = 0; i < len; i++) { if (islower((unsigned char)f_name[i])) f_name[i] = (char)toupper(f_name[i]); } return mystrdup(f_name); } /*-------------------------------------------------------------------------*/ static int check_for_duplicate_instr (const char *f_name, const char *key, int redef_ok) /* Check if either or already appear in instr[], and print * appropriate diagnostics. If is true, a new for an existing * is allowed. * Return true if there is a duplicate for , false if not. */ { size_t i; int rc; rc = 0; for (i = 0; i < (size_t)num_buff; i++) { if (!strcmp(f_name, instr[i].f_name)) { if (!strcmp(key, instr[i].key)) { rc = 1; got_error = 1; fprintf(stderr, "Error: Entry '%s':'%s' duplicated.\n" , f_name, key); } else if (!redef_ok) { rc = 1; got_error = 1; fprintf(stderr, "Error: Entry '%s':'%s' redefined to '%s'.\n" , f_name, instr[i].key, key); } } else if (!strcmp(key, instr[i].key)) { fprintf(stderr, "Entry '%s':'%s' duplicated as '%s':... .\n" , instr[i].f_name, instr[i].key, f_name); } } return rc; } /*-------------------------------------------------------------------------*/ static int check_for_duplicate_string (const char *key, const char *buf) /* Check if either or already appear in instr[], and print * appropriate diagnostics. * Return true if there is a duplicate for , false if not. */ { size_t i; int rc; rc = 0; for (i = 0; i < (size_t)num_buff; i++) { if (!strcmp(key, instr[i].key)) { rc = 1; got_error = 1; if (!strcmp(buf, instr[i].buf)) { fprintf(stderr, "Error: Entry '%s':'%s' duplicated.\n" , key, buf); } else { fprintf(stderr, "Error: Entry '%s':'%s' redefined to '%s'.\n" , key, instr[i].buf, buf); } } else if (!strcmp(buf, instr[i].buf)) { fprintf(stderr, "Warning: Entry '%s':'%s' duplicated as '%s':... .\n" , instr[i].key, instr[i].buf, key); } } return rc; } #if defined(__MWERKS__) && !defined(WARN_ALL) # pragma warn_possunwant off # pragma warn_implicitconv off #endif %} /*=========================================================================*/ /* P A R S E R */ /*-------------------------------------------------------------------------*/ %union { int number; char *string; } %token PARSE_FUNC_SPEC PARSE_STRING_SPEC %token NAME ID %token VOID INT STRING OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY %token MIXED UNKNOWN NUL STRUCT %token DEFAULT %token CODES EFUNS TEFUNS END %token UN_OP BIN_OP TRI_OP %type VOID MIXED UNKNOWN NUL STRUCT %type INT STRING OBJECT MAPPING FLOAT CLOSURE SYMBOL QUOTED_ARRAY %type basic arg_type /* Value is the basic type value */ %type opt_star opt_ref /* Value is the appropriate bitflag or 0. */ %type type /* Value is the complete type, incl. *- and &-modifier bitflags */ %type arg_list typel typel2 /* Value is the number of arguments (so far) */ %type optional_optype /* 0: No op-type given * 1: UN_OP, 2: BIN_OP, 3: TRI_OP * Or in other words: the number of operands :-) */ %type ID optional_ID optional_default NAME optional_name /* Value is the parsed identifier or NULL (resp. "" for optional_ID). */ %% all: PARSE_FUNC_SPEC func_spec | PARSE_STRING_SPEC string_spec ; /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ func_spec: codes END efuns END tefuns; codes: CODES | codes code; efuns: EFUNS funcs; tefuns: TEFUNS funcs; /* --- Codes --- */ optional_name: /* empty */ { $$ = NULL; } | NAME; optional_optype: /* empty */ { $$ = 0; } | UN_OP { $$ = 1; } | BIN_OP { $$ = 2; } | TRI_OP { $$ = 3; } ; code: optional_name ID optional_optype { char *f_name, buff[500]; if (num_buff >= MAX_FUNC) yyerror("Too many codes and efuns in total!\n"); if (!$1) $1 = mystrdup($2); f_name = make_f_name($2); check_for_duplicate_instr(f_name, $2, 0); instr[num_buff].code_class = current_code_class; num_instr[current_code_class]++; #ifdef USE_STRUCTS if ($3 == 0) sprintf(buff, "{ %s, %s-%s_OFFSET, 0, 0, -1, { 0, NULL } , -1, -1, \"%s\", NULL },\n" , classprefix[instr[num_buff].code_class] , f_name, classtag[instr[num_buff].code_class] , $1); else sprintf(buff, "{ %s, %s-%s_OFFSET, %d, %d, 0, { TYPE_ANY, NULL }, -1, -1, \"%s\", NULL },\n" , classprefix[instr[num_buff].code_class] , f_name, classtag[instr[num_buff].code_class] , $3, $3 , $1); #else if ($3 == 0) sprintf(buff, "{ %s, %s-%s_OFFSET, 0, 0, -1, { 0 } , -1, -1, \"%s\", NULL },\n" , classprefix[instr[num_buff].code_class] , f_name, classtag[instr[num_buff].code_class] , $1); else sprintf(buff, "{ %s, %s-%s_OFFSET, %d, %d, 0, { TYPE_ANY }, -1, -1, \"%s\", NULL },\n" , classprefix[instr[num_buff].code_class] , f_name, classtag[instr[num_buff].code_class] , $3, $3 , $1); #endif /* USE_STRUCTS */ if (strlen(buff) > sizeof buff) fatal("Local buffer overflow!\n"); instr[num_buff].f_name = f_name; instr[num_buff].key = mystrdup($2); instr[num_buff].buf = mystrdup(buff); num_buff++; free($1); } ; /* --- Efuns --- */ funcs: /* empty */ | funcs func ; optional_ID: ID | /* empty */ { $$ = ""; } ; optional_default: DEFAULT ':' ID { $$ = $3; } | /* empty */ { $$ = "0"; } ; func: type ID optional_ID '(' arg_list optional_default ')' optional_name ';' { char buff[500]; char *f_name; char *f_prefix; int code_class; int i; int max_arg, arg_index, lpc_index; if (num_buff >= MAX_FUNC) yyerror("Too many codes and efuns in total!\n"); if (min_arg == -1) min_arg = $5; max_arg = $5; /* Make the correct f_name and determine the code class */ if ($3[0] == '\0') { code_class = current_code_class; if (current_code_class == C_SEFUN) { if (!unlimit_max && min_arg == max_arg) { if (max_arg < 5) code_class = C_EFUN0 + min_arg; else { code_class = C_EFUNV; fprintf(stderr , "Efun '%s' has %d arguments and will be " "considered a 'varargs' efun.\n" , $2, min_arg); } } if (unlimit_max || min_arg != max_arg) { code_class = C_EFUNV; } if (code_class == C_SEFUN) { char buf[100]; sprintf(buf, "Efun '%s' can't be classified.\n", $2); fatal(buf); } } f_name = make_f_name($2); check_for_duplicate_instr(f_name, $2, 0); instr[num_buff].code_class = code_class; num_instr[code_class]++; f_prefix = classprefix[code_class]; } else { f_name = mystrdup($3); check_for_duplicate_instr(f_name, $3, 1); instr[num_buff].code_class = code_class = C_ALIAS; num_instr[C_ALIAS]++; f_prefix = NULL; } /* Search the function's signature in arg_types[] */ for (i = 0; i < last_current_type; i++) { int j; for (j = 0; j+i < last_current_type && j < curr_arg_type_size; j++) { if (curr_arg_types[j] != arg_types[i+j]) break; } if (j == curr_arg_type_size) break; } if (i == last_current_type) { /* It's a new signature, put it into arg_types[] */ int j; for (j = 0; j < curr_arg_type_size; j++) { arg_types[last_current_type++] = curr_arg_types[j]; if (last_current_type == NELEMS(arg_types)) yyerror("Array 'arg_types' is too small"); } } arg_index = i; /* Search the function's signature in lpc_types[]. * For efuns using a (...) argument the last listed lpc_type is 0, * which doesn't need to be compared or stored. */ for (i = 0; i < last_current_lpc_type; i++) { int j; Bool mismatch = MY_FALSE; for ( j = 0 ; j+i < last_current_lpc_type && j < curr_lpc_type_size ; j++) { if ((j+1 < curr_lpc_type_size || curr_lpc_types[j] != 0) && (curr_lpc_types[j] != lpc_types[i+j])) { mismatch = MY_TRUE; break; } } if (!mismatch) break; } if (i + curr_lpc_type_size > last_current_lpc_type) { /* It's a new signature, its first (last_current_lpc_type - i) * args matching the last entries in lpc_types. * TODO: An even better strategy would be to sort the signatures * TODO:: by size and then do the overlapping store with the * TODO:: longest one first. */ int j; for (j = last_current_lpc_type - i; j < curr_lpc_type_size; j++) { if (j+1 < curr_lpc_type_size || curr_lpc_types[j] != 0) { lpc_types[last_current_lpc_type++] = curr_lpc_types[j]; } if (last_current_lpc_type == NELEMS(lpc_types)) yyerror("Array 'lpc_types' is too small"); } } lpc_index = i; /* Store the data */ if (code_class != C_ALIAS && code_class != C_SEFUN) { char * tag; tag = (code_class == C_EFUN) ? classtag[C_CODE] : classtag[code_class]; #ifdef USE_STRUCTS sprintf(buff, "{ %s, %s-%s_OFFSET, %d, %d, %s, { %s, NULL }, %d, %d, \"%s\"" , f_prefix, f_name, tag , unlimit_max ? -1 : max_arg, min_arg , $6, ctype($1), arg_index, lpc_index, $2 ); #else sprintf(buff, "{ %s, %s-%s_OFFSET, %d, %d, %s, { %s }, %d, %d, \"%s\"" , f_prefix, f_name, tag , unlimit_max ? -1 : max_arg, min_arg , $6, ctype($1), arg_index, lpc_index, $2 ); #endif /* USE_STRUCTS */ } else { #ifdef USE_STRUCTS sprintf(buff, "{ 0, 0, %d, %d, %s, { %s, NULL}, %d, %d, \"%s\"" , unlimit_max ? -1 : max_arg, min_arg , $6, ctype($1), arg_index, lpc_index, $2 ); #else sprintf(buff, "{ 0, 0, %d, %d, %s, { %s }, %d, %d, \"%s\"" , unlimit_max ? -1 : max_arg, min_arg , $6, ctype($1), arg_index, lpc_index, $2 ); #endif /* USE_STRUCTS */ } if ($8 != NULL) sprintf(buff+strlen(buff), ", \"%s\"", $8); else strcat(buff, ", NULL"); strcat(buff, " },\n"); if (strlen(buff) > sizeof buff) fatal("Local buffer overwritten !\n"); instr[num_buff].f_name = f_name; instr[num_buff].key = mystrdup($2); instr[num_buff].buf = mystrdup(buff); num_buff++; /* Reset for next function */ min_arg = -1; unlimit_max = MY_FALSE; curr_arg_type_size = 0; curr_lpc_type_size = 0; curr_lpc_types[0] = 0; } ; /* --- Types and Argument lists --- */ type: basic opt_star opt_ref { $$ = $1 | $2 | $3; }; basic: VOID | INT | STRING | MAPPING | FLOAT | MIXED | OBJECT | CLOSURE | UNKNOWN | SYMBOL | QUOTED_ARRAY | STRUCT | NUL ; opt_star : '*' { $$ = MF_TYPE_MOD_POINTER; } | { $$ = 0; } ; opt_ref : '&' { $$ = MF_TYPE_MOD_REFERENCE; } | { $$ = 0; } ; arg_list: /* empty */ { $$ = 0; } | typel2 { $$ = 1; if ($1) min_arg = 0; } | arg_list ',' typel2 { $$ = $1 + 1; if ($3) min_arg = $$ - 1; } ; typel2: typel { $$ = $1; curr_arg_types[curr_arg_type_size++] = 0; if (curr_arg_type_size == NELEMS(curr_arg_types)) yyerror("Too many arguments"); curr_lpc_type_size++; if (curr_lpc_type_size == NELEMS(curr_lpc_types)) yyerror("Too many arguments"); curr_lpc_types[curr_lpc_type_size] = 0; } ; arg_type: type { if ($1 != VOID) { if ($1 != NUL) curr_arg_types[curr_arg_type_size++] = $1; if (curr_arg_type_size == NELEMS(curr_arg_types)) yyerror("Too many arguments"); curr_lpc_types[curr_lpc_type_size] |= type2flag($1); } $$ = $1; } ; typel: arg_type { $$ = ($1 == VOID && min_arg == -1); } | typel '|' arg_type { $$ = (min_arg == -1 && ($1 || $3 == VOID));} | '.' '.' '.' { $$ = min_arg == -1 ; unlimit_max = MY_TRUE; } ; /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ string_spec: stringdefs ; stringdefs: /* empty */ | stringdefs stringdef ; stringdef: ID NAME { char *cp; if (num_buff >= MAX_FUNC) yyerror("Too many string definitions!\n"); /* Copy the data parsed into the instr[] array */ /* Make the correct f_name and determine the code class */ check_for_duplicate_string($1, $2); instr[num_buff].key = mystrdup($1); instr[num_buff].buf = mystrdup($2); /* Make sure that the .key is all uppercase */ for (cp = instr[num_buff].key; *cp != '\0'; cp++) if (isalpha((unsigned char)*cp) && islower((unsigned char)*cp)) *cp = toupper(*cp); /* Prepare for next string */ num_buff++; } ; /* stringdef */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ %% #ifdef __MWERKS__ # pragma warn_possunwant reset # pragma warn_implicitconv reset #endif /*=========================================================================*/ /* L E X E R */ /* The Lexer is used to parse CONFIG, FILE_MACHINE, FUNC_SPEC and STRING_SPEC, * and to create THE_LANG from PRO_LANG. */ /*-------------------------------------------------------------------------*/ /* The recognized type names */ struct type { const char *name; /* name of the type */ int num; /* the type's parser code */ }; static struct type types[] = { { "void", VOID } , { "int", INT } , { "string", STRING } , { "object", OBJECT } , { "mapping", MAPPING } , { "float", FLOAT } , { "closure", CLOSURE } , { "symbol", SYMBOL } , { "quoted_array", QUOTED_ARRAY } , { "mixed", MIXED } , { "null", NUL } , { "unknown", UNKNOWN } #ifdef USE_STRUCTS , { "struct", STRUCT } #endif }; /*-------------------------------------------------------------------------*/ /* Defined macros. * * The macros are kept in the usual hashtable. */ #define MAKE_FUNC_DEFHASH 101 #define defhash(str) (whashstr((str), 12) % MAKE_FUNC_DEFHASH) struct defn { char *name; /* Macro name */ char *exps; /* Expanded macro text */ int num_arg; /* Number of arguments or -1 for plain macros */ struct defn *next; /* Next entry in the hash chain */ }; static struct defn *deftab[MAKE_FUNC_DEFHASH]; /*-------------------------------------------------------------------------*/ /* The stack to handle pending #ifs. */ static struct ifstate { struct ifstate *next; int state; } *iftop = NULL; /* Values of ifstate.state: */ #define EXPECT_ELSE 1 #define EXPECT_ENDIF 2 /*-------------------------------------------------------------------------*/ static FILE *fpr, *fpw; /* Input and output file. */ static int current_line; /* Number of the current line. */ static const char *current_file; /* Name of the current file. */ static int last_line; /* Last line outside an %if-block. */ static char *outp; /* Next unprocessed character in the input buffer */ static int parsetype; /* Either PARSE_FUNC_SPEC or PARSE_STRING_SPEC, sent as first token back * to the parser. */ static Bool parsetype_sent = MY_FALSE; /* Set to TRUE after parsetype was sent to the parser. */ /*-------------------------------------------------------------------------*/ /* The definitions and tables for the preprocessor expression evaluator. * For a detailed explanation look into lex.c - it uses slightly * different tables, but the basic structure is the same. */ #define BNOT 1 #define LNOT 2 #define UMINUS 3 #define UPLUS 4 #define MULT 1 #define DIV 2 #define MOD 3 #define BPLUS 4 #define BMINUS 5 #define LSHIFT 6 #define RSHIFT 7 #define LESS 8 #define LEQ 9 #define GREAT 10 #define GEQ 11 #define EQ 12 #define NEQ 13 #define BAND 14 #define XOR 15 #define BOR 16 #define LAND 17 #define LOR 18 #define QMARK 19 static char _optab[]= {0,4,0,0,0,26,56,0,0,0,18,14,0,10,0,22,0,0,0,0,0,0,0,0,0,0,0,0,30,50,40,74, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,0,1}; static char optab2[]= {BNOT,0,0,LNOT,'=',NEQ,7,0,0,UMINUS,0,BMINUS,10,UPLUS,0,BPLUS,10, 0,0,MULT,11,0,0,DIV,11,0,0,MOD,11, 0,'<',LSHIFT,9,'=',LEQ,8,0,LESS,8,0,'>',RSHIFT,9,'=',GEQ,8,0,GREAT,8, 0,'=',EQ,7,0,0,0,'&',LAND,3,0,BAND,6,0,'|',LOR,2,0,BOR,4, 0,0,XOR,5,0,0,QMARK,1}; #define optab1(c) (_optab[(c)-' ']) /*-------------------------------------------------------------------------*/ static char mygetc (void) /* Return the next character from the input buffer. */ { return *outp++; } /*-------------------------------------------------------------------------*/ static void myungetc (char c) /* Unput character so that the next mygetc() returns it. */ { *--outp = c; } /*-------------------------------------------------------------------------*/ static void add_input (const char *p) /* Insert text

at the current point in the input stream so that * the next mygetc()s will read it. */ { size_t l; l = strlen(p); outp -= l; strncpy(outp, p, l); } /*-------------------------------------------------------------------------*/ static void add_define (const char * name, int num_arg, const char *exps) /* Add the definition for the macro with arguments * and replacement text to the table of macros. */ { int i; struct defn *ndef; i = defhash(name); ndef = malloc(sizeof(struct defn)); if (!ndef) { abort(); } ndef->next = deftab[i]; ndef->exps = mystrdup(exps); ndef->num_arg = num_arg; ndef->name = mystrdup(name); deftab[i] = ndef; } /*-------------------------------------------------------------------------*/ static struct defn * lookup_define (const char *s) /* Lookup the macro and return a pointer to its defn structure. * Return NULL if the macro is not defined. */ { struct defn *curr, *prev; int h; h = defhash(s); curr = deftab[h]; prev = 0; while (curr) { if (!strcmp(curr->name, s)) /* found it */ { if (prev) /* not at head of list */ { prev->next = curr->next; curr->next = deftab[h]; deftab[h] = curr; } return curr; } prev = curr; curr = curr->next; } /* not found */ return NULL; } /*-------------------------------------------------------------------------*/ static int expand_define (char *s) /* Try to expand macro . * If it is not defined, just return 0. * If it is a plain macro, add it to the input stream and return 1. * If it is a function macro, just return -1. */ { struct defn *p; p = lookup_define(s); if (!p) return 0; if (p->num_arg < 0) { add_input(p->exps); } else { return -1; } return 1; } /*-------------------------------------------------------------------------*/ static char * nextword (char *str) /* Find the next word in the input stream starting with and * return a pointer to its first character. The end of the word * is marked with a '\0'. */ { char *cp; while (!lexwhite(*str)) str++; while ( lexwhite(*str)) str++; for (cp = str; isalunum(*cp); ) cp++; *cp = '\0'; return str; } /*-------------------------------------------------------------------------*/ static Bool skip_to (char mark, const char *token, const char *atoken) /* Skip the file fpr linewise until one of the following control statements * is encountered: * token: returns true * atoken: returns false (only if is not NULL). * * An end of file aborts the program. * Nested if...endif blocks are skipped altogether. */ { char b[20]; /* The word after */ char *p; int c; /* Current character */ int nest; /* if nesting depth */ FILE *fp = fpr; for (nest = 0;;) { current_line++; c = fgetc(fp); if (c == mark) { /* Get the statement word into b[] */ do { c = fgetc(fp); } while(lexwhite(c)); for (p = b; c != '\n' && c != EOF; ) { if (p < b+sizeof b-1) *p++ = (char)c; c = fgetc(fp); } *p++ = '\0'; for(p = b; *p && !lexwhite(*p); p++) NOOP; *p = '\0'; if (strcmp(b, "if") == 0 || strcmp(b, "ifdef") == 0 || strcmp(b, "ifndef") == 0) { nest++; } else if (nest > 0) { if (strcmp(b, "endif") == 0) nest--; } else { if (strcmp(b, token) == 0) return MY_TRUE; else if (atoken && strcmp(b, atoken) == 0) return MY_FALSE; } } else { /* Skip the line altogether */ while (c != '\n' && c != EOF) { c = fgetc(fp); } if (c == EOF) { fprintf(stderr, "Unexpected end of file while skipping"); abort(); } } } /* for() */ /* NOTREACHED */ } /* skip_to() */ /*-------------------------------------------------------------------------*/ static void compensate_lines (void) /* To compensate for skipped blocks, print as many \n to fpw as * the difference between last_line and current_line determines. * last_line is current_line+1 at the end. */ { for (; last_line <= current_line; last_line++) fputc('\n', fpw); } /*-------------------------------------------------------------------------*/ static void handle_cond (char mark, int c) /* Evaluate the boolean condition of a preprocessor #if statement. * If necessary, skip to the condition branch to read next, and/or * push a new state onto the ifstate-stack. * If is '%', the number of skipped lines is compensated. */ { struct ifstate *p; if (c || skip_to(mark, "else", "endif")) { p = (struct ifstate *)malloc(sizeof(struct ifstate)); p->next = iftop; iftop = p; p->state = c ? EXPECT_ELSE : EXPECT_ENDIF; } if (mark == '%') compensate_lines(); } /*-------------------------------------------------------------------------*/ static void handle_if (char mark, const char *str) /* Evaluate the if condition */ { int cond; add_input(str); cond = cond_get_exp(0); if (mygetc() != '\n') { yyerror("Condition too complex in #/%if"); fflush(stdout); if (mark == '%') exit(1); while(mygetc() != '\n') NOOP; } else handle_cond(mark, cond); } /*-------------------------------------------------------------------------*/ static void handle_else (char mark) /* Handle an else statement. */ { if (iftop && iftop->state == EXPECT_ELSE) { struct ifstate *p = iftop; iftop = p->next; free((char *)p); skip_to(mark, "endif", (const char *)0); } else { fprintf(stderr, "Unexpected #/%%else line %d\n", current_line); abort(); } } /*-------------------------------------------------------------------------*/ static void handle_endif (void) /* Handle an else statement. */ { if (iftop && ( iftop->state == EXPECT_ENDIF || iftop->state == EXPECT_ELSE)) { struct ifstate *p = iftop; iftop = p->next; free((char *)p); } else { fprintf(stderr, "Unexpected #/%%endif line %d\n", current_line); abort(); } } /*-------------------------------------------------------------------------*/ static int name_to_type (char *name) /* Return the proper TYPE_ value for the type which must * start with 'TYPE_'. * Return -1 if the name was not recognized. */ { while (isspace((unsigned char)*name)) name++; if ( strncmp(name, "TYPE_", (size_t)5) ) return -1; name += 5; if ( !strcmp(name, "ANY") ) return TYPE_ANY; if ( !strcmp(name, "NUMBER") ) return TYPE_NUMBER; if ( !strcmp(name, "FLOAT") ) return TYPE_FLOAT; if ( !strcmp(name, "STRING") ) return TYPE_STRING; if ( !strcmp(name, "OBJECT") ) return TYPE_OBJECT; if ( !strcmp(name, "MAPPING") ) return TYPE_MAPPING; if ( !strcmp(name, "CLOSURE") ) return TYPE_CLOSURE; #ifdef USE_STRUCTS if ( !strcmp(name, "STRUCT") ) return TYPE_STRUCT; #endif return -1; } /*-------------------------------------------------------------------------*/ static int name_to_hook(char *name) /* Return the proper H_ value for the driverhook which must * start with 'H_' * Return -1 if the name was not recognized. */ { while (isspace((unsigned char)*name)) name++; if ( strncmp(name, "H_", (size_t)2) ) return -1; name += 2; if ( !strcmp(name, "MOVE_OBJECT0") ) return H_MOVE_OBJECT0; if ( !strcmp(name, "MOVE_OBJECT1") ) return H_MOVE_OBJECT1; if ( !strcmp(name, "LOAD_UIDS") ) return H_LOAD_UIDS; if ( !strcmp(name, "CLONE_UIDS") ) return H_CLONE_UIDS; if ( !strcmp(name, "CREATE_SUPER") ) return H_CREATE_SUPER; if ( !strcmp(name, "CREATE_OB") ) return H_CREATE_OB; if ( !strcmp(name, "CREATE_CLONE") ) return H_CREATE_CLONE; if ( !strcmp(name, "RESET") ) return H_RESET; if ( !strcmp(name, "CLEAN_UP") ) return H_CLEAN_UP; if ( !strcmp(name, "MODIFY_COMMAND") ) return H_MODIFY_COMMAND; if ( !strcmp(name, "NOTIFY_FAIL") ) return H_NOTIFY_FAIL; if ( !strcmp(name, "NO_IPC_SLOT") ) return H_NO_IPC_SLOT; if ( !strcmp(name, "INCLUDE_DIRS") ) return H_INCLUDE_DIRS; if ( !strcmp(name, "TELNET_NEG") ) return H_TELNET_NEG; if ( !strcmp(name, "NOECHO") ) return H_NOECHO; if ( !strcmp(name, "ERQ_STOP") ) return H_ERQ_STOP; if ( !strcmp(name, "MODIFY_COMMAND_FNAME") ) return H_MODIFY_COMMAND_FNAME; if ( !strcmp(name, "COMMAND") ) return H_COMMAND; if ( !strcmp(name, "SEND_NOTIFY_FAIL") ) return H_SEND_NOTIFY_FAIL; if ( !strcmp(name, "AUTO_INCLUDE") ) return H_AUTO_INCLUDE; if ( !strcmp(name, "DEFAULT_METHOD") ) return H_DEFAULT_METHOD; if ( !strcmp(name, "DEFAULT_PROMPT") ) return H_DEFAULT_PROMPT; if ( !strcmp(name, "PRINT_PROMPT") ) return H_PRINT_PROMPT; if ( !strcmp(name, "REGEXP_PACKAGE") ) return H_REGEXP_PACKAGE; return -1; } /*-------------------------------------------------------------------------*/ static void handle_map (char *str, int size, int (* name_to_index)(char *) ) /* Create a lookup table with elements from the input * text (and possibly more lines read from fpr if * ends in \). * * The input text is supposed to be a comma separated list * of : pairs: the text "" is parsed * and translated into a number by (). * is the assigned to the table element indexed * by this number. * * Unassigned table elements default to the text '0' or * to the value set with a 'default:' pair. * * After the list of pairs is read, the text for the table * is written as '{ , ... , }' to fpw. */ { char **map; /* The map */ char deflt[256]; /* Default table entry text */ char *del = NULL; /* Position of the current ':' or ',' */ char *val; /* Position of the current */ int i; char *output_del = ""; /* How to separate table entries */ /* Initialize the map */ map = (char **)alloca(size * sizeof *map); strcpy(deflt, "0"); for (i = 0; i < size; i++) { map[i] = deflt; } /* Process the input list */ do { /* Set str to the next non-blank character, * reading a new line if necessary */ while (isspace((unsigned char)*str)) str++; if (*str == '\\') { str = alloca((size_t)MAKE_FUNC_MAXLINE + 1); if (!fgets(str, MAKE_FUNC_MAXLINE, fpr)) break; current_line++; if (del) { output_del = "\n"; } } /* Find and mark the elements of the next pair */ del = strchr(str, ':'); if (!del) break; *del = '\0'; val = del+1; del = strchr(val, ','); if (!del) { del = strchr(val, '\n'); if (del) { *del = '\0'; del = NULL; } } else { *del = '\0'; } /* Evaluate the current pair */ if ( !strcmp(str, "default") ) { strncpy(deflt, val, sizeof(deflt)); deflt[sizeof deflt - 1] = '\0'; } else { i = (*name_to_index)(str); if (i < 0) { fprintf(stderr, "Can't translate '%s' into an index.\n", str); exit(-1); } map[i] = val; } str = del+1; } while (del); /* Write the generated map */ fprintf(fpw, "{"); fputs(output_del, fpw); for (i = 0; i < size; i++) { fprintf(fpw, "%s,", map[i]); fputs(output_del, fpw); } fprintf(fpw, "};\n"); } /* handle_map() */ /*-------------------------------------------------------------------------*/ static int exgetc(void) /* Get the first character of the next element of a condition * and return it, leaving the input pointing to the rest of it. * Comments are skipped, identifiers not defined as macros are * replaced with ' 0 ', the predicate 'defined()' is * replaced with ' 0 ' or ' 1 ' depending on the result. */ { char c; c = mygetc(); while (isalpha((unsigned char)c) || c == '_' ) { char word[512], *p; int space_left; p = word; space_left = sizeof(word); do { *p++ = c; c = mygetc(); } while (isalunum(c) && --space_left); if (!space_left) fatal("Too long word.\n"); myungetc(c); *p = '\0'; if (strcmp(word, "defined") == 0) { /* handle the defined "function" in #if */ do c = mygetc(); while(lexwhite(c)); if (c != '(') { yyerror("Missing ( in defined"); continue; } do c = mygetc(); while(lexwhite(c)); p = word; space_left = sizeof(word); while ( isalunum(c) && --space_left) { *p++ = c; c = mygetc(); } *p = '\0'; while(lexwhite(c)) c = mygetc(); if (c != ')') { yyerror("Missing ) in defined"); continue; } if (lookup_define(word)) add_input(" 1 "); else add_input(" 0 "); } else { int res; res = expand_define(word); if (res < 0) { yyerror("Unimplemented macro expansion"); return 0; } if (!res) add_input(" 0 "); } c = mygetc(); } return c; } /*-------------------------------------------------------------------------*/ static int cond_get_exp (int priority) /* Evaluate the expression in the input buffer at a priority of at least * and return the result. * * The function assumes to be called at the proper beginning of * an expression, i.e. if it encounters an operator even before a value, * it must be unary. */ { int c; int value,value2,x; do c = exgetc(); while ( lexwhite(c) ); /* Evaluate the first value */ if (c == '(') { /* It's a parenthesized subexpression */ value = cond_get_exp(0); do c = exgetc(); while ( lexwhite(c) ); if (c != ')' ) { yyerror("bracket not paired in #if"); if (!c) myungetc('\0'); } } else if ( ispunct(c) ) { /* It is an unary operator */ x = optab1(c); if (!x) { yyerror("illegal character in #if"); return 0; } /* Get the value for this unary operator */ value = cond_get_exp(12); /* Evaluate the operator */ switch ( optab2[x-1] ) { case BNOT : value = ~value; break; case LNOT : value = !value; break; case UMINUS: value = -value; break; case UPLUS : value = value; break; default : yyerror("illegal unary operator in #if"); return 0; } } else { /* It must be a number */ int base; if ( !lexdigit(c) ) { if (!c) { yyerror("missing expression in #if"); myungetc('\0'); } else yyerror("illegal character in #if"); return 0; } value = 0; /* Determine the base of the number */ if (c != '0') base=10; else { c = mygetc(); if (c == 'x' || c == 'X' ) { base = 16; c = mygetc(); } else base = 8; } /* Now parse the number */ for(;;) { if ( isdigit(c) ) x = -'0'; else if ( isupper(c) ) x = -'A'+10; else if ( islower(c) ) x = -'a'+10; else break; x += c; if (x > base) break; value = value * base + x; c = mygetc(); } myungetc((char)c); } /* Now evaluate the following pairs (if any) */ for (;;) { do c = exgetc(); while ( lexwhite(c) ); /* An operator must come next */ if ( !ispunct(c) ) break; /* Can it be an operator at all? */ x = optab1(c); if (!x) break; /* See if the optab[] defines an operator for these characters */ value2 = mygetc(); for (;; x += 3) { if (!optab2[x]) { myungetc((char)value2); if ( !optab2[x+1] ) { yyerror("illegal operator use in #if"); return 0; } break; } if (value2 == optab2[x]) break; } /* If the priority of the operator is too low, we are done * with this (sub)expression. */ if (priority >= optab2[x+2]) { if (optab2[x]) myungetc((char)value2); break; } /* Get the second operand and perform the operation */ value2 = cond_get_exp(optab2[x+2]); switch ( optab2[x+1] ) { case MULT : value *= value2; break; case DIV : value /= value2; break; case MOD : value %= value2; break; case BPLUS : value += value2; break; case BMINUS : value -= value2; break; case LSHIFT : value <<= value2; break; case RSHIFT : value >>= value2; break; case LESS : value = value < value2; break; case LEQ : value = value <= value2; break; case GREAT : value = value > value2; break; case GEQ : value = value >= value2; break; case EQ : value = value == value2; break; case NEQ : value = value != value2; break; case BAND : value &= value2; break; case XOR : value ^= value2; break; case BOR : value |= value2; break; case LAND : value = value && value2; break; case LOR : value = value || value2; break; case QMARK : do c=exgetc(); while( lexwhite(c) ); if ( c!=':' ) { yyerror("'?' without ':' in #if"); myungetc((char)c); return 0; } if ( value ) { cond_get_exp(1); value = value2; } else value = cond_get_exp(1); break; } /* switch() */ } /* for() */ myungetc((char)c); return value; } /* cond_get_expr() */ /*-------------------------------------------------------------------------*/ static Bool make_func_isescaped (char c) /* Return true if is one of the escapable characters (e.g. \r or \"), * false if not. */ { switch(c) { case '\007': case '\b' : case '\t' : case '\n' : case '\013': case '\014': case '\r' : return MY_TRUE; } if (c == '\\' || c == '\"') return MY_TRUE; return MY_FALSE; } /*-------------------------------------------------------------------------*/ static Bool make_func_issavedel (char c) /* Return true if is a delimiter in save/restore object (e.g. ',' or ';'), * false if not. */ { switch(c) { case ',' : /* in Arrays */ case ';' : /* in Mappings */ case ':' : /* in Mappings */ case '|' : /* in Closures */ case '\n': return MY_TRUE; } return MY_FALSE; } /*-------------------------------------------------------------------------*/ static int ident (char c) /* Parse an identifier (first character is ) from fpr and classify it. * * The typenames in types[] return the associated type code, the * string "default" returns DEFAULT (both only when currently parsing * for EFUN class identifiers in FUNC_SPEC). * * When parsing CODEs in FUNC_SPEC, the strings "unary", "binary" and * "ternary" return UN_OP, BIN_OP and TRI_OP respectively. * * Other identifiers are stored in yylval.string and return ID. */ { char buff[100]; size_t len, i; for (len = 0; isalunum(c); c = (char)getc(fpr)) { if (len == sizeof buff - 1) { yyerror("Too long indentifier"); do c = (char)getc(fpr); while (isalunum(c)); break; } buff[len++] = c; } (void)ungetc(c, fpr); buff[len] = '\0'; if ( parsetype == PARSE_FUNC_SPEC && C_IS_EFUN(current_code_class) ) { for (i=0; i < NELEMS(types); i++) { if (strcmp(buff, types[i].name) == 0) { yylval.number = types[i].num; return types[i].num; } } if (strcmp(buff, "default") == 0) return DEFAULT; } if ( parsetype == PARSE_FUNC_SPEC && C_IS_CODE(current_code_class) ) { if (strcmp(buff, "unary") == 0) return UN_OP; if (strcmp(buff, "binary") == 0) return BIN_OP; if (strcmp(buff, "ternary") == 0) return TRI_OP; } yylval.string = mystrdup(buff); return ID; } /* ident() */ #if 0 /*-------------------------------------------------------------------------*/ static const char * type_str (int n) /* Create a string representation of type in a static buffer * and return it. Unknown types return 'What?'. */ { int type = n & 0xffff; size_t i; for (i = 0; i < NELEMS(types); i++) { if (types[i].num == type) { if (n & MF_TYPE_MOD_REFERENCE) { static char buff[100]; const char *str; str = type_str(n & ~MF_TYPE_MOD_REFERENCE); if (strlen(str) + 3 > sizeof buff) fatal("Local buffer too small in type_str()!\n"); sprintf(buff, "%s &", str); return buff; } if (n & MF_TYPE_MOD_POINTER) { static char buff[100]; if (strlen(types[i].name) + 3 > sizeof buff) fatal("Local buffer too small in type_str()!\n"); sprintf(buff, "%s *", types[i].name); return buff; } return types[i].name; } } return "What?"; } /* type_str() */ #endif /*-------------------------------------------------------------------------*/ static void skip_comment (void) /* Skip a block comment on the input stream fpr. It is assumed that * the begin marker was already read. */ { int c; for(;;) { while((c = getc(fpr)) != '*') { if (c == EOF) { yyerror("End of file in a comment"); return; } if (c == '\n') { current_line++; } } do { if ((c = getc(fpr)) == '/') return; if (c == '\n') { current_line++; } } while(c == '*'); } } /* skip_comment() */ /*-------------------------------------------------------------------------*/ static int yylex1 (void) /* Parse the next lexical element from the input file and return * its token value and -1 on end of file. */ { register int c; for(;;) { size_t match_tmp; #define MATCH(str) (isspace((unsigned char)line_buffer[match_tmp=strlen(str)]) &&\ strncmp(str, line_buffer, match_tmp) == 0) char line_buffer[MAKE_FUNC_MAXLINE+1]; char defbuf[MAKE_FUNC_MAXLINE + 1]; outp = defbuf + sizeof(defbuf) - 1; line_buffer[MAKE_FUNC_MAXLINE] = '\0'; switch(c = getc(fpr)) { case ' ': case '\t': case '\r': continue; case '#': { int line; char file[MAXPATHLEN+1]; (void) fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr); if ( sscanf(line_buffer, "%d \"%s\"",&line,file ) == 2 ) { current_line = line+1; continue; } current_line++; if MATCH("if") { handle_if('#', line_buffer+3); } else if MATCH("ifdef") { handle_cond('#', lookup_define(nextword(line_buffer)) != 0); } else if MATCH("ifndef") { handle_cond('#', lookup_define(nextword(line_buffer)) == 0); continue; } else if MATCH("else") { handle_else('#'); } else if MATCH("endif") { handle_endif(); } else { yyerror("unrecognised '#' directive\n"); } continue; } case '%': { static int send_end = 0; if (send_end) { send_end = 0; ungetc('%', fpr); return END; } send_end = 1; fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr); current_line++; if (parsetype == PARSE_FUNC_SPEC) { if (MATCH("codes")) { current_code_class=C_CODE; return CODES; } if (MATCH("efuns")) { current_code_class=C_EFUN; return EFUNS; } if (MATCH("tefuns")) { current_code_class=C_SEFUN; return TEFUNS; } } return '%'; #undef MATCH } case '\"': { char buff[100]; int len; for (len=0; c = getc(fpr),c != '\"';) { if (len == sizeof buff - 1) { yyerror("Too long name"); do c = getc(fpr); while (c != '\"' && c != '\n' && c != EOF); (void)ungetc(c, fpr); break; } if (c == '\n' || c == EOF) { yyerror("unterminated name"); (void)ungetc(c, fpr); break; } buff[len++] = (char)c; } buff[len] = '\0'; yylval.string = mystrdup(buff); return NAME; } case '\n': current_line++; continue; case EOF: return -1; case '/': if ( (c=getc(fpr)) == '*') { skip_comment(); continue; } else { (void)ungetc(c, fpr); return '/'; } default: if (isalpha(c)) return ident((char)c); return c; } } /* for() */ } /* yylex1() */ /*-------------------------------------------------------------------------*/ static int yylex (void) /* Parse the next lexical element from the input file and return * its token value and -1 on end of file. * This just calls yylex1(). */ { int res; if (!parsetype_sent) { parsetype_sent = MY_TRUE; return parsetype; } res = yylex1(); #if 0 fprintf(stderr,"yylex returns %d '%c'\n",res,res); #endif return res; } /*-------------------------------------------------------------------------*/ static void yyerror (const char *str) /* Print the error message with information about the current * parsing position and exit. */ { fprintf(stderr, "%s:%d: %s\n", current_file, current_line, str); exit(1); } /*=========================================================================*/ /*-------------------------------------------------------------------------*/ static const char * etype (long n) /* Express type (in bitfield encoding) in the runtime type symbols * of interpret.h. * Return a pointer to a constant string. */ { static char str[200]; if (n & LPC_T_ANY) return "TF_ANYTYPE"; if (!n) return "0"; str[0] = '\0'; # define CONVERT(type, string) \ if (n & type) \ { \ if (str[0] != '\0') \ strcat(str, "|"); \ strcat(str, string); \ n ^= type; \ } CONVERT(LPC_T_LVALUE, "TF_LVALUE"); CONVERT(LPC_T_POINTER, "TF_POINTER"); CONVERT(LPC_T_NUMBER, "TF_NUMBER"); CONVERT(LPC_T_STRING, "TF_STRING"); CONVERT(LPC_T_OBJECT, "TF_OBJECT"); CONVERT(LPC_T_MAPPING, "TF_MAPPING"); CONVERT(LPC_T_FLOAT, "TF_FLOAT"); CONVERT(LPC_T_CLOSURE, "TF_CLOSURE"); CONVERT(LPC_T_SYMBOL, "TF_SYMBOL"); CONVERT(LPC_T_QUOTED_ARRAY, "TF_QUOTED_ARRAY"); CONVERT(LPC_T_NULL, "TF_NULL"); #ifdef USE_STRUCTS CONVERT(LPC_T_STRUCT, "TF_STRUCT"); #endif /* USE_STRUCTS */ # undef CONVERT if (n != 0) { printf("Error: Can't convert bit-flags %lx to LPC runtime type.\n" , (long)n); yyerror("Illegal type for argument"); return "What ?"; } return str; } /* etype() */ /*-------------------------------------------------------------------------*/ static long type2flag (int n) /* Convert type into the bitfield value needed to create the LPC * argument types. */ { if (n & MF_TYPE_MOD_REFERENCE) return LPC_T_LVALUE; if (n & MF_TYPE_MOD_POINTER) return LPC_T_POINTER; n &= ~(MF_TYPE_MOD_REFERENCE|MF_TYPE_MOD_POINTER); switch(n) { case VOID: return 0; break; case STRING: return LPC_T_STRING; break; case INT: return LPC_T_NUMBER; break; case OBJECT: return LPC_T_OBJECT; break; case MAPPING: return LPC_T_MAPPING; break; case FLOAT: return LPC_T_FLOAT; break; case CLOSURE: return LPC_T_CLOSURE; break; case SYMBOL: return LPC_T_SYMBOL; break; case MIXED: return LPC_T_ANY; break; case NUL: return LPC_T_NULL; break; case UNKNOWN: return LPC_T_ANY; break; case QUOTED_ARRAY: return LPC_T_QUOTED_ARRAY; break; #ifdef USE_STRUCTS case STRUCT: return LPC_T_STRUCT; break; #endif default: yyerror("(type2flag) Bad type!"); return 0; } } /* type2flag() */ /*-------------------------------------------------------------------------*/ static const char * ctype (int n) /* Express type in the compiler type symbols of exec.h. * Return a pointer to a constant string. */ { static char buff[100]; /* 100 is such a comfortable size :-) */ const char *p = NULL; buff[0] = '\0'; if (n & MF_TYPE_MOD_REFERENCE) strcat(buff, "TYPE_MOD_REFERENCE|"); if (n & MF_TYPE_MOD_POINTER) strcat(buff, "TYPE_MOD_POINTER|"); n &= ~(MF_TYPE_MOD_REFERENCE|MF_TYPE_MOD_POINTER); switch(n) { case VOID: p = "TYPE_VOID"; break; case STRING: p = "TYPE_STRING"; break; case INT: p = "TYPE_NUMBER"; break; case OBJECT: p = "TYPE_OBJECT"; break; case MAPPING: p = "TYPE_MAPPING"; break; case FLOAT: p = "TYPE_FLOAT"; break; case CLOSURE: p = "TYPE_CLOSURE"; break; case SYMBOL: p = "TYPE_SYMBOL"; break; case MIXED: p = "TYPE_ANY"; break; case UNKNOWN: p = "TYPE_UNKNOWN"; break; case NUL: p = "0"; break; case QUOTED_ARRAY: p = "TYPE_QUOTED_ARRAY"; break; #ifdef USE_STRUCTS case STRUCT: p = "TYPE_STRUCT"; break; #endif default: yyerror("(ctype) Bad type!"); return 0; } strcat(buff, p); if (strlen(buff) + 1 > sizeof buff) fatal("Local buffer overwritten in ctype()"); return buff; } /* ctype() */ /*-------------------------------------------------------------------------*/ static int efuncmp (int i, int j) /* Compare the function entries and . * Return 1 if is greater than , 0 if equal, if lesser. * * This predicate is used to sort the function/code tables read * from FUNC_SPEC. */ { int result; result = instr[i].code_class - instr[j].code_class; if ( result ) return result; if (C_IS_CODE(instr[i].code_class)) return 0; return strcmp(instr[i].key, instr[j].key); } /*-------------------------------------------------------------------------*/ static void read_config_file (char *fname) /* Read the config file to learn about the defines used by the * gamedriver. */ { size_t match_tmp; char line_buffer[MAKE_FUNC_MAXLINE + 1]; char defbuf[MAKE_FUNC_MAXLINE + 1]; /* --- Read in the file to see what defines are used by the gamedriver --- */ outp = defbuf + sizeof(defbuf) - 1; if ((fpr = fopen(fname, "r")) == 0) { perror(fname); fflush(stdout); exit(1); } current_line = 0; current_file = fname; #define MATCH(str) (isspace((unsigned char)line_buffer[1+(match_tmp=strlen(str))]) &&\ strncmp(str, line_buffer+1, match_tmp) == 0) while (fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr)) { char *name; current_line++; if ( *line_buffer != '#' ) continue; if MATCH("if") { handle_if('#', line_buffer+4); continue; } if MATCH("ifdef") { handle_cond('#', lookup_define(nextword(line_buffer)) != 0); continue; } if MATCH("ifndef") { handle_cond('#', lookup_define(nextword(line_buffer)) == 0); continue; } if MATCH("else") { handle_else('#'); continue; } if MATCH("endif") { handle_endif(); continue; } if MATCH("undef") { struct defn *old_def; old_def = lookup_define(nextword(line_buffer)); if (old_def) old_def->name[0] = '\0'; continue; } if ( !MATCH("define") ) { continue; } /* is trusted to be syntactically correct. After all, it was * included by the source of this program. */ { char *cp, *exps; int num_arg; cp = line_buffer+8; while( isspace((unsigned char)*cp)) cp++; name = cp; while(isalunum((unsigned char)*cp)) cp++; num_arg = *cp == '(' ? 0 : -1; if (*cp == '\n') { exps = cp; *cp = '\0'; } else { *cp++ = '\0'; while( isspace((unsigned char)*cp)) cp++; exps = cp; while(*cp != '\n') cp++; *cp = '\0'; } add_define(name, num_arg, exps); } } fclose(fpr); #undef MATCH } /* read_config_file() */ /*-------------------------------------------------------------------------*/ static void read_config (void) /* Read the file CONFIG to learn about the defines used by the gamedriver. */ { /* --- Read in CONFIG to see what defines are used by the gamedriver --- */ read_config_file(CONFIG); /* Sanity check on some of those USE_ defines: undefine * those which are not supported on the host system. */ { const char * defnames[] = { #ifndef HAS_IPV6 "USE_IPV6", #endif #ifndef HAS_MYSQL "USE_MYSQL", #endif NULL }; int i; for (i = 0; defnames[i] != NULL; i++) { struct defn *old_def; old_def = lookup_define(defnames[i]); if (old_def) { old_def->name[0] = '\0'; } } } } /* read_config() */ /*-------------------------------------------------------------------------*/ static void read_machine (void) /* Read the file FILE_MACHINE to learn about the defines used by the machine. */ { /* Some predefined macros */ #ifdef AMIGA add_define("AMIGA",-1,""); #endif #ifdef __BEOS__ add_define("__BEOS__", -1, ""); #endif #ifdef DEBUG add_define("DEBUG", -1, ""); #endif #ifdef HAVE_GETRUSAGE add_define("HAVE_GETRUSAGE",-1,""); #endif #ifdef TRACE_CODE add_define("TRACE_CODE",-1,""); #endif /* --- Read in FILE_MACHINE to see what defines are used by the gamedriver --- */ read_config_file(FILE_MACHINE); } /* read_machine() */ /*-------------------------------------------------------------------------*/ static void read_func_spec (void) /* Read the file FUNC_SPEC and create the instruction tables. */ { int i, j; for (i = 0; i < C_TOTAL; i++) num_instr[i] = 0; if ((fpr = fopen(FUNC_SPEC, "r")) == NULL) { perror(FUNC_SPEC); exit(1); } got_error = 0; current_line = 1; current_file = FUNC_SPEC; parsetype = PARSE_FUNC_SPEC; parsetype_sent = MY_FALSE; num_buff = 0; min_arg = -1; unlimit_max = MY_FALSE; curr_arg_type_size = 0; curr_lpc_type_size = 0; curr_lpc_types[0] = 0; yyparse(); fclose(fpr); /* Print code usage statistics */ fprintf(stderr, "Primary codes: %3d\n" "Primary efuns: %3d\n" "Tabled efuns: %3d (%d + %d + %d + %d + %d)\n" , num_instr[C_CODE] , num_instr[C_EFUN] , num_instr[C_EFUN0]+num_instr[C_EFUN1]+num_instr[C_EFUN2] +num_instr[C_EFUN3]+num_instr[C_EFUN4] , num_instr[C_EFUN0], num_instr[C_EFUN1], num_instr[C_EFUN2] , num_instr[C_EFUN3], num_instr[C_EFUN4] ); fprintf(stderr, "Tabled vararg efuns: %3d\n" , num_instr[C_EFUNV] ); /* Combining the two print statements into one triggers bugs on * AIX 5.2: * Visual Age C 6.0 compiles the for() assignment 'i = C_CODE+1' * such that i is assigned a large number (12298). * gcc 2.9 generates code for the same assignment which causes * the mkfunc program to segfault. * The bugs could not be reproduced with a simple test program. */ /* Compute the code offsets. * This means that instr_offset[C_ALIAS] is also the * the number of real instructions. */ instr_offset[C_CODE] = 0; for (i = C_CODE+1; i < C_TOTAL; i++) { instr_offset[i] = instr_offset[i-1] + num_instr[i-1]; } /* Check if the code ranges has been exhausted */ if ( (num_instr[C_CODE] + num_instr[C_EFUN]) > 255 ) fatal("Codes exhausted!"); for (i = C_EFUN+1; i < C_ALIAS; i++) if (num_instr[i] > 255) fatal("Codes exhausted!\n"); if (num_instr[C_SEFUN]) { printf("Sefuns: %d\n", num_instr[C_SEFUN]); fatal("Unclassified sefuns encountered.\n"); } /* Now sort the table of functions and codes (eeek, bubblesort!) * This sort makes sure that functions of the same class * are consecutive within the instr[] array, and therefore * really are at the places where instr_offset[] says they are. * It therefore estables that the index in instr[] is the * instruction code. */ for (i = num_buff; --i >= 0; ) { for (j = 0; j < i; j++) if (efuncmp(i,j) < 0) { struct instrdata_s tmp; tmp = instr[i]; instr[i] = instr[j]; instr[j] = tmp; } } } /* read_func_spec() */ /*-------------------------------------------------------------------------*/ static void read_string_spec (void) /* Read the file STRING_SPEC. */ { if ((fpr = fopen(STRING_SPEC, "r")) == NULL) { perror(STRING_SPEC); exit(1); } got_error = 0; current_line = 1; current_file = STRING_SPEC; parsetype = PARSE_STRING_SPEC; parsetype_sent = MY_FALSE; num_buff = 0; yyparse(); fclose(fpr); } /* read_string_spec() */ /*-------------------------------------------------------------------------*/ static void create_efun_defs (void) /* Create the file EFUN_DEFS */ { int i, j, k; unsigned char c; char * pattern; if ((fpw = fopen(EFUN_DEFS, "w")) == NULL) { perror(EFUN_DEFS); exit(1); } fprintf(fpw, "#ifndef EFUN_DEFS_C__\n" "#define EFUN_DEFS_C__ 1\n" "\n" "/* DO NOT EDIT!\n" " *\n" " * This file is created automatically by make_func from\n" " * the specifications in " FUNC_SPEC ".\n" " * It is meant to be included in lex.c\n" " */\n" "\n" "#include \"exec.h\" /* struct instr_s == instr_t */\n" "\n" ); fprintf(fpw, "/*----------------------------------------------------------------------*/\n" "\n" "/* The table of all instructions\n" " */\n" "\n" "instr_t instrs[] = {\n" ); for (k = C_CODE, i = 0; k <= C_ALIAS; k++) { switch(k) { case C_CODE: fprintf(fpw, "\n /* --- codes --- */\n\n"); break; case C_EFUN: fprintf(fpw, "\n /* --- efuns --- */\n\n"); break; case C_EFUN0: fprintf(fpw, "\n /* --- 0-arg efuns --- */\n\n"); break; case C_EFUN1: fprintf(fpw, "\n /* --- 1-arg efuns --- */\n\n"); break; case C_EFUN2: fprintf(fpw, "\n /* --- 2-arg efuns --- */\n\n"); break; case C_EFUN3: fprintf(fpw, "\n /* --- 3-arg efuns --- */\n\n"); break; case C_EFUN4: fprintf(fpw, "\n /* --- 4-arg efuns --- */\n\n"); break; case C_EFUNV: fprintf(fpw, "\n /* --- vararg efuns --- */\n\n"); break; case C_ALIAS: fprintf(fpw, "\n /* --- aliased efuns --- */\n\n"); break; } j = instr_offset[k] + num_instr[k]; for (; i < j; i++) { fprintf(fpw, " /* %3d */ %s", i, instr[i].buf); } } fprintf(fpw, "};\n\n\n"); fprintf(fpw, "/* Aliased efuns.\n" " * Index it with -F_LAST_INSTRUCTION-1 to retrieve\n" " * the real instruction code.\n" " */\n\n"); fprintf(fpw, "short efun_aliases[] = {\n"); { Bool gotone = MY_FALSE; for (i = 0; i < num_buff; i++) { if (instr[i].code_class == C_ALIAS) { fprintf(fpw, " %s,\n", instr[i].f_name); gotone = MY_TRUE; } } if (!gotone) fprintf(fpw, " 0 /* dummy to keep the compilers happy */\n"); } fprintf(fpw, "};\n\n\n"); fprintf(fpw, "/* Table of function argument signatures (compiler).\n" " * The internal structure is that of arg_types[] in make_func.\n" " */\n\n" ); fprintf(fpw, "fulltype_t efun_arg_types[] = {\n /* 0 */ "); for (i = 0; i < last_current_type; i++) { if (arg_types[i] == 0) { #ifdef USE_STRUCTS fprintf(fpw, "{ 0, NULL },\n"); #else fprintf(fpw, "{ 0 },\n"); #endif /* USE_STRUCTS */ if (i < last_current_type - 1) fprintf(fpw, " /* %3d */ ", i+1); } else #ifdef USE_STRUCTS fprintf(fpw, "{ %s, NULL }, ", ctype(arg_types[i])); #else fprintf(fpw, "{ %s }, ", ctype(arg_types[i])); #endif /* USE_STRUCTS */ } fprintf(fpw, "};\n\n\n"); fprintf(fpw, "/* Table of function argument signatures (runtime).\n" " * The internal structure is that of lpc_types[] in make_func.\n" " */\n\n" ); fprintf(fpw, "long efun_lpc_types[] = {\n "); for (i = 0; i < last_current_lpc_type; i++) { fprintf(fpw, " /* %3d */ %s,\n", i, etype(lpc_types[i])); } fprintf(fpw, "};\n\n\n"); pattern = "Variable 'pattern' not initialized."; for (k = C_EFUN0; k < C_ALIAS; k++) { switch(k) { case C_EFUN0: fprintf(fpw, "/* Prototypes of the tabled efuns\n */\n\n"); pattern = "extern svalue_t *f_%s(svalue_t *);\n"; break; case C_EFUNV: fprintf(fpw, "/* Prototypes of the tabled vararg efuns\n */\n\n"); pattern = "extern svalue_t *v_%s(svalue_t *, int);\n"; break; } i = instr_offset[k]; j = i + num_instr[k]; for (; i < j; i++) { fprintf(fpw, pattern, instr[i].key); } if (num_instr[k]) fprintf(fpw, "\n\n"); } { char * prefix = NULL; for (k = C_EFUN0; k < C_ALIAS; k++) { switch(k) { case C_EFUN0: fprintf(fpw, "/* The table of tabled efuns\n */\n\n"); fprintf(fpw, "svalue_t *(*efun_table[]) (svalue_t *) = {\n"); prefix = "f_"; break; case C_EFUNV: fprintf(fpw, "/* The table of tabled vararg efuns\n */\n\n"); fprintf(fpw, "svalue_t *(*vefun_table[]) (svalue_t *, int) = {\n"); prefix = "v_"; break; } i = instr_offset[k]; j = i + num_instr[k]; for(; i < j; i++) { fprintf(fpw, " /* %3d */ %s%s,\n", i, prefix, instr[i].key); } if (k == C_EFUNV-1 || k == C_ALIAS-1) fprintf(fpw, "};\n\n\n"); } } /* TODO: create a my-ctype.[ch] and a utility program to create * TODO:: these two files once and for all. */ fprintf(fpw, "/*----------------------------------------------------------------------*/\n" "\n" "/* Our own ctype implementation. This way we can be sure that\n" " * (a) we won't choke on non-ASCII characters\n" " * (b) we are fast\n" " * (c) we get the non-standard classifications we need anyway\n" " */\n" "\n" ); fprintf(fpw, "#define lexwhite(c) (_my_ctype[(unsigned char)(c)]&%d)\n",_MCTs); fprintf(fpw, "#define leXdigit(c) (_my_ctype[(unsigned char)(c)]&%d)\n",_MCTx); fprintf(fpw, "\n" "unsigned char _my_ctype[] = {"); c = '\0'; do { if (!(c & 0xf)) fprintf(fpw, "\n "); fprintf(fpw, "%d," , ( (isascii(c) && make_func_isescaped(c)) ? _MCTe : 0 ) | ( (isascii(c) && make_func_issavedel(c)) ? _MCTt : 0 ) | ( (isascii(c) && isdigit ((unsigned char)c)) ? _MCTd : 0 ) | ( (isascii(c) && isspace ((unsigned char)c) && c != '\n') ? _MCTs : 0 ) | ( (isascii(c) && isxdigit((unsigned char)c)) ? _MCTx : 0 ) | ( ((isascii(c) && (isalnum ((unsigned char)c) || c == '_')) || (((unsigned char)c) >= 0xC0)) ? _MCTa : 0 ) ); c++; } while (c != '\0'); fprintf(fpw, "\n};\n"); fprintf(fpw, "\n" "/************************************************************************/\n" "\n" "#endif /* EFUN_DEFS_C__ */\n" ); fclose(fpw); } /* create_efun_defs() */ /*-------------------------------------------------------------------------*/ static void create_instrs (void) /* Create the file THE_INSTRS */ { int i, j, k; int last_instr; if ((fpw = fopen(THE_INSTRS, "w")) == NULL) { perror(THE_INSTRS); exit(1); } fprintf(fpw, "#ifndef INSTRS_H__\n" "#define INSTRS_H__ 1\n" "\n" "/* DO NOT EDIT!\n" " *\n" " * This file is created automatically by make_func from\n" " * the specifications in " FUNC_SPEC ".\n" " *\n" " * It holds the bytecode values for all machine instructions, plus\n" " * declarations of the tables holding information about the instructions.\n" " */\n" "\n" "#include \"exec.h\" /* struct instr_s == instr_t */\n" "\n" ); last_instr = instr_offset[C_ALIAS] - 1; for (k = C_CODE; k < C_ALIAS; k++) { char * str; switch (k) { case C_CODE: str = "internal"; break; case C_EFUN: str = "efun"; break; case C_EFUN0: str = "efun0"; break; case C_EFUN1: str = "efun1"; break; case C_EFUN2: str = "efun2"; break; case C_EFUN3: str = "efun3"; break; case C_EFUN4: str = "efun4"; break; case C_EFUNV: str = "efunv"; break; default: { char buf[100]; sprintf(buf, "create_instrs(): Invalid value %ld for loop index.\n", (long)k); fatal(buf); str = NULL; /* To keep the compiler happy */ break; } } fprintf(fpw, "#define %s_OFFSET (%d)\n" "#define %s_COUNT (%d)\n" " /* First offset and number of %s opcodes.\n" " */\n\n" , classtag[k], instr_offset[k] , classtag[k], num_instr[k] , str ); } fprintf(fpw, "#define LAST_INSTRUCTION_CODE (%d)\n" " /* The highest token value in use.\n" " */\n\n" , last_instr ); fprintf(fpw, "#define TEFUN_OFFSET EFUN0_OFFSET\n" " /* Offset of the first tabled efun.\n" " */\n\n" ); fprintf(fpw, "extern instr_t instrs[];\n" "extern short efun_aliases[];\n" "extern fulltype_t efun_arg_types[];\n" "extern long efun_lpc_types[];\n" "extern svalue_t *(*efun_table[])(svalue_t *);\n" "extern svalue_t *(*vefun_table[])(svalue_t *, int);\n" ); fprintf(fpw, " /* All tables are defined in " EFUN_DEFS " and compiled into lex.c\n" " * TODO: We might as well create efun_defs.h and compile it separately.\n" " */\n" ); for (k = C_CODE; k < C_ALIAS; k++) { if (!num_instr[k]) continue; switch(k) { case C_CODE: fprintf(fpw,"\n/* --- codes --- */\n\n"); break; case C_EFUN: fprintf(fpw,"\n/* --- efuns --- */\n\n"); break; case C_EFUN0: fprintf(fpw,"\n/* --- efun0s --- */\n\n"); break; case C_EFUN1: fprintf(fpw,"\n/* --- efun1s --- */\n\n"); break; case C_EFUN2: fprintf(fpw,"\n/* --- efun2s --- */\n\n"); break; case C_EFUN3: fprintf(fpw,"\n/* --- efun3s --- */\n\n"); break; case C_EFUN4: fprintf(fpw,"\n/* --- efun4s --- */\n\n"); break; case C_EFUNV: fprintf(fpw,"\n/* --- vefuns --- */\n\n"); break; } i = instr_offset[k]; j = i + num_instr[k]; for ( ; i < j; i++) { fprintf(fpw, "#define %-30s (%d)\n" , make_f_name(instr[i].key), i); } } fprintf(fpw, "\n" "/************************************************************************/\n" "\n" "#endif /* INSTRS_H__ */\n" ); fclose(fpw); } /* create_instrs() */ /*-------------------------------------------------------------------------*/ static void create_lang (void) /* Create the file THE_LANG from PRO_LANG, */ { size_t match_tmp; char line_buffer[MAKE_FUNC_MAXLINE + 1]; Bool bPrintedNotice; #define MATCH(str) (isspace((unsigned char)line_buffer[1+(match_tmp=strlen(str))]) &&\ strncmp(str, line_buffer+1, match_tmp) == 0) if ((fpr = fopen(PRO_LANG, "r")) == NULL) { perror(PRO_LANG); exit(1); } if ((fpw = fopen(THE_LANG, "w")) == NULL) { perror(THE_LANG); exit(1); } current_line = 0; bPrintedNotice = MY_FALSE; while (fgets(line_buffer, MAKE_FUNC_MAXLINE, fpr)) { current_line++; if (*line_buffer == '%') { last_line = current_line; if MATCH("if") { handle_if('%', line_buffer+4); continue; } if MATCH("ifdef") { handle_cond('%', lookup_define(nextword(line_buffer)) != 0); continue; } if MATCH("ifndef") { handle_cond('%', lookup_define(nextword(line_buffer)) == 0); continue; } if MATCH("else") { handle_else('%'); compensate_lines(); continue; } if MATCH("endif") { handle_endif(); compensate_lines(); continue; } if MATCH("line") { fprintf(fpw, "#line %d \"%s\"\n", current_line+1, PRO_LANG); continue; } if MATCH("typemap") { handle_map(line_buffer+9, TYPEMAP_SIZE, name_to_type); continue; } if MATCH("hookmap") { handle_map(line_buffer+9, NUM_DRIVER_HOOKS, name_to_hook); continue; } if MATCH("//") { /* c++ - resembling comment */ fputs("\n", fpw); continue; } if (!bPrintedNotice) { if MATCH("{") { bPrintedNotice = MY_TRUE; fputs( "%{\n" "\n" "/* DO NOT EDIT!\n" " *\n" " * This file is created automatically by make_func from\n" " * the template " PRO_LANG " according to the specs in " FUNC_SPEC ".\n" " */\n" "\n" , fpw); fprintf(fpw, "#line %d \"%s\"\n", current_line+1, PRO_LANG); continue; } } } fputs(line_buffer, fpw); } fclose(fpr), fclose(fpw); #undef MATCH } /* create_lang() */ /*-------------------------------------------------------------------------*/ static void create_stdstrings (void) /* Create the files STDSTRINGS.[ch]. */ { int i; /* Create stdstrings.h */ if ((fpw = fopen(STDSTRINGS ".h", "w")) == NULL) { perror(STDSTRINGS ".h"); exit(1); } fputs( "#ifndef STDSTRINGS_H__\n" "#define STDSTRINGS_H__ 1\n" "\n" "/* DO NOT EDIT!\n" " *\n" " * This file is created automatically by make_func from\n" " * the specifications in " STRING_SPEC ".\n" " */\n" "\n" "#include \"driver.h\"\n" "#include \"typedefs.h\"\n" "\n" "/* --- Common used shared strings. --- */\n" "/* The most common strings, including all the predefined applies,\n" " * are kept in shstrings[] for faster usage.\n" " * The indices are SHX_xxx, the defines STR_xxx expand to shstring[SHX_xxx]\n" " */\n" "\n" "enum StandardStrings {\n" , fpw); for (i = 0; i < num_buff; i++) { if (!i) fprintf(fpw, " SHX_%-12s = 0 /* \"%s\" */\n" , instr[i].key, instr[i].buf); else fprintf(fpw, " , SHX_%-16s /* \"%s\" */\n" , instr[i].key, instr[i].buf); } fputs( "\n" " , SHSTR_NOSTRINGS /* The number of strings */\n" "};\n" "\n" "extern string_t *shstring[SHSTR_NOSTRINGS];\n" "\n" , fpw); for (i = 0; i < num_buff; i++) { fprintf(fpw, "#define STR_%-16s shstring[SHX_%s]\n" , instr[i].key, instr[i].key); } fputs( "\n" "/* --- Prototypes --- */\n" "extern void init_standard_strings(void);\n" "\n" "#endif /* STDSTRINGS_H__ */\n" , fpw); fclose(fpw); /* Create stdstrings.c */ if ((fpw = fopen(STDSTRINGS ".c", "w")) == NULL) { perror(STDSTRINGS ".c"); exit(1); } fputs( "/* DO NOT EDIT!\n" " *\n" " * This file is created automatically by make_func from\n" " * the specifications in " STRING_SPEC ".\n" " *\n" " * It's purpose is to define and initialize the standard shared string\n" " * table shstring[].\n" " */\n" "\n" "#include \"driver.h\"\n" "#include \"typedefs.h\"\n" "\n" "#include \"" STDSTRINGS ".h\"\n" "#include \"mstrings.h\"\n" "\n" "/*-------------------------------------------------------------------------*/\n" "\n" "string_t *shstring[SHSTR_NOSTRINGS];\n" " /* Table of common used and therefore shared strings.\n" " */\n" "\n" "/*-------------------------------------------------------------------------*/\n" "void\n" "init_standard_strings (void)\n" "\n" "/* Initialize the common string table.\n" " */\n" "\n" "{\n" "# define INIT(x,s) shstring[x] = mstring_new_tabled(s MTRACE_ARG);\n" "\n" , fpw); for (i = 0; i < num_buff; i++) fprintf(fpw, " INIT(SHX_%s, \"%s\");\n" , instr[i].key, instr[i].buf); fputs( "\n" "# undef INIT\n" "} /* init_standard_strings() */\n" "\n" "/************************************************************************/\n" , fpw); fclose(fpw); } /* create_stdstrings() */ /*-------------------------------------------------------------------------*/ int main (int argc, char ** argv) /* The main program. */ { enum { NONE = 0, MakeInstrs, MakeLang, MakeStrings } action = NONE; /* --- Check what we have to do --- */ if (argc == 2) { if (!strcasecmp(argv[1], "instrs")) action = MakeInstrs; else if (!strcasecmp(argv[1], "lang")) action = MakeLang; else if (!strcasecmp(argv[1], "strings")) action = MakeStrings; } if (action == NONE) { fputs( "Usage: make_func instrs|lang|strings\n" "\n" " make_func instrs\n" " creates " EFUN_DEFS " and " THE_INSTRS ", reads " CONFIG " and " FUNC_SPEC ".\n" "\n" " make_func lang\n" " creates " THE_LANG " from " PRO_LANG ", reads " CONFIG ".\n" "\n" " make_func strings\n" " creates " STDSTRINGS ".[ch], reads " CONFIG " and " STRING_SPEC ".\n" , stderr); return 1; /* TODO: There are constants for this */ } /* --- Read the config files --- */ read_machine(); read_config(); if (action == MakeInstrs) read_func_spec(); if (action == MakeStrings) read_string_spec(); if (got_error) return 1; /* --- Create the output files --- */ if (action == MakeInstrs) { create_efun_defs(); create_instrs(); } if (action == MakeLang) create_lang(); if (action == MakeStrings) create_stdstrings(); /* --- That's it --- */ return 0; } /* main() */ #if defined(__MWERKS__) && !defined(WARN_ALL) # pragma warn_possunwant off # pragma warn_implicitconv off #endif /***************************************************************************/